New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor Notification::permission to follow the same code path as Permissions::query #2982
base: main
Are you sure you want to change the base?
Conversation
Source/WebKit/WebProcess/WebCoreSupport/WebNotificationClient.cpp
Outdated
Show resolved
Hide resolved
Source/WebKit/WebProcess/WebCoreSupport/WebNotificationClient.cpp
Outdated
Show resolved
Hide resolved
Source/WebKit/WebProcess/WebCoreSupport/WebNotificationClient.cpp
Outdated
Show resolved
Hide resolved
Source/WebKit/WebProcess/WebCoreSupport/WebNotificationClient.cpp
Outdated
Show resolved
Hide resolved
5971c5d
to
3c2f221
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this needs to be be done after adding Worker support for Permissions API, and is better be done after change event is supported (or making web process able to cache the state to avoid sync messages).
Source/WebKit/WebProcess/WebCoreSupport/WebNotificationClient.cpp
Outdated
Show resolved
Hide resolved
Source/WebKit/WebProcess/WebCoreSupport/WebPermissionController.cpp
Outdated
Show resolved
Hide resolved
Source/WebKit/WebProcess/WebCoreSupport/WebNotificationClient.cpp
Outdated
Show resolved
Hide resolved
EWS run on previous version of this PR (hash 3c2f221)
|
3c2f221
to
7e0bfbe
Compare
|
EWS run on previous version of this PR (hash 7e0bfbe)
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test failure looks relevant, please fix them
Source/WebKit/WebProcess/WebCoreSupport/WebPermissionController.cpp
Outdated
Show resolved
Hide resolved
Source/WebKit/WebProcess/WebCoreSupport/WebNotificationClient.cpp
Outdated
Show resolved
Hide resolved
Source/WebKit/WebProcess/WebCoreSupport/WebNotificationClient.cpp
Outdated
Show resolved
Hide resolved
Source/WebKit/WebProcess/WebCoreSupport/WebNotificationClient.cpp
Outdated
Show resolved
Hide resolved
Source/WebKit/WebProcess/WebCoreSupport/WebNotificationClient.cpp
Outdated
Show resolved
Hide resolved
Source/WebKit/WebProcess/WebCoreSupport/WebNotificationClient.cpp
Outdated
Show resolved
Hide resolved
7e0bfbe
to
07ac0dc
Compare
β¦missions::query https://bugs.webkit.org/show_bug.cgi?id=243502 Reviewed by NOBODY (OOPS!). There are two functions for obtaining the permission state of the Notifications API. We need to provide support for both to ensure compatibility with web standards. This patch ensures that they follow the same code path since having two different paths is unnecessary and hurts maintainability. This code path goes to the UIProcess and then to the client to get the permission state. This patch also removes the now uneeded cache in WebNotificationManager that Notification::permission was relying on and enables the WebProcess cache in WebPermissionController to be used. * LayoutTests/http/tests/notifications/service-worker-notification-permission-expected.txt: Added. * LayoutTests/http/tests/notifications/service-worker-notification-permission.html: Added. * LayoutTests/http/tests/notifications/service-worker-notification-permission.js: Added. (self.onmessage): * LayoutTests/http/tests/push-api/subscribe-deny-permissions-on-prompt-expected.txt: * LayoutTests/http/tests/push-api/subscribe-deny-permissions-on-prompt.html: * LayoutTests/platform/mac-wk1/TestExpectations: * Source/WebCore/Headers.cmake: * Source/WebCore/Modules/permissions/PermissionController.h: * Source/WebCore/Modules/permissions/Permissions.cpp: (WebCore::Permissions::sourceFromContext): (WebCore::sourceFromContext): Deleted. * Source/WebCore/Modules/permissions/Permissions.h: * Source/WebCore/WebCore.xcodeproj/project.pbxproj: * Source/WebKit/UIProcess/WebPageProxy.cpp: (WebKit::WebPageProxy::queryPermission): (WebKit::WebPageProxy::clearNotificationPermissionState): * Source/WebKit/UIProcess/WebPermissionControllerProxy.cpp: (WebKit::WebPermissionControllerProxy::querySync): * Source/WebKit/UIProcess/WebPermissionControllerProxy.h: * Source/WebKit/UIProcess/WebPermissionControllerProxy.messages.in: * Source/WebKit/WebProcess/Notifications/NotificationPermissionRequestManager.cpp: (WebKit::NotificationPermissionRequestManager::startRequest): (WebKit::NotificationPermissionRequestManager::permissionLevel): Deleted. * Source/WebKit/WebProcess/Notifications/NotificationPermissionRequestManager.h: * Source/WebKit/WebProcess/Notifications/WebNotificationManager.cpp: (WebKit::WebNotificationManager::initialize): (WebKit::WebNotificationManager::didUpdateNotificationDecision): (WebKit::WebNotificationManager::didRemoveNotificationDecisions): (WebKit::WebNotificationManager::removeAllPermissionsForTesting): (WebKit::WebNotificationManager::policyForOrigin const): Deleted. * Source/WebKit/WebProcess/Notifications/WebNotificationManager.h: * Source/WebKit/WebProcess/WebCoreSupport/WebNotificationClient.cpp: (WebKit::WebNotificationClient::requestPermission): (WebKit::WebNotificationClient::checkPermission): (WebKit::WebNotificationClient::clearNotificationPermissionState): Deleted. * Source/WebKit/WebProcess/WebCoreSupport/WebNotificationClient.h: * Source/WebKit/WebProcess/WebCoreSupport/WebPermissionController.cpp: (WebKit::WebPermissionController::query): (WebKit::WebPermissionController::querySync): (WebKit::WebPermissionController::webPageProxyIdentifier): (WebKit::WebPermissionController::removeEntryFromCache): (WebKit::WebPermissionController::clearCache): (WebKit::WebPermissionController::tryProcessingRequests): * Source/WebKit/WebProcess/WebCoreSupport/WebPermissionController.h: (isType): * Source/WebKit/WebProcess/WebPage/WebPage.cpp: (WebKit::WebPage::clearNotificationPermissionState): Deleted. * Source/WebKit/WebProcess/WebPage/WebPage.h: * Source/WebKit/WebProcess/WebPage/WebPage.messages.in: * Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp: (WTR::TestRunner::removeAllWebNotificationPermissions): * Tools/WebKitTestRunner/TestController.cpp: (WTR::TestController::removeAllWebNotificationPermissions): * Tools/WebKitTestRunner/TestController.h: * Tools/WebKitTestRunner/TestInvocation.cpp: (WTR::TestInvocation::didReceiveSynchronousMessageFromInjectedBundle):
07ac0dc
to
92a17b9
Compare
|
EWS run on previous version of this PR (hash 07ac0dc)
|
|
reject('Test requires internals.'); | ||
return; | ||
} | ||
if (!window.internals) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain in commit message that why this change is needed? It's not clear to me why pushManager would return different results after this patch
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WebNotificationClient::checkPermission
(and thus, Notification::permission
) used to get the permission state from a cache in WebNotificationManager
. This cache did not get updated by calling TestController::denyNotificationPermissionOnPrompt
and then WebNotificationClient::requestPermission
(which is what happens in this test). So when request2 occurred, WebNotificationClient::checkPermission
would return default, then fail the transient activation check and reject the promise with a "user gesture" error.
This patch removes this cache and refactors WebNotificationClient::checkPermission
(and thus, Notification::permission
) to get the permission state from the client (which is TestController
in the case of this test). The cache in TestController
that stores the permissions state does get updated by calling TestController::denyNotificationPermissionOnPrompt
and then WebNotificationClient::requestPermission
. So now, when request2 occurs, WebNotificationClient::checkPermission
returns denied and rejects the promise with a "user denied push permission" error before reaching the transient activation check.
I spoke with @bnham about this and learned that the point of this layout test is to check that given a user gesture, the first call to PushManager::subscribe
would consume the transient activation and that the second call would not have a transient activation. Due to the change described above, this check on the second call was being circumvented. We can ensure that this check occurs by altering the test the way I did in this patch.
You're right, the reason for change is not clear just by looking at the test, so I'll update the commit message with the above explanation once the bots finish and I make any other suggested changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From what's described above, It seems the right thing is to update WebKitTestRunner, making it cache the request result like a real client would do, instead of changing the test, since you don't intend to change the tested behavior (that first subscribe request fails due to user activation and second fails due to permission request).
Our current design for permissions is that each query and request goes to client, which means client is responsible to cache the result if needed.
Specifically, you might call setPermission
on WebNotificationProvider
in TestController::decidePolicyForNotificationPermissionRequest.
(to simulate user denies the prompt and browser remembers the choice).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe WebKitTestRunner already caches the result. TestController::decidePolicyForNotificationPermissionRequest.
already calls setPermission
on WebNotificationProvider
. denyOnPrompt means that if you request/prompt for permission, the state will be set to denied. The test was expecting that after denyOnPrompt is called, then first request does a prompt, the second request will get that the permission state is default? But that doesn't seem to make sense. The permission state should be denied, no? The reason that the test was passing before this patch was because it was relying on the fact that WebKitTestRunner wasn't updating the cache in WebNotificationManager. Now that this cache is gone, and checkPermission actually goes to the client, and testRunner returns the result based on its cache in WebNotificationProvider, the second request correctly gets back that the permission state is denied.
auto& webPermissionController = downcast<WebPermissionController>(PermissionController::shared()); | ||
for (auto const& [originString, allowed] : parameters.notificationPermissions) { | ||
auto topOrigin = SecurityOrigin::createFromString(originString)->data(); | ||
auto permissionState = allowed ? PermissionState::Granted : PermissionState::Denied; | ||
webPermissionController.updateCache(ClientOrigin { topOrigin, SecurityOriginData { } }, PermissionDescriptor { PermissionName::Notifications }, permissionState); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why this change? I thought we only want one implementation for both APIs, i.e. we will only use queryPermission to pull notification's permission.
This makes the cache dependent on WKNotificationManager: e.g. if client implements queryPermission but not WKNotificationManager, the result will be wrong (because cache will not be updated).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This patch contains other code changes that ensure that calling queryPermission
will also update the cache (if the permission state being queried is the state of the Notifications API.
The reason for this change was that in a previous version of this patch, the EWS bots detected a API test failure on GTK. The failure was that Notification::permission
was incorrectly returning "default" instead of "granted". It turns out that the test client did not implement queryPermission
and the test had been succeeding because of the cache in WebNotificationManager
. Since this patch removes that cache and refactors Notification::permission
to use queryPermission
, the result was coming back as default. There were two options to fix this:
- Implement
queryPermission
in the GTK test client - Make the not-working WebProcess cache in
WebPermissionController
work for notifications.
The second option seemed easier and more beneficial since we'll have to do this for the on change event later on anyways. I wasn't sure how to test if this will fix the API test since it was a GTK failure, so I guess we'll have to wait for the bots to check. :(
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This patch contains other code changes that ensure that calling queryPermission will also update the cache (if the permission state being queried is the state of the Notifications API.
Let's say client implements queryPermission but not WKNotificationManager, and user sets allowed notification (e.g. in per-site settings in Safari) for a site.
For the first permission::query, client returns allowed, and WebPermissionController caches the result. Then user decides to change the setting to denied (no longer wants to receive notification from the site), and tells WebKit client. For the second permission query, what would be returned with your patch?
IIUC, before this patch, notification.permission relies on WKNotificationManager and permission.query relies on queryPermission. After this patch, both rely on either WKNotificationManager or queryPermission + WKNotificationManager, which does not seem like a progress to me.
The second option seemed easier and more beneficial since we'll have to do this for the on change event later on anyways.
The reason we implement this before change event is because we think this change would work on its own. If this change relies on cache to work, then I think we should implement change event first, since the cache might not be in this form (an origin-to-permission map). You can imagine having WebPermissionController track PermissionStatus (or PermissionObserver) objects which hold the state, then you will need to revamp the cache.
Source/WebKit/WebProcess/Notifications/NotificationPermissionRequestManager.cpp
Show resolved
Hide resolved
for (auto const& [originString, allowed] : parameters.notificationPermissions) { | ||
auto topOrigin = SecurityOrigin::createFromString(originString)->data(); | ||
auto permissionState = allowed ? PermissionState::Granted : PermissionState::Denied; | ||
webPermissionController.updateCache(ClientOrigin { topOrigin, SecurityOriginData { } }, PermissionDescriptor { PermissionName::Notifications }, permissionState); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment, your ClientOrigin seems wrong.
auto& webPermissionController = downcast<WebPermissionController>(PermissionController::shared()); | ||
auto topOrigin = SecurityOrigin::createFromString(originString)->data(); | ||
auto permissionState = allowed ? PermissionState::Granted : PermissionState::Denied; | ||
webPermissionController.updateCache(ClientOrigin { topOrigin, SecurityOriginData { } }, PermissionDescriptor { PermissionName::Notifications }, permissionState); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, bad ClientOrigin.
for (auto& originString : originStrings) | ||
m_permissionsMap.remove(originString); | ||
webPermissionController.removeEntryFromCache(ClientOrigin { SecurityOrigin::createFromString(originString)->data(), SecurityOriginData { } }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also bad.
void WebNotificationManager::removeAllPermissionsForTesting() | ||
{ | ||
#if ENABLE(NOTIFICATIONS) | ||
m_permissionsMap.clear(); | ||
auto& webPermissionController = downcast<WebPermissionController>(PermissionController::shared()); | ||
webPermissionController.clearCache(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, this seems wrong isn't it? We used to clear only notification permissions, now we clear ALL permissions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, this is a mistake. I'll fix this in the next upload of the patch.
NotificationClient::Permission resultPermission; | ||
callOnMainRunLoopAndWait([&resultPermission, origin = origin->data().toString().isolatedCopy()] { | ||
resultPermission = WebProcess::singleton().supplement<WebNotificationManager>()->policyForOrigin(origin); | ||
auto* page = m_page ? m_page->corePage() : nullptr; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
m_page is a main thread object and is thus not safe to access off the main thread like you're doing here.
if (!permissionState) | ||
return; | ||
|
||
switch (*permissionState) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like we should have a helper function to convert one enum type to the other.
@@ -74,3 +79,7 @@ class WebPermissionController final : public CanMakeWeakPtr<WebPermissionControl | |||
}; | |||
|
|||
} // namespace WebCore | |||
|
|||
SPECIALIZE_TYPE_TRAITS_BEGIN(WebKit::WebPermissionController) | |||
static bool isType(const WebCore::PermissionController&) { return true; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How could this possibly be correct? You're doing zero type validation?
PASS: service worker permissionState was prompt | ||
PASS: document permissionState was prompt | ||
PASS: service worker subscribe was error: NotAllowedError | ||
PASS: document subscribe without user gesture was error: NotAllowedError | ||
PASS: document subscribe with user gesture was error: NotAllowedError | ||
PASS: document subscribe with consumed user gesture failed with user gesture error | ||
PASS: document subscribe with user gesture consumed the user gesture |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The difference in behavior here is worrisome. This is meant to be a refactoring that maintain pre-existing behavior. Mixing behavior changes and refactoring in the same patch is usually a recipe for having a bad time later.
92a17b9
92a17b9