Skip to content
Permalink
Browse files
WebKit Managed Notifications: Skeleton NotificationProvider.
<rdar://84275562> and https://bugs.webkit.org/show_bug.cgi?id=231786

Reviewed by Alex Christensen.

Source/WebKit:

No new tests (No behavior change yet)

Currently WebNotificationManagerProxy manages dispatching out stuff to the NotificationProvider SPI
that - for example - Safari on Mac implements.

The interface between the ManagerProxy and the Provider is already the perfect way to add an alternative behavior.

This introduces the skeleton class of an alternative NotificationProvider that will eventually implement
WebKit-managed notifications.

While under development it will have a global runtime switch so we can A/B test between the client-managed and
WebKit-managed notifications. That switch would eventually be replaced by full compile-time behavior which would
be decided by a shipping vendor.

Note: This patch enables the new flag BUILT_IN_NOTIFICATIONS to get the new mechanisms building, but that flag by
itself doesn't expose Notifications interfaces in WebCore.
That will still require enabling NOTIFICATIONS separately.

* SourcesCocoa.txt:
* UIProcess/API/APINotificationProvider.h:
(API::NotificationProvider::isClientReplaceable const): A temporary mechanism to prevent API clients from overriding
  the built-in NotificationProvider if the feature is enabled. Useful right now because existing Safari does that with existing SPI.

* UIProcess/API/APIUIClient.h:
(API::UIClient::decidePolicyForNotificationPermissionRequest):

* UIProcess/API/Cocoa/WKUIDelegatePrivate.h:

* UIProcess/Cocoa/UIDelegate.h:
* UIProcess/Cocoa/UIDelegate.mm:
(WebKit::UIDelegate::setDelegate):
(WebKit::UIDelegate::UIClient::decidePolicyForNotificationPermissionRequest):

* UIProcess/Notifications/Cocoa/WebNotificationProviderCocoa.h: Copied from Source/WebKit/UIProcess/API/APINotificationProvider.h.
* UIProcess/Notifications/Cocoa/WebNotificationProviderCocoa.mm: Added.
(WebKit::WebNotificationProviderCocoa::createIfEnabled):
(WebKit::WebNotificationProviderCocoa::WebNotificationProviderCocoa):
(WebKit::WebNotificationProviderCocoa::show):
(WebKit::WebNotificationProviderCocoa::cancel):
(WebKit::WebNotificationProviderCocoa::didDestroyNotification):
(WebKit::WebNotificationProviderCocoa::clearNotifications):
(WebKit::WebNotificationProviderCocoa::addNotificationManager):
(WebKit::WebNotificationProviderCocoa::removeNotificationManager):
(WebKit::WebNotificationProviderCocoa::notificationPermissions):

* UIProcess/Notifications/WebNotificationManagerProxy.cpp:
(WebKit::WebNotificationManagerProxy::WebNotificationManagerProxy):
(WebKit::WebNotificationManagerProxy::setProvider):

Fix changes to the Unified Build:
* UIProcess/ios/WKHoverPlatter.h:
* UIProcess/ios/WKHoverPlatter.mm:
* WebKit.xcodeproj/project.pbxproj:

Source/WebKitLegacy/mac:

* WebCoreSupport/WebNotificationClient.mm:
(-[WebNotificationPolicyListener NO_RETURN_DUE_TO_ASSERT]):
(-[WebNotificationPolicyListener denyOnlyThisRequest]): Deleted.

Source/WTF:

* Scripts/Preferences/WebPreferencesDebug.yaml:
* wtf/PlatformEnableCocoa.h:


Canonical link: https://commits.webkit.org/243049@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@284240 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
beidson committed Oct 15, 2021
1 parent 0939ddd commit 0aa508c855b318c540d09485281869da8563014d
Showing 18 changed files with 306 additions and 28 deletions.
@@ -1,3 +1,13 @@
2021-10-15 Brady Eidson <beidson@apple.com>

WebKit Managed Notifications: Skeleton NotificationProvider.
<rdar://84275562> and https://bugs.webkit.org/show_bug.cgi?id=231786

Reviewed by Alex Christensen.

* Scripts/Preferences/WebPreferencesDebug.yaml:
* wtf/PlatformEnableCocoa.h:

2021-10-15 Robin Morisset <rmorisset@apple.com>

Revert r284230, my last fixes to the watch build make it break tests
@@ -32,6 +32,17 @@ AcceleratedDrawingEnabled:
WebCore:
default: false

