Title: [283276] trunk
Revision
283276
Author
[email protected]
Date
2021-09-29 16:28:22 -0700 (Wed, 29 Sep 2021)

Log Message

[Cocoa] add _WKInspectorExtension SPI to evaluate script on an extension tab
https://bugs.webkit.org/show_bug.cgi?id=230646
<rdar://problem/83420328>

Reviewed by Devin Rousso.

Source/WebCore:

Exercised by new API test: WKInspectorExtension.CanEvaluateScriptInExtensionTab

* inspector/InspectorFrontendHost.h:
* inspector/InspectorFrontendHost.idl:
* inspector/InspectorFrontendHost.cpp:
(WebCore::InspectorFrontendHost::evaluateScriptInExtensionTab):
Find the global object that corresponds to the passed-in <iframe> and
try to evaluate scriptSource in the mainThreadNormalWorld() of that <iframe>.

* html/HTMLIFrameElement.idl: Add [JSGenerateToNativeObject] so that
it's possible to pass HTMLIFrameElement to the IDL function and convert it
to the native object (HTMLIFrameElement&) from a JSValue.

Source/WebInspectorUI:

Add a new InspectorFrontendAPI method to evaluate script on an iframe within
Web Inspector. This in turn calls out to InspectorFrontendHost to do the actual evaluation.
Otherwise, the CSP policy set by the tab content may block any such evaluation
if the 'script-src' directive does not include 'unsafe-eval'.

* UserInterface/Protocol/InspectorFrontendAPI.js:
(InspectorFrontendAPI.showExtensionTab):
(InspectorFrontendAPI.evaluateScriptInExtensionTab):
Call through to the WebInspectorExtensionController method.

* UserInterface/Controllers/WebInspectorExtensionController.js:
(WI.WebInspectorExtensionController.prototype.evaluateScriptInExtensionTab): Added.
Try to get the <iframe> for a extensionTabID, and use InspectorFrontendHost to
evaluate script in the context of the <iframe>. Be sure to correctly wrap the result.

* UserInterface/Views/WebInspectorExtensionTabContentView.js:
(WI.WebInspectorExtensionTabContentView):
(WI.WebInspectorExtensionTabContentView.prototype.get iframeElement):
(WI.WebInspectorExtensionTabContentView.shouldSaveTab):
(WI.WebInspectorExtensionTabContentView.prototype.initialLayout): Deleted.
While writing the API test, I saw that the first evaluation frequently failed
because the <iframe> did not exist. Change this class so that the <iframe>
is created in the constructor. Add a getter for the <iframe> element.

(WI.WebInspectorExtensionTabContentView.prototype._extensionFrameDidLoad):
(WI.WebInspectorExtensionTabContentView.prototype._maybeDispatchDidShowExtensionTab):
While writing this patch, it became apparent that didShowExtensionTab() was being
called prior to the iframe actually completing its initial load. Then, the test
would try to evaluate script on about:blank instead of the actual tab content.
To fix this, require that the <iframe> be attached and have fired the `onload` event
before we notify clients that it has been 'shown'.

* UserInterface/Main.html:
Adjust the default CSP policy to not mention img-src. This allows ports such as
Cocoa to set their own img-src CSP directive. These changes are necessary to allow
images to load from custom URL schemes.

* UserInterface/Views/TabBrowser.js:
(WI.TabBrowser.prototype.bestTabContentViewForRepresentedObject):
The new API test exposes a bug in this assertion, namely, that it does not account
for the situation where a tab does not wish to be saved. In that case, the displayed
WebInspectorExtensionTabContentView is *not* at index 0 of WI.TabBrowser.recentTabContentViews.
This is correctly handled with a special case in WI.TabBrowser._tabBarItemSelected,
so incorporate that logic into the assertion.

Source/WebKit:

Add new testing API for evaluating script expressions in the context of a
tab created by _WKInspectorExtension. For the most part, this is implemented
in the same way as the -evaluateScript: method, but the script is evaluated
within the Web Inspector frontend itself rather than in the inspected page.

To avoid CSP issues, the actual evaluation is performed on subframes using a
new InspectorFrontendHost method which takes an <iframe> and script source.

Along the way, tweak Web Inspector's CSP policy to allow loading images from
custom URL schemes as specified using _WKInspectorConfiguration. This is so
that tab icons from the test-resource: scheme can be loaded in the main frame
of Web Inspector's WKWebView under testing situations.

* SourcesCocoa.txt:
* WebKit.xcodeproj/project.pbxproj:
Add new files.

* UIProcess/API/APIInspectorExtension.h:
* UIProcess/API/APIInspectorExtension.cpp:
(API::InspectorExtension::evaluateScriptInExtensionTab):
Based on evaluateScript(). Call through to the shared extension controller.

* UIProcess/API/Cocoa/_WKInspectorExtensionPrivateForTesting.h: Added.
* UIProcess/API/Cocoa/_WKInspectorExtensionTesting.mm: Added.
(-[_WKInspectorExtension _evaluateScript:inExtensionTabWithIdentifier:completionHandler:]):
Added. Call through to the shared extension controller.

* UIProcess/Inspector/WebInspectorUIExtensionControllerProxy.h:
* UIProcess/Inspector/WebInspectorUIExtensionControllerProxy.cpp:
(WebKit::WebInspectorUIExtensionControllerProxy::evaluateScriptInExtensionTab):
Based on evaluateScript(). Send IPC to the Inspector WebProcess.

* WebProcess/Inspector/WebInspectorUIExtensionController.h:
* WebProcess/Inspector/WebInspectorUIExtensionController.messages.in:
* WebProcess/Inspector/WebInspectorUIExtensionController.cpp:
(WebKit::WebInspectorUIExtensionController::evaluateScriptInExtensionTab):
Based on evaluateScriptForExtension. Call into the frontend API
which will perform the actual evaluation on the <iframe> contentWindow.

* UIProcess/Inspector/mac/WKInspectorResourceURLSchemeHandler.mm:
(-[WKInspectorResourceURLSchemeHandler webView:startURLSchemeTask:]):
Specify the list of custom protocols as allowable sources for 'img-src'.
The 'img-src' directive also includes 'file: blob: resource:' as allowable
sources, since this was the previous CSP policy defined in Main.html.

* UIProcess/Cocoa/GroupActivities/GroupActivitiesSessionNotifier.mm:
Fix UnifiedSources fallout by including a missing header.

Tools:

Add a new test to exercise the SPI. The test sets up an _WKInspectorExtension,
creates a tab, evaluates script on the tab, and later reads back the stored value.

Notably, this test would fail if the extension tab is not currently showing.
This is a bug and will be addressed as part of https://bugs.webkit.org/show_bug.cgi?id=230758.

* TestWebKitAPI/SourcesCocoa.txt:
* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
Add new files.

* TestWebKitAPI/Tests/WebKitCocoa/InspectorExtension-basic-tab.html:
Add inline <script> to set window._secretValue. This is checked by the API test.

* TestWebKitAPI/Tests/WebKitCocoa/WKInspectorExtension.mm: Added.
(resetGlobalState):
(-[UIDelegateForTestingInspectorExtension _webView:didAttachLocalInspector:]):
(-[UIDelegateForTestingInspectorExtension _webView:configurationForLocalInspector:]):
(-[InspectorExtensionDelegateForTestingInspectorExtension inspectorExtension:didShowTabWithIdentifier:]):
(-[InspectorExtensionDelegateForTestingInspectorExtension inspectorExtension:didHideTabWithIdentifier:]):
(TEST):

* TestWebKitAPI/Tests/WebKitCocoa/WKInspectorExtensionDelegate.mm:
(-[UIDelegateForTestingInspectorExtensionDelegate _webView:configurationForLocalInspector:]):
(TEST):
Adopt fixes from WKInspectorExtension that allow extension tab content and icons to load.

* TestWebKitAPI/cocoa/TestInspectorURLSchemeHandler.h: Added.
* TestWebKitAPI/cocoa/TestInspectorURLSchemeHandler.mm: Copied from Source/WebKit/UIProcess/Inspector/mac/WKInspectorResourceURLSchemeHandler.mm.
(-[TestInspectorURLSchemeHandler webView:startURLSchemeTask:]):
(-[TestInspectorURLSchemeHandler webView:stopURLSchemeTask:]):
Add a simple URLSchemeHandler which allows serving test resources from the TestWebKitAPI.resources directory.
This is necessary to test _WKInspectorExtension tabs, which must load their content from a custom URL scheme.

* TestWebKitAPI/cocoa/TestWKWebView.mm:
Fix UnifiedSources fallout by adding a missing include.

Modified Paths

Added Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (283275 => 283276)


--- trunk/Source/WebCore/ChangeLog	2021-09-29 23:27:35 UTC (rev 283275)
+++ trunk/Source/WebCore/ChangeLog	2021-09-29 23:28:22 UTC (rev 283276)
@@ -1,3 +1,24 @@
+2021-09-29  BJ Burg  <[email protected]>
+
+        [Cocoa] add _WKInspectorExtension SPI to evaluate script on an extension tab
+        https://bugs.webkit.org/show_bug.cgi?id=230646
+        <rdar://problem/83420328>
+
+        Reviewed by Devin Rousso.
+
+        Exercised by new API test: WKInspectorExtension.CanEvaluateScriptInExtensionTab
+
+        * inspector/InspectorFrontendHost.h:
+        * inspector/InspectorFrontendHost.idl:
+        * inspector/InspectorFrontendHost.cpp:
+        (WebCore::InspectorFrontendHost::evaluateScriptInExtensionTab):
+        Find the global object that corresponds to the passed-in <iframe> and
+        try to evaluate scriptSource in the mainThreadNormalWorld() of that <iframe>.
+
+        * html/HTMLIFrameElement.idl: Add [JSGenerateToNativeObject] so that
+        it's possible to pass HTMLIFrameElement to the IDL function and convert it
+        to the native object (HTMLIFrameElement&) from a JSValue.
+
 2021-09-29  Alan Bujtas  <[email protected]>
 
         [LFC][IFC] Use the first-line style when measuring text content when applicable

Modified: trunk/Source/WebCore/html/HTMLIFrameElement.idl (283275 => 283276)


