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;
+}