BuiltInNotifications:
type: bool
humanReadableName: "Built-In Web Notifications"
humanReadableDescription: "Enable built-in WebKit managed notifications"
webcoreBinding: none
condition: ENABLE(BUILT_IN_NOTIFICATIONS)
exposed: [ WebKit ]
defaultValue:
WebKit:
default: false

# FIXME: WebKit has an alternate name for this called 'ShowDebugBorders'. We should standardize on one name.
CompositingBordersVisible:
type: bool
@@ -434,6 +434,10 @@
#define ENABLE_NOTIFICATIONS 1
#endif

#if !defined(ENABLE_BUILT_IN_NOTIFICATIONS) && PLATFORM(IOS)
#define ENABLE_BUILT_IN_NOTIFICATIONS 1
#endif

#if !defined(ENABLE_ORIENTATION_EVENTS) && PLATFORM(IOS_FAMILY)
#define ENABLE_ORIENTATION_EVENTS 1
#endif
@@ -1,3 +1,64 @@
2021-10-15 Brady Eidson <beidson@apple.com>

WebKit Managed Notifications: Skeleton NotificationProvider.
<rdar://84275562> and https://bugs.webkit.org/show_bug.cgi?id=231786

Reviewed by Alex Christensen.

No new tests (No behavior change yet)

Currently WebNotificationManagerProxy manages dispatching out stuff to the NotificationProvider SPI
that - for example - Safari on Mac implements.

The interface between the ManagerProxy and the Provider is already the perfect way to add an alternative behavior.

This introduces the skeleton class of an alternative NotificationProvider that will eventually implement
WebKit-managed notifications.

While under development it will have a global runtime switch so we can A/B test between the client-managed and
WebKit-managed notifications. That switch would eventually be replaced by full compile-time behavior which would
be decided by a shipping vendor.

Note: This patch enables the new flag BUILT_IN_NOTIFICATIONS to get the new mechanisms building, but that flag by
itself doesn't expose Notifications interfaces in WebCore.
That will still require enabling NOTIFICATIONS separately.

* SourcesCocoa.txt:
* UIProcess/API/APINotificationProvider.h:
(API::NotificationProvider::isClientReplaceable const): A temporary mechanism to prevent API clients from overriding
the built-in NotificationProvider if the feature is enabled. Useful right now because existing Safari does that with existing SPI.

* UIProcess/API/APIUIClient.h:
(API::UIClient::decidePolicyForNotificationPermissionRequest):

* UIProcess/API/Cocoa/WKUIDelegatePrivate.h:

* UIProcess/Cocoa/UIDelegate.h:
* UIProcess/Cocoa/UIDelegate.mm:
(WebKit::UIDelegate::setDelegate):
(WebKit::UIDelegate::UIClient::decidePolicyForNotificationPermissionRequest):

* UIProcess/Notifications/Cocoa/WebNotificationProviderCocoa.h: Copied from Source/WebKit/UIProcess/API/APINotificationProvider.h.
* UIProcess/Notifications/Cocoa/WebNotificationProviderCocoa.mm: Added.
(WebKit::WebNotificationProviderCocoa::createIfEnabled):
(WebKit::WebNotificationProviderCocoa::WebNotificationProviderCocoa):
(WebKit::WebNotificationProviderCocoa::show):
(WebKit::WebNotificationProviderCocoa::cancel):
(WebKit::WebNotificationProviderCocoa::didDestroyNotification):
(WebKit::WebNotificationProviderCocoa::clearNotifications):
(WebKit::WebNotificationProviderCocoa::addNotificationManager):
(WebKit::WebNotificationProviderCocoa::removeNotificationManager):
(WebKit::WebNotificationProviderCocoa::notificationPermissions):

* UIProcess/Notifications/WebNotificationManagerProxy.cpp:
(WebKit::WebNotificationManagerProxy::WebNotificationManagerProxy):
(WebKit::WebNotificationManagerProxy::setProvider):

Fix changes to the Unified Build:
* UIProcess/ios/WKHoverPlatter.h:
* UIProcess/ios/WKHoverPlatter.mm:
* WebKit.xcodeproj/project.pbxproj:

2021-10-14 Alex Christensen <achristensen@webkit.org>

Add AdAttributionDaemon plist on iOS
@@ -454,6 +454,8 @@ UIProcess/Inspector/mac/WKInspectorWKWebView.mm

UIProcess/Network/NetworkProcessProxyCocoa.mm

UIProcess/Notifications/Cocoa/WebNotificationProviderCocoa.mm