--- trunk/Source/WebCore/html/HTMLIFrameElement.idl	2021-09-29 23:27:35 UTC (rev 283275)
+++ trunk/Source/WebCore/html/HTMLIFrameElement.idl	2021-09-29 23:28:22 UTC (rev 283276)
@@ -19,7 +19,8 @@
  */
 
 [
-    Exposed=Window
+    Exposed=Window,
+    JSGenerateToNativeObject
 ] interface HTMLIFrameElement : HTMLElement {
     [Reflect, CEReactions=NotNeeded] attribute DOMString align;
     [Reflect, CEReactions=NotNeeded] attribute DOMString frameBorder;

Modified: trunk/Source/WebCore/inspector/InspectorFrontendHost.cpp (283275 => 283276)


--- trunk/Source/WebCore/inspector/InspectorFrontendHost.cpp	2021-09-29 23:27:35 UTC (rev 283275)
+++ trunk/Source/WebCore/inspector/InspectorFrontendHost.cpp	2021-09-29 23:28:22 UTC (rev 283276)
@@ -42,6 +42,7 @@
 #include "FloatRect.h"
 #include "FocusController.h"
 #include "Frame.h"
+#include "HTMLIFrameElement.h"
 #include "HitTestResult.h"
 #include "InspectorController.h"
 #include "InspectorDebuggableType.h"
@@ -69,6 +70,7 @@
 namespace WebCore {
 
 using namespace Inspector;
+using ValueOrException = Expected<JSC::JSValue, ExceptionDetails>;
 
 #if ENABLE(CONTEXT_MENUS)
 class FrontendMenuProvider : public ContextMenuProvider {
@@ -706,6 +708,28 @@
     
     m_client->didHideExtensionTab(extensionID, extensionTabID);
 }
+
+ExceptionOr<JSC::JSValue> InspectorFrontendHost::evaluateScriptInExtensionTab(HTMLIFrameElement& extensionFrameElement, const String& scriptSource)
+{
+    Frame* frame = extensionFrameElement.contentFrame();
+    if (!frame)
+        return Exception { InvalidStateError, "Unable to find global object for <iframe>"_s };
+
+    Ref<Frame> protectedFrame(*frame);
+
+    JSDOMGlobalObject* frameGlobalObject = frame->script().globalObject(mainThreadNormalWorld());
+    if (!frameGlobalObject)
+        return Exception { InvalidStateError, "Unable to find global object for <iframe>"_s };
+
+    JSC::SuspendExceptionScope scope(&frameGlobalObject->vm());
+    ValueOrException result = frame->script().evaluateInWorld(ScriptSourceCode(scriptSource), mainThreadNormalWorld());
+    
+    if (!result)
+        return Exception { InvalidStateError, result.error().message };
+
+    return WTFMove(result.value());
+}
+
 #endif // ENABLE(INSPECTOR_EXTENSIONS)
 
 

Modified: trunk/Source/WebCore/inspector/InspectorFrontendHost.h (283275 => 283276)


--- trunk/Source/WebCore/inspector/InspectorFrontendHost.h	2021-09-29 23:27:35 UTC (rev 283275)
+++ trunk/Source/WebCore/inspector/InspectorFrontendHost.h	2021-09-29 23:28:22 UTC (rev 283276)
@@ -30,6 +30,7 @@
 
 #include "ContextMenu.h"
 #include "ContextMenuProvider.h"
+#include "ExceptionOr.h"
 #include <wtf/RefCounted.h>
 #include <wtf/Vector.h>
 #include <wtf/text/WTFString.h>
@@ -39,6 +40,7 @@
 class DOMWrapperWorld;
 class Event;
 class FrontendMenuProvider;
+class HTMLIFrameElement;
 class InspectorFrontendClient;
 class Page;
 
@@ -141,6 +143,7 @@
 #if ENABLE(INSPECTOR_EXTENSIONS)
     void didShowExtensionTab(const String& extensionID, const String& extensionTabID);
     void didHideExtensionTab(const String& extensionID, const String& extensionTabID);
+    ExceptionOr<JSC::JSValue> evaluateScriptInExtensionTab(HTMLIFrameElement& extensionFrame, const String& scriptSource);
 #endif
 
 private:

Modified: trunk/Source/WebCore/inspector/InspectorFrontendHost.idl (283275 => 283276)


--- trunk/Source/WebCore/inspector/InspectorFrontendHost.idl	2021-09-29 23:27:35 UTC (rev 283275)
+++ trunk/Source/WebCore/inspector/InspectorFrontendHost.idl	2021-09-29 23:28:22 UTC (rev 283276)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007-2020 Apple Inc. All rights reserved.
+ * Copyright (C) 2007-2021 Apple Inc. All rights reserved.
  * Copyright (C) 2008 Matt Lilek <[email protected]>
  * Copyright (C) 2009 Google Inc. All rights reserved.
  *
@@ -100,6 +100,7 @@
     readonly attribute boolean supportsWebExtensions;
     [Conditional=INSPECTOR_EXTENSIONS] undefined didShowExtensionTab(DOMString extensionID, DOMString extensionTabID);
     [Conditional=INSPECTOR_EXTENSIONS] undefined didHideExtensionTab(DOMString extensionID, DOMString extensionTabID);
+    [Conditional=INSPECTOR_EXTENSIONS] any evaluateScriptInExtensionTab(HTMLIFrameElement extensionFrame, DOMString scriptSource);
 };
 
 dictionary ContextMenuItem {

Modified: trunk/Source/WebInspectorUI/ChangeLog (283275 => 283276)


--- trunk/Source/WebInspectorUI/ChangeLog	2021-09-29 23:27:35 UTC (rev 283275)
+++ trunk/Source/WebInspectorUI/ChangeLog	2021-09-29 23:28:22 UTC (rev 283276)
@@ -1,3 +1,56 @@
+2021-09-29  BJ Burg  <[email protected]>
+
+        [Cocoa] add _WKInspectorExtension SPI to evaluate script on an extension tab
+        https://bugs.webkit.org/show_bug.cgi?id=230646
+        <rdar://problem/83420328>
+
+        Reviewed by Devin Rousso.
+
+        Add a new InspectorFrontendAPI method to evaluate script on an iframe within
+        Web Inspector. This in turn calls out to InspectorFrontendHost to do the actual evaluation.
+        Otherwise, the CSP policy set by the tab content may block any such evaluation
+        if the 'script-src' directive does not include 'unsafe-eval'.
+
+        * UserInterface/Protocol/InspectorFrontendAPI.js:
+        (InspectorFrontendAPI.showExtensionTab):
+        (InspectorFrontendAPI.evaluateScriptInExtensionTab):
+        Call through to the WebInspectorExtensionController method.
+
+        * UserInterface/Controllers/WebInspectorExtensionController.js:
+        (WI.WebInspectorExtensionController.prototype.evaluateScriptInExtensionTab): Added.
+        Try to get the <iframe> for a extensionTabID, and use InspectorFrontendHost to
+        evaluate script in the context of the <iframe>. Be sure to correctly wrap the result.
+
+        * UserInterface/Views/WebInspectorExtensionTabContentView.js:
+        (WI.WebInspectorExtensionTabContentView):
+        (WI.WebInspectorExtensionTabContentView.prototype.get iframeElement):
+        (WI.WebInspectorExtensionTabContentView.shouldSaveTab):
+        (WI.WebInspectorExtensionTabContentView.prototype.initialLayout): Deleted.
+        While writing the API test, I saw that the first evaluation frequently failed
+        because the <iframe> did not exist. Change this class so that the <iframe>
+        is created in the constructor. Add a getter for the <iframe> element.
+
+        (WI.WebInspectorExtensionTabContentView.prototype._extensionFrameDidLoad):
+        (WI.WebInspectorExtensionTabContentView.prototype._maybeDispatchDidShowExtensionTab):
+        While writing this patch, it became apparent that didShowExtensionTab() was being
+        called prior to the iframe actually completing its initial load. Then, the test
+        would try to evaluate script on about:blank instead of the actual tab content.
+        To fix this, require that the <iframe> be attached and have fired the `onload` event
+        before we notify clients that it has been 'shown'.
+
+        * UserInterface/Main.html:
+        Adjust the default CSP policy to not mention img-src. This allows ports such as
+        Cocoa to set their own img-src CSP directive. These changes are necessary to allow
+        images to load from custom URL schemes.
+
+        * UserInterface/Views/TabBrowser.js:
+        (WI.TabBrowser.prototype.bestTabContentViewForRepresentedObject):
+        The new API test exposes a bug in this assertion, namely, that it does not account
+        for the situation where a tab does not wish to be saved. In that case, the displayed
+        WebInspectorExtensionTabContentView is *not* at index 0 of WI.TabBrowser.recentTabContentViews.
+        This is correctly handled with a special case in WI.TabBrowser._tabBarItemSelected,
+        so incorporate that logic into the assertion.
+
 2021-09-28  BJ Burg  <[email protected]>
 
         Web Inspector: add settings option for 'Show Mock Web Extension Tab' in engineering builds

Modified: trunk/Source/WebInspectorUI/UserInterface/Controllers/WebInspectorExtensionController.js (283275 => 283276)


--- trunk/Source/WebInspectorUI/UserInterface/Controllers/WebInspectorExtensionController.js	2021-09-29 23:27:35 UTC (rev 283275)
+++ trunk/Source/WebInspectorUI/UserInterface/Controllers/WebInspectorExtensionController.js	2021-09-29 23:28:22 UTC (rev 283276)
@@ -170,6 +170,27 @@
             return WI.WebInspectorExtension.ErrorCode.InternalError;
         }
     }
+
+    evaluateScriptInExtensionTab(extensionTabID, scriptSource)
+    {
+        let tabContentView = this._extensionTabContentViewForExtensionTabIDMap.get(extensionTabID);
+        if (!tabContentView) {
+            WI.reportInternalError("Unable to evaluate with unknown extensionTabID: " + extensionTabID);
+            return WI.WebInspectorExtension.ErrorCode.InvalidRequest;
+        }
+
+        let iframe = tabContentView.iframeElement;
+        if (!(iframe instanceof HTMLIFrameElement)) {
+            WI.reportInternalError("Unable to evaluate without an <iframe> for extensionTabID: " + extensionTabID);
+            return WI.WebInspectorExtension.ErrorCode.InvalidRequest;
+        }
+
+        try {
+            return {result: InspectorFrontendHost.evaluateScriptInExtensionTab(iframe, scriptSource)};
+        } catch (error) {
+            return {error: error.message};
+        }
+    }
 };
 
 WI.WebInspectorExtensionController.Event = {

Modified: trunk/Source/WebInspectorUI/UserInterface/Main.html (283275 => 283276)


--- trunk/Source/WebInspectorUI/UserInterface/Main.html	2021-09-29 23:27:35 UTC (rev 283275)
+++ trunk/Source/WebInspectorUI/UserInterface/Main.html	2021-09-29 23:28:22 UTC (rev 283276)
@@ -27,10 +27,10 @@
 <head>
     <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
     <!--
-        Note that some WebKit ports may set custom 'frame-src' and 'connect-src' directives via HTTP response header.
+        Note that some WebKit ports may set custom 'frame-src', 'img-src', and 'connect-src' directives via HTTP response header.
         The combined CSP policy requires a request to be allowed by both directive lists, so 'default-src' is omitted.
     -->
-    <meta http-equiv="Content-Security-Policy" content="img-src * file: blob: resource:; media-src * blob:; font-src * blob:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' data:; object-src 'none'">
+    <meta http-equiv="Content-Security-Policy" content="media-src * blob:; font-src * blob:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' data:; object-src 'none'">
 
     <link rel="stylesheet" href=""
 

Modified: trunk/Source/WebInspectorUI/UserInterface/Protocol/InspectorFrontendAPI.js (283275 => 283276)


--- trunk/Source/WebInspectorUI/UserInterface/Protocol/InspectorFrontendAPI.js	2021-09-29 23:27:35 UTC (rev 283275)
+++ trunk/Source/WebInspectorUI/UserInterface/Protocol/InspectorFrontendAPI.js	2021-09-29 23:28:22 UTC (rev 283276)
@@ -235,5 +235,13 @@
     showExtensionTab(extensionTabID)
     {
         return WI.sharedApp.extensionController.showExtensionTab(extensionTabID);
-    }
+    },
+
+    // Returns a string (WI.WebInspectorExtension.ErrorCode) if an error occurred that prevented evaluation.
+    // Returns an object with a 'result' key and value that is the result of the script evaluation.
+    // Returns an object with an 'error' key and value in the case that an exception was thrown.
+    evaluateScriptInExtensionTab(extensionTabID, scriptSource)
+    {
+        return WI.sharedApp.extensionController.evaluateScriptInExtensionTab(extensionTabID, scriptSource);
+    },
 };

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/TabBrowser.js (283275 => 283276)


