Skip to content
This repository has been archived by the owner on Nov 1, 2022. It is now read-only.

Close #7164: Add getSubscription to AutoPushFeature if one exists #7172

Merged
merged 2 commits into from
May 29, 2020

Conversation

jonalmeida
Copy link
Contributor

@jonalmeida jonalmeida commented May 29, 2020


There are two parts to this fix:

First, we added the ability to check if a subscription exists first to match the
API requirement for WebPush support in GeckoView. We do this because the
call to getSubscription on the engine side expects it to work as a
check before creating a subscription, where-as our implementation used a
"get or create" pattern instead.

The fallout from this is that visiting sites that check for
subscriptions were immediately given one without user permission.

Second, with the previous patch, we fixed a bug where we need to check if a
subscription exists. This new (correct) behaviour broke our original
implementation of how to handle the null case in the engine.

Integration PR for Reference Browser: mozilla-mobile/reference-browser#1208

Fenix PR will be a fast-follow.

Closes #7164

Pull Request checklist

  • Quality: This PR builds and passes detekt/ktlint checks (A pre-push hook is recommended)
  • Tests: This PR includes thorough tests or an explanation of why it does not
  • Changelog: This PR includes a changelog entry or does not need one
  • Accessibility: The code in this PR follows accessibility best practices or does not include any user facing features

After merge

  • Milestone: Make sure issues closed by this pull request are added to the milestone of the version currently in development.
  • Breaking Changes: If this is a breaking change, please push a draft PR on Reference Browser to address the breaking issues.

@jonalmeida jonalmeida added the 🕵️‍♀️ needs review PRs that need to be reviewed label May 29, 2020
@codecov
Copy link

codecov bot commented May 29, 2020

Codecov Report

Merging #7172 into master will increase coverage by 0.80%.
The diff coverage is 60.00%.

Impacted file tree graph

@@             Coverage Diff              @@
##             master    #7172      +/-   ##
============================================
+ Coverage     77.26%   78.06%   +0.80%     
+ Complexity     5048     4782     -266     
============================================
  Files           675      625      -50     
  Lines         24705    22323    -2382     
  Branches       3641     3329     -312     
============================================
- Hits          19088    17427    -1661     
+ Misses         4111     3492     -619     
+ Partials       1506     1404     -102     
Impacted Files Coverage Δ Complexity Δ
...mozilla/components/feature/push/AutoPushFeature.kt 88.88% <50.00%> (-2.29%) 26.00 <1.00> (+1.00) ⬇️
...java/mozilla/components/feature/push/Connection.kt 79.78% <75.00%> (-0.22%) 0.00 <0.00> (ø)
...onents/support/migration/FennecSessionMigration.kt
...illa/components/service/pocket/PocketJSONParser.kt
...ava/mozilla/components/support/migration/Result.kt
...service/pocket/data/PocketListenArticleMetadata.kt
...lla/components/support/migration/GeckoMigration.kt
.../mozilla/components/support/migration/Exception.kt
...ava/mozilla/components/feature/p2p/view/P2PView.kt
...mponents/feature/contextmenu/ContextMenuFeature.kt
... and 42 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 428b9b4...aa4e35d. Read the comment docs.

Copy link
Contributor

@grigoryk grigoryk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

r+. I think we can iterate a bit on making this API a little more robust, but let's not block on that to land this fix.

fun getSubscription(
scope: String,
appServerKey: String? = null,
block: (AutoPushSubscription?) -> Unit
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider this for a follow-up, if you want:

This API hides the fact that block may be called on a thread that's different from the caller's thread. What about just returning a Deferred<AutoPushSubscription?>, instead? The caller will need to manage the coroutines but at least it'll be obvious what's going on and doesn't leave a lot of room for error.

I made this mistake with the account manager callbacks, and the silent thread switching keeps biting us since it's just so easy to forget about when you're using the APIs.

I see that it's a broader pattern here, so I'll let you decide if this is something to be concerned about in practice. At the very least, I suggest documenting this within @param block docstring.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a fair point that we should invoke the callback on the caller's thread. I'll have to spend some time to figure out how this would affect our integrations.

When we last tried exposing the async functions, it was a bit more complicated to complete the GeckoResult async call with the kotlin async call. We opted to follow the WebExtension learnings since it had a similar one-to-one mapping of APIs.

Now that we have these nested calls where we need to make async checks before another async call, we should re-visit that.

For that reason, I stuck with the current system and hid the containsSubscription call internally.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We haven't (thankfully) run into issues with being on the wrong thread since consumers always need to be on a background thread to wait for messages, but that doesn't fix the problem with this design that you rightfully spotted.

appServerKey: String? = null,
block: (AutoPushSubscription?) -> Unit
) {
connection.ifInitialized {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the block may never even get called?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it could if the push native layer is not initialized, but we would have much bigger problems here if we can get this far in the call stack having not crashed yet.

This has come up before so maybe we call simplify this call by putting it within the Connection. It was meant to satisfy the push state along and remove the null-ability when needing to use the native API.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Filed #7184 to track this work.

@@ -154,6 +159,14 @@ internal class RustPushConnection(
return pushApi.unsubscribeAll()
}

@GuardedBy("this")
override suspend fun containsSubscription(scope: PushScope): Boolean = synchronized(this) {
val pushApi = api
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need a local variable? you already synchronzied the call to this, so as long as every access to api is also synchronzied it seems unnecessary?

} else {
result.completeExceptionally(WebPushException("Retrieving subscription failed."))
}
result.complete(subscription?.toGeckoSubscription())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Due to connection.ifInitialized this may never get called, I think. That seems like a problem, if we never complete the result.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Answered below.

…one exists

We added the ability to check if a subscription exists first to match the
API requirement for WebPush support in GeckoView. We do this because the
call to `getSubscription` on the engine side expects it to work as a
check before creating a subscription, where-as our implementation used a
"get or create" pattern instead.

The fallout from this is that visiting sites that check for
subscriptions were immediately given one without user permission.
…nally

With the previous patch, we fixed a bug where we need to check if a
subscription exists. This new (correct) behaviour broke our original
implementation of how to handle the null case in the engine.
@jonalmeida
Copy link
Contributor Author

bors r=grigoryk

@bors
Copy link

bors bot commented May 29, 2020

Build succeeded:

@bors bors bot merged commit 811e8ab into mozilla-mobile:master May 29, 2020
@jonalmeida jonalmeida deleted the issue-7164 branch May 29, 2020 20:53
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
🕵️‍♀️ needs review PRs that need to be reviewed
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add support for checking if a push subscription exists
2 participants