UIProcess/ios/forms/WKAirPlayRoutePicker.mm
UIProcess/ios/forms/WKDatePickerViewController.mm
UIProcess/ios/forms/WKDateTimeInputControl.mm
@@ -50,6 +50,8 @@ class NotificationProvider {
virtual void addNotificationManager(WebKit::WebNotificationManagerProxy&) { }
virtual void removeNotificationManager(WebKit::WebNotificationManagerProxy&) { }

virtual bool isClientReplaceable() const { return true; }

virtual HashMap<WTF::String, bool> notificationPermissions() { return HashMap<WTF::String, bool>(); };
};

@@ -137,7 +137,7 @@ class UIClient {
virtual void decidePolicyForGeolocationPermissionRequest(WebKit::WebPageProxy&, WebKit::WebFrameProxy&, const WebKit::FrameInfoData&, Function<void(bool)>&) { }
virtual void decidePolicyForUserMediaPermissionRequest(WebKit::WebPageProxy&, WebKit::WebFrameProxy&, SecurityOrigin&, SecurityOrigin&, WebKit::UserMediaPermissionRequestProxy& request) { request.doDefaultAction(); }
virtual void checkUserMediaPermissionForOrigin(WebKit::WebPageProxy&, WebKit::WebFrameProxy&, SecurityOrigin&, SecurityOrigin&, WebKit::UserMediaPermissionCheckProxy& request) { request.deny(); }
virtual void decidePolicyForNotificationPermissionRequest(WebKit::WebPageProxy&, SecurityOrigin&, CompletionHandler<void(bool allowed)>&& completionHandler) { completionHandler(false); }
virtual void decidePolicyForNotificationPermissionRequest(WebKit::WebPageProxy&, API::SecurityOrigin&, CompletionHandler<void(bool allowed)>&& completionHandler) { completionHandler(false); }
virtual void requestStorageAccessConfirm(WebKit::WebPageProxy&, WebKit::WebFrameProxy*, const WebCore::RegistrableDomain& requestingDomain, const WebCore::RegistrableDomain& currentDomain, CompletionHandler<void(bool)>&& completionHandler) { completionHandler(true); }

// Printing.
@@ -160,6 +160,8 @@ struct UIEdgeInsets;

- (void)_webView:(WKWebView *)webView startXRSessionWithCompletionHandler:(void (^)(id))completionHandler WK_API_AVAILABLE(macos(12.0), ios(15.0));

- (void)_webView:(WKWebView *)webView requestNotificationPermissionForSecurityOrigin:(WKSecurityOrigin *)securityOrigin decisionHandler:(void (^)(BOOL))decisionHandler WK_API_AVAILABLE(macos(10.13.4), ios(WK_IOS_TBA));

#if TARGET_OS_IPHONE

- (BOOL)_webView:(WKWebView *)webView shouldIncludeAppLinkActionsForElement:(_WKActivatedElementInfo *)element WK_API_AVAILABLE(ios(9.0));
@@ -242,7 +244,6 @@ struct UIEdgeInsets;
- (CGFloat)_webViewFooterHeight:(WKWebView *)webView WK_API_AVAILABLE(macos(10.13.4));
- (void)_webView:(WKWebView *)webView drawHeaderInRect:(CGRect)rect forPageWithTitle:(NSString *)title URL:(NSURL *)url WK_API_AVAILABLE(macos(10.13.4));
- (void)_webView:(WKWebView *)webView drawFooterInRect:(CGRect)rect forPageWithTitle:(NSString *)title URL:(NSURL *)url WK_API_AVAILABLE(macos(10.13.4));
- (void)_webView:(WKWebView *)webView requestNotificationPermissionForSecurityOrigin:(WKSecurityOrigin *)securityOrigin decisionHandler:(void (^)(BOOL))decisionHandler WK_API_AVAILABLE(macos(10.13.4));
- (void)_webView:(WKWebView *)webview mouseDidMoveOverElement:(_WKHitTestResult *)hitTestResult withFlags:(NSEventModifierFlags)flags userInfo:(id <NSSecureCoding>)userInfo;
- (void)_webView:(WKWebView *)webView didExceedBackgroundResourceLimitWhileInForeground:(_WKResourceLimit)limit WK_API_AVAILABLE(macos(10.13.4));
- (void)_webView:(WKWebView *)webView setResizable:(BOOL)isResizable WK_API_AVAILABLE(macos(10.13.4));
@@ -104,6 +104,9 @@ class UIDelegate : public CanMakeWeakPtr<UIDelegate> {
void didResignInputElementStrongPasswordAppearance(WebPageProxy&, API::Object*) final;
bool takeFocus(WebPageProxy*, WKFocusDirection) final;
void handleAutoplayEvent(WebPageProxy&, WebCore::AutoplayEvent, OptionSet<WebCore::AutoplayEventFlags>) final;

void decidePolicyForNotificationPermissionRequest(WebPageProxy&, API::SecurityOrigin&, CompletionHandler<void(bool allowed)>&&) final;

#if PLATFORM(MAC)
void showPage(WebPageProxy*) final;
void focus(WebPageProxy*) final;
@@ -122,7 +125,6 @@ class UIDelegate : public CanMakeWeakPtr<UIDelegate> {
void drawHeader(WebPageProxy&, WebFrameProxy&, WebCore::FloatRect&&) final;
void drawFooter(WebPageProxy&, WebFrameProxy&, WebCore::FloatRect&&) final;

void decidePolicyForNotificationPermissionRequest(WebPageProxy&, API::SecurityOrigin&, CompletionHandler<void(bool allowed)>&&) final;
void unavailablePluginButtonClicked(WebPageProxy&, WKPluginUnavailabilityReason, API::Dictionary&) final;
void mouseDidMoveOverElement(WebPageProxy&, const WebHitTestResultData&, OptionSet<WebEvent::Modifier>, API::Object*);
void didClickAutoFillButton(WebPageProxy&, API::Object*) final;
@@ -194,6 +196,7 @@ class UIDelegate : public CanMakeWeakPtr<UIDelegate> {
bool webViewDidResignInputElementStrongPasswordAppearanceWithUserInfo : 1;
bool webViewTakeFocus : 1;
bool webViewHandleAutoplayEventWithFlags : 1;
bool webViewRequestNotificationPermissionForSecurityOriginDecisionHandler : 1;
#if PLATFORM(MAC)
bool showWebView : 1;
bool focusWebView : 1;
@@ -215,7 +218,6 @@ class UIDelegate : public CanMakeWeakPtr<UIDelegate> {
bool webViewDidExceedBackgroundResourceLimitWhileInForeground : 1;
bool webViewSaveDataToFileSuggestedFilenameMimeTypeOriginatingURL : 1;
bool webViewRunOpenPanelWithParametersInitiatedByFrameCompletionHandler : 1;
bool webViewRequestNotificationPermissionForSecurityOriginDecisionHandler : 1;
bool webViewConfigurationForLocalInspector : 1;
bool webViewDidAttachLocalInspector : 1;
bool webViewWillCloseLocalInspector : 1;
@@ -113,6 +113,7 @@
m_delegateMethods.webViewDidResignInputElementStrongPasswordAppearanceWithUserInfo = [delegate respondsToSelector:@selector(_webView:didResignInputElementStrongPasswordAppearanceWithUserInfo:)];
m_delegateMethods.webViewTakeFocus = [delegate respondsToSelector:@selector(_webView:takeFocus:)];
m_delegateMethods.webViewHandleAutoplayEventWithFlags = [delegate respondsToSelector:@selector(_webView:handleAutoplayEvent:withFlags:)];
m_delegateMethods.webViewRequestNotificationPermissionForSecurityOriginDecisionHandler = [delegate respondsToSelector:@selector(_webView:requestNotificationPermissionForSecurityOrigin:decisionHandler:)];

#if PLATFORM(MAC)
m_delegateMethods.showWebView = [delegate respondsToSelector:@selector(_showWebView:)];
@@ -135,7 +136,6 @@
m_delegateMethods.webViewDidExceedBackgroundResourceLimitWhileInForeground = [delegate respondsToSelector:@selector(_webView:didExceedBackgroundResourceLimitWhileInForeground:)];
m_delegateMethods.webViewSaveDataToFileSuggestedFilenameMimeTypeOriginatingURL = [delegate respondsToSelector:@selector(_webView:saveDataToFile:suggestedFilename:mimeType:originatingURL:)];
m_delegateMethods.webViewRunOpenPanelWithParametersInitiatedByFrameCompletionHandler = [delegate respondsToSelector:@selector(webView:runOpenPanelWithParameters:initiatedByFrame:completionHandler:)];
m_delegateMethods.webViewRequestNotificationPermissionForSecurityOriginDecisionHandler = [delegate respondsToSelector:@selector(_webView:requestNotificationPermissionForSecurityOrigin:decisionHandler:)];
m_delegateMethods.webViewConfigurationForLocalInspector = [delegate respondsToSelector:@selector(_webView:configurationForLocalInspector:)];
m_delegateMethods.webViewDidAttachLocalInspector = [delegate respondsToSelector:@selector(_webView:didAttachLocalInspector:)];
m_delegateMethods.webViewWillCloseLocalInspector = [delegate respondsToSelector:@selector(_webView:willCloseLocalInspector:)];
@@ -616,6 +616,27 @@ static _WKAutoplayEvent toWKAutoplayEvent(WebCore::AutoplayEvent event)
[(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate->m_webView.get().get() handleAutoplayEvent:toWKAutoplayEvent(event) withFlags:toWKAutoplayEventFlags(flags)];
}

void UIDelegate::UIClient::decidePolicyForNotificationPermissionRequest(WebKit::WebPageProxy&, API::SecurityOrigin& securityOrigin, CompletionHandler<void(bool allowed)>&& completionHandler)
{
if (!m_uiDelegate)
return completionHandler(false);

if (!m_uiDelegate->m_delegateMethods.webViewRequestNotificationPermissionForSecurityOriginDecisionHandler)
return completionHandler(false);

auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return completionHandler(false);

auto checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(_webView:requestNotificationPermissionForSecurityOrigin:decisionHandler:));
[(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate->m_webView.get().get() requestNotificationPermissionForSecurityOrigin:wrapper(securityOrigin) decisionHandler:makeBlockPtr([completionHandler = WTFMove(completionHandler), checker = WTFMove(checker)] (BOOL result) mutable {
if (checker->completionHandlerHasBeenCalled())
return;
checker->didCallCompletionHandler();
completionHandler(result);
}).get()];
}

#if PLATFORM(MAC)
bool UIDelegate::UIClient::canRunModal() const
{
@@ -947,27 +968,6 @@ static _WKResourceLimit toWKResourceLimit(WKResourceLimit limit)
[(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate->m_webView.get().get() saveDataToFile:wrapper(data) suggestedFilename:suggestedFilename mimeType:mimeType originatingURL:originatingURL];
}

void UIDelegate::UIClient::decidePolicyForNotificationPermissionRequest(WebKit::WebPageProxy&, API::SecurityOrigin& securityOrigin, CompletionHandler<void(bool allowed)>&& completionHandler)
{
if (!m_uiDelegate)
return completionHandler(false);

if (!m_uiDelegate->m_delegateMethods.webViewRequestNotificationPermissionForSecurityOriginDecisionHandler)
return completionHandler(false);

auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return completionHandler(false);

auto checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(_webView:requestNotificationPermissionForSecurityOrigin:decisionHandler:));
[(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate->m_webView.get().get() requestNotificationPermissionForSecurityOrigin:wrapper(securityOrigin) decisionHandler:makeBlockPtr([completionHandler = WTFMove(completionHandler), checker = WTFMove(checker)] (BOOL result) mutable {
if (checker->completionHandlerHasBeenCalled())
return;
checker->didCallCompletionHandler();
completionHandler(result);
}).get()];
}

Ref<API::InspectorConfiguration> UIDelegate::UIClient::configurationForLocalInspector(WebPageProxy&, WebInspectorUIProxy& inspector)
{
if (!m_uiDelegate)
@@ -0,0 +1,60 @@
/*
* Copyright (C) 2021 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/

#pragma once

#if ENABLE(BUILT_IN_NOTIFICATIONS) && PLATFORM(COCOA)

#include "APINotificationProvider.h"
#include <wtf/Forward.h>

namespace WebKit {

class WebNotification;
class WebNotificationManagerProxy;
class WebPageProxy;

class WebNotificationProviderCocoa : public API::NotificationProvider {
WTF_MAKE_FAST_ALLOCATED;
public:
static std::unique_ptr<WebNotificationProviderCocoa> createIfEnabled();
explicit WebNotificationProviderCocoa();

void show(WebPageProxy&, WebNotification&) final;
void cancel(WebNotification&) final;
void didDestroyNotification(WebNotification&) final;
void clearNotifications(const Vector<uint64_t>& notificationIDs) final;

void addNotificationManager(WebNotificationManagerProxy&) final;
void removeNotificationManager(WebNotificationManagerProxy&) final;

bool isClientReplaceable() const final { return false; }

HashMap<WTF::String, bool> notificationPermissions() final;
};

} // namespace WebKit

#endif // ENABLE(BUILT_IN_NOTIFICATIONS) && PLATFORM(COCOA)

0 comments on commit 0aa508c

Please sign in to comment.