--- trunk/Source/WebInspectorUI/UserInterface/Views/TabBrowser.js	2021-09-29 23:27:35 UTC (rev 283275)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/TabBrowser.js	2021-09-29 23:28:22 UTC (rev 283276)
@@ -120,7 +120,8 @@
 
     bestTabContentViewForRepresentedObject(representedObject, options = {})
     {
-        console.assert(!this.selectedTabContentView || this.selectedTabContentView === this._recentTabContentViews[0]);
+        let shouldSaveTab = this.selectedTabContentView?.constructor.shouldSaveTab() || this.selectedTabContentView?.constructor.shouldPinTab();
+        console.assert(!this.selectedTabContentView || this.selectedTabContentView === this._recentTabContentViews[0] || !shouldSaveTab);
 
         let tabContentView = this._recentTabContentViews.find((tabContentView) => tabContentView.type === options.preferredTabType);
         if (tabContentView && tabContentView.canShowRepresentedObject(representedObject))

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/WebInspectorExtensionTabContentView.js (283275 => 283276)


--- trunk/Source/WebInspectorUI/UserInterface/Views/WebInspectorExtensionTabContentView.js	2021-09-29 23:27:35 UTC (rev 283275)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/WebInspectorExtensionTabContentView.js	2021-09-29 23:28:22 UTC (rev 283276)
@@ -39,11 +39,21 @@
         this._extensionTabID = extensionTabID;
         this._tabInfo = tabInfo;
         this._sourceURL = sourceURL;
+
+        // FIXME: the <iframe>'s document is implicitly reloaded when this
+        // content view's element is detached and later re-attached to the DOM.
+        // This is a bug and will be addressed in <https://webkit.org/b/230758>.
+        this._iframeElement = this.element.appendChild(document.createElement("iframe"));
+        this._iframeElement.addEventListener("load", this._extensionFrameDidLoad.bind(this));
+        this._iframeElement.src = ""
+
+        this._frameContentDidLoad = false;
     }
 
     // Public
 
     get extensionTabID() { return this._extensionTabID; }
+    get iframeElement() { return this._iframeElement; }
 
     get type()
     {
@@ -59,8 +69,7 @@
     {
         super.attached();
 
-        if (InspectorFrontendHost.supportsWebExtensions)
-            InspectorFrontendHost.didShowExtensionTab(this._extension.extensionID, this._extensionTabID);
+        this._maybeDispatchDidShowExtensionTab();
     }
 
     detached()
@@ -78,14 +87,21 @@
 
     static shouldSaveTab() { return false; }
 
-    // Protected
+    // Private
 
-    initialLayout()
+    _extensionFrameDidLoad()
     {
-        super.initialLayout();
+        this._frameContentDidLoad = true;
+        this._maybeDispatchDidShowExtensionTab();
+    }
 
-        let iframeElement = this.element.appendChild(document.createElement("iframe"));
-        iframeElement.src = ""
+    _maybeDispatchDidShowExtensionTab()
+    {
+        if (!this._frameContentDidLoad || !this.element.isConnected)
+            return;
+
+        if (InspectorFrontendHost.supportsWebExtensions)
+            InspectorFrontendHost.didShowExtensionTab(this._extension.extensionID, this._extensionTabID);
     }
 };
 

Modified: trunk/Source/WebKit/ChangeLog (283275 => 283276)


--- trunk/Source/WebKit/ChangeLog	2021-09-29 23:27:35 UTC (rev 283275)
+++ trunk/Source/WebKit/ChangeLog	2021-09-29 23:28:22 UTC (rev 283276)
@@ -1,3 +1,59 @@
+2021-09-29  BJ Burg  <[email protected]>
+
+        [Cocoa] add _WKInspectorExtension SPI to evaluate script on an extension tab
+        https://bugs.webkit.org/show_bug.cgi?id=230646
+        <rdar://problem/83420328>
+
+        Reviewed by Devin Rousso.
+
+        Add new testing API for evaluating script expressions in the context of a
+        tab created by _WKInspectorExtension. For the most part, this is implemented
+        in the same way as the -evaluateScript: method, but the script is evaluated
+        within the Web Inspector frontend itself rather than in the inspected page.
+
+        To avoid CSP issues, the actual evaluation is performed on subframes using a
+        new InspectorFrontendHost method which takes an <iframe> and script source.
+
+        Along the way, tweak Web Inspector's CSP policy to allow loading images from
+        custom URL schemes as specified using _WKInspectorConfiguration. This is so
+        that tab icons from the test-resource: scheme can be loaded in the main frame
+        of Web Inspector's WKWebView under testing situations.
+
+        * SourcesCocoa.txt:
+        * WebKit.xcodeproj/project.pbxproj:
+        Add new files.
+
+        * UIProcess/API/APIInspectorExtension.h:
+        * UIProcess/API/APIInspectorExtension.cpp:
+        (API::InspectorExtension::evaluateScriptInExtensionTab):
+        Based on evaluateScript(). Call through to the shared extension controller.
+
+        * UIProcess/API/Cocoa/_WKInspectorExtensionPrivateForTesting.h: Added.
+        * UIProcess/API/Cocoa/_WKInspectorExtensionTesting.mm: Added.
+        (-[_WKInspectorExtension _evaluateScript:inExtensionTabWithIdentifier:completionHandler:]):
+        Added. Call through to the shared extension controller.
+
+        * UIProcess/Inspector/WebInspectorUIExtensionControllerProxy.h:
+        * UIProcess/Inspector/WebInspectorUIExtensionControllerProxy.cpp:
+        (WebKit::WebInspectorUIExtensionControllerProxy::evaluateScriptInExtensionTab):
+        Based on evaluateScript(). Send IPC to the Inspector WebProcess.
+
+        * WebProcess/Inspector/WebInspectorUIExtensionController.h:
+        * WebProcess/Inspector/WebInspectorUIExtensionController.messages.in:
+        * WebProcess/Inspector/WebInspectorUIExtensionController.cpp:
+        (WebKit::WebInspectorUIExtensionController::evaluateScriptInExtensionTab):
+        Based on evaluateScriptForExtension. Call into the frontend API
+        which will perform the actual evaluation on the <iframe> contentWindow.
+
+        * UIProcess/Inspector/mac/WKInspectorResourceURLSchemeHandler.mm:
+        (-[WKInspectorResourceURLSchemeHandler webView:startURLSchemeTask:]):
+        Specify the list of custom protocols as allowable sources for 'img-src'.
+        The 'img-src' directive also includes 'file: blob: resource:' as allowable
+        sources, since this was the previous CSP policy defined in Main.html.
+
+        * UIProcess/Cocoa/GroupActivities/GroupActivitiesSessionNotifier.mm:
+        Fix UnifiedSources fallout by including a missing header.
+
 2021-09-29  Chris Dumez  <[email protected]>
 
         Use isolated NSURLSessions for each first party registrable domain

Modified: trunk/Source/WebKit/SourcesCocoa.txt (283275 => 283276)


--- trunk/Source/WebKit/SourcesCocoa.txt	2021-09-29 23:27:35 UTC (rev 283275)
+++ trunk/Source/WebKit/SourcesCocoa.txt	2021-09-29 23:28:22 UTC (rev 283276)
@@ -288,6 +288,7 @@
 UIProcess/API/Cocoa/_WKInspectorTesting.mm
 UIProcess/API/Cocoa/_WKInspectorDebuggableInfo.mm
 UIProcess/API/Cocoa/_WKInspectorExtension.mm
+UIProcess/API/Cocoa/_WKInspectorExtensionTesting.mm
 UIProcess/API/Cocoa/_WKInspectorWindow.mm
 UIProcess/API/Cocoa/_WKInternalDebugFeature.mm
 UIProcess/API/Cocoa/_WKLinkIconParameters.mm

Modified: trunk/Source/WebKit/UIProcess/API/APIInspectorExtension.cpp (283275 => 283276)


--- trunk/Source/WebKit/UIProcess/API/APIInspectorExtension.cpp	2021-09-29 23:27:35 UTC (rev 283275)
+++ trunk/Source/WebKit/UIProcess/API/APIInspectorExtension.cpp	2021-09-29 23:28:22 UTC (rev 283276)
@@ -81,6 +81,18 @@
     m_extensionControllerProxy->reloadForExtension(m_identifier, ignoreCache, userAgent, injectedScript, WTFMove(completionHandler));
 }
 
+// For testing.
+
+void InspectorExtension::evaluateScriptInExtensionTab(const Inspector::ExtensionTabID& extensionTabID, const WTF::String& scriptSource, WTF::CompletionHandler<void(Inspector::ExtensionEvaluationResult)>&& completionHandler)
+{
+    if (!m_extensionControllerProxy) {
+        completionHandler(makeUnexpected(Inspector::ExtensionError::ContextDestroyed));
+        return;
+    }
+
+    m_extensionControllerProxy->evaluateScriptInExtensionTab(extensionTabID, scriptSource, WTFMove(completionHandler));
+}
+
 } // namespace API
 
 #endif // ENABLE(INSPECTOR_EXTENSIONS)

Modified: trunk/Source/WebKit/UIProcess/API/APIInspectorExtension.h (283275 => 283276)


--- trunk/Source/WebKit/UIProcess/API/APIInspectorExtension.h	2021-09-29 23:27:35 UTC (rev 283275)
+++ trunk/Source/WebKit/UIProcess/API/APIInspectorExtension.h	2021-09-29 23:28:22 UTC (rev 283276)
@@ -49,8 +49,11 @@
 
     void createTab(const WTF::String& tabName, const WTF::URL& tabIconURL, const WTF::URL& sourceURL, WTF::CompletionHandler<void(Expected<Inspector::ExtensionTabID, Inspector::ExtensionError>)>&&);
     void evaluateScript(const WTF::String& scriptSource, const std::optional<WTF::URL>& frameURL, const std::optional<WTF::URL>& contextSecurityOrigin, const std::optional<bool>& useContentScriptContext, WTF::CompletionHandler<void(Inspector::ExtensionEvaluationResult)>&&);
-    void reloadIgnoringCache(const std::optional<bool>& ignoreCache, const std::optional<WTF::String>& userAgent, const std::optional<WTF::String>& injectedScript,  WTF::CompletionHandler<void(Inspector::ExtensionEvaluationResult)>&&);
+    void reloadIgnoringCache(const std::optional<bool>& ignoreCache, const std::optional<WTF::String>& userAgent, const std::optional<WTF::String>& injectedScript, WTF::CompletionHandler<void(Inspector::ExtensionEvaluationResult)>&&);
 
