Title: [260350] trunk
Revision
260350
Author
beid...@apple.com
Date
2020-04-19 22:55:13 -0700 (Sun, 19 Apr 2020)

Log Message

Add WKScriptMessageHandler API that asynchronously responds with a promise.
rdar://problem/57243492 and https://bugs.webkit.org/show_bug.cgi?id=206398

Reviewed by Andy Estes.
Source/WebCore:

Covered by new API tests.

Updated for moving an #include into implementation files:
* bindings/js/JSDOMPromiseDeferred.cpp:
* bindings/js/JSDOMPromiseDeferred.h:
* html/HTMLMediaElement.cpp:
* page/DOMWindow.cpp:
* workers/service/ServiceWorkerGlobalScope.cpp:

* page/UserMessageHandler.cpp:
(WebCore::UserMessageHandler::postMessage): Return a promise to be fulfilled by the API client.
* page/UserMessageHandler.h:
* page/UserMessageHandler.idl:
* page/UserMessageHandlerDescriptor.h:

Source/WebKit:

Change webkit.messageHandlers.<name>.postMessage() to return a promise instead of undefined.

Allow for that promise to be resolved by an asynchronous reply block up in the API client.
This is like the spiritual opposite version of [WKWebView callAsyncFunction:...]

And while we're adding a new script message handler variant, we're adding it sandboxed by WKContentWorld.

* Shared/API/APISerializedScriptValue.h:
* UIProcess/API/Cocoa/APISerializedScriptValueCocoa.mm:
(API::validateObject):
(API::coreValueFromNSObject):
(API::SerializedScriptValue::createFromNSObject):
(API::SerializedScriptValue::wireBytesFromNSObject): Deleted.

* UIProcess/API/Cocoa/WKScriptMessage.h: Now that script message handlers can be per-world, messages
  declare which world they were posted from.
* UIProcess/API/Cocoa/WKScriptMessage.mm:
(-[WKScriptMessage _initWithBody:webView:frameInfo:name:world:]):
(-[WKScriptMessage world]):
(-[WKScriptMessage _initWithBody:webView:frameInfo:name:]): Deleted.
* UIProcess/API/Cocoa/WKScriptMessageInternal.h:

* UIProcess/API/Cocoa/WKScriptMessageHandlerWithReply.h: Added.
  Declare the new protocol for a script message handler that can reply to messages asynchronously.

* UIProcess/API/Cocoa/WKUserContentController.h:
* UIProcess/API/Cocoa/WKUserContentController.mm:
(-[WKUserContentController _addScriptMessageHandler:]):
(-[WKUserContentController addScriptMessageHandler:name:]):
(-[WKUserContentController addScriptMessageHandler:contentWorld:name:]):
(-[WKUserContentController addScriptMessageHandlerWithReply:contentWorld:name:]):
(-[WKUserContentController removeScriptMessageHandlerForName:contentWorld:]):
(-[WKUserContentController removeAllScriptMessageHandlersFromContentWorld:]):
(-[WKUserContentController removeAllScriptMessageHandlers]):
* UIProcess/API/Cocoa/WKUserContentControllerInternal.h:
* UIProcess/API/Cocoa/WKUserContentControllerPrivate.h:

* UIProcess/API/Cocoa/WKWebView.mm:
(-[WKWebView _evaluateJavaScript:asAsyncFunction:withArguments:forceUserGesture:inFrame:inWorld:completionHandler:]):
Update for new shared API::SerializedScriptValue initialization.

* UIProcess/API/glib/WebKitUserContentManager.cpp:
* UIProcess/API/gtk/WebKitRemoteInspectorProtocolHandler.cpp:
* UIProcess/Inspector/socket/RemoteInspectorProtocolHandler.cpp:

* UIProcess/UserContent/WebScriptMessageHandler.h:
* UIProcess/UserContent/WebUserContentControllerProxy.cpp:
(WebKit::WebUserContentControllerProxy::removeAllUserMessageHandlers):
(WebKit::WebUserContentControllerProxy::didPostMessage):
* UIProcess/UserContent/WebUserContentControllerProxy.h:
* UIProcess/UserContent/WebUserContentControllerProxy.messages.in:

* WebKit.xcodeproj/project.pbxproj:

* WebProcess/InjectedBundle/API/gtk/DOM/WebKitDOMDOMWindow.cpp:
(webkit_dom_dom_window_webkit_message_handlers_post_message):

* WebProcess/UserContent/WebUserContentController.cpp:
(WebKit::WebUserContentController::removeAllUserScriptMessageHandlers):
(WebKit::WebUserContentController::removeAllUserScriptMessageHandlersForWorlds):
* WebProcess/UserContent/WebUserContentController.h:
* WebProcess/UserContent/WebUserContentController.messages.in:

Source/WTF:

* wtf/CompletionHandler.h:
(WTF::CompletionHandlerWithFinalizer<Out): Add a variant of CompletionHandler that allows for a Finalizer function
  to handle cases where the Completion function hasn't been called at destruction time.

Tools:

* TestWebKitAPI/Tests/WebKitCocoa/UserContentController.mm:
(TEST):
(webViewForScriptMessageHandlerMultipleHandlerRemovalTest):
(-[AsyncScriptMessageHandler userContentController:didReceiveScriptMessage:replyHandler:]):

Modified Paths

Added Paths

Diff

Modified: trunk/Source/WTF/ChangeLog (260349 => 260350)


--- trunk/Source/WTF/ChangeLog	2020-04-20 05:15:35 UTC (rev 260349)
+++ trunk/Source/WTF/ChangeLog	2020-04-20 05:55:13 UTC (rev 260350)
@@ -1,3 +1,14 @@
+2020-04-19  Brady Eidson  <beid...@apple.com>
+
+        Add WKScriptMessageHandler API that asynchronously responds with a promise.
+        rdar://problem/57243492 and https://bugs.webkit.org/show_bug.cgi?id=206398
+
+        Reviewed by Andy Estes.
+
+        * wtf/CompletionHandler.h:
+        (WTF::CompletionHandlerWithFinalizer<Out): Add a variant of CompletionHandler that allows for a Finalizer function
+          to handle cases where the Completion function hasn't been called at destruction time.
+
 2020-04-19  Yusuke Suzuki  <ysuz...@apple.com>
 
         [JSC] Enable BigInt

Modified: trunk/Source/WTF/wtf/CompletionHandler.h (260349 => 260350)


--- trunk/Source/WTF/wtf/CompletionHandler.h	2020-04-20 05:15:35 UTC (rev 260349)
+++ trunk/Source/WTF/wtf/CompletionHandler.h	2020-04-20 05:55:13 UTC (rev 260350)
@@ -69,6 +69,49 @@
 #endif
 };
 
