Title: [263727] trunk
Revision
263727
Author
beid...@apple.com
Date
2020-06-29 22:43:19 -0700 (Mon, 29 Jun 2020)

Log Message

_javascript_ cannot be injected into iframes
<rdar://problem/54168946> and https://bugs.webkit.org/show_bug.cgi?id=213556

Reviewed by Geoff Garen.
Source/WebCore:

Covered by API tests.

* bindings/js/ExceptionDetails.h: Start a collection of "exception types" that will grow quickly,
  beginning with the specialized "missing frame" type.

Source/WebKit:

This adds a few mechanisms:
- Allows for WKUserScripts to have a target content world
- Allows "evaluateJavaScript" and "callAsyncJavaScript" to target a specific frame
- Allows for the completion handlers of those methods to be able to distinguish
  failure-due-to-missing-frame.

* Shared/WebCoreArgumentCoders.cpp:
(IPC::ArgumentCoder<ExceptionDetails>::encode):
(IPC::ArgumentCoder<ExceptionDetails>::decode):
* Shared/WebCoreArgumentCoders.h:

* UIProcess/API/Cocoa/WKError.h:
* UIProcess/API/Cocoa/WKError.mm:
(localizedDescriptionForErrorCode):

* UIProcess/API/Cocoa/WKUserScript.h:
* UIProcess/API/Cocoa/WKUserScript.mm:
(-[WKUserScript initWithSource:injectionTime:forMainFrameOnly:]):
(-[WKUserScript initWithSource:injectionTime:forMainFrameOnly:inContentWorld:]):
* UIProcess/API/Cocoa/WKWebView.h:

* UIProcess/API/Cocoa/WKWebView.mm:
(-[WKWebView evaluateJavaScript:inFrame:inContentWorld:completionHandler:]):
(-[WKWebView callAsyncJavaScript:arguments:inFrame:inContentWorld:completionHandler:]):
(nsErrorFromExceptionDetails):
(-[WKWebView _evaluateJavaScript:asAsyncFunction:withSourceURL:withArguments:forceUserGesture:inFrame:inWorld:completionHandler:]):
(-[WKWebView callAsyncJavaScript:arguments:inContentWorld:completionHandler:]): Deleted.

* WebProcess/WebPage/WebPage.cpp:
(WebKit::WebPage::runJavaScript):

Tools:

* TestWebKitAPI/Tests/WebKitCocoa/AsyncFunction.mm:
(TestWebKitAPI::TEST):
* TestWebKitAPI/Tests/WebKitCocoa/UserContentController.mm:
(TEST):
* TestWebKitAPI/Tests/WebKitCocoa/WKURLSchemeHandler-1.mm:
* TestWebKitAPI/Tests/WebKitCocoa/WKWebViewEvaluateJavaScript.mm:
(TEST):
(-[FramesMessageHandler userContentController:didReceiveScriptMessage:]):
* TestWebKitAPI/cocoa/TestWKWebView.mm:
(-[WKWebView objectByCallingAsyncFunction:withArguments:error:]):

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (263726 => 263727)


--- trunk/Source/WebCore/ChangeLog	2020-06-30 05:33:36 UTC (rev 263726)
+++ trunk/Source/WebCore/ChangeLog	2020-06-30 05:43:19 UTC (rev 263727)
@@ -1,3 +1,15 @@
+2020-06-29  Brady Eidson  <beid...@apple.com>
+
+        _javascript_ cannot be injected into iframes
+        <rdar://problem/54168946> and https://bugs.webkit.org/show_bug.cgi?id=213556
+
+        Reviewed by Geoff Garen.
+
+        Covered by API tests.
+
+        * bindings/js/ExceptionDetails.h: Start a collection of "exception types" that will grow quickly,
+          beginning with the specialized "missing frame" type.
+
 2020-06-29  Sam Weinig  <wei...@apple.com>
 
         Convert AppCache manifest parser over to using StringParsingBuffer

Modified: trunk/Source/WebCore/bindings/js/ExceptionDetails.h (263726 => 263727)


--- trunk/Source/WebCore/bindings/js/ExceptionDetails.h	2020-06-30 05:33:36 UTC (rev 263726)
+++ trunk/Source/WebCore/bindings/js/ExceptionDetails.h	2020-06-30 05:43:19 UTC (rev 263727)
@@ -30,9 +30,16 @@
 namespace WebCore {
 
 struct ExceptionDetails {
+    enum class Type : uint8_t {
+        Script,
+        InvalidTargetFrame,
+    };
+
     String message;
     int lineNumber { 0 };
     int columnNumber { 0 };
+    Type type { Type::Script };
+
     // This bizarre explicit initialization of String is because older compilers (like on High Sierra)
     // don't properly handle partial initialization lists unless every struct member has an explicit default value.
     // Once we stop building on those platforms we can remove this.
@@ -40,3 +47,13 @@
 };
 
 } // namespace WebCore
+
+namespace WTF {
+template<> struct EnumTraits<WebCore::ExceptionDetails::Type> {
+    using values = EnumValues<
+        WebCore::ExceptionDetails::Type,
+        WebCore::ExceptionDetails::Type::Script,
+        WebCore::ExceptionDetails::Type::InvalidTargetFrame
+    >;
+};
+}

Modified: trunk/Source/WebKit/ChangeLog (263726 => 263727)


--- trunk/Source/WebKit/ChangeLog	2020-06-30 05:33:36 UTC (rev 263726)
+++ trunk/Source/WebKit/ChangeLog	2020-06-30 05:43:19 UTC (rev 263727)
@@ -1,3 +1,41 @@
+2020-06-29  Brady Eidson  <beid...@apple.com>
+
+        _javascript_ cannot be injected into iframes
+        <rdar://problem/54168946> and https://bugs.webkit.org/show_bug.cgi?id=213556
+
+        Reviewed by Geoff Garen.
+        
+        This adds a few mechanisms:
+        - Allows for WKUserScripts to have a target content world
+        - Allows "evaluateJavaScript" and "callAsyncJavaScript" to target a specific frame
+        - Allows for the completion handlers of those methods to be able to distinguish 
+          failure-due-to-missing-frame.
+
+        * Shared/WebCoreArgumentCoders.cpp:
+        (IPC::ArgumentCoder<ExceptionDetails>::encode):
+        (IPC::ArgumentCoder<ExceptionDetails>::decode):
+        * Shared/WebCoreArgumentCoders.h:
+
+        * UIProcess/API/Cocoa/WKError.h:
+        * UIProcess/API/Cocoa/WKError.mm:
+        (localizedDescriptionForErrorCode):
+
+        * UIProcess/API/Cocoa/WKUserScript.h:
+        * UIProcess/API/Cocoa/WKUserScript.mm:
+        (-[WKUserScript initWithSource:injectionTime:forMainFrameOnly:]):
+        (-[WKUserScript initWithSource:injectionTime:forMainFrameOnly:inContentWorld:]):
+        * UIProcess/API/Cocoa/WKWebView.h:
+
+        * UIProcess/API/Cocoa/WKWebView.mm:
+        (-[WKWebView evaluateJavaScript:inFrame:inContentWorld:completionHandler:]):
+        (-[WKWebView callAsyncJavaScript:arguments:inFrame:inContentWorld:completionHandler:]):
+        (nsErrorFromExceptionDetails):
+        (-[WKWebView _evaluateJavaScript:asAsyncFunction:withSourceURL:withArguments:forceUserGesture:inFrame:inWorld:completionHandler:]):
+        (-[WKWebView callAsyncJavaScript:arguments:inContentWorld:completionHandler:]): Deleted.
+
+        * WebProcess/WebPage/WebPage.cpp:
+        (WebKit::WebPage::runJavaScript):
+
 2020-06-29  Alex Christensen  <achristen...@webkit.org>
 
         Make _WKWebsiteDataStoreConfiguration SPI for HSTS storage to replace _WKProcessPoolConfiguration.hstsStorageDirectory

Modified: trunk/Source/WebKit/Shared/WebCoreArgumentCoders.cpp (263726 => 263727)


--- trunk/Source/WebKit/Shared/WebCoreArgumentCoders.cpp	2020-06-30 05:33:36 UTC (rev 263726)
+++ trunk/Source/WebKit/Shared/WebCoreArgumentCoders.cpp	2020-06-30 05:43:19 UTC (rev 263727)
@@ -2659,6 +2659,7 @@
     encoder << info.message;
     encoder << info.lineNumber;
     encoder << info.columnNumber;
+    encoder << info.type;
     encoder << info.sourceURL;
 }
 