+    // For testing.
+    void evaluateScriptInExtensionTab(const Inspector::ExtensionTabID&, const WTF::String& scriptSource, WTF::CompletionHandler<void(Inspector::ExtensionEvaluationResult)>&&);
+
     InspectorExtensionClient* client() const { return m_client.get(); }
     void setClient(UniqueRef<InspectorExtensionClient>&&);
 

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


--- trunk/Source/WebKit/UIProcess/API/Cocoa/_WKInspectorExtensionPrivateForTesting.h	                        (rev 0)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/_WKInspectorExtensionPrivateForTesting.h	2021-09-29 23:28:22 UTC (rev 283276)
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "_WKInspectorExtension.h"
+
+#if !TARGET_OS_IPHONE
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface _WKInspectorExtension (WKTesting)
+- (void)_evaluateScript:(NSString *)scriptSource inExtensionTabWithIdentifier:(NSString *)extensionTabIdentifier completionHandler:(void(^)(NSError * _Nullable, NSDictionary * _Nullable result))completionHandler;
+@end
+
+NS_ASSUME_NONNULL_END
+
+#endif // !TARGET_OS_IPHONE

Added: trunk/Source/WebKit/UIProcess/API/Cocoa/_WKInspectorExtensionTesting.mm (0 => 283276)


--- trunk/Source/WebKit/UIProcess/API/Cocoa/_WKInspectorExtensionTesting.mm	                        (rev 0)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/_WKInspectorExtensionTesting.mm	2021-09-29 23:28:22 UTC (rev 283276)
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "config.h"
+
+#if ENABLE(INSPECTOR_EXTENSIONS)
+
+#import "APISerializedScriptValue.h"
+#import "InspectorExtensionTypes.h"
+#import "WKError.h"
+#import "WKWebViewInternal.h"
+#import "_WKInspectorExtensionInternal.h"
+#import "_WKInspectorExtensionPrivateForTesting.h"
+#import <WebCore/ExceptionDetails.h>
+#import <WebCore/WebCoreObjCExtras.h>
+#import <wtf/BlockPtr.h>
+
+// This file exists to centralize all fragile code that is used by _WKInspectorExtension API tests.
+
+@implementation _WKInspectorExtension (WKTesting)
+
+- (void)_evaluateScript:(NSString *)scriptSource inExtensionTabWithIdentifier:(NSString *)extensionTabIdentifier completionHandler:(void(^)(NSError *, NSDictionary *))completionHandler
+{
+    _extension->evaluateScriptInExtensionTab(extensionTabIdentifier, scriptSource, [protectedSelf = retainPtr(self), capturedBlock = makeBlockPtr(WTFMove(completionHandler))] (Inspector::ExtensionEvaluationResult&& result) mutable {
+        if (!result) {
+            capturedBlock([NSError errorWithDomain:WKErrorDomain code:WKErrorUnknown userInfo:@{ NSLocalizedFailureReasonErrorKey: Inspector::extensionErrorToString(result.error()) }], nil);
+            return;
+        }
+        
+        auto valueOrException = result.value();
+        if (!valueOrException) {
+            capturedBlock(nsErrorFromExceptionDetails(valueOrException.error()).get(), nil);
+            return;
+        }
+
+        id body = API::SerializedScriptValue::deserialize(valueOrException.value()->internalRepresentation(), 0);
+        capturedBlock(nil, body);
+    });
+}
+
+@end
+
+#endif // ENABLE(INSPECTOR_EXTENSIONS)

Modified: trunk/Source/WebKit/UIProcess/Cocoa/GroupActivities/GroupActivitiesSessionNotifier.mm (283275 => 283276)


--- trunk/Source/WebKit/UIProcess/Cocoa/GroupActivities/GroupActivitiesSessionNotifier.mm	2021-09-29 23:27:35 UTC (rev 283275)
+++ trunk/Source/WebKit/UIProcess/Cocoa/GroupActivities/GroupActivitiesSessionNotifier.mm	2021-09-29 23:28:22 UTC (rev 283276)
@@ -28,6 +28,7 @@
 
 #if ENABLE(MEDIA_SESSION_COORDINATOR) && HAVE(GROUP_ACTIVITIES)
 
+#import "GroupActivitiesCoordinator.h"
 #import "WKGroupSession.h"
 #import "WebPageProxy.h"
 

Modified: trunk/Source/WebKit/UIProcess/Inspector/WebInspectorUIExtensionControllerProxy.cpp (283275 => 283276)


--- trunk/Source/WebKit/UIProcess/Inspector/WebInspectorUIExtensionControllerProxy.cpp	2021-09-29 23:27:35 UTC (rev 283275)
+++ trunk/Source/WebKit/UIProcess/Inspector/WebInspectorUIExtensionControllerProxy.cpp	2021-09-29 23:28:22 UTC (rev 283276)
@@ -199,7 +199,32 @@
     });
 }
 
+// API for testing.
 
+void WebInspectorUIExtensionControllerProxy::evaluateScriptInExtensionTab(const Inspector::ExtensionTabID& extensionTabID, const String& scriptSource, WTF::CompletionHandler<void(Inspector::ExtensionEvaluationResult)>&& completionHandler)
+{
+    whenFrontendHasLoaded([weakThis = makeWeakPtr(this), extensionTabID, scriptSource, completionHandler = WTFMove(completionHandler)] () mutable {
+        if (!weakThis || !weakThis->m_inspectorPage) {
+            completionHandler(makeUnexpected(Inspector::ExtensionError::ContextDestroyed));
+            return;
+        }
+
+        weakThis->m_inspectorPage->sendWithAsyncReply(Messages::WebInspectorUIExtensionController::EvaluateScriptInExtensionTab {extensionTabID, scriptSource}, [completionHandler = WTFMove(completionHandler)](const IPC::DataReference& dataReference, const std::optional<WebCore::ExceptionDetails>& details, const std::optional<Inspector::ExtensionError>& error) mutable {
+            if (error) {
+                completionHandler(makeUnexpected(error.value()));
+                return;
+            }
+
+            if (details) {
+                Expected<RefPtr<API::SerializedScriptValue>, WebCore::ExceptionDetails> returnedValue = makeUnexpected(details.value());
+                return completionHandler({ returnedValue });
+            }
+
+            completionHandler({ { API::SerializedScriptValue::adopt({ dataReference.data(), dataReference.size() }).ptr() } });
+        });
+    });
+}
+
 // WebInspectorUIExtensionControllerProxy IPC messages.
 
 void WebInspectorUIExtensionControllerProxy::didShowExtensionTab(const Inspector::ExtensionID& extensionID, const Inspector::ExtensionTabID& extensionTabID)

Modified: trunk/Source/WebKit/UIProcess/Inspector/WebInspectorUIExtensionControllerProxy.h (283275 => 283276)


--- trunk/Source/WebKit/UIProcess/Inspector/WebInspectorUIExtensionControllerProxy.h	2021-09-29 23:27:35 UTC (rev 283275)
+++ trunk/Source/WebKit/UIProcess/Inspector/WebInspectorUIExtensionControllerProxy.h	2021-09-29 23:28:22 UTC (rev 283276)
@@ -62,6 +62,9 @@
     void reloadForExtension(const Inspector::ExtensionID&, const std::optional<bool>& ignoreCache, const std::optional<String>& userAgent, const std::optional<String>& injectedScript, WTF::CompletionHandler<void(Inspector::ExtensionEvaluationResult)>&&);
     void showExtensionTab(const Inspector::ExtensionTabID&, CompletionHandler<void(Expected<void, Inspector::ExtensionError>)>&&);
 
+    // API for testing.
+    void evaluateScriptInExtensionTab(const Inspector::ExtensionTabID&, const String& scriptSource, WTF::CompletionHandler<void(Inspector::ExtensionEvaluationResult)>&&);
+
     // WebInspectorUIExtensionControllerProxy IPC messages.
     void didShowExtensionTab(const Inspector::ExtensionID&, const Inspector::ExtensionTabID&);
     void didHideExtensionTab(const Inspector::ExtensionID&, const Inspector::ExtensionTabID&);

Modified: trunk/Source/WebKit/UIProcess/Inspector/mac/WKInspectorResourceURLSchemeHandler.mm (283275 => 283276)


--- trunk/Source/WebKit/UIProcess/Inspector/mac/WKInspectorResourceURLSchemeHandler.mm	2021-09-29 23:27:35 UTC (rev 283275)
+++ trunk/Source/WebKit/UIProcess/Inspector/mac/WKInspectorResourceURLSchemeHandler.mm	2021-09-29 23:28:22 UTC (rev 283276)
@@ -122,7 +122,8 @@
 
         // Allow fetches for resources that use a registered custom URL scheme.
         if (_allowedURLSchemesForCSP && [self.mainResourceURLsForCSP containsObject:requestURL]) {
-            NSString *stringForCSPPolicy = [NSString stringWithFormat:@"connect-src * %@:", [_allowedURLSchemesForCSP.get().allObjects componentsJoinedByString:@": "]];
+            NSString *listOfCustomProtocols = [NSString stringWithFormat:@"%@:", [_allowedURLSchemesForCSP.get().allObjects componentsJoinedByString:@": "]];
+            NSString *stringForCSPPolicy = [NSString stringWithFormat:@"connect-src * %@; img-src * file: blob: resource: %@", listOfCustomProtocols, listOfCustomProtocols];
             [headerFields setObject:stringForCSPPolicy forKey:@"Content-Security-Policy"];
         }
 

Modified: trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj (283275 => 283276)


--- trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj	2021-09-29 23:27:35 UTC (rev 283275)
+++ trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj	2021-09-29 23:28:22 UTC (rev 283276)
@@ -1575,6 +1575,8 @@
 		99B16764252BBE620073140E /* WebInspectorUIExtensionController.h in Headers */ = {isa = PBXBuildFile; fileRef = 99B16761252BBE610073140E /* WebInspectorUIExtensionController.h */; };
 		99C3AE2D1DADA6AD00AF5C16 /* WebAutomationSessionMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = 99C3AE2C1DADA6A700AF5C16 /* WebAutomationSessionMacros.h */; };
 		99C607EB26FA9D4900A0953F /* _WKInspectorIBActions.h in Headers */ = {isa = PBXBuildFile; fileRef = 99C607EA26FA9D4800A0953F /* _WKInspectorIBActions.h */; settings = {ATTRIBUTES = (Private, ); }; };