+// Wraps a Function to make sure it is called at most once.
+// If the CompletionHandlerWithFinalizer is destroyed and the function hasn't yet been called,
+// the finalizer is invoked with the function as its argument.
+template<typename> class CompletionHandlerWithFinalizer;
+template <typename Out, typename... In>
+class CompletionHandlerWithFinalizer<Out(In...)> {
+    WTF_MAKE_FAST_ALLOCATED;
+public:
+    template<typename CallableType, class = typename std::enable_if<std::is_rvalue_reference<CallableType&&>::value>::type>
+    CompletionHandlerWithFinalizer(CallableType&& callable, Function<void(Function<Out(In...)>&)>&& finalizer)
+        : m_function(WTFMove(callable))
+        , m_finalizer(WTFMove(finalizer))
+    {
+    }
+
+    CompletionHandlerWithFinalizer(CompletionHandlerWithFinalizer&&) = default;
+    CompletionHandlerWithFinalizer& operator=(CompletionHandlerWithFinalizer&&) = default;
+
+    ~CompletionHandlerWithFinalizer()
+    {
+        if (!m_function)
+            return;
+
+        m_finalizer(m_function);
+    }
+
+    explicit operator bool() const { return !!m_function; }
+
+    Out operator()(In... in)
+    {
+        ASSERT(m_wasConstructedOnMainThread == isMainThread());
+        ASSERT_WITH_MESSAGE(m_function, "Completion handler should not be called more than once");
+        return std::exchange(m_function, nullptr)(std::forward<In>(in)...);
+    }
+
+private:
+    Function<Out(In...)> m_function;
+    Function<void(Function<Out(In...)>&)> m_finalizer;
+#if ASSERT_ENABLED
+    bool m_wasConstructedOnMainThread { isMainThread() };
+#endif
+};
+
 namespace Detail {
 
 template<typename Out, typename... In>
@@ -115,3 +158,4 @@
 
 using WTF::CompletionHandler;
 using WTF::CompletionHandlerCallingScope;
+using WTF::CompletionHandlerWithFinalizer;

Modified: trunk/Source/WebCore/ChangeLog (260349 => 260350)


--- trunk/Source/WebCore/ChangeLog	2020-04-20 05:15:35 UTC (rev 260349)
+++ trunk/Source/WebCore/ChangeLog	2020-04-20 05:55:13 UTC (rev 260350)
@@ -1,3 +1,25 @@
+2020-04-19  Brady Eidson  <beid...@apple.com>
+
+        Add WKScriptMessageHandler API that asynchronously responds with a promise.
+        rdar://problem/57243492 and https://bugs.webkit.org/show_bug.cgi?id=206398
+
+        Reviewed by Andy Estes.
+
+        Covered by new API tests.
+
+        Updated for moving an #include into implementation files:
+        * bindings/js/JSDOMPromiseDeferred.cpp:
+        * bindings/js/JSDOMPromiseDeferred.h:    
+        * html/HTMLMediaElement.cpp:
+        * page/DOMWindow.cpp:
+        * workers/service/ServiceWorkerGlobalScope.cpp:
+        
+        * page/UserMessageHandler.cpp:
+        (WebCore::UserMessageHandler::postMessage): Return a promise to be fulfilled by the API client.
+        * page/UserMessageHandler.h:
+        * page/UserMessageHandler.idl:
+        * page/UserMessageHandlerDescriptor.h:
+
 2020-04-19  Zalan Bujtas  <za...@apple.com>
 
         [LFC][TFC] Add column spanning support for flexible table width

Modified: trunk/Source/WebCore/bindings/js/JSDOMPromiseDeferred.cpp (260349 => 260350)


--- trunk/Source/WebCore/bindings/js/JSDOMPromiseDeferred.cpp	2020-04-20 05:15:35 UTC (rev 260349)
+++ trunk/Source/WebCore/bindings/js/JSDOMPromiseDeferred.cpp	2020-04-20 05:55:13 UTC (rev 260350)
@@ -27,6 +27,7 @@
 #include "JSDOMPromiseDeferred.h"
 
 #include "DOMWindow.h"
+#include "EventLoop.h"
 #include "JSDOMPromise.h"
 #include "JSDOMWindow.h"
 #include <_javascript_Core/BuiltinNames.h>

Modified: trunk/Source/WebCore/bindings/js/JSDOMPromiseDeferred.h (260349 => 260350)


--- trunk/Source/WebCore/bindings/js/JSDOMPromiseDeferred.h	2020-04-20 05:15:35 UTC (rev 260349)
+++ trunk/Source/WebCore/bindings/js/JSDOMPromiseDeferred.h	2020-04-20 05:55:13 UTC (rev 260350)
@@ -25,7 +25,6 @@
 
 #pragma once
 
-#include "EventLoop.h"
 #include "ExceptionOr.h"
 #include "JSDOMConvert.h"
 #include "JSDOMGuardedObject.h"

Modified: trunk/Source/WebCore/html/HTMLMediaElement.cpp (260349 => 260350)


--- trunk/Source/WebCore/html/HTMLMediaElement.cpp	2020-04-20 05:15:35 UTC (rev 260349)
+++ trunk/Source/WebCore/html/HTMLMediaElement.cpp	2020-04-20 05:55:13 UTC (rev 260350)
@@ -48,6 +48,7 @@
 #include "Document.h"
 #include "DocumentLoader.h"
 #include "ElementChildIterator.h"
+#include "EventLoop.h"
 #include "EventNames.h"
 #include "Frame.h"
 #include "FrameLoader.h"

Modified: trunk/Source/WebCore/page/DOMWindow.cpp (260349 => 260350)


--- trunk/Source/WebCore/page/DOMWindow.cpp	2020-04-20 05:15:35 UTC (rev 260349)
+++ trunk/Source/WebCore/page/DOMWindow.cpp	2020-04-20 05:55:13 UTC (rev 260350)
@@ -57,6 +57,7 @@
 #include "Element.h"
 #include "EventHandler.h"
 #include "EventListener.h"
+#include "EventLoop.h"
 #include "EventNames.h"
 #include "FloatRect.h"
 #include "FocusController.h"

Modified: trunk/Source/WebCore/page/UserMessageHandler.cpp (260349 => 260350)


--- trunk/Source/WebCore/page/UserMessageHandler.cpp	2020-04-20 05:15:35 UTC (rev 260349)
+++ trunk/Source/WebCore/page/UserMessageHandler.cpp	2020-04-20 05:55:13 UTC (rev 260350)
@@ -29,7 +29,9 @@
 #if ENABLE(USER_MESSAGE_HANDLERS)
 
 #include "Frame.h"
+#include "JSDOMPromiseDeferred.h"
 #include "SerializedScriptValue.h"
+#include <_javascript_Core/JSCJSValue.h>
 
 namespace WebCore {
 
@@ -41,14 +43,29 @@
 
 UserMessageHandler::~UserMessageHandler() = default;
 
-ExceptionOr<void> UserMessageHandler::postMessage(RefPtr<SerializedScriptValue>&& value)
+ExceptionOr<void> UserMessageHandler::postMessage(RefPtr<SerializedScriptValue>&& value, Ref<DeferredPromise>&& promise)
 {
     // Check to see if the descriptor has been removed. This can happen if the host application has
     // removed the named message handler at the WebKit2 API level.
-    if (!m_descriptor)
+    if (!m_descriptor) {
+        promise->reject(Exception { InvalidAccessError });
         return Exception { InvalidAccessError };
+    }
 
-    m_descriptor->didPostMessage(*this, value.get());
+    m_descriptor->didPostMessage(*this, value.get(), [promise = WTFMove(promise)](SerializedScriptValue* result, const String& errorMessage) {
+        auto* globalObject = promise->globalObject();
+        if (!globalObject)
+            return;
+
+        if (!errorMessage.isNull()) {
+            JSC::JSLockHolder lock(globalObject);
+            promise->reject<IDLAny>(JSC::createError(globalObject, errorMessage));
+            return;
+        }
+
+        ASSERT(result);
+        promise->resolve<IDLAny>(result->deserialize(*globalObject, globalObject));
+    });
     return { };
 }
 

Modified: trunk/Source/WebCore/page/UserMessageHandler.h (260349 => 260350)


--- trunk/Source/WebCore/page/UserMessageHandler.h	2020-04-20 05:15:35 UTC (rev 260349)
+++ trunk/Source/WebCore/page/UserMessageHandler.h	2020-04-20 05:55:13 UTC (rev 260350)
@@ -33,6 +33,8 @@
 
 namespace WebCore {
 
+class DeferredPromise;
+
 class UserMessageHandler : public RefCounted<UserMessageHandler>, public FrameDestructionObserver {
 public:
     static Ref<UserMessageHandler> create(Frame& frame, UserMessageHandlerDescriptor& descriptor)
@@ -41,7 +43,7 @@
     }
     virtual ~UserMessageHandler();
 
-    ExceptionOr<void> postMessage(RefPtr<SerializedScriptValue>&&);
+    ExceptionOr<void> postMessage(RefPtr<SerializedScriptValue>&&, Ref<DeferredPromise>&&);
 
     UserMessageHandlerDescriptor* descriptor() { return m_descriptor.get(); }
     void invalidateDescriptor() { m_descriptor = nullptr; }

Modified: trunk/Source/WebCore/page/UserMessageHandler.idl (260349 => 260350)


--- trunk/Source/WebCore/page/UserMessageHandler.idl	2020-04-20 05:15:35 UTC (rev 260349)
+++ trunk/Source/WebCore/page/UserMessageHandler.idl	2020-04-20 05:55:13 UTC (rev 260350)
@@ -26,5 +26,5 @@
 [
     Conditional=USER_MESSAGE_HANDLERS
 ] interface UserMessageHandler {
-    [MayThrowException] void postMessage(SerializedScriptValue message);
+    [MayThrowException] Promise<any> postMessage(SerializedScriptValue message);
 };

Modified: trunk/Source/WebCore/page/UserMessageHandlerDescriptor.h (260349 => 260350)


--- trunk/Source/WebCore/page/UserMessageHandlerDescriptor.h	2020-04-20 05:15:35 UTC (rev 260349)
+++ trunk/Source/WebCore/page/UserMessageHandlerDescriptor.h	2020-04-20 05:55:13 UTC (rev 260350)
@@ -46,7 +46,7 @@
     WEBCORE_EXPORT DOMWrapperWorld& world();
     WEBCORE_EXPORT const DOMWrapperWorld& world() const;
 
-    virtual void didPostMessage(UserMessageHandler&, SerializedScriptValue*) = 0;
+    virtual void didPostMessage(UserMessageHandler&, SerializedScriptValue*, WTF::Function<void(SerializedScriptValue*, const String&)>&&) = 0;
 
 private:
     AtomString m_name;

Modified: trunk/Source/WebCore/workers/service/ServiceWorkerGlobalScope.cpp (260349 => 260350)


--- trunk/Source/WebCore/workers/service/ServiceWorkerGlobalScope.cpp	2020-04-20 05:15:35 UTC (rev 260349)
+++ trunk/Source/WebCore/workers/service/ServiceWorkerGlobalScope.cpp	2020-04-20 05:55:13 UTC (rev 260350)
@@ -28,6 +28,7 @@
 
 #if ENABLE(SERVICE_WORKER)
 
+#include "EventLoop.h"
 #include "ExtendableEvent.h"
 #include "JSDOMPromiseDeferred.h"
 #include "SWContextManager.h"

Modified: trunk/Source/WebKit/ChangeLog (260349 => 260350)


--- trunk/Source/WebKit/ChangeLog	2020-04-20 05:15:35 UTC (rev 260349)
+++ trunk/Source/WebKit/ChangeLog	2020-04-20 05:55:13 UTC (rev 260350)
@@ -1,3 +1,73 @@
+2020-04-19  Brady Eidson  <beid...@apple.com>
+
+        Add WKScriptMessageHandler API that asynchronously responds with a promise.
+        rdar://problem/57243492 and https://bugs.webkit.org/show_bug.cgi?id=206398
+
+        Reviewed by Andy Estes.
+        
+        Change webkit.messageHandlers.<name>.postMessage() to return a promise instead of undefined.
+        
+        Allow for that promise to be resolved by an asynchronous reply block up in the API client.
+        This is like the spiritual opposite version of [WKWebView callAsyncFunction:...]
+        
+        And while we're adding a new script message handler variant, we're adding it sandboxed by WKContentWorld.
+
+        * Shared/API/APISerializedScriptValue.h:
+        * UIProcess/API/Cocoa/APISerializedScriptValueCocoa.mm:
+        (API::validateObject):
+        (API::coreValueFromNSObject):
+        (API::SerializedScriptValue::createFromNSObject):
+        (API::SerializedScriptValue::wireBytesFromNSObject): Deleted.
+
+        * UIProcess/API/Cocoa/WKScriptMessage.h: Now that script message handlers can be per-world, messages
+          declare which world they were posted from.
+        * UIProcess/API/Cocoa/WKScriptMessage.mm:
+        (-[WKScriptMessage _initWithBody:webView:frameInfo:name:world:]):
+        (-[WKScriptMessage world]):
+        (-[WKScriptMessage _initWithBody:webView:frameInfo:name:]): Deleted.
+        * UIProcess/API/Cocoa/WKScriptMessageInternal.h:
+
+        * UIProcess/API/Cocoa/WKScriptMessageHandlerWithReply.h: Added.
+          Declare the new protocol for a script message handler that can reply to messages asynchronously.
+
+        * UIProcess/API/Cocoa/WKUserContentController.h:
+        * UIProcess/API/Cocoa/WKUserContentController.mm:
+        (-[WKUserContentController _addScriptMessageHandler:]):
+        (-[WKUserContentController addScriptMessageHandler:name:]):
+        (-[WKUserContentController addScriptMessageHandler:contentWorld:name:]):
+        (-[WKUserContentController addScriptMessageHandlerWithReply:contentWorld:name:]):
+        (-[WKUserContentController removeScriptMessageHandlerForName:contentWorld:]):
+        (-[WKUserContentController removeAllScriptMessageHandlersFromContentWorld:]):
+        (-[WKUserContentController removeAllScriptMessageHandlers]):
+        * UIProcess/API/Cocoa/WKUserContentControllerInternal.h:
+        * UIProcess/API/Cocoa/WKUserContentControllerPrivate.h:
+        
+        * UIProcess/API/Cocoa/WKWebView.mm:
+        (-[WKWebView _evaluateJavaScript:asAsyncFunction:withArguments:forceUserGesture:inFrame:inWorld:completionHandler:]):
+        Update for new shared API::SerializedScriptValue initialization.
+        
+        * UIProcess/API/glib/WebKitUserContentManager.cpp:
+        * UIProcess/API/gtk/WebKitRemoteInspectorProtocolHandler.cpp:
+        * UIProcess/Inspector/socket/RemoteInspectorProtocolHandler.cpp:
+        
+        * UIProcess/UserContent/WebScriptMessageHandler.h:
+        * UIProcess/UserContent/WebUserContentControllerProxy.cpp:
+        (WebKit::WebUserContentControllerProxy::removeAllUserMessageHandlers):
+        (WebKit::WebUserContentControllerProxy::didPostMessage):
+        * UIProcess/UserContent/WebUserContentControllerProxy.h:
+        * UIProcess/UserContent/WebUserContentControllerProxy.messages.in:
+        
+        * WebKit.xcodeproj/project.pbxproj:
+        
+        * WebProcess/InjectedBundle/API/gtk/DOM/WebKitDOMDOMWindow.cpp:
+        (webkit_dom_dom_window_webkit_message_handlers_post_message):
+        
+        * WebProcess/UserContent/WebUserContentController.cpp:
+        (WebKit::WebUserContentController::removeAllUserScriptMessageHandlers):
+        (WebKit::WebUserContentController::removeAllUserScriptMessageHandlersForWorlds):
+        * WebProcess/UserContent/WebUserContentController.h:
+        * WebProcess/UserContent/WebUserContentController.messages.in:
+
 2020-04-19  David Kilzer  <ddkil...@apple.com>
 
         REGRESSION (r244091): Leak of TaskStateChangedCallbackType due to missing -dealloc

Modified: trunk/Source/WebKit/Shared/API/APISerializedScriptValue.h (260349 => 260350)


--- trunk/Source/WebKit/Shared/API/APISerializedScriptValue.h	2020-04-20 05:15:35 UTC (rev 260349)
+++ trunk/Source/WebKit/Shared/API/APISerializedScriptValue.h	2020-04-20 05:55:13 UTC (rev 260350)
@@ -61,7 +61,7 @@
     
 #if PLATFORM(COCOA) && defined(__OBJC__)
     static id deserialize(WebCore::SerializedScriptValue&, JSValueRef* exception);
-    static Optional<Vector<uint8_t>> wireBytesFromNSObject(id);
+    static RefPtr<SerializedScriptValue> createFromNSObject(id);
 #endif
 
     IPC::DataReference dataReference() const { return m_serializedScriptValue->data(); }

Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/APISerializedScriptValueCocoa.mm (260349 => 260350)


--- trunk/Source/WebKit/UIProcess/API/Cocoa/APISerializedScriptValueCocoa.mm	2020-04-20 05:15:35 UTC (rev 260349)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/APISerializedScriptValueCocoa.mm	2020-04-20 05:55:13 UTC (rev 260350)
@@ -80,20 +80,64 @@
     return value.toObject;
 }
 
-Optional<Vector<uint8_t>> SerializedScriptValue::wireBytesFromNSObject(id object)
+static bool validateObject(id argument)
 {
+    if ([argument isKindOfClass:[NSString class]] || [argument isKindOfClass:[NSNumber class]] || [argument isKindOfClass:[NSDate class]] || [argument isKindOfClass:[NSNull class]])
+        return true;
+
+    if ([argument isKindOfClass:[NSArray class]]) {
+        __block BOOL valid = true;
+
+        [argument enumerateObjectsUsingBlock:^(id object, NSUInteger, BOOL *stop) {
+            if (!validateObject(object)) {
+                valid = false;
+                *stop = YES;
+            }
+        }];
+
+        return valid;
+    }
+
+    if ([argument isKindOfClass:[NSDictionary class]]) {
+        __block bool valid = true;
+
+        [argument enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
+            if (!validateObject(key) || !validateObject(value)) {
+                valid = false;
+                *stop = YES;
+            }
+        }];
+
+        return valid;
+    }
+
+    return false;
+}
+
+static RefPtr<WebCore::SerializedScriptValue> coreValueFromNSObject(id object)
+{
+    if (object && !validateObject(object))
+        return nullptr;
+
     ASSERT(RunLoop::isMain());
     JSContext* context = sharedContext().ensureContext();
     JSValue *value = [JSValue valueWithObject:object inContext:context];
     if (!value)
-        return WTF::nullopt;
+        return nullptr;
 
     auto globalObject = toJS([context JSGlobalContextRef]);
     ASSERT(globalObject);
     JSC::JSLockHolder lock(globalObject);
 
-    auto coreValue = WebCore::SerializedScriptValue::create(*globalObject, toJS(globalObject, [value JSValueRef]));
-    return coreValue->toWireBytes();
+    return WebCore::SerializedScriptValue::create(*globalObject, toJS(globalObject, [value JSValueRef]));
 }
 
+RefPtr<SerializedScriptValue> SerializedScriptValue::createFromNSObject(id object)
+{
+    auto coreValue = coreValueFromNSObject(object);
+    if (!coreValue)
+        return nullptr;
+    return create(coreValue.releaseNonNull());
+}
+
 } // API

Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKScriptMessage.h (260349 => 260350)