@@ -2673,6 +2674,9 @@
     if (!decoder.decode(result.columnNumber))
         return false;
 
+    if (!decoder.decode(result.type))
+        return false;
+
     if (!decoder.decode(result.sourceURL))
         return false;
 

Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKError.h (263726 => 263727)


--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKError.h	2020-06-30 05:33:36 UTC (rev 263726)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKError.h	2020-06-30 05:43:19 UTC (rev 263727)
@@ -58,6 +58,7 @@
     WKErrorContentRuleListStoreVersionMismatch WK_API_AVAILABLE(macos(10.13), ios(11.0)),
     WKErrorAttributedStringContentFailedToLoad WK_API_AVAILABLE(macos(10.15), ios(13.0)),
     WKErrorAttributedStringContentLoadTimedOut WK_API_AVAILABLE(macos(10.15), ios(13.0)),
+    WKErrorJavaScriptInvalidFrameTarget WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA)),
 } WK_API_AVAILABLE(macos(10.10), ios(8.0));
 
 NS_ASSUME_NONNULL_END

Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKError.mm (263726 => 263727)


--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKError.mm	2020-06-30 05:33:36 UTC (rev 263726)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKError.mm	2020-06-30 05:43:19 UTC (rev 263727)
@@ -73,6 +73,9 @@
 
     case WKErrorAttributedStringContentLoadTimedOut:
         return WEB_UI_STRING("Timed out while loading attributed string content", "WKErrorAttributedStringContentLoadTimedOut description");
+
+    case WKErrorJavaScriptInvalidFrameTarget:
+        return WEB_UI_STRING("_javascript_ execution targeted an invalid frame", "WKErrorJavaScriptInvalidFrameTarget description");
     }
 }
 

Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKUserScript.h (263726 => 263727)


--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKUserScript.h	2020-06-30 05:33:36 UTC (rev 263726)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKUserScript.h	2020-06-30 05:43:19 UTC (rev 263727)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2014-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
@@ -57,9 +57,18 @@
  @param source The script source.
  @param injectionTime When the script should be injected.
  @param forMainFrameOnly Whether the script should be injected into all frames or just the main frame.
+ @discussion Calling this method is the same as calling `initWithSource:injectionTime:forMainFrameOnly:inContentWorld:` with a `contentWorld` value of `WKContentWorld.pageWorld`
  */
 - (instancetype)initWithSource:(NSString *)source injectionTime:(WKUserScriptInjectionTime)injectionTime forMainFrameOnly:(BOOL)forMainFrameOnly;
 
+/*! @abstract Returns an initialized user script that can be added to a @link WKUserContentController @/link.
+ @param source The script source.
+ @param injectionTime When the script should be injected.
+ @param forMainFrameOnly Whether the script should be injected into all frames or just the main frame.
+ @param contentWorld The WKContentWorld in which to inject the script.
+ */
+- (instancetype)initWithSource:(NSString *)source injectionTime:(WKUserScriptInjectionTime)injectionTime forMainFrameOnly:(BOOL)forMainFrameOnly inContentWorld:(WKContentWorld *)contentWorld WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
+
 @end
 
 NS_ASSUME_NONNULL_END

Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKUserScript.mm (263726 => 263727)


--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKUserScript.mm	2020-06-30 05:33:36 UTC (rev 263726)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKUserScript.mm	2020-06-30 05:43:19 UTC (rev 263727)
@@ -33,10 +33,15 @@
 
 - (instancetype)initWithSource:(NSString *)source injectionTime:(WKUserScriptInjectionTime)injectionTime forMainFrameOnly:(BOOL)forMainFrameOnly
 {
+    return [self initWithSource:source injectionTime:injectionTime forMainFrameOnly:forMainFrameOnly inContentWorld:WKContentWorld.pageWorld];
+}
+
+- (instancetype)initWithSource:(NSString *)source injectionTime:(WKUserScriptInjectionTime)injectionTime forMainFrameOnly:(BOOL)forMainFrameOnly inContentWorld:(WKContentWorld *)contentWorld
+{
     if (!(self = [super init]))
         return nil;
 
-    API::Object::constructInWrapper<API::UserScript>(self, WebCore::UserScript { source, API::UserScript::generateUniqueURL(), { }, { }, API::toWebCoreUserScriptInjectionTime(injectionTime), forMainFrameOnly ? WebCore::UserContentInjectedFrames::InjectInTopFrameOnly : WebCore::UserContentInjectedFrames::InjectInAllFrames, WebCore::WaitForNotificationBeforeInjecting::No }, API::ContentWorld::pageContentWorld());
+    API::Object::constructInWrapper<API::UserScript>(self, WebCore::UserScript { source, API::UserScript::generateUniqueURL(), { }, { }, API::toWebCoreUserScriptInjectionTime(injectionTime), forMainFrameOnly ? WebCore::UserContentInjectedFrames::InjectInTopFrameOnly : WebCore::UserContentInjectedFrames::InjectInAllFrames, WebCore::WaitForNotificationBeforeInjecting::No }, *contentWorld->_contentWorld);
 
     return self;
 }

Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.h (263726 => 263727)


--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.h	2020-06-30 05:33:36 UTC (rev 263726)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.h	2020-06-30 05:43:19 UTC (rev 263727)
@@ -38,6 +38,7 @@
 @class WKContentWorld;
 @class WKFindConfiguration;
 @class WKFindResult;
+@class WKFrameInfo;
 @class WKNavigation;
 @class WKPDFConfiguration;
 @class WKSnapshotConfiguration;
@@ -220,14 +221,25 @@
  @param _javascript_String The _javascript_ string to evaluate.
  @param completionHandler A block to invoke when script evaluation completes or fails.
  @discussion The completionHandler is passed the result of the script evaluation or an error.
+ Calling this method is equivalent to calling `evaluateJavaScript:inFrame:inContentWorld:completionHandler:` with:
+   - A `frame` value of `nil` to represent the main frame
+   - A `contentWorld` value of `WKContentWorld.pageWorld`
 */
 - (void)evaluateJavaScript:(NSString *)_javascript_String completionHandler:(void (^ _Nullable)(_Nullable id, NSError * _Nullable error))completionHandler;
 
 /* @abstract Evaluates the given _javascript_ string.
  @param _javascript_String The _javascript_ string to evaluate.
+ @param frame A WKFrameInfo identifying the frame in which to evaluate the _javascript_ string.
  @param contentWorld The WKContentWorld in which to evaluate the _javascript_ string.
  @param completionHandler A block to invoke when script evaluation completes or fails.
  @discussion The completionHandler is passed the result of the script evaluation or an error.
+
+ Passing nil is equivalent to targeting the main frame.
+ If the frame argument no longer represents a valid frame by the time WebKit attempts to call the _javascript_ function your completion handler will be called with a WKErrorJavaScriptInvalidFrameTarget error.
+ This might happen for a number of reasons, including but not limited to:
+     - The target frame has been removed from the DOM via _javascript_
+     - A parent frame has navigated, destroying all of its previous child frames
+
  No matter which WKContentWorld you use to evaluate your _javascript_ string, you can make changes to the underlying web content. (e.g. the Document and its DOM structure)
  Such changes will be visible to script executing in all WKContentWorlds.
  Evaluating your _javascript_ string can leave behind other changes to global state visibile to _javascript_. (e.g. `window.myVariable = 1;`)
@@ -235,11 +247,12 @@
  evaluateJavaScript: is a great way to set up global state for future _javascript_ execution in a given world. (e.g. Importing libraries/utilities that future _javascript_ execution will rely on)
  Once your global state is set up, consider using callAsyncJavaScript: for more flexible interaction with the _javascript_ programming model.
 */