+		99C607F026FB91E800A0953F /* _WKInspectorExtensionPrivateForTesting.h in Headers */ = {isa = PBXBuildFile; fileRef = 99C607EE26FB91E800A0953F /* _WKInspectorExtensionPrivateForTesting.h */; settings = {ATTRIBUTES = (Private, ); }; };
+		99C607F126FB91E900A0953F /* _WKInspectorExtensionTesting.mm in Sources */ = {isa = PBXBuildFile; fileRef = 99C607EF26FB91E800A0953F /* _WKInspectorExtensionTesting.mm */; };
 		99C81D5A1C20E7E2005C4C82 /* AutomationClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 99C81D551C20DFBE005C4C82 /* AutomationClient.h */; };
 		99C81D5D1C21F38B005C4C82 /* APIAutomationClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 99C81D5B1C20E817005C4C82 /* APIAutomationClient.h */; };
 		99E714C51C124A0400665B3A /* _WKAutomationDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 99E714C11C1249E600665B3A /* _WKAutomationDelegate.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -5125,6 +5127,8 @@
 		99C3AE261DAD948500AF5C16 /* WebAutomationSessionCocoa.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WebAutomationSessionCocoa.mm; sourceTree = "<group>"; };
 		99C3AE2C1DADA6A700AF5C16 /* WebAutomationSessionMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebAutomationSessionMacros.h; sourceTree = "<group>"; };
 		99C607EA26FA9D4800A0953F /* _WKInspectorIBActions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _WKInspectorIBActions.h; sourceTree = "<group>"; };
+		99C607EE26FB91E800A0953F /* _WKInspectorExtensionPrivateForTesting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _WKInspectorExtensionPrivateForTesting.h; sourceTree = "<group>"; };
+		99C607EF26FB91E800A0953F /* _WKInspectorExtensionTesting.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = _WKInspectorExtensionTesting.mm; sourceTree = "<group>"; };
 		99C81D551C20DFBE005C4C82 /* AutomationClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AutomationClient.h; sourceTree = "<group>"; };
 		99C81D561C20DFBE005C4C82 /* AutomationClient.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AutomationClient.mm; sourceTree = "<group>"; };
 		99C81D5B1C20E817005C4C82 /* APIAutomationClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = APIAutomationClient.h; sourceTree = "<group>"; };
@@ -8214,6 +8218,8 @@
 				996B2BA025E2591100719379 /* _WKInspectorExtensionDelegate.h */,
 				997965A2253128C700B31AE3 /* _WKInspectorExtensionHost.h */,
 				99B16755252BB7E10073140E /* _WKInspectorExtensionInternal.h */,
+				99C607EE26FB91E800A0953F /* _WKInspectorExtensionPrivateForTesting.h */,
+				99C607EF26FB91E800A0953F /* _WKInspectorExtensionTesting.mm */,
 				99C607EA26FA9D4800A0953F /* _WKInspectorIBActions.h */,
 				5CAFDE442130843600B1F7E1 /* _WKInspectorInternal.h */,
 				9979CA57237F49F00039EC05 /* _WKInspectorPrivate.h */,
@@ -12003,6 +12009,7 @@
 				996B2BA125E2591100719379 /* _WKInspectorExtensionDelegate.h in Headers */,
 				997965A3253128C700B31AE3 /* _WKInspectorExtensionHost.h in Headers */,
 				99B16758252BB7E10073140E /* _WKInspectorExtensionInternal.h in Headers */,
+				99C607F026FB91E800A0953F /* _WKInspectorExtensionPrivateForTesting.h in Headers */,
 				99C607EB26FA9D4900A0953F /* _WKInspectorIBActions.h in Headers */,
 				5CAFDE472130846A00B1F7E1 /* _WKInspectorInternal.h in Headers */,
 				9979CA58237F49F10039EC05 /* _WKInspectorPrivate.h in Headers */,
@@ -14403,6 +14410,7 @@
 				5790A6812567A1AC0077C5A7 /* _WKAuthenticatorResponse.mm in Sources */,
 				5790A66725679CEA0077C5A7 /* _WKAuthenticatorSelectionCriteria.mm in Sources */,
 				5CBD595C2280EDF4002B22AA /* _WKCustomHeaderFields.mm in Sources */,
+				99C607F126FB91E900A0953F /* _WKInspectorExtensionTesting.mm in Sources */,
 				5790A67525679F740077C5A7 /* _WKPublicKeyCredentialCreationOptions.mm in Sources */,
 				5790A66D25679EB70077C5A7 /* _WKPublicKeyCredentialDescriptor.mm in Sources */,
 				5790A657256799DA0077C5A7 /* _WKPublicKeyCredentialEntity.mm in Sources */,

Modified: trunk/Source/WebKit/WebProcess/Inspector/WebInspectorUIExtensionController.cpp (283275 => 283276)


--- trunk/Source/WebKit/WebProcess/Inspector/WebInspectorUIExtensionController.cpp	2021-09-29 23:27:35 UTC (rev 283275)
+++ trunk/Source/WebKit/WebProcess/Inspector/WebInspectorUIExtensionController.cpp	2021-09-29 23:28:22 UTC (rev 283276)
@@ -59,7 +59,7 @@
     WebProcess::singleton().removeMessageReceiver(Messages::WebInspectorUIExtensionController::messageReceiverName(), m_inspectorPageIdentifier);
 }
 
-std::optional<Inspector::ExtensionError> WebInspectorUIExtensionController::parseExtensionErrorFromEvaluationResult(InspectorFrontendAPIDispatcher::EvaluationResult result)
+std::optional<Inspector::ExtensionError> WebInspectorUIExtensionController::parseExtensionErrorFromEvaluationResult(InspectorFrontendAPIDispatcher::EvaluationResult result) const
 {
     if (!result) {
         switch (result.error()) {
@@ -158,7 +158,7 @@
     });
 }
 
-JSC::JSObject* WebInspectorUIExtensionController::unwrapEvaluationResultAsObject(InspectorFrontendAPIDispatcher::EvaluationResult result)
+JSC::JSObject* WebInspectorUIExtensionController::unwrapEvaluationResultAsObject(InspectorFrontendAPIDispatcher::EvaluationResult result) const
 {
     if (!result)
         return nullptr;
@@ -359,6 +359,74 @@
     });
 }
 
+// WebInspectorUIExtensionController IPC messages for testing.
+
+void WebInspectorUIExtensionController::evaluateScriptInExtensionTab(const Inspector::ExtensionTabID& extensionTabID, const String& scriptSource, CompletionHandler<void(const IPC::DataReference&, const std::optional<WebCore::ExceptionDetails>&, const std::optional<Inspector::ExtensionError>&)>&& completionHandler)
+{
+    if (!m_frontendClient) {
+        completionHandler({ }, std::nullopt, Inspector::ExtensionError::InvalidRequest);
+        return;
+    }
+
+    Vector<Ref<JSON::Value>> arguments {
+        JSON::Value::create(extensionTabID),
+        JSON::Value::create(scriptSource),
+    };
+
+    m_frontendClient->frontendAPIDispatcher().dispatchCommandWithResultAsync("evaluateScriptInExtensionTab"_s, WTFMove(arguments), [weakThis = makeWeakPtr(this), completionHandler = WTFMove(completionHandler)](InspectorFrontendAPIDispatcher::EvaluationResult&& result) mutable {
+        if (!weakThis) {
+            completionHandler({ }, std::nullopt, Inspector::ExtensionError::ContextDestroyed);
+            return;
+        }
+
+        auto* frontendGlobalObject = weakThis->m_frontendClient->frontendAPIDispatcher().frontendGlobalObject();
+        if (!frontendGlobalObject) {
+            completionHandler({ }, std::nullopt, Inspector::ExtensionError::ContextDestroyed);
+            return;
+        }
+
+        if (auto parsedError = weakThis->parseExtensionErrorFromEvaluationResult(result)) {
+            if (!result.value().has_value()) {
+                auto exceptionDetails = result.value().error();
+                LOG(Inspector, "Internal error encountered while evaluating upon the frontend: at %s:%d:%d: %s", exceptionDetails.sourceURL.utf8().data(), exceptionDetails.lineNumber, exceptionDetails.columnNumber, exceptionDetails.message.utf8().data());
+            } else
+                LOG(Inspector, "Internal error encountered while evaluating upon the frontend.");
+
+            completionHandler({ }, std::nullopt, parsedError);
+            return;
+        }
+
+        // Expected result is either an ErrorString or {result: <any>} or {error: string}.
+        auto objectResult = weakThis->unwrapEvaluationResultAsObject(result);
+        if (!objectResult) {
+            LOG(Inspector, "Unexpected non-object value returned from InspectorFrontendAPI.createTabForExtension().");
+            completionHandler({ }, std::nullopt, Inspector::ExtensionError::InternalError);
+            return;
+        }
+        ASSERT(result.has_value());
+
+        JSC::JSValue errorPayload = objectResult->get(frontendGlobalObject, JSC::Identifier::fromString(frontendGlobalObject->vm(), "error"_s));
+        if (!errorPayload.isUndefined()) {
+            if (!errorPayload.isString()) {
+                completionHandler({ }, std::nullopt, Inspector::ExtensionError::InternalError);
+                return;
+            }
+
+            completionHandler({ }, ExceptionDetails { errorPayload.toWTFString(frontendGlobalObject) }, std::nullopt);
+            return;
+        }
+
+        JSC::JSValue resultPayload = objectResult->get(frontendGlobalObject, JSC::Identifier::fromString(frontendGlobalObject->vm(), "result"_s));
+        auto serializedResultValue = SerializedScriptValue::create(*frontendGlobalObject, resultPayload);
+        if (!serializedResultValue) {
+            completionHandler({ }, std::nullopt, Inspector::ExtensionError::InternalError);
+            return;
+        }
+
+        completionHandler(serializedResultValue->data(), std::nullopt, std::nullopt);
+    });
+}
+
 void WebInspectorUIExtensionController::didShowExtensionTab(const Inspector::ExtensionID& extensionID, const Inspector::ExtensionTabID& extensionTabID)
 {
     WebProcess::singleton().parentProcessConnection()->send(Messages::WebInspectorUIExtensionControllerProxy::DidShowExtensionTab { extensionID, extensionTabID }, m_inspectorPageIdentifier);

Modified: trunk/Source/WebKit/WebProcess/Inspector/WebInspectorUIExtensionController.h (283275 => 283276)


--- trunk/Source/WebKit/WebProcess/Inspector/WebInspectorUIExtensionController.h	2021-09-29 23:27:35 UTC (rev 283275)
+++ trunk/Source/WebKit/WebProcess/Inspector/WebInspectorUIExtensionController.h	2021-09-29 23:28:22 UTC (rev 283276)
@@ -70,13 +70,16 @@
     void reloadForExtension(const Inspector::ExtensionID&, const std::optional<bool>& ignoreCache, const std::optional<String>& userAgent, const std::optional<String>& injectedScript, CompletionHandler<void(const std::optional<Inspector::ExtensionError>&)>&&);
     void showExtensionTab(const Inspector::ExtensionTabID&, CompletionHandler<void(Expected<void, Inspector::ExtensionError>)>&&);
 
+    // WebInspectorUIExtensionController IPC messages for testing.
+    void evaluateScriptInExtensionTab(const Inspector::ExtensionTabID&, const String& scriptSource, CompletionHandler<void(const IPC::DataReference&, const std::optional<WebCore::ExceptionDetails>&, const std::optional<Inspector::ExtensionError>&)>&&);
+
     // Callbacks from the frontend.
     void didShowExtensionTab(const Inspector::ExtensionID&, const Inspector::ExtensionTabID&);
     void didHideExtensionTab(const Inspector::ExtensionID&, const Inspector::ExtensionTabID&);
 
 private:
-    JSC::JSObject* unwrapEvaluationResultAsObject(WebCore::InspectorFrontendAPIDispatcher::EvaluationResult);
-    std::optional<Inspector::ExtensionError> parseExtensionErrorFromEvaluationResult(WebCore::InspectorFrontendAPIDispatcher::EvaluationResult);
+    JSC::JSObject* unwrapEvaluationResultAsObject(WebCore::InspectorFrontendAPIDispatcher::EvaluationResult) const;
+    std::optional<Inspector::ExtensionError> parseExtensionErrorFromEvaluationResult(WebCore::InspectorFrontendAPIDispatcher::EvaluationResult) const;
 
     WeakPtr<WebCore::InspectorFrontendClient> m_frontendClient;
     WebCore::PageIdentifier m_inspectorPageIdentifier;

Modified: trunk/Source/WebKit/WebProcess/Inspector/WebInspectorUIExtensionController.messages.in (283275 => 283276)


--- trunk/Source/WebKit/WebProcess/Inspector/WebInspectorUIExtensionController.messages.in	2021-09-29 23:27:35 UTC (rev 283275)
+++ trunk/Source/WebKit/WebProcess/Inspector/WebInspectorUIExtensionController.messages.in	2021-09-29 23:28:22 UTC (rev 283276)
@@ -30,6 +30,9 @@
     EvaluateScriptForExtension(String extensionID, String scriptSource, std::optional<URL> frameURL, std::optional<URL> contextSecurityOrigin, std::optional<bool> useContentScriptContext) -> (IPC::DataReference resultData, std::optional<WebCore::ExceptionDetails> details, std::optional<Inspector::ExtensionError> error) Async
     ReloadForExtension(String extensionID, std::optional<bool> ignoreCache, std::optional<String> userAgent, std::optional<String> injectedScript) -> (std::optional<Inspector::ExtensionError> error) Async
     ShowExtensionTab(String extensionTabIdentifier) -> (Expected<void, Inspector::ExtensionError> result) Async
+    
+    // For testing.
+    EvaluateScriptInExtensionTab(String extensionTabID, String scriptSource) -> (IPC::DataReference resultData, std::optional<WebCore::ExceptionDetails> details, std::optional<Inspector::ExtensionError> error) Async
 }
 
 #endif // ENABLE(INSPECTOR_EXTENSIONS)