--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKScriptMessage.h	2020-04-20 05:15:35 UTC (rev 260349)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKScriptMessage.h	2020-04-20 05:55:13 UTC (rev 260350)
@@ -29,6 +29,7 @@
 
 NS_ASSUME_NONNULL_BEGIN
 
+@class WKContentWorld;
 @class WKFrameInfo;
 @class WKWebView;
 
@@ -54,6 +55,9 @@
  */
 @property (nonatomic, readonly, copy) NSString *name;
 
+/*! @abstract The content world from which the message was sent. */
+@property (nonatomic, readonly) WKContentWorld *world WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
+
 @end
 
 NS_ASSUME_NONNULL_END

Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKScriptMessage.mm (260349 => 260350)


--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKScriptMessage.mm	2020-04-20 05:15:35 UTC (rev 260349)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKScriptMessage.mm	2020-04-20 05:55:13 UTC (rev 260350)
@@ -35,9 +35,10 @@
     WeakObjCPtr<WKWebView> _webView;
     RetainPtr<WKFrameInfo> _frameInfo;
     RetainPtr<NSString> _name;
+    RetainPtr<WKContentWorld> _world;
 }
 
-- (instancetype)_initWithBody:(id)body webView:(WKWebView *)webView frameInfo:(WKFrameInfo *)frameInfo name:(NSString *)name
+- (instancetype)_initWithBody:(id)body webView:(WKWebView *)webView frameInfo:(WKFrameInfo *)frameInfo name:(NSString *)name world:(WKContentWorld *)world
 {
     if (!(self = [super init]))
         return nil;
@@ -46,6 +47,7 @@
     _webView = webView;
     _frameInfo = frameInfo;
     _name = adoptNS([name copy]);
+    _world = world;
 
     return self;
 }
@@ -70,4 +72,9 @@
     return _name.get();
 }
 
+- (WKContentWorld *)world
+{
+    return _world.get();
+}
+
 @end

Added: trunk/Source/WebKit/UIProcess/API/Cocoa/WKScriptMessageHandlerWithReply.h (0 => 260350)


--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKScriptMessageHandlerWithReply.h	                        (rev 0)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKScriptMessageHandlerWithReply.h	2020-04-20 05:55:13 UTC (rev 260350)
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#import <WebKit/WKFoundation.h>
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@class WKScriptMessage;
+@class WKUserContentController;
+
+/*! A class conforming to  the WKScriptMessageHandlerWithReply protocol provides a
+method for receiving messages from _javascript_ running in a webpage and replying to them asynchronously.
+*/
+@protocol WKScriptMessageHandlerWithReply <NSObject>
+
+/*! @abstract Invoked when a script message is received from a webpage.
+ @param userContentController The user content controller invoking the delegate method.
+ @param message The script message received.
+ @param replyHandler A block to be called with the result of processing the message.
+ @discussion When the _javascript_ running in your application's web content called window.webkit.messageHandlers.<name>.postMessage(<messageBody>),
+ a _javascript_ Promise object was returned. The values passed to the replyHandler are used to resolve that Promise.
+
+ Passing a non-nil NSString value to the second parameter of the replyHandler signals an error.
+ No matter what value you pass to the first parameter of the replyHandler,
+ the Promise will be rejected with a _javascript_ error object whose message property is set to that errorMessage string.
+
+ If the second parameter to the replyHandler is nil, the first argument will be serialized into its _javascript_ equivalent and the Promise will be fulfilled with the resulting value.
+ If the first argument is nil then the Promise will be resolved with a _javascript_ value of "undefined"
+
+ Allowed non-nil result types are:
+ NSNumber, NSNull, NSString, NSDate, NSArray, and NSDictionary.
+ Any NSArray or NSDictionary containers can only contain objects of those types.
+
+ The replyHandler can be called at most once.
+ If the replyHandler is deallocated before it is called, the Promise will be rejected with a _javascript_ Error object
+ with an appropriate message indicating the handler was never called.
+
+ Example:
+
+ With a WKScriptMessageHandlerWithReply object installed with the name "testHandler", consider the following _javascript_:
+ @textblock
+     var promise = window.webkit.messageHandlers.testHandler.postMessage("Fulfill me with 42");
+     await p;
+     return p;
+ @/textblock
+
+ And consider the following WKScriptMessageHandlerWithReply implementation:
+ @textblock
+     - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message replyHandler:(void (^)(id, NSString *))replyHandler
+     {
+        if ([message.body isEqual:@"Fulfill me with 42"])
+            replyHandler(@42, nil);
+        else
+            replyHandler(nil, @"Unexpected message received");
+     }
+ @/textblock
+
+ In this example:
+   - The _javascript_ code sends a message to your application code with the body @"Fulfill me with 42"
+   - _javascript_ execution is suspended while waiting for the resulting promise to resolve.
+   - Your message handler is invoked with that message and a block to call with the reply when ready.
+   - Your message handler sends the value @42 as a reply.
+   - The _javascript_ promise is fulfilled with the value 42.
+   - _javascript_ execution continues and the value 42 is returned.
+ */
+- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message replyHandler:(void (^)(id reply, NSString *errorMessage))replyHandler WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
+@end
+
+NS_ASSUME_NONNULL_END

Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKScriptMessageInternal.h (260349 => 260350)


--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKScriptMessageInternal.h	2020-04-20 05:15:35 UTC (rev 260349)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKScriptMessageInternal.h	2020-04-20 05:55:13 UTC (rev 260350)
@@ -27,6 +27,6 @@
 
 @interface WKScriptMessage ()
 
-- (instancetype)_initWithBody:(id)body webView:(WKWebView *)webView frameInfo:(WKFrameInfo *)frameInfo name:(NSString *)name;
+- (instancetype)_initWithBody:(id)body webView:(WKWebView *)webView frameInfo:(WKFrameInfo *)frameInfo name:(NSString *)name world:(WKContentWorld *)world;
 
 @end

Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKUserContentController.h (260349 => 260350)


--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKUserContentController.h	2020-04-20 05:15:35 UTC (rev 260349)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKUserContentController.h	2020-04-20 05:55:13 UTC (rev 260350)
@@ -30,8 +30,10 @@
 NS_ASSUME_NONNULL_BEGIN
 
 @class WKContentRuleList;
+@class WKContentWorld;
 @class WKUserScript;
 @protocol WKScriptMessageHandler;
+@protocol WKScriptMessageHandlerWithReply;
 
 /*! A WKUserContentController object provides a way for _javascript_ to post
  messages to a web view.
@@ -56,19 +58,75 @@
 - (void)removeAllUserScripts;
 
 /*! @abstract Adds a script message handler.
- @param scriptMessageHandler The message handler to add.
+ @param scriptMessageHandler The script message handler to add.
+ @param contentWorld The WKContentWorld in which to add the script message handler.
  @param name The name of the message handler.
- @discussion Adding a scriptMessageHandler adds a function
- window.webkit.messageHandlers.<name>.postMessage(<messageBody>) for all
- frames.
+ @discussion Adding a script message handler adds a function
+ window.webkit.messageHandlers.<name>.postMessage(<messageBody>) to all frames, available in the given WKContentWorld.
+
+ The name argument must be a non-empty string.
+
+ Each WKContentWorld can have any number of script message handlers, but only one per unique name.
+
+ Once any script message handler has been added to a WKContentWorld for a given name, it is an error to add another
+ script message handler to that WKContentWorld for that same name without first removing the previous script message handler.
+
+ The above restriction applies to any type of script message handler - WKScriptMessageHandler and WKScriptMessageHandlerWithReply
+ objects will conflict with each other if you try to add them to the same WKContentWorld with the same name.
  */