-- (void)evaluateJavaScript:(NSString *)_javascript_String inContentWorld:(WKContentWorld *)contentWorld completionHandler:(void (^ _Nullable)(_Nullable id, NSError * _Nullable error))completionHandler NS_REFINED_FOR_SWIFT WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
+- (void)evaluateJavaScript:(NSString *)_javascript_String inFrame:(nullable WKFrameInfo *)frame inContentWorld:(WKContentWorld *)contentWorld completionHandler:(void (^ _Nullable)(_Nullable id, NSError * _Nullable error))completionHandler NS_REFINED_FOR_SWIFT WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
 
 /* @abstract Calls the given _javascript_ string as an async _javascript_ function, passing the given named arguments to that function.
  @param functionBody The _javascript_ string to use as the function body.
  @param arguments A dictionary representing the arguments to be passed to the function call.
+ @param frame A WKFrameInfo identifying the frame in which to call the _javascript_ function.
  @param contentWorld The WKContentWorld in which to call the _javascript_ function.
  @param completionHandler A block to invoke with the return value of the function call, or with the asynchronous resolution of the function's return value.
  @discussion The functionBody string is treated as an anonymous _javascript_ function body that can be called with named arguments.
@@ -271,6 +284,12 @@
  NSNumber, NSString, NSDate, NSArray, NSDictionary, and NSNull.
  Any NSArray or NSDictionary containers can only contain objects of those types.
 
+ Passing nil is equivalent to targeting the main frame.
+ If the frame argument no longer represents a valid frame by the time WebKit attempts to call the _javascript_ function your completion handler will be called with a WKErrorJavaScriptInvalidFrameTarget error.
+ This might happen for a number of reasons, including but not limited to:
+     - The target frame has been removed from the DOM via _javascript_
+     - A parent frame has navigated, destroying all of its previous child frames
+
  No matter which WKContentWorld you use to call your _javascript_ function, you can make changes to the underlying web content. (e.g. the Document and its DOM structure)
  Such changes will be visible to script executing in all WKContentWorlds.
  Calling your _javascript_ function can leave behind other changes to global state visibile to _javascript_. (e.g. `window.myVariable = 1;`)
@@ -300,7 +319,7 @@
 
  The above function text will create a promise that will fulfull with the value 42 after a one second delay, wait for it to resolve, then return the fulfillment value of 42.
 */