Modified: trunk/Tools/ChangeLog (283275 => 283276)


--- trunk/Tools/ChangeLog	2021-09-29 23:27:35 UTC (rev 283275)
+++ trunk/Tools/ChangeLog	2021-09-29 23:28:22 UTC (rev 283276)
@@ -1,3 +1,47 @@
+2021-09-29  BJ Burg  <[email protected]>
+
+        [Cocoa] add _WKInspectorExtension SPI to evaluate script on an extension tab
+        https://bugs.webkit.org/show_bug.cgi?id=230646
+        <rdar://problem/83420328>
+
+        Reviewed by Devin Rousso.
+
+        Add a new test to exercise the SPI. The test sets up an _WKInspectorExtension,
+        creates a tab, evaluates script on the tab, and later reads back the stored value.
+
+        Notably, this test would fail if the extension tab is not currently showing.
+        This is a bug and will be addressed as part of https://bugs.webkit.org/show_bug.cgi?id=230758.
+
+        * TestWebKitAPI/SourcesCocoa.txt:
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        Add new files.
+
+        * TestWebKitAPI/Tests/WebKitCocoa/InspectorExtension-basic-tab.html:
+        Add inline <script> to set window._secretValue. This is checked by the API test.
+
+        * TestWebKitAPI/Tests/WebKitCocoa/WKInspectorExtension.mm: Added.
+        (resetGlobalState):
+        (-[UIDelegateForTestingInspectorExtension _webView:didAttachLocalInspector:]):
+        (-[UIDelegateForTestingInspectorExtension _webView:configurationForLocalInspector:]):
+        (-[InspectorExtensionDelegateForTestingInspectorExtension inspectorExtension:didShowTabWithIdentifier:]):
+        (-[InspectorExtensionDelegateForTestingInspectorExtension inspectorExtension:didHideTabWithIdentifier:]):
+        (TEST):
+
+        * TestWebKitAPI/Tests/WebKitCocoa/WKInspectorExtensionDelegate.mm:
+        (-[UIDelegateForTestingInspectorExtensionDelegate _webView:configurationForLocalInspector:]):
+        (TEST):
+        Adopt fixes from WKInspectorExtension that allow extension tab content and icons to load.
+
+        * TestWebKitAPI/cocoa/TestInspectorURLSchemeHandler.h: Added.
+        * TestWebKitAPI/cocoa/TestInspectorURLSchemeHandler.mm: Copied from Source/WebKit/UIProcess/Inspector/mac/WKInspectorResourceURLSchemeHandler.mm.
+        (-[TestInspectorURLSchemeHandler webView:startURLSchemeTask:]):
+        (-[TestInspectorURLSchemeHandler webView:stopURLSchemeTask:]):
+        Add a simple URLSchemeHandler which allows serving test resources from the TestWebKitAPI.resources directory.
+        This is necessary to test _WKInspectorExtension tabs, which must load their content from a custom URL scheme.
+
+        * TestWebKitAPI/cocoa/TestWKWebView.mm:
+        Fix UnifiedSources fallout by adding a missing include.
+
 2021-09-29  Alex Christensen  <[email protected]>
 
         Migrate _WKDownload tests from TCPServer to HTTPServer

Modified: trunk/Tools/TestWebKitAPI/SourcesCocoa.txt (283275 => 283276)


--- trunk/Tools/TestWebKitAPI/SourcesCocoa.txt	2021-09-29 23:27:35 UTC (rev 283275)
+++ trunk/Tools/TestWebKitAPI/SourcesCocoa.txt	2021-09-29 23:28:22 UTC (rev 283276)
@@ -27,6 +27,7 @@
 cocoa/PlatformUtilitiesCocoa.mm
 cocoa/TestCocoa.mm
 cocoa/TestDownloadDelegate.mm
+cocoa/TestInspectorURLSchemeHandler.mm
 cocoa/TestLegacyDownloadDelegate.mm
 cocoa/TestNavigationDelegate.mm
 cocoa/TestProtocol.mm

Modified: trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj (283275 => 283276)


--- trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj	2021-09-29 23:27:35 UTC (rev 283275)
+++ trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj	2021-09-29 23:28:22 UTC (rev 283276)
@@ -893,6 +893,7 @@
 		9999108B1F393C96008AD455 /* Copying.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9999108A1F393C8B008AD455 /* Copying.mm */; };
 		999B7EE32551C63B00F450A4 /* WKInspectorExtensionHost.mm in Sources */ = {isa = PBXBuildFile; fileRef = 999B7EE22551C63B00F450A4 /* WKInspectorExtensionHost.mm */; };
 		99B4F9C624EDED9700022B82 /* WKInspectorDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 99B4F9C524EDED9600022B82 /* WKInspectorDelegate.mm */; };
+		99C607ED26FB90AC00A0953F /* WKInspectorExtension.mm in Sources */ = {isa = PBXBuildFile; fileRef = 99C607EC26FB90AC00A0953F /* WKInspectorExtension.mm */; };
 		99E2846426F91F7F0003F1FA /* WKInspectorExtensionDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 99E2846326F91F7F0003F1FA /* WKInspectorExtensionDelegate.mm */; };
 		99E2846626F93DB50003F1FA /* InspectorExtension-TabIcon-30x30.png in Copy Resources */ = {isa = PBXBuildFile; fileRef = 99E2846526F93D760003F1FA /* InspectorExtension-TabIcon-30x30.png */; };
 		99E2846826F941540003F1FA /* InspectorExtension-basic-tab.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 99E2846726F9413B0003F1FA /* InspectorExtension-basic-tab.html */; };
@@ -1519,7 +1520,6 @@
 				07E1F6A21FFC44FA0096C7EC /* getDisplayMedia.html in Copy Resources */,
 				467C565321B5ED130057516D /* GetSessionCookie.html in Copy Resources */,
 				41661C662355E85E00D33C27 /* getUserMedia-webaudio.html in Copy Resources */,
-				074994421EA5034B000DA44D /* invalidDeviceIDHashSalts in Copy Resources */,
 				074994421EA5034B000DA44E /* getUserMedia.html in Copy Resources */,
 				074994521EA5034B000DA44E /* getUserMedia2.html in Copy Resources */,
 				074994421EA5034B000DA45E /* getUserMediaAudioVideoCapture.html in Copy Resources */,
@@ -1578,6 +1578,7 @@
 				CE6D0EE32426B932002AD901 /* insert-text.html in Copy Resources */,
 				99E2846826F941540003F1FA /* InspectorExtension-basic-tab.html in Copy Resources */,
 				99E2846626F93DB50003F1FA /* InspectorExtension-TabIcon-30x30.png in Copy Resources */,
+				074994421EA5034B000DA44D /* invalidDeviceIDHashSalts in Copy Resources */,
 				57F56A5C1C7F8CC100F31D7E /* IsNavigationActionTrusted.html in Copy Resources */,
 				C9B4AD2C1ECA6F7F00F5FEA0 /* js-autoplay-audio.html in Copy Resources */,
 				C99B675D1E39722000FC6C80 /* js-play-with-controls.html in Copy Resources */,
@@ -2624,6 +2625,9 @@
 		9999108A1F393C8B008AD455 /* Copying.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Copying.mm; sourceTree = "<group>"; };
 		999B7EE22551C63B00F450A4 /* WKInspectorExtensionHost.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WKInspectorExtensionHost.mm; sourceTree = "<group>"; };
 		99B4F9C524EDED9600022B82 /* WKInspectorDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WKInspectorDelegate.mm; sourceTree = "<group>"; };
+		99C607EC26FB90AC00A0953F /* WKInspectorExtension.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WKInspectorExtension.mm; sourceTree = "<group>"; };
+		99C607F426FD5A1E00A0953F /* TestInspectorURLSchemeHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TestInspectorURLSchemeHandler.h; path = cocoa/TestInspectorURLSchemeHandler.h; sourceTree = "<group>"; };
+		99C607F526FD5A1F00A0953F /* TestInspectorURLSchemeHandler.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = TestInspectorURLSchemeHandler.mm; path = cocoa/TestInspectorURLSchemeHandler.mm; sourceTree = "<group>"; };
 		99E2846326F91F7F0003F1FA /* WKInspectorExtensionDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WKInspectorExtensionDelegate.mm; sourceTree = "<group>"; };
 		99E2846526F93D760003F1FA /* InspectorExtension-TabIcon-30x30.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "InspectorExtension-TabIcon-30x30.png"; sourceTree = "<group>"; };
 		99E2846726F9413B0003F1FA /* InspectorExtension-basic-tab.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "InspectorExtension-basic-tab.html"; sourceTree = "<group>"; };
@@ -3323,6 +3327,8 @@
 				5CE7594722A883A500C12409 /* TestContextMenuDriver.mm */,
 				DF6BC4702534E120008F63CC /* TestDownloadDelegate.h */,
 				DF6BC46F2534E120008F63CC /* TestDownloadDelegate.mm */,