+- (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler contentWorld:(WKContentWorld *)world name:(NSString *)name WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
+
+/*! @abstract Adds a script message handler.
+ @param scriptMessageHandlerWithReply The script message handler to add.
+ @param contentWorld The WKContentWorld in which to add the script message handler.
+ @param name The name of the message handler.
+ @discussion Adding a script message handler adds a function
+ window.webkit.messageHandlers.<name>.postMessage(<messageBody>) to all frames, available in the given WKContentWorld.
+
+ The name argument must be a non-empty string.
+
+ Each WKContentWorld can have any number of script message handlers, but only one per unique name.
+
+ Once any script message handler has been added to a WKContentWorld for a given name, it is an error to add another
+ script message handler to that WKContentWorld for that same name without first removing the previous script message handler.
+
+ The above restriction applies to any type of script message handler - WKScriptMessageHandlerWithReply and WKScriptMessageHandler
+ objects will conflict with each other if you try to add them to the same WKContentWorld with the same name.
+
+ Refer to the WKScriptMessageHandlerWithReply documentation for examples of how it is more flexible than WKScriptMessageHandler.
+ */
+- (void)addScriptMessageHandlerWithReply:(id <WKScriptMessageHandlerWithReply>)scriptMessageHandlerWithReply contentWorld:(WKContentWorld *)contentWorld name:(NSString *)name WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
+
+/*! @abstract Adds a script message handler to the main world used by page content itself.
+ @param scriptMessageHandler The script message handler to add.
+ @param name The name of the message handler.
+ @discussion Calling this method is equivalent to calling addScriptMessageHandler:contentWorld:name:
+ with [WKContentWorld pageWorld] as the contentWorld argument.
+ */
 - (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name;
 
 /*! @abstract Removes a script message handler.
  @param name The name of the message handler to remove.
+ @param contentWorld The WKContentWorld from which to remove the script message handler.
  */
+- (void)removeScriptMessageHandlerForName:(NSString *)name contentWorld:(WKContentWorld *)contentWorld WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
+
+/*! @abstract Removes a script message handler.
+ @param name The name of the message handler to remove.
+ @discussion Calling this method is equivalent to calling removeScriptMessageHandlerForName:contentWorld:
+  with [WKContentWorld pageWorld] as the contentWorld argument.
+ */
 - (void)removeScriptMessageHandlerForName:(NSString *)name;
 
+/*! @abstract Removes all script message handlers from a given WKContentWorld.
+ @param contentWorld The WKContentWorld from which to remove all script message handlers.
+ */
+- (void)removeAllScriptMessageHandlersFromContentWorld:(WKContentWorld *)contentWorld WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
+
+/*! @abstract Removes all associated script message handlers.
+ */
+- (void)removeAllScriptMessageHandlers WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
+
 /*! @abstract Adds a content rule list.
  @param contentRuleList The content rule list to add.
  */

Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKUserContentController.mm (260349 => 260350)


--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKUserContentController.mm	2020-04-20 05:15:35 UTC (rev 260349)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKUserContentController.mm	2020-04-20 05:55:13 UTC (rev 260350)
@@ -34,6 +34,7 @@
 #import "WKFrameInfoInternal.h"
 #import "WKNSArray.h"
 #import "WKScriptMessageHandler.h"
+#import "WKScriptMessageHandlerWithReply.h"
 #import "WKScriptMessageInternal.h"
 #import "WKUserScriptInternal.h"
 #import "WKWebViewInternal.h"
@@ -125,38 +126,116 @@
         : m_controller(controller)
         , m_handler(handler)
         , m_name(adoptNS([name copy]))
+        , m_supportsAsyncReply(false)
     {
     }
-    
-    void didPostMessage(WebKit::WebPageProxy& page, WebKit::FrameInfoData&& frameInfoData, WebCore::SerializedScriptValue& serializedScriptValue) override
+
+    ScriptMessageHandlerDelegate(WKUserContentController *controller, id <WKScriptMessageHandlerWithReply> handler, NSString *name)
+        : m_controller(controller)
+        , m_handler(handler)
+        , m_name(adoptNS([name copy]))
+        , m_supportsAsyncReply(true)
     {
+    }
+
+    void didPostMessage(WebKit::WebPageProxy& page, WebKit::FrameInfoData&& frameInfoData, API::ContentWorld& world, WebCore::SerializedScriptValue& serializedScriptValue) final
+    {
         @autoreleasepool {
             RetainPtr<WKFrameInfo> frameInfo = wrapper(API::FrameInfo::create(WTFMove(frameInfoData), &page));
             id body = API::SerializedScriptValue::deserialize(serializedScriptValue, 0);
-            auto message = adoptNS([[WKScriptMessage alloc] _initWithBody:body webView:fromWebPageProxy(page) frameInfo:frameInfo.get() name:m_name.get()]);
+            auto message = adoptNS([[WKScriptMessage alloc] _initWithBody:body webView:fromWebPageProxy(page) frameInfo:frameInfo.get() name:m_name.get() world:wrapper(world)]);
         
-            [m_handler userContentController:m_controller.get() didReceiveScriptMessage:message.get()];
+            [(id<WKScriptMessageHandler>)m_handler.get() userContentController:m_controller.get() didReceiveScriptMessage:message.get()];
         }
     }
 
+    bool supportsAsyncReply() final
+    {
+        return m_supportsAsyncReply;
+    }
+
+    void didPostMessageWithAsyncReply(WebKit::WebPageProxy& page, WebKit::FrameInfoData&& frameInfoData, API::ContentWorld& world, WebCore::SerializedScriptValue& serializedScriptValue, Function<void(API::SerializedScriptValue*, const String&)>&& replyHandler) final
+    {
+        ASSERT(m_supportsAsyncReply);
+
+        auto finalizer = [](Function<void(API::SerializedScriptValue*, const String&)>& function) {
+            function(nullptr, "WKWebView API client did not respond to this postMessage"_s);
+        };
+        __block auto handler = CompletionHandlerWithFinalizer<void(API::SerializedScriptValue*, const String&)>(WTFMove(replyHandler), WTFMove(finalizer));
+
+        @autoreleasepool {
+            RetainPtr<WKFrameInfo> frameInfo = wrapper(API::FrameInfo::create(WTFMove(frameInfoData), &page));
+            id body = API::SerializedScriptValue::deserialize(serializedScriptValue, 0);
+            auto message = adoptNS([[WKScriptMessage alloc] _initWithBody:body webView:fromWebPageProxy(page) frameInfo:frameInfo.get() name:m_name.get() world:wrapper(world)]);
+
+            [(id<WKScriptMessageHandlerWithReply>)m_handler.get() userContentController:m_controller.get() didReceiveScriptMessage:message.get() replyHandler:^(id result, NSString *errorMessage) {
+                if (errorMessage) {
+                    handler(nullptr, errorMessage);
+                    return;
+                }
+
+                auto serializedValue = API::SerializedScriptValue::createFromNSObject(result);
+                if (!serializedValue) {
+                    handler(nullptr, "The result value passed back from the WKWebView API client was unable to be serialized"_s);
+                    return;
+                }
+
+                handler(serializedValue.get(), { });
+            }];
+        }
+    }
+
 private:
     RetainPtr<WKUserContentController> m_controller;
-    RetainPtr<id <WKScriptMessageHandler>> m_handler;
+    RetainPtr<id> m_handler;
     RetainPtr<NSString> m_name;
+    bool m_supportsAsyncReply;
 };
 
+- (void)_addScriptMessageHandler:(WebKit::WebScriptMessageHandler&)scriptMessageHandler
+{
+    if (!_userContentControllerProxy->addUserScriptMessageHandler(scriptMessageHandler))
+        [NSException raise:NSInvalidArgumentException format:@"Attempt to add script message handler with name '%@' when one already exists.", (NSString *)scriptMessageHandler.name()];
+}
+
 - (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name
 {
     auto handler = WebKit::WebScriptMessageHandler::create(makeUnique<ScriptMessageHandlerDelegate>(self, scriptMessageHandler, name), name, API::ContentWorld::pageContentWorld());
-    if (!_userContentControllerProxy->addUserScriptMessageHandler(handler.get()))
-        [NSException raise:NSInvalidArgumentException format:@"Attempt to add script message handler with name '%@' when one already exists.", name];
+    [self _addScriptMessageHandler:handler.get()];
 }
 
+- (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler contentWorld:(WKContentWorld *)world name:(NSString *)name
+{
+    auto handler = WebKit::WebScriptMessageHandler::create(makeUnique<ScriptMessageHandlerDelegate>(self, scriptMessageHandler, name), name, *world->_contentWorld);
+    [self _addScriptMessageHandler:handler.get()];
+}
+
+- (void)addScriptMessageHandlerWithReply:(id <WKScriptMessageHandlerWithReply>)scriptMessageHandler contentWorld:(WKContentWorld *)world name:(NSString *)name
+{
+    auto handler = WebKit::WebScriptMessageHandler::create(makeUnique<ScriptMessageHandlerDelegate>(self, scriptMessageHandler, name), name, *world->_contentWorld);
+    [self _addScriptMessageHandler:handler.get()];
+}
+
 - (void)removeScriptMessageHandlerForName:(NSString *)name
 {
     _userContentControllerProxy->removeUserMessageHandlerForName(name, API::ContentWorld::pageContentWorld());
 }
 
+- (void)removeScriptMessageHandlerForName:(NSString *)name contentWorld:(WKContentWorld *)contentWorld
+{
+    _userContentControllerProxy->removeUserMessageHandlerForName(name, *contentWorld->_contentWorld);
+}
+
+- (void)removeAllScriptMessageHandlersFromContentWorld:(WKContentWorld *)contentWorld
+{
+    _userContentControllerProxy->removeAllUserMessageHandlers(*contentWorld->_contentWorld);
+}
+
+- (void)removeAllScriptMessageHandlers
+{
+    _userContentControllerProxy->removeAllUserMessageHandlers();
+}
+
 #pragma mark WKObject protocol implementation
 
 - (API::Object&)_apiObject

Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKUserContentControllerInternal.h (260349 => 260350)


--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKUserContentControllerInternal.h	2020-04-20 05:15:35 UTC (rev 260349)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKUserContentControllerInternal.h	2020-04-20 05:55:13 UTC (rev 260350)
@@ -29,11 +29,11 @@
 #import "WebUserContentControllerProxy.h"
 
 namespace WebKit {
-
 template<> struct WrapperTraits<WebUserContentControllerProxy> {
     using WrapperClass = WKUserContentController;
 };
 
+class WebScriptMessageHandler;
 }
 
 @interface WKUserContentController () <WKObject> {
@@ -40,4 +40,7 @@
 @package
     API::ObjectStorage<WebKit::WebUserContentControllerProxy> _userContentControllerProxy;
 }
+
+- (void)_addScriptMessageHandler:(WebKit::WebScriptMessageHandler&)scriptMessageHandler;
+
 @end

Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKUserContentControllerPrivate.h (260349 => 260350)


--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKUserContentControllerPrivate.h	2020-04-20 05:15:35 UTC (rev 260349)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKUserContentControllerPrivate.h	2020-04-20 05:55:13 UTC (rev 260350)
@@ -25,6 +25,7 @@
 
 #import <WebKit/WKUserContentController.h>
 
+@class WKContentWorld;
 @class WKUserScript;
 @class _WKUserContentFilter;
 @class _WKUserContentWorld;

Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm (260349 => 260350)


--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm	2020-04-20 05:15:35 UTC (rev 260349)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm	2020-04-20 05:55:13 UTC (rev 260350)
@@ -898,15 +898,13 @@
 
     for (id key in arguments) {
         id value = [arguments objectForKey:key];
-        if (!validateArgument(value)) {
-            errorMessage = @"Function argument values must be one of the following types, or contain only the following types: NSString, NSNumber, NSDate, NSArray, and NSDictionary";
+        auto serializedValue = API::SerializedScriptValue::createFromNSObject(value);
+        if (!serializedValue) {
+            errorMessage = @"Function argument values must be one of the following types, or contain only the following types: NSNumber, NSNull, NSDate, NSString, NSArray, and NSDictionary";
             break;
         }
-    
-        auto wireBytes = API::SerializedScriptValue::wireBytesFromNSObject(value);
-        // Since we've validated the input dictionary above, we should never fail to serialize it into wire bytes.
-        ASSERT(wireBytes);
-        argumentsMap->set(key, *wireBytes);
+
+        argumentsMap->set(key, serializedValue->internalRepresentation().toWireBytes());
     }
 
     if (errorMessage && handler) {

Modified: trunk/Source/WebKit/UIProcess/API/glib/WebKitUserContentManager.cpp (260349 => 260350)


--- trunk/Source/WebKit/UIProcess/API/glib/WebKitUserContentManager.cpp	2020-04-20 05:15:35 UTC (rev 260349)
+++ trunk/Source/WebKit/UIProcess/API/glib/WebKitUserContentManager.cpp	2020-04-20 05:55:13 UTC (rev 260350)
@@ -190,7 +190,7 @@
     {
     }
 
-    void didPostMessage(WebPageProxy&, FrameInfoData&&, WebCore::SerializedScriptValue& serializedScriptValue) override
+    void didPostMessage(WebPageProxy&, FrameInfoData&&, API::ContentWorld&, WebCore::SerializedScriptValue& serializedScriptValue) override
     {
         WebKitJavascriptResult* jsResult = webkitJavascriptResultCreate(serializedScriptValue);
         g_signal_emit(m_manager, signals[SCRIPT_MESSAGE_RECEIVED], m_handlerName, jsResult);
@@ -197,6 +197,15 @@
         webkit_javascript_result_unref(jsResult);
     }
 
+    bool supportsAsyncReply() override
+    {
+        return false;
+    }
+    
+    void didPostMessageWithAsyncReply(WebPageProxy&, FrameInfoData&&, API::ContentWorld&, WebCore::SerializedScriptValue&, WTF::Function<void(API::SerializedScriptValue*, const String&)>&&) override
+    {
+    }
+    
     virtual ~ScriptMessageClientGtk() { }
 
 private:

Modified: trunk/Source/WebKit/UIProcess/API/gtk/WebKitRemoteInspectorProtocolHandler.cpp (260349 => 260350)


--- trunk/Source/WebKit/UIProcess/API/gtk/WebKitRemoteInspectorProtocolHandler.cpp	2020-04-20 05:15:35 UTC (rev 260349)
+++ trunk/Source/WebKit/UIProcess/API/gtk/WebKitRemoteInspectorProtocolHandler.cpp	2020-04-20 05:55:13 UTC (rev 260350)
@@ -42,7 +42,7 @@
     {
     }
 
-    void didPostMessage(WebPageProxy& page, FrameInfoData&&, WebCore::SerializedScriptValue& serializedScriptValue) override
+    void didPostMessage(WebPageProxy& page, FrameInfoData&&, API::ContentWorld&, WebCore::SerializedScriptValue& serializedScriptValue) override
     {
         String message = serializedScriptValue.toString();
         Vector<String> tokens = message.split(':');
@@ -53,6 +53,15 @@
         m_inspectorProtocolHandler.inspect(requestURL.hostAndPort(), tokens[0].toUInt64(), tokens[1].toUInt64(), tokens[2]);
     }
 
+    bool supportsAsyncReply() override
+    {
+        return false;
+    }
+    
+    void didPostMessageWithAsyncReply(WebPageProxy&, FrameInfoData&&, API::ContentWorld&, WebCore::SerializedScriptValue&, WTF::Function<void(API::SerializedScriptValue*, const String&)>&&) override
+    {
+    }
+
     ~ScriptMessageClient() { }
 
 private:

Modified: trunk/Source/WebKit/UIProcess/Inspector/socket/RemoteInspectorProtocolHandler.cpp (260349 => 260350)


--- trunk/Source/WebKit/UIProcess/Inspector/socket/RemoteInspectorProtocolHandler.cpp	2020-04-20 05:15:35 UTC (rev 260349)
+++ trunk/Source/WebKit/UIProcess/Inspector/socket/RemoteInspectorProtocolHandler.cpp	2020-04-20 05:55:13 UTC (rev 260350)
@@ -52,7 +52,7 @@
 
     ~ScriptMessageClient() { }
 
-    void didPostMessage(WebPageProxy& page, FrameInfoData&&, WebCore::SerializedScriptValue& serializedScriptValue) override
+    void didPostMessage(WebPageProxy& page, FrameInfoData&&, API::ContentWorld&, WebCore::SerializedScriptValue& serializedScriptValue) override
     {
         auto tokens = serializedScriptValue.toString().split(":");
         if (tokens.size() != 3)
@@ -61,6 +61,15 @@
         URL requestURL { { }, page.pageLoadState().url() };
         m_inspectorProtocolHandler.inspect(requestURL.hostAndPort(), tokens[0].toUIntStrict(), tokens[1].toUIntStrict(), tokens[2]);
     }
+    
+    bool supportsAsyncReply() override
+    {
+        return false;
+    }
+    
+    void didPostMessageWithAsyncReply(WebPageProxy&, FrameInfoData&&, API::ContentWorld&, WebCore::SerializedScriptValue&, WTF::Function<void(API::SerializedScriptValue*, const String&)>&&) override
+    {
+    }
 
 private:
     RemoteInspectorProtocolHandler& m_inspectorProtocolHandler;

Modified: trunk/Source/WebKit/UIProcess/UserContent/WebScriptMessageHandler.h (260349 => 260350)


--- trunk/Source/WebKit/UIProcess/UserContent/WebScriptMessageHandler.h	2020-04-20 05:15:35 UTC (rev 260349)
+++ trunk/Source/WebKit/UIProcess/UserContent/WebScriptMessageHandler.h	2020-04-20 05:55:13 UTC (rev 260350)
@@ -39,6 +39,7 @@
 
 namespace API {
 class ContentWorld;
+class SerializedScriptValue;
 }
 
 namespace WebKit {
@@ -52,7 +53,9 @@
     class Client {
     public:
         virtual ~Client() { }
-        virtual void didPostMessage(WebPageProxy&, FrameInfoData&&, WebCore::SerializedScriptValue&) = 0;
+        virtual void didPostMessage(WebPageProxy&, FrameInfoData&&, API::ContentWorld&, WebCore::SerializedScriptValue&) = 0;
+        virtual bool supportsAsyncReply() = 0;
+        virtual void didPostMessageWithAsyncReply(WebPageProxy&, FrameInfoData&&, API::ContentWorld&, WebCore::SerializedScriptValue&, WTF::Function<void(API::SerializedScriptValue*, const String&)>&&) = 0;
     };
 
     static Ref<WebScriptMessageHandler> create(std::unique_ptr<Client>, const String& name, API::ContentWorld&);

Modified: trunk/Source/WebKit/UIProcess/UserContent/WebUserContentControllerProxy.cpp (260349 => 260350)


--- trunk/Source/WebKit/UIProcess/UserContent/WebUserContentControllerProxy.cpp	2020-04-20 05:15:35 UTC (rev 260349)
+++ trunk/Source/WebKit/UIProcess/UserContent/WebUserContentControllerProxy.cpp	2020-04-20 05:55:13 UTC (rev 260350)
@@ -28,6 +28,7 @@
 
 #include "APIArray.h"
 #include "APIContentWorld.h"
+#include "APISerializedScriptValue.h"
 #include "APIUserScript.h"
 #include "APIUserStyleSheet.h"
 #include "DataReference.h"
@@ -316,7 +317,7 @@
 void WebUserContentControllerProxy::removeAllUserMessageHandlers(API::ContentWorld& world)
 {
     for (auto& process : m_processes)
-        process.send(Messages::WebUserContentController::RemoveAllUserScriptMessageHandlers({ world.identifier() }), identifier());
+        process.send(Messages::WebUserContentController::RemoveAllUserScriptMessageHandlersForWorlds({ world.identifier() }), identifier());
 
     unsigned numberRemoved = 0;
     m_scriptMessageHandlers.removeIf([&](auto& entry) {
@@ -328,8 +329,14 @@
     });
 }
 
-void WebUserContentControllerProxy::didPostMessage(IPC::Connection& connection, WebPageProxyIdentifier pageProxyID, FrameInfoData&& frameInfoData, uint64_t messageHandlerID, const IPC::DataReference& dataReference)
+void WebUserContentControllerProxy::removeAllUserMessageHandlers()
 {
+    for (auto& process : m_processes)
+        process.send(Messages::WebUserContentController::RemoveAllUserScriptMessageHandlers(), identifier());
+}
+
+void WebUserContentControllerProxy::didPostMessage(WebPageProxyIdentifier pageProxyID, FrameInfoData&& frameInfoData, uint64_t messageHandlerID, const IPC::DataReference& dataReference, Messages::WebUserContentControllerProxy::DidPostMessage::AsyncReply&& reply)
+{
     WebPageProxy* page = WebProcessProxy::webPage(pageProxyID);
     if (!page)
         return;
@@ -341,7 +348,21 @@
     if (!handler)
         return;
 
-    handler->client().didPostMessage(*page, WTFMove(frameInfoData), WebCore::SerializedScriptValue::adopt(dataReference.vector()));
+    if (!handler->client().supportsAsyncReply()) {
+        handler->client().didPostMessage(*page, WTFMove(frameInfoData), handler->world(),  WebCore::SerializedScriptValue::adopt(dataReference.vector()));
+        reply({ }, { });
+        return;
+    }
+
+    handler->client().didPostMessageWithAsyncReply(*page, WTFMove(frameInfoData), handler->world(),  WebCore::SerializedScriptValue::adopt(dataReference.vector()), [reply = WTFMove(reply)](API::SerializedScriptValue* value, const String& errorMessage) mutable {
+        if (errorMessage.isNull()) {
+            ASSERT(value);
+            reply({ value->internalRepresentation().toWireBytes() }, { });
+            return;
+        }
+
+        reply({ }, errorMessage);
+    });
 }
 
 #if ENABLE(CONTENT_EXTENSIONS)

Modified: trunk/Source/WebKit/UIProcess/UserContent/WebUserContentControllerProxy.h (260349 => 260350)


--- trunk/Source/WebKit/UIProcess/UserContent/WebUserContentControllerProxy.h	2020-04-20 05:15:35 UTC (rev 260349)
+++ trunk/Source/WebKit/UIProcess/UserContent/WebUserContentControllerProxy.h	2020-04-20 05:55:13 UTC (rev 260350)
@@ -30,6 +30,7 @@
 #include "MessageReceiver.h"
 #include "UserContentControllerIdentifier.h"
 #include "WebPageProxyIdentifier.h"
+#include "WebUserContentControllerProxyMessages.h"
 #include <WebCore/PageIdentifier.h>
 #include <wtf/Forward.h>
 #include <wtf/HashCountedSet.h>
@@ -98,6 +99,7 @@
     bool addUserScriptMessageHandler(WebScriptMessageHandler&);
     void removeUserMessageHandlerForName(const String&, API::ContentWorld&);
     void removeAllUserMessageHandlers(API::ContentWorld&);
+    void removeAllUserMessageHandlers();
 
 #if ENABLE(CONTENT_EXTENSIONS)
     void addNetworkProcess(NetworkProcessProxy&);
@@ -118,7 +120,7 @@
     // IPC::MessageReceiver.
     void didReceiveMessage(IPC::Connection&, IPC::Decoder&) override;
 
-    void didPostMessage(IPC::Connection&, WebPageProxyIdentifier, FrameInfoData&&, uint64_t messageHandlerID, const IPC::DataReference&);
+    void didPostMessage(WebPageProxyIdentifier, FrameInfoData&&, uint64_t messageHandlerID, const IPC::DataReference&, Messages::WebUserContentControllerProxy::DidPostMessage::AsyncReply&&);
 
     void addContentWorld(API::ContentWorld&);
 

Modified: trunk/Source/WebKit/UIProcess/UserContent/WebUserContentControllerProxy.messages.in (260349 => 260350)


--- trunk/Source/WebKit/UIProcess/UserContent/WebUserContentControllerProxy.messages.in	2020-04-20 05:15:35 UTC (rev 260349)
+++ trunk/Source/WebKit/UIProcess/UserContent/WebUserContentControllerProxy.messages.in	2020-04-20 05:55:13 UTC (rev 260350)
@@ -24,5 +24,5 @@
  */
 
 messages -> WebUserContentControllerProxy {
-DidPostMessage(WebKit::WebPageProxyIdentifier pageID, struct WebKit::FrameInfoData frameInfoData, uint64_t messageHandlerID, IPC::DataReference message) WantsConnection
+DidPostMessage(WebKit::WebPageProxyIdentifier pageID, struct WebKit::FrameInfoData frameInfoData, uint64_t messageHandlerID, IPC::DataReference message) -> (IPC::DataReference resultValue, String errorMessage) Async
 }

Modified: trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj (260349 => 260350)


--- trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj	2020-04-20 05:15:35 UTC (rev 260349)
+++ trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj	2020-04-20 05:55:13 UTC (rev 260350)
@@ -973,6 +973,7 @@
 		510523741C73D38B007993CB /* WebIDBConnectionToServerMessages.h in Headers */ = {isa = PBXBuildFile; fileRef = 510523731C73D37B007993CB /* WebIDBConnectionToServerMessages.h */; };
 		510523751C73D38F007993CB /* WebIDBConnectionToServerMessageReceiver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 510523721C73D37B007993CB /* WebIDBConnectionToServerMessageReceiver.cpp */; };
 		5106D7C418BDBE73000AB166 /* ContextMenuContextData.h in Headers */ = {isa = PBXBuildFile; fileRef = 5106D7C018BDBE73000AB166 /* ContextMenuContextData.h */; };
+		5109099723DACBF2003B1E4C /* WKScriptMessageHandlerWithReply.h in Headers */ = {isa = PBXBuildFile; fileRef = 5109099423D41F13003B1E4C /* WKScriptMessageHandlerWithReply.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		510AFFBA16542048001BA05E /* WebResourceLoader.h in Headers */ = {isa = PBXBuildFile; fileRef = 510AFFB816542048001BA05E /* WebResourceLoader.h */; };
 		510F59101DDE296900412FF5 /* _WKIconLoadingDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 5143B25E1DDCDFD10014FAC6 /* _WKIconLoadingDelegate.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		510F59111DDE297000412FF5 /* _WKLinkIconParameters.h in Headers */ = {isa = PBXBuildFile; fileRef = 51C0C9791DDD78540032CAD3 /* _WKLinkIconParameters.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -3599,6 +3600,7 @@
 		5105B0F31630872E00E27709 /* NetworkProcessProxy.messages.in */ = {isa = PBXFileReference; lastKnownFileType = text; path = NetworkProcessProxy.messages.in; sourceTree = "<group>"; };
 		5106D7BF18BDBE73000AB166 /* ContextMenuContextData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ContextMenuContextData.cpp; sourceTree = "<group>"; };
 		5106D7C018BDBE73000AB166 /* ContextMenuContextData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ContextMenuContextData.h; sourceTree = "<group>"; };
+		5109099423D41F13003B1E4C /* WKScriptMessageHandlerWithReply.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKScriptMessageHandlerWithReply.h; sourceTree = "<group>"; };
 		510AFFB716542048001BA05E /* WebResourceLoader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = WebResourceLoader.cpp; path = Network/WebResourceLoader.cpp; sourceTree = "<group>"; };
 		510AFFB816542048001BA05E /* WebResourceLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WebResourceLoader.h; path = Network/WebResourceLoader.h; sourceTree = "<group>"; };
 		510AFFCE16542CBD001BA05E /* WebResourceLoader.messages.in */ = {isa = PBXFileReference; lastKnownFileType = text; name = WebResourceLoader.messages.in; path = Network/WebResourceLoader.messages.in; sourceTree = "<group>"; };
@@ -7394,6 +7396,7 @@
 				1A7E377718E4A4FE003D0FFF /* WKScriptMessage.h */,
 				1A7E377618E4A4FE003D0FFF /* WKScriptMessage.mm */,
 				1A7E377418E4A33A003D0FFF /* WKScriptMessageHandler.h */,
+				5109099423D41F13003B1E4C /* WKScriptMessageHandlerWithReply.h */,
 				7CC99A3518EF7CBC0048C8B4 /* WKScriptMessageInternal.h */,
 				51CD1C5F1B34B9C900142CA5 /* WKSecurityOrigin.h */,
 				51CD1C601B34B9C900142CA5 /* WKSecurityOrigin.mm */,
@@ -11320,6 +11323,7 @@
 				BCEE7AD112817988009827DA /* WebProcessProxyMessages.h in Headers */,
 				BCE0E425168B7A280057E66A /* WebProcessSupplement.h in Headers */,
 				1A1E093418861D3800D2DC49 /* WebProgressTrackerClient.h in Headers */,
+				5109099723DACBF2003B1E4C /* WKScriptMessageHandlerWithReply.h in Headers */,
 				512F589D12A8838800629530 /* WebProtectionSpace.h in Headers */,
 				461CCCA6231485AA00B659B9 /* WebRemoteObjectRegistry.h in Headers */,
 				37948404150C350600E52CE9 /* WebRenderLayer.h in Headers */,

Modified: trunk/Source/WebKit/WebProcess/InjectedBundle/API/gtk/DOM/WebKitDOMDOMWindow.cpp (260349 => 260350)


--- trunk/Source/WebKit/WebProcess/InjectedBundle/API/gtk/DOM/WebKitDOMDOMWindow.cpp	2020-04-20 05:15:35 UTC (rev 260349)
+++ trunk/Source/WebKit/WebProcess/InjectedBundle/API/gtk/DOM/WebKitDOMDOMWindow.cpp	2020-04-20 05:55:13 UTC (rev 260350)
@@ -25,6 +25,8 @@
 #include <WebCore/DOMException.h>
 #include <WebCore/Document.h>
 #include "GObjectEventListener.h"
+#include <WebCore/JSDOMGlobalObject.h>
+#include <WebCore/JSDOMPromiseDeferred.h>
 #include <WebCore/JSExecState.h>
 #include <WebCore/SerializedScriptValue.h>
 #include <WebCore/UserMessageHandlersNamespace.h>
@@ -1069,8 +1071,17 @@
     auto handler = webkitNamespace->messageHandlers()->namedItem(WebCore::mainThreadNormalWorld(), String::fromUTF8(handlerName));
     if (!handler)
         return FALSE;
+    
+    auto* scriptExecutionContext = ((WebCore::ContextDestructionObserver*)domWindow)->scriptExecutionContext();
+    if (!scriptExecutionContext)
+        return FALSE;
+    
+    auto* globalObject = toJSDOMGlobalObject(*scriptExecutionContext, WebCore::mainThreadNormalWorld());
+    if (!globalObject)
+        return FALSE;
 
-    auto result = handler->postMessage(WebCore::SerializedScriptValue::create(String::fromUTF8(message)));
+    auto promise = WebCore::DeferredPromise::create(*globalObject);
+    auto result = handler->postMessage(WebCore::SerializedScriptValue::create(String::fromUTF8(message)), adoptRef(*(promise.leakRef())));
     if (result.hasException())
         return FALSE;
 

Modified: trunk/Source/WebKit/WebProcess/UserContent/WebUserContentController.cpp (260349 => 260350)


--- trunk/Source/WebKit/WebProcess/UserContent/WebUserContentController.cpp	2020-04-20 05:15:35 UTC (rev 260349)
+++ trunk/Source/WebKit/WebProcess/UserContent/WebUserContentController.cpp	2020-04-20 05:55:13 UTC (rev 260350)
@@ -254,7 +254,7 @@
     }
 
     // WebCore::UserMessageHandlerDescriptor
-    void didPostMessage(WebCore::UserMessageHandler& handler, WebCore::SerializedScriptValue* value) override
+    void didPostMessage(WebCore::UserMessageHandler& handler, WebCore::SerializedScriptValue* value, WTF::Function<void(SerializedScriptValue*, const String&)>&& completionHandler) override
     {
         WebCore::Frame* frame = handler.frame();
         if (!frame)
@@ -268,7 +268,17 @@
         if (!webPage)
             return;
 
-        WebProcess::singleton().parentProcessConnection()->send(Messages::WebUserContentControllerProxy::DidPostMessage(webPage->webPageProxyIdentifier(), webFrame->info(), m_identifier, IPC::DataReference(value->data())), m_controller->identifier());
+        auto messageReplyHandler = [completionHandler = WTFMove(completionHandler)](const IPC::DataReference& resultValue, const String& errorMessage) {
+            if (!errorMessage.isNull()) {
+                completionHandler(nullptr, errorMessage);
+                return;
+            }
+
+            auto value = SerializedScriptValue::createFromWireBytes(resultValue.vector());
+            completionHandler(value.ptr(), { });
+        };
+
+        WebProcess::singleton().parentProcessConnection()->sendWithAsyncReply(Messages::WebUserContentControllerProxy::DidPostMessage(webPage->webPageProxyIdentifier(), webFrame->info(), m_identifier, IPC::DataReference(value->data())), WTFMove(messageReplyHandler), m_controller->identifier());
     }
 
     RefPtr<WebUserContentController> m_controller;
@@ -309,9 +319,20 @@
 #endif
 }
 
-void WebUserContentController::removeAllUserScriptMessageHandlers(const Vector<ContentWorldIdentifier>& worldIdentifiers)
+void WebUserContentController::removeAllUserScriptMessageHandlers()
 {
 #if ENABLE(USER_MESSAGE_HANDLERS)
+    if (m_userMessageHandlers.isEmpty())
+        return;
+
+    m_userMessageHandlers.clear();
+    invalidateAllRegisteredUserMessageHandlerInvalidationClients();
+#endif
+}
+
+void WebUserContentController::removeAllUserScriptMessageHandlersForWorlds(const Vector<ContentWorldIdentifier>& worldIdentifiers)
+{
+#if ENABLE(USER_MESSAGE_HANDLERS)
     bool userMessageHandlersChanged = false;
     for (auto& worldIdentifier : worldIdentifiers) {
         auto it = worldMap().find(worldIdentifier);

Modified: trunk/Source/WebKit/WebProcess/UserContent/WebUserContentController.h (260349 => 260350)


--- trunk/Source/WebKit/WebProcess/UserContent/WebUserContentController.h	2020-04-20 05:15:35 UTC (rev 260349)
+++ trunk/Source/WebKit/WebProcess/UserContent/WebUserContentController.h	2020-04-20 05:55:13 UTC (rev 260350)
@@ -100,7 +100,8 @@
     void removeAllUserStyleSheets(const Vector<ContentWorldIdentifier>&);
 
     void removeUserScriptMessageHandler(ContentWorldIdentifier, uint64_t userScriptIdentifier);
-    void removeAllUserScriptMessageHandlers(const Vector<ContentWorldIdentifier>&);
+    void removeAllUserScriptMessageHandlersForWorlds(const Vector<ContentWorldIdentifier>&);
+    void removeAllUserScriptMessageHandlers();
 
 #if ENABLE(CONTENT_EXTENSIONS)
     void removeContentRuleList(const String& name);

Modified: trunk/Source/WebKit/WebProcess/UserContent/WebUserContentController.messages.in (260349 => 260350)


--- trunk/Source/WebKit/WebProcess/UserContent/WebUserContentController.messages.in	2020-04-20 05:15:35 UTC (rev 260349)
+++ trunk/Source/WebKit/WebProcess/UserContent/WebUserContentController.messages.in	2020-04-20 05:55:13 UTC (rev 260350)
@@ -37,7 +37,8 @@
 
     AddUserScriptMessageHandlers(Vector<struct WebKit::WebScriptMessageHandlerData> scriptMessageHandlers);
     RemoveUserScriptMessageHandler(WebKit::ContentWorldIdentifier worldIdentifier, uint64_t identifier);
-    RemoveAllUserScriptMessageHandlers(Vector<WebKit::ContentWorldIdentifier> worldIdentifiers);
+    RemoveAllUserScriptMessageHandlersForWorlds(Vector<WebKit::ContentWorldIdentifier> worldIdentifiers);
+    RemoveAllUserScriptMessageHandlers();
 
 #if ENABLE(CONTENT_EXTENSIONS)
     AddContentRuleLists(Vector<std::pair<String, WebKit::WebCompiledContentRuleListData>> contentFilters);

Modified: trunk/Tools/ChangeLog (260349 => 260350)


--- trunk/Tools/ChangeLog	2020-04-20 05:15:35 UTC (rev 260349)
+++ trunk/Tools/ChangeLog	2020-04-20 05:55:13 UTC (rev 260350)
@@ -1,3 +1,15 @@
+2020-04-19  Brady Eidson  <beid...@apple.com>
+
+        Add WKScriptMessageHandler API that asynchronously responds with a promise.
+        rdar://problem/57243492 and https://bugs.webkit.org/show_bug.cgi?id=206398
+
+        Reviewed by Andy Estes.
+
+        * TestWebKitAPI/Tests/WebKitCocoa/UserContentController.mm:
+        (TEST):
+        (webViewForScriptMessageHandlerMultipleHandlerRemovalTest):
+        (-[AsyncScriptMessageHandler userContentController:didReceiveScriptMessage:replyHandler:]):
+
 2020-04-19  Ross Kirsling  <ross.kirsl...@sony.com>
 
         [ECMA-402] Intl.RelativeTimeFormat missing in WebKit

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/UserContentController.mm (260349 => 260350)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/UserContentController.mm	2020-04-20 05:15:35 UTC (rev 260349)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/UserContentController.mm	2020-04-20 05:55:13 UTC (rev 260350)
@@ -30,7 +30,10 @@
 #import "TestNavigationDelegate.h"
 #import "TestUIDelegate.h"
 #import "TestWKWebView.h"
+#import <WebKit/WKContentWorld.h>
 #import <WebKit/WKProcessPoolPrivate.h>
+#import <WebKit/WKScriptMessage.h>
+#import <WebKit/WKScriptMessageHandlerWithReply.h>
 #import <WebKit/WKUserContentControllerPrivate.h>
 #import <WebKit/WKUserScript.h>
 #import <WebKit/WKUserScriptPrivate.h>
@@ -74,6 +77,9 @@
 
 TEST(WKUserContentController, ScriptMessageHandlerBasicPost)
 {
+    scriptMessages.clear();
+    receivedScriptMessage = false;
+
     RetainPtr<ScriptMessageHandler> handler = adoptNS([[ScriptMessageHandler alloc] init]);
     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
     [[configuration userContentController] addScriptMessageHandler:handler.get() name:@"testHandler"];
@@ -95,6 +101,9 @@
 
 TEST(WKUserContentController, ScriptMessageHandlerBasicPostIsolatedWorld)
 {
+    scriptMessages.clear();
+    receivedScriptMessage = false;
+
     RetainPtr<_WKUserContentWorld> world = [_WKUserContentWorld worldWithName:@"TestWorld"];
 
     RetainPtr<ScriptMessageHandler> handler = adoptNS([[ScriptMessageHandler alloc] init]);
@@ -144,6 +153,9 @@
 
 TEST(WKUserContentController, ScriptMessageHandlerBasicRemove)
 {
+    scriptMessages.clear();
+    receivedScriptMessage = false;
+
     RetainPtr<ScriptMessageHandler> handler = adoptNS([[ScriptMessageHandler alloc] init]);
     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
     RetainPtr<WKUserContentController> userContentController = [configuration userContentController];
@@ -188,6 +200,8 @@
 
 TEST(WKUserContentController, ScriptMessageHandlerCallRemovedHandler)
 {
+    receivedScriptMessage = false;
+
     RetainPtr<ScriptMessageHandler> handler = adoptNS([[ScriptMessageHandler alloc] init]);
     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
     RetainPtr<WKUserContentController> userContentController = [configuration userContentController];
@@ -205,22 +219,23 @@
 
     [userContentController removeScriptMessageHandlerForName:@"handlerToRemove"];
 
+    __block bool done = false;
     // Test that we throw an exception if you try to use a message handler that has been removed.
-    [webView evaluateJavaScript:
-        @"try {"
-         "    handlerToRemove.postMessage('FAIL');"
-         "} catch (e) {"
-         "    window.webkit.messageHandlers.handlerToPost.postMessage('PASS');"
-         "}" completionHandler:nil];
+    [webView callAsyncJavaScript:@"return handlerToRemove.postMessage('FAIL')" arguments:nil inContentWorld:[WKContentWorld pageWorld] completionHandler:^ (id value, NSError * error) {
+        EXPECT_NULL(value);
+        EXPECT_NOT_NULL(error);
+        EXPECT_TRUE([[error description] containsString:@"InvalidAccessError"]);
+        done = true;
+    }];
 
-    TestWebKitAPI::Util::run(&receivedScriptMessage);
+    TestWebKitAPI::Util::run(&done);
     receivedScriptMessage = false;
-
-    EXPECT_WK_STREQ(@"PASS", (NSString *)[scriptMessages[0] body]);
 }
 
 static RetainPtr<WKWebView> webViewForScriptMessageHandlerMultipleHandlerRemovalTest(WKWebViewConfiguration *configuration)
 {
+    receivedScriptMessage = false;
+
     RetainPtr<ScriptMessageHandler> handler = adoptNS([[ScriptMessageHandler alloc] init]);
     RetainPtr<WKWebViewConfiguration> configurationCopy = adoptNS([configuration copy]);
     [configurationCopy setUserContentController:[[[WKUserContentController alloc] init] autorelease]];
@@ -236,6 +251,9 @@
 
 TEST(WKUserContentController, ScriptMessageHandlerMultipleHandlerRemoval)
 {
+    scriptMessages.clear();
+    receivedScriptMessage = false;
+
     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
     RetainPtr<_WKProcessPoolConfiguration> processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
     [configuration setProcessPool:[[[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()] autorelease]];
@@ -262,6 +280,9 @@
 #if !PLATFORM(IOS_FAMILY) // FIXME: hangs in the iOS simulator
 TEST(WKUserContentController, ScriptMessageHandlerWithNavigation)
 {
+    scriptMessages.clear();
+    receivedScriptMessage = false;
+
     RetainPtr<ScriptMessageHandler> handler = adoptNS([[ScriptMessageHandler alloc] init]);
     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
     [[configuration userContentController] addScriptMessageHandler:handler.get() name:@"testHandler"];
@@ -293,6 +314,9 @@
 
 TEST(WKUserContentController, ScriptMessageHandlerReplaceWithSameName)
 {
+    scriptMessages.clear();
+    receivedScriptMessage = false;
+
     RetainPtr<ScriptMessageHandler> handler = adoptNS([[ScriptMessageHandler alloc] init]);
     RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
     RetainPtr<WKUserContentController> userContentController = [configuration userContentController];
@@ -833,6 +857,9 @@
 
 TEST(WKUserContentController, InjectUserScriptImmediately)
 {
+    scriptMessages.clear();
+    receivedScriptMessage = false;
+
     auto handler = adoptNS([[ScriptMessageHandler alloc] init]);
     auto startAllFrames = adoptNS([[WKUserScript alloc] initWithSource:@"window.webkit.messageHandlers.testHandler.postMessage('start all')" injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO]);
     auto endMainFrameOnly = adoptNS([[WKUserScript alloc] initWithSource:@"window.webkit.messageHandlers.testHandler.postMessage('end main')" injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES]);
@@ -902,3 +929,274 @@
     EXPECT_WK_STREQ([delegate waitForAlert], "waited for notification");
     EXPECT_WK_STREQ([delegate waitForAlert], "document parsing ended");
 }
+
+@interface AsyncScriptMessageHandler : NSObject <WKScriptMessageHandlerWithReply>
+@end
+
+@implementation AsyncScriptMessageHandler
+
+- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message replyHandler:(void (^)(id, NSString *errorMessage))replyHandler
+{
+    if ([message.name isEqualToString:@"otherWorldHandler"])
+        EXPECT_TRUE(message.world != nil);
+    if ([message.body isKindOfClass:[NSString class]]) {
+        if ([message.body isEqualToString:@"Fulfill"]) {
+            replyHandler(@"Fulfilled!", nil);
+            return;
+        }
+        if ([message.body isEqualToString:@"Reject"]) {
+            replyHandler(nil, @"Rejected!");
+            return;
+        }
+        if ([message.body isEqualToString:@"Undefined"]) {
+            replyHandler(nil, nil);
+            return;
+        }
+        if ([message.body isEqualToString:@"Do nothing"]) {
+            // Drop the reply handler without responding to see what happens
+            return;
+        }
+        if ([message.body isEqualToString:@"Invalid reply"]) {
+            replyHandler([[[NSData alloc] init] autorelease], nil);
+            return;
+        }
+    }
+
+    // All other inputs should just be round tripped back to the message handler
+    replyHandler(message.body, nil);
+}
+
+@end
+
+TEST(WKUserContentController, MessageHandlerAPI)
+{
+    RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+    auto handler = adoptNS([[AsyncScriptMessageHandler alloc] init]);
+    [[configuration userContentController] addScriptMessageHandlerWithReply:handler.get() contentWorld:WKContentWorld.pageWorld name:@"testHandler1"];
+
+    bool hadException = false;
+    @try {
+        [[configuration userContentController] addScriptMessageHandlerWithReply:handler.get() contentWorld:WKContentWorld.pageWorld name:@"testHandler1"];
+    } @catch (NSException *exception) {
+        hadException = true;
+    }
+
+    EXPECT_TRUE(hadException);
+    [[configuration userContentController] addScriptMessageHandlerWithReply:handler.get() contentWorld:WKContentWorld.defaultClientWorld name:@"testHandler2"];
+
+    auto *world = [WKContentWorld worldWithName:@"otherWorld"];
+    [[configuration userContentController] addScriptMessageHandlerWithReply:handler.get() contentWorld:world name:@"testHandler3"];
+    [[configuration userContentController] addScriptMessageHandlerWithReply:handler.get() contentWorld:world name:@"testHandler4"];
+    [[configuration userContentController] addScriptMessageHandlerWithReply:handler.get() contentWorld:world name:@"testHandler5"];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
+
+    bool done = false;
+    NSString *functionBody = @"var p = window.webkit.messageHandlers[handler].postMessage(arg); await p; return p;";
+
+    // pageWorld is where testhandler1 lives
+    [webView callAsyncJavaScript:functionBody arguments:@{ @"handler" : @"testHandler1", @"arg" : @1 } inContentWorld:WKContentWorld.pageWorld completionHandler:[&] (id result, NSError *error) {
+        EXPECT_NULL(error);
+        EXPECT_TRUE([result isEqualToNumber:@1]);
+        done = true;
+    }];
+    TestWebKitAPI::Util::run(&done);
+    done = false;
+
+    // Trying to find testHandler1 in the defaultClientWorld should fail
+    [webView callAsyncJavaScript:functionBody arguments:@{ @"handler" : @"testHandler1", @"arg" : @1 } inContentWorld:WKContentWorld.defaultClientWorld completionHandler:[&] (id result, NSError *error) {
+        EXPECT_NULL(result);
+        EXPECT_NOT_NULL(error);
+        done = true;
+    }];
+    TestWebKitAPI::Util::run(&done);
+    done = false;
+
+    // defaultClientWorld is where testhandler2 lives
+    [webView callAsyncJavaScript:functionBody arguments:@{ @"handler" : @"testHandler2", @"arg" : @1 } inContentWorld:WKContentWorld.defaultClientWorld completionHandler:[&] (id result, NSError *error) {
+        EXPECT_NULL(error);
+        EXPECT_TRUE([result isEqualToNumber:@1]);
+        done = true;
+    }];
+    TestWebKitAPI::Util::run(&done);
+    done = false;
+
+    // But if we remvoe it, it should no longer live there, and using it should cause an error.
+    [[configuration userContentController] removeScriptMessageHandlerForName:@"testHandler2" contentWorld:WKContentWorld.defaultClientWorld];
+    [webView callAsyncJavaScript:functionBody arguments:@{ @"handler" : @"testHandler2", @"arg" : @1 } inContentWorld:WKContentWorld.defaultClientWorld completionHandler:[&] (id result, NSError *error) {
+        EXPECT_NULL(result);
+        EXPECT_NOT_NULL(error);
+        done = true;
+    }];
+    TestWebKitAPI::Util::run(&done);
+    done = false;
+
+    // Verify handlers 3, 4, and 5 are all in the custom world.
+    [webView callAsyncJavaScript:functionBody arguments:@{ @"handler" : @"testHandler3", @"arg" : @1 } inContentWorld:world completionHandler:[&] (id result, NSError *error) {
+        EXPECT_NULL(error);
+        EXPECT_TRUE([result isEqualToNumber:@1]);
+    }];
+    [webView callAsyncJavaScript:functionBody arguments:@{ @"handler" : @"testHandler4", @"arg" : @1 } inContentWorld:world completionHandler:[&] (id result, NSError *error) {
+        EXPECT_NULL(error);
+        EXPECT_TRUE([result isEqualToNumber:@1]);
+    }];
+    [webView callAsyncJavaScript:functionBody arguments:@{ @"handler" : @"testHandler5", @"arg" : @1 } inContentWorld:world completionHandler:[&] (id result, NSError *error) {
+        EXPECT_NULL(error);
+        EXPECT_TRUE([result isEqualToNumber:@1]);
+        done = true;
+    }];
+    TestWebKitAPI::Util::run(&done);
+    done = false;
+
+    // Remove 3 from the wrong world, verify it is still there in the custom world.
+    [[configuration userContentController] removeScriptMessageHandlerForName:@"testHandler3" contentWorld:WKContentWorld.defaultClientWorld];
+    [webView callAsyncJavaScript:functionBody arguments:@{ @"handler" : @"testHandler3", @"arg" : @1 } inContentWorld:world completionHandler:[&] (id result, NSError *error) {
+        EXPECT_NULL(error);
+        EXPECT_TRUE([result isEqualToNumber:@1]);
+        done = true;
+    }];
+    TestWebKitAPI::Util::run(&done);
+    done = false;
+
+    // Remove 3 from the correct world, verify it is gone, but 4 and 5 are still there.
+    [[configuration userContentController] removeScriptMessageHandlerForName:@"testHandler3" contentWorld:world];
+    [webView callAsyncJavaScript:functionBody arguments:@{ @"handler" : @"testHandler3", @"arg" : @1 } inContentWorld:world completionHandler:[&] (id result, NSError *error) {
+        EXPECT_NULL(result);
+        EXPECT_NOT_NULL(error);
+    }];
+    [webView callAsyncJavaScript:functionBody arguments:@{ @"handler" : @"testHandler4", @"arg" : @1 } inContentWorld:world completionHandler:[&] (id result, NSError *error) {
+        EXPECT_NULL(error);
+        EXPECT_TRUE([result isEqualToNumber:@1]);
+    }];
+    [webView callAsyncJavaScript:functionBody arguments:@{ @"handler" : @"testHandler5", @"arg" : @1 } inContentWorld:world completionHandler:[&] (id result, NSError *error) {
+        EXPECT_NULL(error);
+        EXPECT_TRUE([result isEqualToNumber:@1]);
+        done = true;
+    }];
+    TestWebKitAPI::Util::run(&done);
+    done = false;
+
+    // Remove "all" in the custom world, verify 4 and 5 are now gone.
+    [[configuration userContentController] removeAllScriptMessageHandlersFromContentWorld:world];
+    [webView callAsyncJavaScript:functionBody arguments:@{ @"handler" : @"testHandler4", @"arg" : @1 } inContentWorld:world completionHandler:[&] (id result, NSError *error) {
+        EXPECT_NULL(result);
+        EXPECT_NOT_NULL(error);
+    }];
+    [webView callAsyncJavaScript:functionBody arguments:@{ @"handler" : @"testHandler5", @"arg" : @1 } inContentWorld:world completionHandler:[&] (id result, NSError *error) {
+        EXPECT_NULL(result);
+        EXPECT_NOT_NULL(error);
+        done = true;
+    }];
+    TestWebKitAPI::Util::run(&done);
+    done = false;
+}
+
+TEST(WKUserContentController, AsyncScriptMessageHandlerBasicPost)
+{
+    RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+    auto handler = adoptNS([[AsyncScriptMessageHandler alloc] init]);
+    [[configuration userContentController] addScriptMessageHandlerWithReply:handler.get() contentWorld:WKContentWorld.pageWorld name:@"testHandler"];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
+
+    bool done = false;
+    NSString *functionBody = @"var p = window.webkit.messageHandlers.testHandler.postMessage('Fulfill'); await p; return p;";
+    [webView callAsyncJavaScript:functionBody arguments:nil inContentWorld:WKContentWorld.pageWorld completionHandler:[&] (id result, NSError *error) {
+        EXPECT_NULL(error);
+        EXPECT_TRUE([result isKindOfClass:[NSString class]]);
+        EXPECT_TRUE([result isEqualToString:@"Fulfilled!"]);
+        done = true;
+    }];
+
+    TestWebKitAPI::Util::run(&done);
+
+    done = false;
+    functionBody = @"var p = window.webkit.messageHandlers.testHandler.postMessage('Reject'); await p; return p;";
+    [webView callAsyncJavaScript:functionBody arguments:nil inContentWorld:WKContentWorld.pageWorld completionHandler:[&] (id result, NSError *error) {
+        EXPECT_NULL(result);
+        EXPECT_TRUE(!!error);
+        EXPECT_TRUE([[error description] containsString:@"Rejected!"]);
+        
+        done = true;
+    }];
+
+    TestWebKitAPI::Util::run(&done);
+
+    done = false;
+    functionBody = @"var p = window.webkit.messageHandlers.testHandler.postMessage('Undefined'); var result = await p; return result == undefined ? 'Yes' : 'No'";
+    [webView callAsyncJavaScript:functionBody arguments:nil inContentWorld:WKContentWorld.pageWorld completionHandler:[&] (id result, NSError *error) {
+        EXPECT_NULL(error);
+        EXPECT_TRUE([result isKindOfClass:[NSString class]]);
+        EXPECT_TRUE([result isEqualToString:@"Yes"]);
+
+        done = true;
+    }];
+
+    TestWebKitAPI::Util::run(&done);
+
+    done = false;
+    functionBody = @"var p = window.webkit.messageHandlers.testHandler.postMessage('Do nothing'); await p; return p;";
+    [webView callAsyncJavaScript:functionBody arguments:nil inContentWorld:WKContentWorld.pageWorld completionHandler:[&] (id result, NSError *error) {
+        EXPECT_NULL(result);
+        EXPECT_TRUE(!!error);
+        EXPECT_TRUE([[error description] containsString:@"did not respond to this postMessage"]);
+
+        done = true;
+    }];
+
+    TestWebKitAPI::Util::run(&done);
+
+    done = false;
+    functionBody = @"var p = window.webkit.messageHandlers.testHandler.postMessage('Invalid reply'); await p; return p;";
+    [webView callAsyncJavaScript:functionBody arguments:nil inContentWorld:WKContentWorld.pageWorld completionHandler:[&] (id result, NSError *error) {
+        EXPECT_NULL(result);
+        EXPECT_TRUE(!!error);
+        EXPECT_TRUE([[error description] containsString:@"unable to be serialized"]);
+
+        done = true;
+    }];
+
+    TestWebKitAPI::Util::run(&done);
+}
+
+TEST(WKUserContentController, WorldLifetime)
+{
+    RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+    auto handler = adoptNS([[AsyncScriptMessageHandler alloc] init]);
+
+    RetainPtr<WKContentWorld> world = [WKContentWorld worldWithName:@"otherWorld"];
+    [[configuration userContentController] addScriptMessageHandlerWithReply:handler.get() contentWorld:world.get() name:@"testHandler"];
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
+
+    // Set a variable in the world.
+    bool done = false;
+    [webView evaluateJavaScript:@"var foo = 'bar'" inContentWorld:world.get() completionHandler:[&] (id result, NSError *error) {
+        EXPECT_NULL(error);
+        done = true;
+    }];
+    TestWebKitAPI::Util::run(&done);
+    done = false;
+
+    // Have the message handler bounce back that value.
+    NSString *functionBody = @"var p = window.webkit.messageHandlers.testHandler.postMessage(foo); await p; return p;";
+    [webView callAsyncJavaScript:functionBody arguments:nil inContentWorld:world.get() completionHandler:[&] (id result, NSError *error) {
+        EXPECT_NULL(error);
+        EXPECT_TRUE([result isKindOfClass:[NSString class]]);
+        EXPECT_TRUE([result isEqualToString:@"bar"]);
+        done = true;
+    }];
+    TestWebKitAPI::Util::run(&done);
+    done = false;
+
+    // Remove the message handler, which used to cause the world to be destroyed in the web process.
+    // But by evaluating JS make sure the value is still there.
+    [[configuration userContentController] removeAllScriptMessageHandlersFromContentWorld:world.get()];
+    [webView evaluateJavaScript:@"foo" inContentWorld:world.get() completionHandler:[&] (id result, NSError *error) {
+        EXPECT_NULL(error);
+        EXPECT_TRUE([result isKindOfClass:[NSString class]]);
+        EXPECT_TRUE([result isEqualToString:@"bar"]);
+        done = true;
+    }];
+    TestWebKitAPI::Util::run(&done);
+    done = false;
+}
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to