-- (void)callAsyncJavaScript:(NSString *)functionBody arguments:(nullable NSDictionary<NSString *, id> *)arguments inContentWorld:(WKContentWorld *)contentWorld completionHandler:(void (^ _Nullable)(_Nullable id, NSError * _Nullable error))completionHandler NS_REFINED_FOR_SWIFT WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
+- (void)callAsyncJavaScript:(NSString *)functionBody arguments:(nullable NSDictionary<NSString *, id> *)arguments inFrame:(nullable WKFrameInfo *)frame inContentWorld:(WKContentWorld *)contentWorld completionHandler:(void (^ _Nullable)(_Nullable id, NSError * _Nullable error))completionHandler NS_REFINED_FOR_SWIFT WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
 
 /*! @abstract Get a snapshot for the visible viewport of WKWebView.
  @param snapshotConfiguration An object that specifies how the snapshot is configured.

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


--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm	2020-06-30 05:33:36 UTC (rev 263726)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm	2020-06-30 05:43:19 UTC (rev 263727)
@@ -820,14 +820,14 @@
     [self _evaluateJavaScript:_javascript_String asAsyncFunction:NO withSourceURL:nil withArguments:nil forceUserGesture:YES inFrame:nil inWorld:WKContentWorld.pageWorld completionHandler:completionHandler];
 }
 
-- (void)evaluateJavaScript:(NSString *)_javascript_String inContentWorld:(WKContentWorld *)contentWorld completionHandler:(void (^)(id, NSError *))completionHandler
+- (void)evaluateJavaScript:(NSString *)_javascript_String inFrame:(WKFrameInfo *)frame inContentWorld:(WKContentWorld *)contentWorld completionHandler:(void (^)(id, NSError *))completionHandler
 {
-    [self _evaluateJavaScript:_javascript_String asAsyncFunction:NO withSourceURL:nil withArguments:nil forceUserGesture:YES inFrame:nil inWorld:contentWorld completionHandler:completionHandler];
+    [self _evaluateJavaScript:_javascript_String asAsyncFunction:NO withSourceURL:nil withArguments:nil forceUserGesture:YES inFrame:frame inWorld:contentWorld completionHandler:completionHandler];
 }
 
-- (void)callAsyncJavaScript:(NSString *)_javascript_String arguments:(NSDictionary<NSString *, id> *)arguments inContentWorld:(WKContentWorld *)contentWorld completionHandler:(void (^)(id, NSError *error))completionHandler
+- (void)callAsyncJavaScript:(NSString *)_javascript_String arguments:(NSDictionary<NSString *, id> *)arguments inFrame:(WKFrameInfo *)frame inContentWorld:(WKContentWorld *)contentWorld completionHandler:(void (^)(id, NSError *error))completionHandler
 {
-    [self _evaluateJavaScript:_javascript_String asAsyncFunction:YES withSourceURL:nil withArguments:arguments forceUserGesture:YES inFrame:nil inWorld:contentWorld completionHandler:completionHandler];
+    [self _evaluateJavaScript:_javascript_String asAsyncFunction:YES withSourceURL:nil withArguments:arguments forceUserGesture:YES inFrame:frame inWorld:contentWorld completionHandler:completionHandler];
 }
 
 static bool validateArgument(id argument)
@@ -864,6 +864,31 @@
     return false;
 }
 
+static RetainPtr<NSError> nsErrorFromExceptionDetails(const WebCore::ExceptionDetails& details)
+{
+    auto userInfo = adoptNS([[NSMutableDictionary alloc] init]);
+
+    WKErrorCode errorCode;
+    switch (details.type) {
+    case WebCore::ExceptionDetails::Type::InvalidTargetFrame:
+        errorCode = WKErrorJavaScriptInvalidFrameTarget;
+        break;
+    case WebCore::ExceptionDetails::Type::Script:
+        errorCode = WKErrorJavaScriptExceptionOccurred;
+        break;
+    }
+
+    [userInfo setObject:localizedDescriptionForErrorCode(errorCode) forKey:NSLocalizedDescriptionKey];
+    [userInfo setObject:details.message forKey:_WKJavaScriptExceptionMessageErrorKey];
+    [userInfo setObject:@(details.lineNumber) forKey:_WKJavaScriptExceptionLineNumberErrorKey];
+    [userInfo setObject:@(details.columnNumber) forKey:_WKJavaScriptExceptionColumnNumberErrorKey];
+
+    if (!details.sourceURL.isEmpty())
+        [userInfo setObject:[NSURL _web_URLWithWTFString:details.sourceURL] forKey:_WKJavaScriptExceptionSourceURLErrorKey];
+
+    return adoptNS([[NSError alloc] initWithDomain:WKErrorDomain code:errorCode userInfo:userInfo.get()]);
+}
+
 - (void)_evaluateJavaScript:(NSString *)_javascript_String asAsyncFunction:(BOOL)asAsyncFunction withSourceURL:(NSURL *)sourceURL withArguments:(NSDictionary<NSString *, id> *)arguments forceUserGesture:(BOOL)forceUserGesture inFrame:(WKFrameInfo *)frame inWorld:(WKContentWorld *)world completionHandler:(void (^)(id, NSError *))completionHandler
 {
     auto handler = adoptNS([completionHandler copy]);
@@ -930,18 +955,7 @@
         auto rawHandler = (void (^)(id, NSError *))handler.get();
         if (details) {
             ASSERT(!serializedScriptValue);
-
-            RetainPtr<NSMutableDictionary> userInfo = adoptNS([[NSMutableDictionary alloc] init]);
-
-            [userInfo setObject:localizedDescriptionForErrorCode(WKErrorJavaScriptExceptionOccurred) forKey:NSLocalizedDescriptionKey];
-            [userInfo setObject:static_cast<NSString *>(details->message) forKey:_WKJavaScriptExceptionMessageErrorKey];
-            [userInfo setObject:@(details->lineNumber) forKey:_WKJavaScriptExceptionLineNumberErrorKey];
-            [userInfo setObject:@(details->columnNumber) forKey:_WKJavaScriptExceptionColumnNumberErrorKey];
-
-            if (!details->sourceURL.isEmpty())
-                [userInfo setObject:[NSURL _web_URLWithWTFString:details->sourceURL] forKey:_WKJavaScriptExceptionSourceURLErrorKey];
-
-            rawHandler(nil, adoptNS([[NSError alloc] initWithDomain:WKErrorDomain code:WKErrorJavaScriptExceptionOccurred userInfo:userInfo.get()]).get());
+            rawHandler(nil, nsErrorFromExceptionDetails(*details).get());
             return;
         }
 

Modified: trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp (263726 => 263727)


--- trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp	2020-06-30 05:33:36 UTC (rev 263726)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp	2020-06-30 05:43:19 UTC (rev 263727)
@@ -3430,7 +3430,7 @@
     // disappear during script execution.
 
     if (!frame || !frame->coreFrame()) {
-        send(Messages::WebPageProxy::ScriptValueCallback({ }, ExceptionDetails { "Unable to execute _javascript_: Page is in invalid state"_s }, callbackID));
+        send(Messages::WebPageProxy::ScriptValueCallback({ }, ExceptionDetails { "Unable to execute _javascript_: Target frame could not be found in the page"_s, 0, 0, ExceptionDetails::Type::InvalidTargetFrame }, callbackID));
         return;
     }
 

Modified: trunk/Tools/ChangeLog (263726 => 263727)


--- trunk/Tools/ChangeLog	2020-06-30 05:33:36 UTC (rev 263726)
+++ trunk/Tools/ChangeLog	2020-06-30 05:43:19 UTC (rev 263727)
@@ -1,3 +1,21 @@
+2020-06-29  Brady Eidson  <beid...@apple.com>
+
+        _javascript_ cannot be injected into iframes
+        <rdar://problem/54168946> and https://bugs.webkit.org/show_bug.cgi?id=213556
+
+        Reviewed by Geoff Garen.
+
+        * TestWebKitAPI/Tests/WebKitCocoa/AsyncFunction.mm:
+        (TestWebKitAPI::TEST):
+        * TestWebKitAPI/Tests/WebKitCocoa/UserContentController.mm:
+        (TEST):
+        * TestWebKitAPI/Tests/WebKitCocoa/WKURLSchemeHandler-1.mm:
+        * TestWebKitAPI/Tests/WebKitCocoa/WKWebViewEvaluateJavaScript.mm:
+        (TEST):
+        (-[FramesMessageHandler userContentController:didReceiveScriptMessage:]):
+        * TestWebKitAPI/cocoa/TestWKWebView.mm:
+        (-[WKWebView objectByCallingAsyncFunction:withArguments:error:]):
+
 2020-06-29  Jonathan Bedard  <jbed...@apple.com>
 
         [TestExpectations] Remove --csv option

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/AsyncFunction.mm (263726 => 263727)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/AsyncFunction.mm	2020-06-30 05:33:36 UTC (rev 263726)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/AsyncFunction.mm	2020-06-30 05:43:19 UTC (rev 263727)
@@ -183,12 +183,12 @@
 {
     auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]);
 
-    [webView callAsyncJavaScript:@"shouldn't crash" arguments:@{ @"invalidparameter" : webView.get() } inContentWorld:WKContentWorld.pageWorld completionHandler:nil];
+    [webView callAsyncJavaScript:@"shouldn't crash" arguments:@{ @"invalidparameter" : webView.get() } inFrame:nil inContentWorld:WKContentWorld.pageWorld completionHandler:nil];
 
     NSString *functionBody = @"return new Promise(function(resolve, reject) { setTimeout(function(){ resolve(42) }, 0); })";
 
     bool done = false;
-    [webView callAsyncJavaScript:functionBody arguments:nil inContentWorld:WKContentWorld.pageWorld completionHandler:[&] (id result, NSError *error) {
+    [webView callAsyncJavaScript:functionBody arguments:nil inFrame:nil inContentWorld:WKContentWorld.pageWorld completionHandler:[&] (id result, NSError *error) {
         EXPECT_NULL(error);
         EXPECT_TRUE([result isKindOfClass:[NSNumber class]]);
         EXPECT_TRUE([result isEqualToNumber:@42]);
@@ -200,7 +200,7 @@
     functionBody = @"return new Promise(function(resolve, reject) { setTimeout(function(){ reject('Rejected!') }, 0); })";
 
     done = false;
-    [webView callAsyncJavaScript:functionBody arguments:nil inContentWorld:WKContentWorld.pageWorld completionHandler:[&] (id result, NSError *error) {
+    [webView callAsyncJavaScript:functionBody arguments:nil inFrame:nil inContentWorld:WKContentWorld.pageWorld completionHandler:[&] (id result, NSError *error) {
         EXPECT_NULL(result);
         EXPECT_TRUE(error != nil);
         EXPECT_TRUE([[error description] containsString:@"Rejected!"]);
@@ -211,7 +211,7 @@
     functionBody = @"let p = new Proxy(function(resolve, reject) { setTimeout(function() { resolve(42); }, 0); }, { }); return { then: p };";
 
     done = false;
-    [webView callAsyncJavaScript:functionBody arguments:nil inContentWorld:WKContentWorld.pageWorld completionHandler:[&] (id result, NSError *error) {
+    [webView callAsyncJavaScript:functionBody arguments:nil inFrame:nil inContentWorld:WKContentWorld.pageWorld completionHandler:[&] (id result, NSError *error) {
         EXPECT_NULL(error);
         EXPECT_TRUE([result isKindOfClass:[NSNumber class]]);
         EXPECT_TRUE([result isEqualToNumber:@42]);
@@ -222,7 +222,7 @@
     functionBody = @"let p = new Proxy(function(resolve, reject) { setTimeout(function() { reject('Rejected!'); }, 0); }, { }); return { then: p };";
 
     done = false;
-    [webView callAsyncJavaScript:functionBody arguments:nil inContentWorld:WKContentWorld.pageWorld completionHandler:[&] (id result, NSError *error) {
+    [webView callAsyncJavaScript:functionBody arguments:nil inFrame:nil inContentWorld:WKContentWorld.pageWorld completionHandler:[&] (id result, NSError *error) {
         EXPECT_NULL(result);
         EXPECT_TRUE(error != nil);
         EXPECT_TRUE([[error description] containsString:@"Rejected!"]);
@@ -234,7 +234,7 @@
     functionBody = @"var r = 0; var p = new Promise(function(fulfill, reject) { setTimeout(function(){ r = 42; fulfill(); }, 5);}); await p; return r;";
 
     done = false;
-    [webView callAsyncJavaScript:functionBody arguments:nil inContentWorld:WKContentWorld.pageWorld completionHandler:[&] (id result, NSError *error) {
+    [webView callAsyncJavaScript:functionBody arguments:nil inFrame:nil inContentWorld:WKContentWorld.pageWorld completionHandler:[&] (id result, NSError *error) {
         EXPECT_NULL(error);
         EXPECT_TRUE([result isKindOfClass:[NSNumber class]]);
         EXPECT_TRUE([result isEqualToNumber:@42]);
@@ -246,7 +246,7 @@
     functionBody = @"var p = new Promise(function(fulfill, reject) { setTimeout(function(){ fulfill('Fulfilled!') }, 5);}); await p; return p;";
 
     done = false;
-    [webView callAsyncJavaScript:functionBody arguments:nil inContentWorld:WKContentWorld.pageWorld completionHandler:[&] (id result, NSError *error) {
+    [webView callAsyncJavaScript:functionBody arguments:nil inFrame:nil inContentWorld:WKContentWorld.pageWorld completionHandler:[&] (id result, NSError *error) {
         EXPECT_NULL(error);
         EXPECT_TRUE([result isKindOfClass:[NSString class]]);
         EXPECT_TRUE([result isEqualToString:@"Fulfilled!"]);
@@ -258,7 +258,7 @@
     functionBody = @"var p = new Promise(function (r) { r(new Promise(function (r) { r(42); })); }); await p; return 'Done';";
 
     done = false;
-    [webView callAsyncJavaScript:functionBody arguments:nil inContentWorld:WKContentWorld.pageWorld completionHandler:[&] (id result, NSError *error) {
+    [webView callAsyncJavaScript:functionBody arguments:nil inFrame:nil inContentWorld:WKContentWorld.pageWorld completionHandler:[&] (id result, NSError *error) {
         EXPECT_NULL(error);
         EXPECT_TRUE([result isKindOfClass:[NSString class]]);
         EXPECT_TRUE([result isEqualToString:@"Done"]);
@@ -270,7 +270,7 @@
     done = false;
     functionBody = @"return new Promise(function(resolve, reject) { })";
     for (int i = 0; i < 500; ++i) {
-        [webView callAsyncJavaScript:functionBody arguments:nil inContentWorld:WKContentWorld.pageWorld completionHandler:[&] (id result, NSError *error) {
+        [webView callAsyncJavaScript:functionBody arguments:nil inFrame:nil inContentWorld:WKContentWorld.pageWorld completionHandler:[&] (id result, NSError *error) {
             EXPECT_NULL(result);
             EXPECT_TRUE(error != nil);
             EXPECT_TRUE([[error description] containsString:@"no longer reachable"]);

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/UserContentController.mm (263726 => 263727)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/UserContentController.mm	2020-06-30 05:33:36 UTC (rev 263726)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/UserContentController.mm	2020-06-30 05:43:19 UTC (rev 263727)
@@ -221,7 +221,7 @@
 
     __block bool done = false;
     // Test that we throw an exception if you try to use a message handler that has been removed.
-    [webView callAsyncJavaScript:@"return handlerToRemove.postMessage('FAIL')" arguments:nil inContentWorld:[WKContentWorld pageWorld] completionHandler:^ (id value, NSError * error) {
+    [webView callAsyncJavaScript:@"return handlerToRemove.postMessage('FAIL')" arguments:nil inFrame:nil inContentWorld:[WKContentWorld pageWorld] completionHandler:^ (id value, NSError * error) {
         EXPECT_NULL(value);
         EXPECT_NOT_NULL(error);
         EXPECT_TRUE([[error description] containsString:@"InvalidAccessError"]);
@@ -1012,7 +1012,7 @@
     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) {
+    [webView callAsyncJavaScript:functionBody arguments:@{ @"handler" : @"testHandler1", @"arg" : @1 } inFrame:nil inContentWorld:WKContentWorld.pageWorld completionHandler:[&] (id result, NSError *error) {
         EXPECT_NULL(error);
         EXPECT_TRUE([result isEqualToNumber:@1]);
         done = true;
@@ -1021,7 +1021,7 @@
     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) {
+    [webView callAsyncJavaScript:functionBody arguments:@{ @"handler" : @"testHandler1", @"arg" : @1 } inFrame:nil inContentWorld:WKContentWorld.defaultClientWorld completionHandler:[&] (id result, NSError *error) {
         EXPECT_NULL(result);
         EXPECT_NOT_NULL(error);
         done = true;
@@ -1030,7 +1030,7 @@
     done = false;
 
     // defaultClientWorld is where testhandler2 lives
-    [webView callAsyncJavaScript:functionBody arguments:@{ @"handler" : @"testHandler2", @"arg" : @1 } inContentWorld:WKContentWorld.defaultClientWorld completionHandler:[&] (id result, NSError *error) {
+    [webView callAsyncJavaScript:functionBody arguments:@{ @"handler" : @"testHandler2", @"arg" : @1 } inFrame:nil inContentWorld:WKContentWorld.defaultClientWorld completionHandler:[&] (id result, NSError *error) {
         EXPECT_NULL(error);
         EXPECT_TRUE([result isEqualToNumber:@1]);
         done = true;
@@ -1040,7 +1040,7 @@
 
     // 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) {
+    [webView callAsyncJavaScript:functionBody arguments:@{ @"handler" : @"testHandler2", @"arg" : @1 } inFrame:nil inContentWorld:WKContentWorld.defaultClientWorld completionHandler:[&] (id result, NSError *error) {
         EXPECT_NULL(result);
         EXPECT_NOT_NULL(error);
         done = true;
@@ -1049,15 +1049,15 @@
     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) {
+    [webView callAsyncJavaScript:functionBody arguments:@{ @"handler" : @"testHandler3", @"arg" : @1 } inFrame:nil 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) {
+    [webView callAsyncJavaScript:functionBody arguments:@{ @"handler" : @"testHandler4", @"arg" : @1 } inFrame:nil 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) {
+    [webView callAsyncJavaScript:functionBody arguments:@{ @"handler" : @"testHandler5", @"arg" : @1 } inFrame:nil inContentWorld:world completionHandler:[&] (id result, NSError *error) {
         EXPECT_NULL(error);
         EXPECT_TRUE([result isEqualToNumber:@1]);
         done = true;
@@ -1067,7 +1067,7 @@
 
     // 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) {
+    [webView callAsyncJavaScript:functionBody arguments:@{ @"handler" : @"testHandler3", @"arg" : @1 } inFrame:nil inContentWorld:world completionHandler:[&] (id result, NSError *error) {
         EXPECT_NULL(error);
         EXPECT_TRUE([result isEqualToNumber:@1]);
         done = true;
@@ -1077,15 +1077,15 @@
 
     // 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) {
+    [webView callAsyncJavaScript:functionBody arguments:@{ @"handler" : @"testHandler3", @"arg" : @1 } inFrame:nil 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) {
+    [webView callAsyncJavaScript:functionBody arguments:@{ @"handler" : @"testHandler4", @"arg" : @1 } inFrame:nil 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) {
+    [webView callAsyncJavaScript:functionBody arguments:@{ @"handler" : @"testHandler5", @"arg" : @1 } inFrame:nil inContentWorld:world completionHandler:[&] (id result, NSError *error) {
         EXPECT_NULL(error);
         EXPECT_TRUE([result isEqualToNumber:@1]);
         done = true;
@@ -1095,11 +1095,11 @@
 
     // 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) {
+    [webView callAsyncJavaScript:functionBody arguments:@{ @"handler" : @"testHandler4", @"arg" : @1 } inFrame:nil 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) {
+    [webView callAsyncJavaScript:functionBody arguments:@{ @"handler" : @"testHandler5", @"arg" : @1 } inFrame:nil inContentWorld:world completionHandler:[&] (id result, NSError *error) {
         EXPECT_NULL(result);
         EXPECT_NOT_NULL(error);
         done = true;
@@ -1118,7 +1118,7 @@
 
     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) {
+    [webView callAsyncJavaScript:functionBody arguments:nil inFrame:nil inContentWorld:WKContentWorld.pageWorld completionHandler:[&] (id result, NSError *error) {
         EXPECT_NULL(error);
         EXPECT_TRUE([result isKindOfClass:[NSString class]]);
         EXPECT_TRUE([result isEqualToString:@"Fulfilled!"]);
@@ -1129,7 +1129,7 @@
 
     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) {
+    [webView callAsyncJavaScript:functionBody arguments:nil inFrame:nil inContentWorld:WKContentWorld.pageWorld completionHandler:[&] (id result, NSError *error) {
         EXPECT_NULL(result);
         EXPECT_TRUE(!!error);
         EXPECT_TRUE([[error description] containsString:@"Rejected!"]);
@@ -1141,7 +1141,7 @@
 
     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) {
+    [webView callAsyncJavaScript:functionBody arguments:nil inFrame:nil inContentWorld:WKContentWorld.pageWorld completionHandler:[&] (id result, NSError *error) {
         EXPECT_NULL(error);
         EXPECT_TRUE([result isKindOfClass:[NSString class]]);
         EXPECT_TRUE([result isEqualToString:@"Yes"]);
@@ -1153,7 +1153,7 @@
 
     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) {
+    [webView callAsyncJavaScript:functionBody arguments:nil inFrame: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"]);
@@ -1165,7 +1165,7 @@
 
     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) {
+    [webView callAsyncJavaScript:functionBody arguments:nil inFrame:nil inContentWorld:WKContentWorld.pageWorld completionHandler:[&] (id result, NSError *error) {
         EXPECT_NULL(result);
         EXPECT_TRUE(!!error);
         EXPECT_TRUE([[error description] containsString:@"unable to be serialized"]);
@@ -1187,7 +1187,7 @@
 
     // Set a variable in the world.
     bool done = false;
-    [webView evaluateJavaScript:@"var foo = 'bar'" inContentWorld:world.get() completionHandler:[&] (id result, NSError *error) {
+    [webView evaluateJavaScript:@"var foo = 'bar'" inFrame:nil inContentWorld:world.get() completionHandler:[&] (id result, NSError *error) {
         EXPECT_NULL(error);
         done = true;
     }];
@@ -1196,7 +1196,7 @@
 
     // 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) {
+    [webView callAsyncJavaScript:functionBody arguments:nil inFrame:nil inContentWorld:world.get() completionHandler:[&] (id result, NSError *error) {
         EXPECT_NULL(error);
         EXPECT_TRUE([result isKindOfClass:[NSString class]]);
         EXPECT_TRUE([result isEqualToString:@"bar"]);
@@ -1208,7 +1208,7 @@
     // 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) {
+    [webView evaluateJavaScript:@"foo" inFrame:nil inContentWorld:world.get() completionHandler:[&] (id result, NSError *error) {
         EXPECT_NULL(error);
         EXPECT_TRUE([result isKindOfClass:[NSString class]]);
         EXPECT_TRUE([result isEqualToString:@"bar"]);

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKWebViewEvaluateJavaScript.mm (263726 => 263727)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKWebViewEvaluateJavaScript.mm	2020-06-30 05:33:36 UTC (rev 263726)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKWebViewEvaluateJavaScript.mm	2020-06-30 05:43:19 UTC (rev 263727)
@@ -158,7 +158,7 @@
     isDone = false;
 
     // Verify that value is visible when evaluating in the pageWorld
-    [webView evaluateJavaScript:@"foo" inContentWorld:WKContentWorld.pageWorld completionHandler:^(id result, NSError *error) {
+    [webView evaluateJavaScript:@"foo" inFrame:nil inContentWorld:WKContentWorld.pageWorld completionHandler:^(id result, NSError *error) {
         EXPECT_TRUE([result isKindOfClass:[NSString class]]);
         EXPECT_TRUE([result isEqualToString:@"bar"]);
         isDone = true;
@@ -168,7 +168,7 @@
     isDone = false;
 
     // Verify that value is not visible when evaluating in the defaultClientWorld
-    [webView evaluateJavaScript:@"foo" inContentWorld:WKContentWorld.defaultClientWorld completionHandler:^(id result, NSError *error) {
+    [webView evaluateJavaScript:@"foo" inFrame:nil inContentWorld:WKContentWorld.defaultClientWorld completionHandler:^(id result, NSError *error) {
         EXPECT_NULL(result);
         isDone = true;
         testsPassed++;
@@ -177,7 +177,7 @@
     isDone = false;
 
     // Verify that value is visible when calling a function in the pageWorld
-    [webView callAsyncJavaScript:@"return foo" arguments:nil inContentWorld:WKContentWorld.pageWorld completionHandler:^(id result, NSError *error) {
+    [webView callAsyncJavaScript:@"return foo" arguments:nil inFrame:nil inContentWorld:WKContentWorld.pageWorld completionHandler:^(id result, NSError *error) {
         EXPECT_TRUE([result isKindOfClass:[NSString class]]);
         EXPECT_TRUE([result isEqualToString:@"bar"]);
         isDone = true;
@@ -187,7 +187,7 @@
     isDone = false;
 
     // Verify that value is not visible when calling a function in the defaultClientWorld
-    [webView callAsyncJavaScript:@"return foo" arguments:nil inContentWorld:WKContentWorld.defaultClientWorld completionHandler:^(id result, NSError *error) {
+    [webView callAsyncJavaScript:@"return foo" arguments:nil inFrame:nil inContentWorld:WKContentWorld.defaultClientWorld completionHandler:^(id result, NSError *error) {
         EXPECT_NULL(result);
         isDone = true;
         testsPassed++;
@@ -201,7 +201,7 @@
     [webView.get().configuration.userContentController _addScriptMessageHandler:handler name:@"testHandlerName" userContentWorld:namedWorld.get()._userContentWorld];
 
     // Set a variable value in that named world.
-    [webView evaluateJavaScript:@"var bar = 'baz'" inContentWorld:namedWorld.get() completionHandler:^(id result, NSError *error) {
+    [webView evaluateJavaScript:@"var bar = 'baz'" inFrame:nil inContentWorld:namedWorld.get() completionHandler:^(id result, NSError *error) {
         EXPECT_NULL(result);
         isDone = true;
         testsPassed++;
@@ -210,7 +210,7 @@
     isDone = false;
 
     // Set a global variable value in that named world via a function call.
-    [webView callAsyncJavaScript:@"window.baz = 'bat'" arguments:nil inContentWorld:namedWorld.get() completionHandler:^(id result, NSError *error) {
+    [webView callAsyncJavaScript:@"window.baz = 'bat'" arguments:nil inFrame:nil inContentWorld:namedWorld.get() completionHandler:^(id result, NSError *error) {
         EXPECT_NULL(result);
         EXPECT_NULL(error);
         isDone = true;
@@ -223,7 +223,7 @@
     [webView.get().configuration.userContentController _removeScriptMessageHandlerForName:@"testHandlerName" userContentWorld:namedWorld.get()._userContentWorld];
 
     // Verify the variables we set are there in that named world.
-    [webView evaluateJavaScript:@"bar" inContentWorld:namedWorld.get() completionHandler:^(id result, NSError *error) {
+    [webView evaluateJavaScript:@"bar" inFrame:nil inContentWorld:namedWorld.get() completionHandler:^(id result, NSError *error) {
         EXPECT_TRUE([result isKindOfClass:[NSString class]]);
         EXPECT_TRUE([result isEqualToString:@"baz"]);
         isDone = true;
@@ -232,7 +232,7 @@
     TestWebKitAPI::Util::run(&isDone);
     isDone = false;
 
-    [webView evaluateJavaScript:@"window.baz" inContentWorld:namedWorld.get() completionHandler:^(id result, NSError *error) {
+    [webView evaluateJavaScript:@"window.baz" inFrame:nil inContentWorld:namedWorld.get() completionHandler:^(id result, NSError *error) {
         EXPECT_TRUE([result isKindOfClass:[NSString class]]);
         EXPECT_TRUE([result isEqualToString:@"bat"]);
         isDone = true;
@@ -242,7 +242,7 @@
     isDone = false;
 
     // Verify they aren't there in the defaultClientWorld.
-    [webView evaluateJavaScript:@"bar" inContentWorld:WKContentWorld.defaultClientWorld completionHandler:^(id result, NSError *error) {
+    [webView evaluateJavaScript:@"bar" inFrame:nil inContentWorld:WKContentWorld.defaultClientWorld completionHandler:^(id result, NSError *error) {
         EXPECT_NULL(result);
         isDone = true;
         testsPassed++;
@@ -250,7 +250,7 @@
     TestWebKitAPI::Util::run(&isDone);
     isDone = false;
 
-    [webView evaluateJavaScript:@"window.baz" inContentWorld:WKContentWorld.defaultClientWorld completionHandler:^(id result, NSError *error) {
+    [webView evaluateJavaScript:@"window.baz" inFrame:nil inContentWorld:WKContentWorld.defaultClientWorld completionHandler:^(id result, NSError *error) {
         EXPECT_NULL(result);
         isDone = true;
         testsPassed++;
@@ -259,7 +259,7 @@
     isDone = false;
 
     // Verify they aren't there in the pageWorld.
-    [webView evaluateJavaScript:@"bar" inContentWorld:WKContentWorld.pageWorld completionHandler:^(id result, NSError *error) {
+    [webView evaluateJavaScript:@"bar" inFrame:nil inContentWorld:WKContentWorld.pageWorld completionHandler:^(id result, NSError *error) {
         EXPECT_NULL(result);
         isDone = true;
         testsPassed++;
@@ -267,7 +267,7 @@
     TestWebKitAPI::Util::run(&isDone);
     isDone = false;
 
-    [webView evaluateJavaScript:@"window.baz" inContentWorld:WKContentWorld.pageWorld completionHandler:^(id result, NSError *error) {
+    [webView evaluateJavaScript:@"window.baz" inFrame:nil inContentWorld:WKContentWorld.pageWorld completionHandler:^(id result, NSError *error) {
         EXPECT_NULL(result);
         isDone = true;
         testsPassed++;
@@ -285,7 +285,7 @@
     [webView synchronouslyLoadHTMLString:@"<html></html>"];
 
     __block bool done = false;
-    [webView evaluateJavaScript:@"window.worldName" inContentWorld:[WKContentWorld worldWithName:@"testName"] completionHandler:^(id result, NSError *error) {
+    [webView evaluateJavaScript:@"window.worldName" inFrame:nil inContentWorld:[WKContentWorld worldWithName:@"testName"] completionHandler:^(id result, NSError *error) {
         EXPECT_WK_STREQ(result, "testName");
         done = true;
     }];
@@ -472,3 +472,337 @@
     TestWebKitAPI::Util::run(&done);
 
 }
+
+static NSMutableSet<WKFrameInfo *> *allFrames;
+@interface FramesMessageHandler : NSObject <WKScriptMessageHandler>
+@end
+
+@implementation FramesMessageHandler
+- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
+{
+    EXPECT_TRUE(message.world == WKContentWorld.defaultClientWorld);
+    [allFrames addObject:message.frameInfo];
+}
+@end
+
+static const char* framesMainResource = R"FRAMESRESOURCE(
+Hello world<br>
+<iframe id='theFrame' src=''></iframe>
+)FRAMESRESOURCE";
+
+static NSString *userScriptSource = @"window.webkit.messageHandlers.framesTester.postMessage('hi');";
+
+// This test loads a document under one protocol.
+// That document embeds an iframe under a different protocol
+// (ensuring they cannot access each other under the cross-origin rules of the web security model)
+// It uses a WKUserScript to collect all of the frames on the page, and then uses new forms of evaluateJavaScript
+// and callAsyncJavaScript to confirm that it can execute JS directly in each of those frames.
+TEST(EvaluateJavaScript, _javascript_InFramesFromPostMessage)
+{
+    allFrames = [[NSMutableSet<WKFrameInfo *> alloc] init];
+    auto messageHandler = adoptNS([[FramesMessageHandler alloc] init]);
+    auto userScript = adoptNS([[WKUserScript alloc] initWithSource:userScriptSource injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO inContentWorld:WKContentWorld.defaultClientWorld]);
+
+    WKWebViewConfiguration *configuration = [[[WKWebViewConfiguration alloc] init] autorelease];
+    [configuration.userContentController addUserScript:userScript.get()];
+    [configuration.userContentController addScriptMessageHandler:messageHandler.get() contentWorld:WKContentWorld.defaultClientWorld name:@"framesTester"];
+
+    auto handler = adoptNS([[TestURLSchemeHandler alloc] init]);
+    [handler setStartURLSchemeTaskHandler:[&](WKWebView *, id<WKURLSchemeTask> task) {
+        if ([task.request.URL.absoluteString isEqualToString:@"framestest://test/index.html"]) {
+            NSData *data = "" stringWithFormat:@"%s", framesMainResource] dataUsingEncoding:NSUTF8StringEncoding];
+            [task didReceiveResponse:[[[NSURLResponse alloc] initWithURL:task.request.URL MIMEType:@"text/html" expectedContentLength:data.length textEncodingName:nil] autorelease]];
+            [task didReceiveData:data];
+            [task didFinish];
+        } else if ([task.request.URL.absoluteString isEqualToString:@"otherprotocol://test/index.html"]) {
+            [task didReceiveResponse:[[[NSURLResponse alloc] initWithURL:task.request.URL MIMEType:@"text/html" expectedContentLength:0 textEncodingName:nil] autorelease]];
+            [task didFinish];
+        } else
+            ASSERT_NOT_REACHED();
+    }];
+
+    [configuration setURLSchemeHandler:handler.get() forURLScheme:@"framestest"];
+    [configuration setURLSchemeHandler:handler.get() forURLScheme:@"otherprotocol"];
+
+    RetainPtr<TestWKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration]);
+    [webView synchronouslyLoadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"framestest://test/index.html"]]];
+
+    EXPECT_EQ(allFrames.count, 2u);
+
+    static size_t finishedFrames = 0;
+    static bool isDone = false;
+
+    for (WKFrameInfo *frame in allFrames) {
+        bool isMainFrame = frame.isMainFrame;
+        [webView callAsyncJavaScript:@"return location.href;" arguments:nil inFrame:frame inContentWorld:WKContentWorld.defaultClientWorld completionHandler:[isMainFrame] (id result, NSError *error) {
+            EXPECT_NULL(error);
+            EXPECT_TRUE([result isKindOfClass:[NSString class]]);
+
+            if (isMainFrame)
+                EXPECT_TRUE([result isEqualToString:@"framestest://test/index.html"]);
+            else
+                EXPECT_TRUE([result isEqualToString:@"otherprotocol://test/index.html"]);
+
+            if (++finishedFrames == allFrames.count * 2)
+                isDone = true;
+        }];
+
+
+        [webView evaluateJavaScript:@"location.href;" inFrame:frame inContentWorld:WKContentWorld.defaultClientWorld completionHandler:[isMainFrame] (id result, NSError *error) {
+            EXPECT_NULL(error);
+            EXPECT_TRUE([result isKindOfClass:[NSString class]]);
+
+            if (isMainFrame)
+                EXPECT_TRUE([result isEqualToString:@"framestest://test/index.html"]);
+            else
+                EXPECT_TRUE([result isEqualToString:@"otherprotocol://test/index.html"]);
+
+            if (++finishedFrames == allFrames.count * 2)
+                isDone = true;
+        }];
+    }
+
+    TestWebKitAPI::Util::run(&isDone);
+}
+
+// This test loads a document under one protocol.
+// That document embeds an iframe under a different protocol
+// (ensuring they cannot access each other under the cross-origin rules of the web security model)
+// It collects all the frames seen during navigation delegate callbacks, and then uses new forms of evaluateJavaScript
+// and callAsyncJavaScript to confirm that it can execute JS directly in each of those frames.
+TEST(EvaluateJavaScript, _javascript_InFramesFromNavigationDelegate)
+{
+    allFrames = [[NSMutableSet<WKFrameInfo *> alloc] init];
+
+    auto handler = adoptNS([[TestURLSchemeHandler alloc] init]);
+    [handler setStartURLSchemeTaskHandler:[&](WKWebView *, id<WKURLSchemeTask> task) {
+        if ([task.request.URL.absoluteString isEqualToString:@"framestest://test/index.html"]) {
+            NSData *data = "" stringWithFormat:@"%s", framesMainResource] dataUsingEncoding:NSUTF8StringEncoding];
+            [task didReceiveResponse:[[[NSURLResponse alloc] initWithURL:task.request.URL MIMEType:@"text/html" expectedContentLength:data.length textEncodingName:nil] autorelease]];
+            [task didReceiveData:data];
+            [task didFinish];
+        } else if ([task.request.URL.absoluteString isEqualToString:@"otherprotocol://test/index.html"]) {
+            NSData *data = "" dataUsingEncoding:NSUTF8StringEncoding];
+            [task didReceiveResponse:[[[NSURLResponse alloc] initWithURL:task.request.URL MIMEType:@"text/html" expectedContentLength:0 textEncodingName:nil] autorelease]];
+            [task didReceiveData:data];
+            [task didFinish];
+        } else
+            ASSERT_NOT_REACHED();
+    }];
+
+    WKWebViewConfiguration *configuration = [[[WKWebViewConfiguration alloc] init] autorelease];
+    [configuration setURLSchemeHandler:handler.get() forURLScheme:@"framestest"];
+    [configuration setURLSchemeHandler:handler.get() forURLScheme:@"otherprotocol"];
+
+    RetainPtr<WKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration]);
+
+    auto navigationDelegate = adoptNS([[TestNavigationDelegate alloc] init]);
+
+    __block bool didFinishNavigation = false;
+    [navigationDelegate setDidFinishNavigation:^(WKWebView *, WKNavigation *) {
+        didFinishNavigation = true;
+    }];
+
+    [navigationDelegate setDecidePolicyForNavigationAction:[&] (WKNavigationAction *action, void (^decisionHandler)(WKNavigationActionPolicy)) {
+        if (action.targetFrame)
+            [allFrames addObject:action.targetFrame];
+
+        decisionHandler(WKNavigationActionPolicyAllow);
+    }];
+
+    [webView setNavigationDelegate:navigationDelegate.get()];
+    [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"framestest://test/index.html"]]];
+
+    TestWebKitAPI::Util::run(&didFinishNavigation);
+
+    EXPECT_EQ(allFrames.count, 2u);
+
+    static size_t finishedFrames = 0;
+    static bool isDone = false;
+
+    for (WKFrameInfo *frame in allFrames) {
+        bool isMainFrame = frame.isMainFrame;
+        [webView callAsyncJavaScript:@"return location.href;" arguments:nil inFrame:frame inContentWorld:WKContentWorld.defaultClientWorld completionHandler:[isMainFrame] (id result, NSError *error) {
+            EXPECT_NULL(error);
+            EXPECT_TRUE([result isKindOfClass:[NSString class]]);
+
+            if (isMainFrame)
+                EXPECT_TRUE([result isEqualToString:@"framestest://test/index.html"]);
+            else
+                EXPECT_TRUE([result isEqualToString:@"otherprotocol://test/index.html"]);
+
+            if (++finishedFrames == allFrames.count * 2)
+                isDone = true;
+        }];
+
+
+        [webView evaluateJavaScript:@"location.href;" inFrame:frame inContentWorld:WKContentWorld.defaultClientWorld completionHandler:[isMainFrame] (id result, NSError *error) {
+            EXPECT_NULL(error);
+            EXPECT_TRUE([result isKindOfClass:[NSString class]]);
+
+            if (isMainFrame)
+                EXPECT_TRUE([result isEqualToString:@"framestest://test/index.html"]);
+            else
+                EXPECT_TRUE([result isEqualToString:@"otherprotocol://test/index.html"]);
+
+            if (++finishedFrames == allFrames.count * 2)
+                isDone = true;
+        }];
+    }
+
+    TestWebKitAPI::Util::run(&isDone);
+}
+
+// This test verifies that evaluating _javascript_ in a frame that has since gone missing
+// due to removal from the DOM results in an appropriate error
+TEST(EvaluateJavaScript, _javascript_InMissingFrameError)
+{
+    allFrames = [[NSMutableSet<WKFrameInfo *> alloc] init];
+
+    auto handler = adoptNS([[TestURLSchemeHandler alloc] init]);
+    [handler setStartURLSchemeTaskHandler:[&](WKWebView *, id<WKURLSchemeTask> task) {
+        if ([task.request.URL.absoluteString isEqualToString:@"framestest://test/index.html"]) {
+            NSData *data = "" stringWithFormat:@"%s", framesMainResource] dataUsingEncoding:NSUTF8StringEncoding];
+            [task didReceiveResponse:[[[NSURLResponse alloc] initWithURL:task.request.URL MIMEType:@"text/html" expectedContentLength:data.length textEncodingName:nil] autorelease]];
+            [task didReceiveData:data];
+            [task didFinish];
+        } else if ([task.request.URL.absoluteString isEqualToString:@"otherprotocol://test/index.html"]) {
+            NSData *data = "" dataUsingEncoding:NSUTF8StringEncoding];
+            [task didReceiveResponse:[[[NSURLResponse alloc] initWithURL:task.request.URL MIMEType:@"text/html" expectedContentLength:0 textEncodingName:nil] autorelease]];
+            [task didReceiveData:data];
+            [task didFinish];
+        } else
+            ASSERT_NOT_REACHED();
+    }];
+
+    WKWebViewConfiguration *configuration = [[[WKWebViewConfiguration alloc] init] autorelease];
+    [configuration setURLSchemeHandler:handler.get() forURLScheme:@"framestest"];
+    [configuration setURLSchemeHandler:handler.get() forURLScheme:@"otherprotocol"];
+
+    RetainPtr<WKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration]);
+
+    auto navigationDelegate = adoptNS([[TestNavigationDelegate alloc] init]);
+
+    __block bool didFinishNavigation = false;
+    [navigationDelegate setDidFinishNavigation:^(WKWebView *, WKNavigation *) {
+        didFinishNavigation = true;
+    }];
+
+    [navigationDelegate setDecidePolicyForNavigationAction:[&] (WKNavigationAction *action, void (^decisionHandler)(WKNavigationActionPolicy)) {
+        if (action.targetFrame)
+            [allFrames addObject:action.targetFrame];
+
+        decisionHandler(WKNavigationActionPolicyAllow);
+    }];
+
+    [webView setNavigationDelegate:navigationDelegate.get()];
+    [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"framestest://test/index.html"]]];
+
+    TestWebKitAPI::Util::run(&didFinishNavigation);
+
+    EXPECT_EQ(allFrames.count, 2u);
+
+    static bool isDone = false;
+    [webView evaluateJavaScript:@"var frame = document.getElementById('theFrame'); frame.parentNode.removeChild(frame);" inFrame:nil inContentWorld:WKContentWorld.defaultClientWorld completionHandler:[] (id result, NSError *error) {
+        isDone = true;
+    }];
+
+    TestWebKitAPI::Util::run(&isDone);
+    isDone = false;
+
+    for (WKFrameInfo *frame in allFrames) {
+        if (frame.isMainFrame)
+            continue;
+
+        [webView callAsyncJavaScript:@"return location.href;" arguments:nil inFrame:frame inContentWorld:WKContentWorld.defaultClientWorld completionHandler:[] (id result, NSError *error) {
+            EXPECT_FALSE(error == nil);
+            EXPECT_TRUE(error.domain == WKErrorDomain);
+            EXPECT_TRUE(error.code == WKErrorJavaScriptInvalidFrameTarget);
+            isDone = true;
+        }];
+    }
+
+    TestWebKitAPI::Util::run(&isDone);
+}
+
+// This test verifies that evaluating _javascript_ in a frame from the previous main navigation results in an error
+TEST(EvaluateJavaScript, _javascript_InMissingFrameAfterNavigationError)
+{
+    allFrames = [[NSMutableSet<WKFrameInfo *> alloc] init];
+
+    auto handler = adoptNS([[TestURLSchemeHandler alloc] init]);
+    [handler setStartURLSchemeTaskHandler:[&](WKWebView *, id<WKURLSchemeTask> task) {
+        if ([task.request.URL.absoluteString isEqualToString:@"framestest://test/index.html"]) {
+            NSData *data = "" stringWithFormat:@"%s", framesMainResource] dataUsingEncoding:NSUTF8StringEncoding];
+            [task didReceiveResponse:[[[NSURLResponse alloc] initWithURL:task.request.URL MIMEType:@"text/html" expectedContentLength:data.length textEncodingName:nil] autorelease]];
+            [task didReceiveData:data];
+            [task didFinish];
+        } else if ([task.request.URL.absoluteString isEqualToString:@"otherprotocol://test/index.html"]) {
+            NSData *data = "" dataUsingEncoding:NSUTF8StringEncoding];
+            [task didReceiveResponse:[[[NSURLResponse alloc] initWithURL:task.request.URL MIMEType:@"text/html" expectedContentLength:0 textEncodingName:nil] autorelease]];
+            [task didReceiveData:data];
+            [task didFinish];
+        } else if ([task.request.URL.absoluteString isEqualToString:@"framestest://index2.html"]) {
+            NSData *data = "" dataUsingEncoding:NSUTF8StringEncoding];
+            [task didReceiveResponse:[[[NSURLResponse alloc] initWithURL:task.request.URL MIMEType:@"text/html" expectedContentLength:0 textEncodingName:nil] autorelease]];
+            [task didReceiveData:data];
+            [task didFinish];
+        } else
+            ASSERT_NOT_REACHED();
+    }];
+
+    WKWebViewConfiguration *configuration = [[[WKWebViewConfiguration alloc] init] autorelease];
+    [configuration setURLSchemeHandler:handler.get() forURLScheme:@"framestest"];
+    [configuration setURLSchemeHandler:handler.get() forURLScheme:@"otherprotocol"];
+
+    RetainPtr<WKWebView> webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration]);
+
+    auto navigationDelegate = adoptNS([[TestNavigationDelegate alloc] init]);
+
+    __block bool didFinishNavigation = false;
+    [navigationDelegate setDidFinishNavigation:^(WKWebView *, WKNavigation *) {
+        didFinishNavigation = true;
+    }];
+
+    [navigationDelegate setDecidePolicyForNavigationAction:[&] (WKNavigationAction *action, void (^decisionHandler)(WKNavigationActionPolicy)) {
+        if (action.targetFrame)
+            [allFrames addObject:action.targetFrame];
+
+        decisionHandler(WKNavigationActionPolicyAllow);
+    }];
+
+    [webView setNavigationDelegate:navigationDelegate.get()];
+    [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"framestest://test/index.html"]]];
+
+    TestWebKitAPI::Util::run(&didFinishNavigation);
+    didFinishNavigation = false;
+
+    EXPECT_EQ(allFrames.count, 2u);
+
+    RetainPtr<WKFrameInfo> iframe;
+    for (WKFrameInfo *frame in allFrames) {
+        if (frame.isMainFrame)
+            continue;
+        iframe = frame;
+        break;
+    }
+
+    EXPECT_NOT_NULL(iframe);
+
+    // Get rid of the frame by navigating
+    [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"framestest://index2.html"]]];
+    TestWebKitAPI::Util::run(&didFinishNavigation);
+    didFinishNavigation = false;
+
+    static bool isDone = false;
+
+    [webView callAsyncJavaScript:@"return location.href;" arguments:nil inFrame:iframe.get() inContentWorld:WKContentWorld.defaultClientWorld completionHandler:[] (id result, NSError *error) {
+        EXPECT_FALSE(error == nil);
+        EXPECT_TRUE(error.domain == WKErrorDomain);
+        EXPECT_TRUE(error.code == WKErrorJavaScriptInvalidFrameTarget);
+        isDone = true;
+    }];
+
+    TestWebKitAPI::Util::run(&isDone);
+    isDone = false;
+}

Modified: trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.mm (263726 => 263727)


--- trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.mm	2020-06-30 05:33:36 UTC (rev 263726)
+++ trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.mm	2020-06-30 05:43:19 UTC (rev 263727)
@@ -198,7 +198,7 @@
         *errorOut = nil;
 
     RetainPtr<id> evalResult;
-    [self callAsyncJavaScript:script arguments:arguments inContentWorld:WKContentWorld.pageWorld completionHandler:[&] (id result, NSError *error) {
+    [self callAsyncJavaScript:script arguments:arguments inFrame:nil inContentWorld:WKContentWorld.pageWorld completionHandler:[&] (id result, NSError *error) {
         evalResult = result;
         if (errorOut)
             *errorOut = [error retain];
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to