+				99C607F426FD5A1E00A0953F /* TestInspectorURLSchemeHandler.h */,
+				99C607F526FD5A1F00A0953F /* TestInspectorURLSchemeHandler.mm */,
 				5C72E8CD244FFCE300381EB7 /* TestLegacyDownloadDelegate.h */,
 				5C72E8CE244FFCE400381EB7 /* TestLegacyDownloadDelegate.mm */,
 				2D1C04A51D76298B000A6816 /* TestNavigationDelegate.h */,
@@ -3666,6 +3672,7 @@
 				370CE2291F57343400E7410B /* WKContentViewTargetForAction.mm */,
 				51D124971E763AF8002B2820 /* WKHTTPCookieStore.mm */,
 				99B4F9C524EDED9600022B82 /* WKInspectorDelegate.mm */,
+				99C607EC26FB90AC00A0953F /* WKInspectorExtension.mm */,
 				99E2846326F91F7F0003F1FA /* WKInspectorExtensionDelegate.mm */,
 				999B7EE22551C63B00F450A4 /* WKInspectorExtensionHost.mm */,
 				A5A729F01F622A9A00DE5A28 /* WKNavigationResponse.mm */,
@@ -4581,6 +4588,7 @@
 		BC90977B125571AE00083756 /* Resources */ = {
 			isa = PBXGroup;
 			children = (
+				4A410F4D19AF7BEF002EBAB4 /* invalidDeviceIDHashSalts */,
 				C045F9461385C2F800C0F3CD /* 18-characters.html */,
 				1C2B81851C89252300A5529F /* Ahem.ttf */,
 				93D3D19B17B1A7B000C7C415 /* all-content-in-one-iframe.html */,
@@ -4629,7 +4637,6 @@
 				BCBD372E125ABBE600D2C29F /* icon.png */,
 				1CC80CE92474F1F7004DC489 /* idempotent-mode-autosizing-only-honors-percentages.html */,
 				CE3524F51B142BBB0028A7C5 /* input-focus-blur.html */,
-				4A410F4D19AF7BEF002EBAB4 /* invalidDeviceIDHashSalts */,
 				C9B4AD2B1ECA6F7600F5FEA0 /* js-autoplay-audio.html */,
 				C99B675B1E3971FC00FC6C80 /* js-play-with-controls.html */,
 				55226A2E1EB969B600C36AD0 /* large-red-square-image.html */,
@@ -5947,6 +5954,7 @@
 				51D124981E763B02002B2820 /* WKHTTPCookieStore.mm in Sources */,
 				7CCE7F1D1A411AE600447C4C /* WKImageCreateCGImageCrash.cpp in Sources */,
 				99B4F9C624EDED9700022B82 /* WKInspectorDelegate.mm in Sources */,
+				99C607ED26FB90AC00A0953F /* WKInspectorExtension.mm in Sources */,
 				99E2846426F91F7F0003F1FA /* WKInspectorExtensionDelegate.mm in Sources */,
 				999B7EE32551C63B00F450A4 /* WKInspectorExtensionHost.mm in Sources */,
 				A5A729F11F622AA700DE5A28 /* WKNavigationResponse.mm in Sources */,

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/InspectorExtension-basic-tab.html (283275 => 283276)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/InspectorExtension-basic-tab.html	2021-09-29 23:27:35 UTC (rev 283275)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/InspectorExtension-basic-tab.html	2021-09-29 23:28:22 UTC (rev 283276)
@@ -1,4 +1,8 @@
 <html>
+<head>
+<script>
+    window._secretValue = {answer:42};
+</script>
 <body>
 <h1>This is a test extension.</h1>
 <p>In a normal extension, this area would show the extension's user interface.</p>

Copied: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKInspectorExtension.mm (from rev 283275, trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKInspectorExtensionDelegate.mm) (0 => 283276)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKInspectorExtension.mm	                        (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKInspectorExtension.mm	2021-09-29 23:28:22 UTC (rev 283276)
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "config.h"
+
+#if ENABLE(INSPECTOR_EXTENSIONS)
+
+#import "TestCocoa.h"
+#import "TestInspectorURLSchemeHandler.h"
+#import "Utilities.h"
+#import <WebKit/WKPreferencesPrivate.h>
+#import <WebKit/WKWebViewPrivate.h>
+#import <WebKit/_WKInspector.h>
+#import <WebKit/_WKInspectorConfiguration.h>
+#import <WebKit/_WKInspectorExtension.h>
+#import <WebKit/_WKInspectorExtensionDelegate.h>
+#import <WebKit/_WKInspectorExtensionPrivateForTesting.h>
+#import <WebKit/_WKInspectorPrivateForTesting.h>
+#import <wtf/RetainPtr.h>
+
+static bool didAttachLocalInspectorCalled = false;
+static bool didShowExtensionTabWasCalled = false;
+static bool didHideExtensionTabWasCalled = false;
+static bool pendingCallbackWasCalled = false;
+static RetainPtr<TestInspectorURLSchemeHandler> sharedURLSchemeHandler;
+static RetainPtr<_WKInspectorExtension> sharedInspectorExtension;
+static RetainPtr<NSString> sharedExtensionTabIdentifier;
+
+static void resetGlobalState()
+{
+    didAttachLocalInspectorCalled = false;
+    didShowExtensionTabWasCalled = false;
+    didHideExtensionTabWasCalled = false;
+    pendingCallbackWasCalled = false;
+}
+
+@interface UIDelegateForTestingInspectorExtension : NSObject <WKUIDelegate>
+@end
+
+@implementation UIDelegateForTestingInspectorExtension
+
+- (void)_webView:(WKWebView *)webView didAttachLocalInspector:(_WKInspector *)inspector
+{
+    EXPECT_EQ(webView._inspector, inspector);
+    didAttachLocalInspectorCalled = true;
+}
+
+- (_WKInspectorConfiguration *)_webView:(WKWebView *)webView configurationForLocalInspector:(_WKInspector *)inspector
+{
+    if (!sharedURLSchemeHandler)
+        sharedURLSchemeHandler = adoptNS([[TestInspectorURLSchemeHandler alloc] init]);
+
+    auto inspectorConfiguration = adoptNS([[_WKInspectorConfiguration alloc] init]);
+    [inspectorConfiguration setURLSchemeHandler:sharedURLSchemeHandler.get() forURLScheme:@"test-resource"];
+    return inspectorConfiguration.autorelease();
+}
+
+@end
+
+
+@interface InspectorExtensionDelegateForTestingInspectorExtension : NSObject <_WKInspectorExtensionDelegate>
+@end
+
+@implementation InspectorExtensionDelegateForTestingInspectorExtension {
+}
+
+- (void)inspectorExtension:(_WKInspectorExtension *)extension didShowTabWithIdentifier:(NSString *)tabIdentifier
+{
+    didShowExtensionTabWasCalled = true;
+}
+
+- (void)inspectorExtension:(_WKInspectorExtension *)extension didHideTabWithIdentifier:(NSString *)tabIdentifier
+{
+    didHideExtensionTabWasCalled = true;
+}
+
+@end
+
+TEST(WKInspectorExtension, CanEvaluateScriptInExtensionTab)
+{
+    resetGlobalState();
+
+    auto webViewConfiguration = adoptNS([WKWebViewConfiguration new]);
+    webViewConfiguration.get().preferences._developerExtrasEnabled = YES;
+    auto webView = adoptNS([[WKWebView alloc] initWithFrame:CGRectMake(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
+    auto uiDelegate = adoptNS([UIDelegateForTestingInspectorExtension new]);
+
+    [webView setUIDelegate:uiDelegate.get()];
+    [webView loadHTMLString:@"<head><title>Test page to be inspected</title></head><body><p>Filler content</p></body>" baseURL:[NSURL URLWithString:@"http://example.com/"]];
+
+    [[webView _inspector] show];
+    TestWebKitAPI::Util::run(&didAttachLocalInspectorCalled);
+
+    auto extensionID = [NSUUID UUID].UUIDString;
+    auto extensionDisplayName = @"FirstExtension";
+
+    // Register the test extension.
+    pendingCallbackWasCalled = false;
+    [[webView _inspector] registerExtensionWithID:extensionID displayName:extensionDisplayName completionHandler:^(NSError * _Nullable error, _WKInspectorExtension * _Nullable extension) {
+        EXPECT_NULL(error);
+        EXPECT_NOT_NULL(extension);
+        sharedInspectorExtension = extension;
+
+        pendingCallbackWasCalled = true;
+    }];
+    TestWebKitAPI::Util::run(&pendingCallbackWasCalled);
+
+    auto extensionDelegate = adoptNS([InspectorExtensionDelegateForTestingInspectorExtension new]);
+    [sharedInspectorExtension setDelegate:extensionDelegate.get()];
+
+    // Create and show an extension tab.
+    auto iconURL = [NSURL URLWithString:@"test-resource://FirstExtension/InspectorExtension-TabIcon-30x30.png"];
+    auto sourceURL = [NSURL URLWithString:@"test-resource://FirstExtension/InspectorExtension-basic-tab.html"];
+
+    pendingCallbackWasCalled = false;
+    [sharedInspectorExtension createTabWithName:@"FirstExtension-Tab" tabIconURL:iconURL sourceURL:sourceURL completionHandler:^(NSError * _Nullable error, NSString * _Nullable extensionTabIdentifier) {
+        EXPECT_NULL(error);
+        EXPECT_NOT_NULL(extensionTabIdentifier);
+        sharedExtensionTabIdentifier = extensionTabIdentifier;
+
+        pendingCallbackWasCalled = true;
+    }];
+    TestWebKitAPI::Util::run(&pendingCallbackWasCalled);
+
+    pendingCallbackWasCalled = false;
+    didShowExtensionTabWasCalled = false;
+    [[webView _inspector] showExtensionTabWithIdentifier:sharedExtensionTabIdentifier.get() completionHandler:^(NSError * _Nullable error) {
+        EXPECT_NULL(error);
+
+        pendingCallbackWasCalled = true;
+    }];
+    TestWebKitAPI::Util::run(&pendingCallbackWasCalled);
+    TestWebKitAPI::Util::run(&didShowExtensionTabWasCalled);
+
+    // Read back a value that is set in the <iframe>'s script context.
+    pendingCallbackWasCalled = false;
+    auto scriptSource2 = @"window._secretValue";
+    [sharedInspectorExtension _evaluateScript:scriptSource2 inExtensionTabWithIdentifier:sharedExtensionTabIdentifier.get() completionHandler:^(NSError * _Nullable error, NSDictionary * _Nullable result) {
+        EXPECT_NULL(error);
+        EXPECT_NOT_NULL(result);
+        EXPECT_NS_EQUAL(result[@"answer"], @42);
+
+        pendingCallbackWasCalled = true;
+    }];
+    TestWebKitAPI::Util::run(&pendingCallbackWasCalled);
+
+    // Check to see that script is actually being evaluated in the <iframe>'s script context.
+    pendingCallbackWasCalled = false;
+    auto scriptSource3 = @"window.top !== window";
+    [sharedInspectorExtension _evaluateScript:scriptSource3 inExtensionTabWithIdentifier:sharedExtensionTabIdentifier.get() completionHandler:^(NSError * _Nullable error, NSDictionary * _Nullable result) {
+        EXPECT_NULL(error);
+        EXPECT_NOT_NULL(result);
+        EXPECT_NS_EQUAL(result, @YES);
+
+        pendingCallbackWasCalled = true;
+    }];
+    TestWebKitAPI::Util::run(&pendingCallbackWasCalled);
+
+    // Unregister the test extension.
+    pendingCallbackWasCalled = false;
+    [[webView _inspector] unregisterExtension:sharedInspectorExtension.get() completionHandler:^(NSError * _Nullable error) {
+        EXPECT_NULL(error);
+
+        pendingCallbackWasCalled = true;
+    }];
+    TestWebKitAPI::Util::run(&pendingCallbackWasCalled);
+}
+
+#endif // ENABLE(INSPECTOR_EXTENSIONS)

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKInspectorExtensionDelegate.mm (283275 => 283276)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKInspectorExtensionDelegate.mm	2021-09-29 23:27:35 UTC (rev 283275)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKInspectorExtensionDelegate.mm	2021-09-29 23:28:22 UTC (rev 283276)
@@ -25,22 +25,25 @@
 
 #import "config.h"
 
+#if ENABLE(INSPECTOR_EXTENSIONS)
+
 #import "Test.h"
+#import "TestInspectorURLSchemeHandler.h"
 #import "Utilities.h"
 #import <WebKit/WKPreferencesPrivate.h>
 #import <WebKit/WKWebViewPrivate.h>
 #import <WebKit/_WKInspector.h>
+#import <WebKit/_WKInspectorConfiguration.h>
 #import <WebKit/_WKInspectorExtension.h>
 #import <WebKit/_WKInspectorExtensionDelegate.h>
 #import <WebKit/_WKInspectorPrivateForTesting.h>
 #import <wtf/RetainPtr.h>
 
-#if ENABLE(INSPECTOR_EXTENSIONS)
-
 static bool didAttachLocalInspectorCalled = false;
 static bool didShowExtensionTabWasCalled = false;
 static bool didHideExtensionTabWasCalled = false;
 static bool pendingCallbackWasCalled = false;
+static RetainPtr<TestInspectorURLSchemeHandler> sharedURLSchemeHandler;
 static RetainPtr<_WKInspectorExtension> sharedInspectorExtension;
 static RetainPtr<NSString> sharedExtensionTabIdentifier;
 
@@ -63,6 +66,16 @@
     didAttachLocalInspectorCalled = true;
 }
 
+- (_WKInspectorConfiguration *)_webView:(WKWebView *)webView configurationForLocalInspector:(_WKInspector *)inspector
+{
+    if (!sharedURLSchemeHandler)
+        sharedURLSchemeHandler = adoptNS([[TestInspectorURLSchemeHandler alloc] init]);
+
+    auto inspectorConfiguration = adoptNS([[_WKInspectorConfiguration alloc] init]);
+    [inspectorConfiguration setURLSchemeHandler:sharedURLSchemeHandler.get() forURLScheme:@"test-resource"];
+    return inspectorConfiguration.autorelease();
+}
+
 @end
 
 
@@ -117,8 +130,8 @@
     [sharedInspectorExtension setDelegate:extensionDelegate.get()];
 
     // Create an extension tab.
-    auto iconURL = [[NSBundle mainBundle] URLForResource:@"InspectorExtension-TabIcon-30x30" withExtension:@"png" subdirectory:@"TestWebKitAPI.resources"];
-    auto sourceURL = [[NSBundle mainBundle] URLForResource:@"InspectorExtension-basic-tab" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+    auto iconURL = [NSURL URLWithString:@"test-resource://FirstExtension/InspectorExtension-TabIcon-30x30.png"];
+    auto sourceURL = [NSURL URLWithString:@"test-resource://FirstExtension/InspectorExtension-basic-tab.html"];
 
     pendingCallbackWasCalled = false;
     [sharedInspectorExtension createTabWithName:@"FirstExtension-Tab" tabIconURL:iconURL sourceURL:sourceURL completionHandler:^(NSError * _Nullable error, NSString * _Nullable extensionTabIdentifier) {
@@ -130,10 +143,6 @@
     }];
     TestWebKitAPI::Util::run(&pendingCallbackWasCalled);
 
-    // Force a known non-extension tab to be shown before showing the extension tab. Otherwise,
-    // if the extension tab was already open, then this test would hang waiting for a didShow callback.
-    [[webView _inspector] showConsole];
-
     pendingCallbackWasCalled = false;
     [[webView _inspector] showExtensionTabWithIdentifier:sharedExtensionTabIdentifier.get() completionHandler:^(NSError * _Nullable error) {
         EXPECT_NULL(error);

Added: trunk/Tools/TestWebKitAPI/cocoa/TestInspectorURLSchemeHandler.h (0 => 283276)


--- trunk/Tools/TestWebKitAPI/cocoa/TestInspectorURLSchemeHandler.h	                        (rev 0)
+++ trunk/Tools/TestWebKitAPI/cocoa/TestInspectorURLSchemeHandler.h	2021-09-29 23:28:22 UTC (rev 283276)
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#import <WebKit/WKFoundation.h>
+#import <WebKit/WKURLSchemeHandler.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface TestInspectorURLSchemeHandler : NSObject <WKURLSchemeHandler>
+@end
+
+NS_ASSUME_NONNULL_END

Copied: trunk/Tools/TestWebKitAPI/cocoa/TestInspectorURLSchemeHandler.mm (from rev 283275, trunk/Source/WebKit/UIProcess/Inspector/mac/WKInspectorResourceURLSchemeHandler.mm) (0 => 283276)


--- trunk/Tools/TestWebKitAPI/cocoa/TestInspectorURLSchemeHandler.mm	                        (rev 0)
+++ trunk/Tools/TestWebKitAPI/cocoa/TestInspectorURLSchemeHandler.mm	2021-09-29 23:28:22 UTC (rev 283276)
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "config.h"
+#import "TestInspectorURLSchemeHandler.h"
+
+#import <WebCore/MIMETypeRegistry.h>
+#import <WebKit/WKURLSchemeTask.h>
+#import <wtf/Assertions.h>
+
+// Note: this class is a simplified version of WKResourceURLSchemeHandler for testing purposes.
+
+@implementation TestInspectorURLSchemeHandler {
+    RetainPtr<NSMapTable<id <WKURLSchemeTask>, NSOperation *>> _fileLoadOperations;
+    RetainPtr<NSBundle> _cachedBundle;
+    RetainPtr<NSOperationQueue> _operationQueue;
+}
+
+// MARK - WKURLSchemeHandler Protocol
+
+- (void)webView:(WKWebView *)webView startURLSchemeTask:(id <WKURLSchemeTask>)urlSchemeTask
+{
+    if (!_cachedBundle)
+        _cachedBundle = [NSBundle mainBundle];
+
+    if (!_fileLoadOperations)
+        _fileLoadOperations = adoptNS([[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory valueOptions:NSPointerFunctionsStrongMemory capacity:5]);
+
+    if (!_operationQueue) {
+        _operationQueue = adoptNS([[NSOperationQueue alloc] init]);
+        _operationQueue.get().underlyingQueue = dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0);
+        _operationQueue.get().qualityOfService = NSOperationQualityOfServiceUserInteractive;
+
+        // The default value (NSOperationQueueDefaultMaxConcurrentOperationCount) results in a large number of threads
+        // that can exceed the soft limit if two Web Inspector instances are being loaded simultaneously.
+        _operationQueue.get().maxConcurrentOperationCount = 4;
+    }
+
+    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
+        dispatch_async(dispatch_get_main_queue(), ^{
+            [_fileLoadOperations removeObjectForKey:urlSchemeTask];
+        });
+
+        NSURL *requestURL = urlSchemeTask.request.URL;
+        NSURL *fileURLForRequest = [_cachedBundle URLForResource:requestURL.relativePath withExtension:@"" subdirectory:@"TestWebKitAPI.resources"];
+        if (!fileURLForRequest) {
+            [urlSchemeTask didFailWithError:[NSError errorWithDomain:NSCocoaErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil]];
+            return;
+        }
+
+        NSError *readError;
+        NSData *fileData = [NSData dataWithContentsOfURL:fileURLForRequest options:0 error:&readError];
+        if (!fileData) {
+            [urlSchemeTask didFailWithError:[NSError errorWithDomain:NSCocoaErrorDomain code:NSURLErrorResourceUnavailable userInfo:@{
+                NSUnderlyingErrorKey: readError,
+            }]];
+            return;
+        }
+
+        NSString *mimeType = WebCore::MIMETypeRegistry::mimeTypeForExtension(fileURLForRequest.pathExtension);
+        if (!mimeType)
+            mimeType = @"application/octet-stream";
+
+        RetainPtr<NSMutableDictionary> headerFields = adoptNS(@{
+            @"Access-Control-Allow-Origin": @"*",
+            @"Content-Length": [NSString stringWithFormat:@"%zu", (size_t)fileData.length],
+            @"Content-Type": mimeType,
+        }.mutableCopy);
+
+        RetainPtr<NSHTTPURLResponse> urlResponse = adoptNS([[NSHTTPURLResponse alloc] initWithURL:urlSchemeTask.request.URL statusCode:200 HTTPVersion:nil headerFields:headerFields.get()]);
+        [urlSchemeTask didReceiveResponse:urlResponse.get()];
+        [urlSchemeTask didReceiveData:fileData];
+        [urlSchemeTask didFinish];
+    }];
+    
+    [_fileLoadOperations setObject:operation forKey:urlSchemeTask];
+    [_operationQueue addOperation:operation];
+}
+
+- (void)webView:(WKWebView *)webView stopURLSchemeTask:(id <WKURLSchemeTask>)urlSchemeTask
+{
+    // Ensure that all blocks with pending removals are dispatched before doing a map lookup.
+    dispatch_async(dispatch_get_main_queue(), ^{
+        if (NSOperation *operation = [_fileLoadOperations objectForKey:urlSchemeTask]) {
+            [operation cancel];
+            [_fileLoadOperations removeObjectForKey:urlSchemeTask];
+        }
+    });
+}
+
+@end

Modified: trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.mm (283275 => 283276)


--- trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.mm	2021-09-29 23:27:35 UTC (rev 283275)
+++ trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.mm	2021-09-29 23:28:22 UTC (rev 283276)
@@ -33,6 +33,7 @@
 
 #import <WebKit/WKContentWorld.h>
 #import <WebKit/WKWebViewConfigurationPrivate.h>
+#import <WebKit/WKWebViewPrivateForTesting.h>
 #import <WebKit/WebKitPrivate.h>
 #import <WebKit/_WKActivatedElementInfo.h>
 #import <WebKit/_WKProcessPoolConfiguration.h>
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to