Add support for converting a local window to a remote window
authorcdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 18 Apr 2018 16:50:36 +0000 (16:50 +0000)
committercdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 18 Apr 2018 16:50:36 +0000 (16:50 +0000)
https://bugs.webkit.org/show_bug.cgi?id=184515
<rdar://problem/39011318>

Reviewed by Ryosuke Niwa.

Source/WebCore:

Add initial support for process-swapping when navigating cross-origin as a result
of a window.open(). The window object returned by window.open() is initially same
origin and is for about:blank. The navigation cross-origin then happens and the
JS wrappers for the window then point to a cross-origin window which is remote (i.e.
hosted in another WebProcess).

The RemoteDOMWindow exposed to JS looks like a regular cross-origin Window with a few
exceptions due to our incomplete implementation (e.g. w.location returns null) and
most of its API is currently not functional. The RemoteDOMWindow API will be implemented
in a follow-up by relying on IPC.

Test: http/tests/navigation/process-swap-window-open.html

* bindings/js/JSDOMGlobalObject.cpp:
(WebCore::JSDOMGlobalObject::scriptExecutionContext const):
* bindings/js/JSDOMPromiseDeferred.h:
* bindings/js/JSDOMWindowProperties.cpp:
(WebCore::JSDOMWindowProperties::getOwnPropertySlot):
* bindings/js/JSDOMWindowProxy.cpp:
(WebCore::JSDOMWindowProxy::finishCreation):
(WebCore::JSDOMWindowProxy::create):
* bindings/js/JSDOMWindowProxy.h:
* bindings/js/JSDOMWrapper.cpp:
(WebCore::JSDOMObject::JSDOMObject):
* bindings/js/JSDOMWrapper.h:
* bindings/js/WindowProxyController.cpp:
(WebCore::WindowProxyController::createWindowProxy):
(WebCore::WindowProxyController::setDOMWindowForWindowProxy):
* bridge/objc/WebScriptObject.mm:
(-[WebScriptObject _isSafeScript]):
Teach more of our bindings code about RemoteDOMWindows.

* dom/Document.cpp:
(WebCore::Document::createDOMWindow):
* loader/FrameLoaderClient.h:
Add FrameLoaderClient function to notify the client when a DOMWindow is constructed in
the frame. This is needed for WebKit2 to link the old window to its new representation
in the new WebProcess.

Source/WebKit:

Add initial support for process-swapping when navigating cross-origin as a result
of a window.open(). The window object returned by window.open() is initially same
origin and is for about:blank. The navigation cross-origin then happens and the
JS wrappers for the window then point to a cross-origin window which is remote (i.e.
hosted in another WebProcess).

The RemoteDOMWindow exposed to JS looks like a regular cross-origin Window with a few
exceptions due to our incomplete implementation (e.g. w.location returns null) and
most of its API is currently not functional. The RemoteDOMWindow API will be implemented
in a follow-up by relying on IPC.

* UIProcess/API/APIProcessPoolConfiguration.cpp:
(API::ProcessPoolConfiguration::copy):
* UIProcess/API/APIProcessPoolConfiguration.h:
* UIProcess/API/C/WKContextConfigurationRef.cpp:
(WKContextConfigurationProcessSwapsOnWindowOpenWithOpener):
(WKContextConfigurationSetProcessSwapsOnWindowOpenWithOpener):
* UIProcess/API/C/WKContextConfigurationRef.h:
* UIProcess/API/Cocoa/_WKProcessPoolConfiguration.h:
* UIProcess/API/Cocoa/_WKProcessPoolConfiguration.mm:
(-[_WKProcessPoolConfiguration setProcessSwapsOnWindowOpenWithOpener:]):
(-[_WKProcessPoolConfiguration processSwapsOnWindowOpenWithOpener]):
Add ProcessPool configuration flag to turn on processSwap on window.open(), even
if there is an opener.

* UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::continueNavigationInNewProcess):
If the navigation was triggered via window.open(), then set up on handler for when
a DOMWindow is constructed for the main frame in the new process.

(WebKit::WebPageProxy::didCreateWindow):
When a Window is constructed for the main frame in a new process on process swap,
notify the old process that its representation of the window should become remote
and provide it with the Frame / Window identifiers it needs.

* UIProcess/WebPageProxy.h:
* UIProcess/WebPageProxy.messages.in:

* UIProcess/WebProcessPool.cpp:
(WebKit::WebProcessPool::processForNavigation):
Do process swapping on cross-origin window.open() if the corresponding setting is
enabled.

* WebProcess/WebCoreSupport/WebFrameLoaderClient.cpp:
(WebKit::WebFrameLoaderClient::didCreateWindow):
* WebProcess/WebCoreSupport/WebFrameLoaderClient.h:

* WebProcess/WebPage/WebPage.cpp:
(WebKit::WebPage::frameBecameRemote):
This is called when process swapping has happened due to a window.open() navigation
cross-origin, when a Frame / Window has been constructed in the new process. We do
the following:
- Construct a RemoteFrame / RemoteWindow using the provided global identifiers to
  represent the Frame / Window in the new process.
- We transfer the WindowProxies from the old Frame's WindowProxyController to the
  new RemoteFrame's WindowProxyController.
- We update the window proxied by those WindowProxies to be the new RemoteWindow.
- We detach the old Frame as it is now remote and represented by the new RemoteFrame
  object we constructed.
- If the old frame was the main frame (always the case currently), we close the page
  as it is no longer needed. The new RemoteFrame is currently owned by the RemoteWindow
  which is kept alive by its JS wrappers.

* WebProcess/WebPage/WebPage.h:
* WebProcess/WebPage/WebPage.messages.in:

Tools:

* MiniBrowser/mac/AppDelegate.m:
(defaultConfiguration):
* MiniBrowser/mac/SettingsController.h:
* MiniBrowser/mac/SettingsController.m:
(-[SettingsController _populateMenu]):
(-[SettingsController validateMenuItem:]):
(-[SettingsController processSwapOnWindowOpenWithOpenerEnabled]):
(-[SettingsController toggleProcessSwapOnWindowOpenWithOpener:]):
Add menu entry in minibrowser to turn on process swap on cross-origin window.open().

* TestWebKitAPI/Tests/WebKitCocoa/ProcessSwapOnNavigation.mm:
Turn on process swap on cross-origin window.open() for corresponding test and update
test to expect that a new WebProcess is created.

* WebKitTestRunner/InjectedBundle/InjectedBundlePage.cpp:
(WTR::InjectedBundlePage::decidePolicyForResponse):
Add null checks for injectedBundle.testRunner(). When we swap process on navigation,
the InjectedBundlePage::decidePolicyForResponse() gets called in the new process.
In this new process, we have constructed a InjectedBundlePage for the page but we
have not initialized the InjectedBundle members such as testRunner.

* WebKitTestRunner/TestController.cpp:
(WTR::TestController::createWebViewWithOptions):
(WTR::updateTestOptionsFromTestHeader):
* WebKitTestRunner/TestOptions.h:
(WTR::TestOptions::hasSameInitializationOptions const):
Add a way for layout tests to turn on process swap on navigation via a test header.

LayoutTests:

Add layout test coverage a cross-origin window.open() to check that the
returned window looks like a regular cross-origin window after being
navigated cross-origin. We are still failing some of the checks because
our implementation is still incomplete. However, it mostly works as
expected.

* TestExpectations:
* http/tests/navigation/process-swap-window-open-expected.txt: Added.
* http/tests/navigation/process-swap-window-open.html: Added.
* platform/wk2/TestExpectations:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@230764 268f45cc-cd09-0410-ab3c-d52691b4dbfc

42 files changed:
LayoutTests/ChangeLog
LayoutTests/TestExpectations
LayoutTests/http/tests/navigation/process-swap-window-open-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/navigation/process-swap-window-open.html [new file with mode: 0644]
LayoutTests/platform/wk2/TestExpectations
Source/WebCore/ChangeLog
Source/WebCore/bindings/js/JSDOMGlobalObject.cpp
Source/WebCore/bindings/js/JSDOMPromiseDeferred.h
Source/WebCore/bindings/js/JSDOMWindowProperties.cpp
Source/WebCore/bindings/js/JSDOMWindowProxy.cpp
Source/WebCore/bindings/js/JSDOMWindowProxy.h
Source/WebCore/bindings/js/JSDOMWrapper.cpp
Source/WebCore/bindings/js/JSDOMWrapper.h
Source/WebCore/bindings/js/WindowProxyController.cpp
Source/WebCore/bridge/objc/WebScriptObject.mm
Source/WebCore/dom/Document.cpp
Source/WebCore/loader/FrameLoaderClient.h
Source/WebCore/page/Frame.h
Source/WebKit/ChangeLog
Source/WebKit/UIProcess/API/APIProcessPoolConfiguration.cpp
Source/WebKit/UIProcess/API/APIProcessPoolConfiguration.h
Source/WebKit/UIProcess/API/C/WKContextConfigurationRef.cpp
Source/WebKit/UIProcess/API/C/WKContextConfigurationRef.h
Source/WebKit/UIProcess/API/Cocoa/_WKProcessPoolConfiguration.h
Source/WebKit/UIProcess/API/Cocoa/_WKProcessPoolConfiguration.mm
Source/WebKit/UIProcess/WebPageProxy.cpp
Source/WebKit/UIProcess/WebPageProxy.h
Source/WebKit/UIProcess/WebPageProxy.messages.in
Source/WebKit/UIProcess/WebProcessPool.cpp
Source/WebKit/WebProcess/WebCoreSupport/WebFrameLoaderClient.cpp
Source/WebKit/WebProcess/WebCoreSupport/WebFrameLoaderClient.h
Source/WebKit/WebProcess/WebPage/WebPage.cpp
Source/WebKit/WebProcess/WebPage/WebPage.h
Source/WebKit/WebProcess/WebPage/WebPage.messages.in
Tools/ChangeLog
Tools/MiniBrowser/mac/AppDelegate.m
Tools/MiniBrowser/mac/SettingsController.h
Tools/MiniBrowser/mac/SettingsController.m
Tools/TestWebKitAPI/Tests/WebKitCocoa/ProcessSwapOnNavigation.mm
Tools/WebKitTestRunner/InjectedBundle/InjectedBundlePage.cpp
Tools/WebKitTestRunner/TestController.cpp
Tools/WebKitTestRunner/TestOptions.h

index d316841..3b30a98 100644 (file)
@@ -1,3 +1,22 @@
+2018-04-18  Chris Dumez  <cdumez@apple.com>
+
+        Add support for converting a local window to a remote window
+        https://bugs.webkit.org/show_bug.cgi?id=184515
+        <rdar://problem/39011318>
+
+        Reviewed by Ryosuke Niwa.
+
+        Add layout test coverage a cross-origin window.open() to check that the
+        returned window looks like a regular cross-origin window after being
+        navigated cross-origin. We are still failing some of the checks because
+        our implementation is still incomplete. However, it mostly works as
+        expected.
+
+        * TestExpectations:
+        * http/tests/navigation/process-swap-window-open-expected.txt: Added.
+        * http/tests/navigation/process-swap-window-open.html: Added.
+        * platform/wk2/TestExpectations:
+
 2018-04-18  Ms2ger  <Ms2ger@igalia.com>
 
         Test gardening.
index bc6e9b8..afa7800 100644 (file)
@@ -109,6 +109,7 @@ http/tests/misc/will-send-request-with-client-provided-http-body.html [ Skip ]
 http/tests/loading/resourceLoadStatistics/ [ Skip ]
 http/tests/resourceLoadStatistics/ [ Skip ]
 http/tests/storageAccess/ [ Skip ]
+http/tests/navigation/process-swap-window-open.html [ Skip ]
 
 # Only Mac and iOS have an implementation of UIScriptController::doAsyncTask().
 fast/harness/uiscriptcontroller [ Skip ]
diff --git a/LayoutTests/http/tests/navigation/process-swap-window-open-expected.txt b/LayoutTests/http/tests/navigation/process-swap-window-open-expected.txt
new file mode 100644 (file)
index 0000000..112623e
--- /dev/null
@@ -0,0 +1,44 @@
+Basic testing for the window returning window.open() becoming remote when navigating cross-origin.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS w.location.href is "about:blank"
+PASS w.name threw exception SecurityError: The operation is insecure..
+FAIL w.location.href should throw a SecurityError. Threw a TypeError.
+PASS w.document threw exception SecurityError: The operation is insecure..
+PASS w.history threw exception SecurityError: The operation is insecure..
+PASS w.locationbar threw exception SecurityError: The operation is insecure..
+PASS w.status threw exception SecurityError: The operation is insecure..
+PASS w.frameElement threw exception SecurityError: The operation is insecure..
+PASS w.navigator threw exception SecurityError: The operation is insecure..
+PASS w.alert threw exception SecurityError: The operation is insecure..
+PASS w.localStorage threw exception SecurityError: The operation is insecure..
+PASS w.sessionStorage threw exception SecurityError: The operation is insecure..
+PASS w.event threw exception SecurityError: The operation is insecure..
+PASS w.window is w
+PASS w.self is w
+PASS w.top is w
+PASS w.parent is w
+PASS w.frames is w
+PASS w.closed is false
+PASS w.length is 0
+FAIL w.opener should be [object Window]. Was null.
+PASS w.close is an instance of Function
+PASS w.focus is an instance of Function
+PASS w.blur is an instance of Function
+PASS w.postMessage is an instance of Function
+PASS w.location did not throw exception.
+FAIL w.location should not be null.
+PASS areArraysEqual(actual_properties, expected_property_names) is true
+PASS Object.getPrototype(w) threw exception TypeError: Object.getPrototype is not a function. (In 'Object.getPrototype(w)', 'Object.getPrototype' is undefined).
+PASS Object.setPrototype(w, {}) threw exception TypeError: Object.setPrototype is not a function. (In 'Object.setPrototype(w, {})', 'Object.setPrototype' is undefined).
+PASS Object.preventExtensions(w) threw exception TypeError: Cannot prevent extensions on this object.
+PASS Object.defineProperty(w, 'foo', {value: 1}) threw exception SecurityError: The operation is insecure..
+PASS delete w.closed threw exception SecurityError: The operation is insecure..
+PASS w.foo = 1 threw exception SecurityError: The operation is insecure..
+PASS successfullyParsed is true
+Some tests failed.
+
+TEST COMPLETE
+
diff --git a/LayoutTests/http/tests/navigation/process-swap-window-open.html b/LayoutTests/http/tests/navigation/process-swap-window-open.html
new file mode 100644 (file)
index 0000000..bc40e45
--- /dev/null
@@ -0,0 +1,71 @@
+<!DOCTYPE html><!-- webkit-test-runner [ enableProcessSwapOnNavigation=true ] -->
+<html>
+<body>
+<script src="/js-test-resources/js-test.js"></script>
+<script>
+description("Basic testing for the window returning window.open() becoming remote when navigating cross-origin.");
+jsTestIsAsync = true;
+
+if (window.testRunner)
+    testRunner.setCanOpenWindows(true);
+
+function wasWindowNavigatedCrossOrigin()
+{
+    try {
+        w.name;
+        return false;
+    } catch (e) {
+        return true;
+    }
+}
+
+function runTest() {
+    forbiddenProperties = ["name", "location.href", "document", "history", "locationbar", "status", "frameElement", "navigator", "alert", "localStorage", "sessionStorage", "event"];
+    for (forbiddenProperty of forbiddenProperties)
+        shouldThrowErrorName("w." + forbiddenProperty, "SecurityError");
+
+    shouldBe("w.window", "w");
+    shouldBe("w.self", "w");
+    shouldBe("w.top", "w");
+    shouldBe("w.parent", "w");
+    shouldBe("w.frames", "w");
+    shouldBeFalse("w.closed");
+    shouldBe("w.length", "0");
+    shouldBe("w.opener", "window");
+    shouldBeType("w.close", "Function");
+    shouldBeType("w.focus", "Function");
+    shouldBeType("w.blur", "Function");
+    shouldBeType("w.postMessage", "Function");
+
+    shouldNotThrow("w.location");
+    shouldNotBe("w.location", "null");
+
+    expected_property_names = ["blur", "close", "closed", "focus", "frames", "length", "location", "opener", "parent", "postMessage", "self", "top", "window"];
+    actual_properties = Object.getOwnPropertyNames(w);
+    shouldBeTrue("areArraysEqual(actual_properties, expected_property_names)");
+
+    shouldThrowErrorName("Object.getPrototype(w)", "TypeError");
+    shouldThrowErrorName("Object.setPrototype(w, {})", "TypeError");
+    shouldThrowErrorName("Object.preventExtensions(w)", "TypeError");
+    shouldThrowErrorName("Object.defineProperty(w, 'foo', {value: 1})", "SecurityError");
+    shouldThrowErrorName("delete w.closed", "SecurityError");
+
+    shouldThrowErrorName("w.foo = 1", "SecurityError");
+
+    finishJSTest();
+}
+
+onload = function() {
+    w = window.open("http://localhost:8000/navigation/resources/success.html");
+    shouldBeEqualToString("w.location.href", "about:blank");
+
+    handle = setInterval(function() {
+        if (wasWindowNavigatedCrossOrigin()) {
+            clearInterval(handle);
+            runTest();
+        }
+    }, 5);
+}
+</script>
+</body>
+</html>
index 10c3e18..6ed2d48 100644 (file)
@@ -707,6 +707,9 @@ http/tests/resourceLoadStatistics/remove-blocking-in-redirect.html [ Skip ]
 http/tests/resourceLoadStatistics/strip-referrer-to-origin-for-prevalent-subresource-redirects.html [ Skip ]
 http/tests/resourceLoadStatistics/strip-referrer-to-origin-for-prevalent-subresource-requests.html [ Skip ]
 
+# Process swapping is only implemented on WebKit2.
+http/tests/navigation/process-swap-window-open.html [ Pass ]
+
 ### END OF (5) Progressions, expected successes that are expected failures in WebKit1.
 ########################################
 
index 53299fa..d969394 100644 (file)
@@ -1,3 +1,50 @@
+2018-04-18  Chris Dumez  <cdumez@apple.com>
+
+        Add support for converting a local window to a remote window
+        https://bugs.webkit.org/show_bug.cgi?id=184515
+        <rdar://problem/39011318>
+
+        Reviewed by Ryosuke Niwa.
+
+        Add initial support for process-swapping when navigating cross-origin as a result
+        of a window.open(). The window object returned by window.open() is initially same
+        origin and is for about:blank. The navigation cross-origin then happens and the
+        JS wrappers for the window then point to a cross-origin window which is remote (i.e.
+        hosted in another WebProcess).
+
+        The RemoteDOMWindow exposed to JS looks like a regular cross-origin Window with a few
+        exceptions due to our incomplete implementation (e.g. w.location returns null) and 
+        most of its API is currently not functional. The RemoteDOMWindow API will be implemented
+        in a follow-up by relying on IPC.
+
+        Test: http/tests/navigation/process-swap-window-open.html
+
+        * bindings/js/JSDOMGlobalObject.cpp:
+        (WebCore::JSDOMGlobalObject::scriptExecutionContext const):
+        * bindings/js/JSDOMPromiseDeferred.h:
+        * bindings/js/JSDOMWindowProperties.cpp:
+        (WebCore::JSDOMWindowProperties::getOwnPropertySlot):
+        * bindings/js/JSDOMWindowProxy.cpp:
+        (WebCore::JSDOMWindowProxy::finishCreation):
+        (WebCore::JSDOMWindowProxy::create):
+        * bindings/js/JSDOMWindowProxy.h:
+        * bindings/js/JSDOMWrapper.cpp:
+        (WebCore::JSDOMObject::JSDOMObject):
+        * bindings/js/JSDOMWrapper.h:
+        * bindings/js/WindowProxyController.cpp:
+        (WebCore::WindowProxyController::createWindowProxy):
+        (WebCore::WindowProxyController::setDOMWindowForWindowProxy):
+        * bridge/objc/WebScriptObject.mm:
+        (-[WebScriptObject _isSafeScript]):
+        Teach more of our bindings code about RemoteDOMWindows.
+
+        * dom/Document.cpp:
+        (WebCore::Document::createDOMWindow):
+        * loader/FrameLoaderClient.h:
+        Add FrameLoaderClient function to notify the client when a DOMWindow is constructed in
+        the frame. This is needed for WebKit2 to link the old window to its new representation
+        in the new WebProcess.
+
 2018-04-17  Zan Dobersek  <zdobersek@igalia.com>
 
         [CMake] Add and enable the ENABLE_CSS_ANIMATIONS_LEVEL_2 feature define
index 7150019..720361e 100644 (file)
@@ -38,6 +38,7 @@
 #include "JSRTCSessionDescription.h"
 #include "JSReadableStream.h"
 #include "JSReadableStreamPrivateConstructors.h"
+#include "JSRemoteDOMWindow.h"
 #include "JSWorkerGlobalScope.h"
 #include "RuntimeEnabledFeatures.h"
 #include "StructuredClone.h"
@@ -197,11 +198,13 @@ ScriptExecutionContext* JSDOMGlobalObject::scriptExecutionContext() const
 {
     if (inherits<JSDOMWindowBase>(vm()))
         return jsCast<const JSDOMWindowBase*>(this)->scriptExecutionContext();
+    if (inherits<JSRemoteDOMWindowBase>(vm()))
+        return nullptr;
     if (inherits<JSWorkerGlobalScopeBase>(vm()))
         return jsCast<const JSWorkerGlobalScopeBase*>(this)->scriptExecutionContext();
     dataLog("Unexpected global object: ", JSValue(this), "\n");
     RELEASE_ASSERT_NOT_REACHED();
-    return 0;
+    return nullptr;
 }
 
 void JSDOMGlobalObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
index 85bc413..7961fb5 100644 (file)
@@ -33,6 +33,8 @@
 
 namespace WebCore {
 
+class JSDOMWindow;
+
 class DeferredPromise : public DOMGuarded<JSC::JSPromiseDeferred> {
 public:
     enum class Mode {
index 8098eff..e9eedc8 100644 (file)
@@ -89,7 +89,15 @@ bool JSDOMWindowProperties::getOwnPropertySlot(JSObject* object, ExecState* stat
     if (proto.isObject() && jsCast<JSObject*>(proto)->hasProperty(state, propertyName))
         return false;
 
-    auto& window = jsCast<JSDOMWindowBase*>(thisObject->globalObject())->wrapped();
+    auto& vm = state->vm();
+
+    // FIXME: We should probably add support for JSRemoteDOMWindowBase too.
+    auto* jsWindow = jsDynamicCast<JSDOMWindowBase*>(vm, thisObject->globalObject());
+    if (!jsWindow)
+        return false;
+
+    auto& window = jsWindow->wrapped();
+
     if (auto* frame = window.frame())
         return jsDOMWindowPropertiesGetOwnPropertySlotNamedItemGetter(thisObject, *frame, state, propertyName, slot);
 
index 2086b3d..37ebece 100644 (file)
@@ -29,8 +29,8 @@
 #include "config.h"
 #include "JSDOMWindowProxy.h"
 
+#include "AbstractFrame.h"
 #include "CommonVM.h"
-#include "Frame.h"
 #include "GCController.h"
 #include "JSDOMWindow.h"
 #include "JSDOMWindowProperties.h"
@@ -49,14 +49,14 @@ inline JSDOMWindowProxy::JSDOMWindowProxy(VM& vm, Structure& structure, DOMWrapp
 {
 }
 
-void JSDOMWindowProxy::finishCreation(VM& vm, DOMWindow& window)
+void JSDOMWindowProxy::finishCreation(VM& vm, AbstractDOMWindow& window)
 {
     Base::finishCreation(vm);
     ASSERT(inherits(vm, info()));
     setWindow(window);
 }
 
-JSDOMWindowProxy& JSDOMWindowProxy::create(JSC::VM& vm, DOMWindow& window, DOMWrapperWorld& world)
+JSDOMWindowProxy& JSDOMWindowProxy::create(JSC::VM& vm, AbstractDOMWindow& window, DOMWrapperWorld& world)
 {
     auto& structure = *JSC::Structure::create(vm, 0, jsNull(), JSC::TypeInfo(JSC::PureForwardingProxyType, StructureFlags), info());
     auto& proxy = *new (NotNull, JSC::allocateCell<JSDOMWindowProxy>(vm.heap)) JSDOMWindowProxy(vm, structure, world);
index 13fb325..e6b4eab 100644 (file)
@@ -43,7 +43,7 @@ class AbstractFrame;
 class JSDOMWindowProxy final : public JSC::JSProxy {
     using Base = JSC::JSProxy;
 public:
-    static JSDOMWindowProxy& create(JSC::VM&, DOMWindow&, DOMWrapperWorld&);
+    static JSDOMWindowProxy& create(JSC::VM&, AbstractDOMWindow&, DOMWrapperWorld&);
     static void destroy(JSCell*);
 
     DECLARE_INFO;
@@ -61,7 +61,7 @@ public:
 
 private:
     JSDOMWindowProxy(JSC::VM&, JSC::Structure&, DOMWrapperWorld&);
-    void finishCreation(JSC::VM&, DOMWindow&);
+    void finishCreation(JSC::VM&, AbstractDOMWindow&);
 
     Ref<DOMWrapperWorld> m_world;
 };
index bbf8482..89a3080 100644 (file)
@@ -29,6 +29,7 @@
 #include "DOMWindow.h"
 #include "DOMWrapperWorld.h"
 #include "JSDOMWindow.h"
+#include "JSRemoteDOMWindow.h"
 #include "SerializedScriptValue.h"
 #include "WebCoreJSClientData.h"
 #include <JavaScriptCore/Error.h>
@@ -37,11 +38,10 @@ namespace WebCore {
 
 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSDOMObject);
 
-JSDOMWindow& JSDOMObject::domWindow() const
+JSDOMObject::JSDOMObject(JSC::Structure* structure, JSC::JSGlobalObject& globalObject)
+    : Base(globalObject.vm(), structure)
 {
-    auto* domWindow = JSC::jsCast<JSDOMWindow*>(JSC::JSNonFinalObject::globalObject());
-    ASSERT(domWindow);
-    return *domWindow;
+    ASSERT(scriptExecutionContext() || globalObject.classInfo() == JSRemoteDOMWindow::info());
 }
 
 CompleteSubspace* outputConstraintSubspaceFor(VM& vm)
index f3ce0be..b543b6a 100644 (file)
@@ -27,7 +27,6 @@
 
 namespace WebCore {
 
-class JSDOMWindow;
 class ScriptExecutionContext;
 
 // JSC allows us to extend JSType. If the highest bit is set, we can add any Object types and they are
@@ -63,14 +62,8 @@ public:
     JSDOMGlobalObject* globalObject() const { return JSC::jsCast<JSDOMGlobalObject*>(JSC::JSNonFinalObject::globalObject()); }
     ScriptExecutionContext* scriptExecutionContext() const { return globalObject()->scriptExecutionContext(); }
 
-    JSDOMWindow& domWindow() const;
-
 protected:
-    JSDOMObject(JSC::Structure* structure, JSC::JSGlobalObject& globalObject)
-        : Base(globalObject.vm(), structure)
-    {
-        ASSERT(scriptExecutionContext());
-    }
+    WEBCORE_EXPORT JSDOMObject(JSC::Structure*, JSC::JSGlobalObject&);
 };
 
 WEBCORE_EXPORT JSC::CompleteSubspace* outputConstraintSubspaceFor(JSC::VM&);
index 730ec31..172ff29 100644 (file)
@@ -79,10 +79,7 @@ JSDOMWindowProxy& WindowProxyController::createWindowProxy(DOMWrapperWorld& worl
 
     VM& vm = world.vm();
 
-    // FIXME: We do not support constructing a JSDOMWindowProxy for a RemoteDOMWindow yet.
-    RELEASE_ASSERT(is<DOMWindow>(m_frame.window()));
-
-    Strong<JSDOMWindowProxy> windowProxy(vm, &JSDOMWindowProxy::create(vm, *downcast<DOMWindow>(m_frame.window()), world));
+    Strong<JSDOMWindowProxy> windowProxy(vm, &JSDOMWindowProxy::create(vm, *m_frame.window(), world));
     Strong<JSDOMWindowProxy> windowProxy2(windowProxy);
     m_windowProxies.add(&world, windowProxy);
     world.didCreateWindowProxy(this);
@@ -140,10 +137,7 @@ void WindowProxyController::setDOMWindowForWindowProxy(AbstractDOMWindow* newDOM
         if (&windowProxy->wrapped() == newDOMWindow)
             continue;
 
-        // FIXME: We do not support setting a RemoteDOMWindow yet.
-        ASSERT(is<DOMWindow>(newDOMWindow));
-
-        windowProxy->setWindow(*downcast<DOMWindow>(newDOMWindow));
+        windowProxy->setWindow(*newDOMWindow);
 
         ScriptController* scriptController = nullptr;
         Page* page = nullptr;
index 39e5665..18b1735 100644 (file)
@@ -276,7 +276,9 @@ void disconnectWindowWrapper(WebScriptObject *windowWrapper)
     // It's not actually correct to call shouldAllowAccessToFrame in this way because
     // JSDOMWindowBase* isn't the right object to represent the currently executing
     // JavaScript. Instead, we should use ExecState, like we do elsewhere.
-    JSDOMWindowBase* target = jsCast<JSDOMWindowBase*>(root->globalObject());
+    auto* target = JSC::jsDynamicCast<JSDOMWindowBase*>(root->globalObject()->vm(), root->globalObject());
+    if (!target)
+        return false;
     return BindingSecurity::shouldAllowAccessToDOMWindow(_private->originRootObject->globalObject()->globalExec(), target->wrapped());
 }
 
index a366676..a8100a4 100644 (file)
@@ -4261,6 +4261,8 @@ void Document::createDOMWindow()
 
     ASSERT(m_domWindow->document() == this);
     ASSERT(m_domWindow->frame() == m_frame);
+
+    m_frame->loader().client().didCreateWindow(*m_domWindow);
 }
 
 void Document::takeDOMWindowFrom(Document* document)
index f8c4b4e..3da90f2 100644 (file)
@@ -71,6 +71,7 @@ class AuthenticationChallenge;
 class CachedFrame;
 class CachedResourceRequest;
 class Color;
+class DOMWindow;
 class DOMWindowExtension;
 class DOMWrapperWorld;
 class DocumentLoader;
@@ -365,6 +366,8 @@ public:
     virtual void getLoadDecisionForIcons(const Vector<std::pair<WebCore::LinkIcon&, uint64_t>>&) { }
     virtual void finishedLoadingIcon(uint64_t, SharedBuffer*) { }
 
+    virtual void didCreateWindow(DOMWindow&) { }
+
 #if ENABLE(APPLICATION_MANIFEST)
     virtual void finishedLoadingApplicationManifest(uint64_t, const std::optional<ApplicationManifest>&) { }
 #endif
index ded96f8..156ed12 100644 (file)
@@ -141,7 +141,7 @@ public:
     void addDestructionObserver(FrameDestructionObserver*);
     void removeDestructionObserver(FrameDestructionObserver*);
 
-    void willDetachPage();
+    WEBCORE_EXPORT void willDetachPage();
     void detachFromPage();
     void disconnectOwnerElement();
 
index 3290018..3d11e06 100644 (file)
@@ -1,3 +1,77 @@
+2018-04-18  Chris Dumez  <cdumez@apple.com>
+
+        Add support for converting a local window to a remote window
+        https://bugs.webkit.org/show_bug.cgi?id=184515
+        <rdar://problem/39011318>
+
+        Reviewed by Ryosuke Niwa.
+
+        Add initial support for process-swapping when navigating cross-origin as a result
+        of a window.open(). The window object returned by window.open() is initially same
+        origin and is for about:blank. The navigation cross-origin then happens and the
+        JS wrappers for the window then point to a cross-origin window which is remote (i.e.
+        hosted in another WebProcess).
+
+        The RemoteDOMWindow exposed to JS looks like a regular cross-origin Window with a few
+        exceptions due to our incomplete implementation (e.g. w.location returns null) and
+        most of its API is currently not functional. The RemoteDOMWindow API will be implemented
+        in a follow-up by relying on IPC.
+
+        * UIProcess/API/APIProcessPoolConfiguration.cpp:
+        (API::ProcessPoolConfiguration::copy):
+        * UIProcess/API/APIProcessPoolConfiguration.h:
+        * UIProcess/API/C/WKContextConfigurationRef.cpp:
+        (WKContextConfigurationProcessSwapsOnWindowOpenWithOpener):
+        (WKContextConfigurationSetProcessSwapsOnWindowOpenWithOpener):
+        * UIProcess/API/C/WKContextConfigurationRef.h:
+        * UIProcess/API/Cocoa/_WKProcessPoolConfiguration.h:
+        * UIProcess/API/Cocoa/_WKProcessPoolConfiguration.mm:
+        (-[_WKProcessPoolConfiguration setProcessSwapsOnWindowOpenWithOpener:]):
+        (-[_WKProcessPoolConfiguration processSwapsOnWindowOpenWithOpener]):
+        Add ProcessPool configuration flag to turn on processSwap on window.open(), even
+        if there is an opener.
+
+        * UIProcess/WebPageProxy.cpp:
+        (WebKit::WebPageProxy::continueNavigationInNewProcess):
+        If the navigation was triggered via window.open(), then set up on handler for when
+        a DOMWindow is constructed for the main frame in the new process.
+
+        (WebKit::WebPageProxy::didCreateWindow):
+        When a Window is constructed for the main frame in a new process on process swap,
+        notify the old process that its representation of the window should become remote
+        and provide it with the Frame / Window identifiers it needs.
+
+        * UIProcess/WebPageProxy.h:
+        * UIProcess/WebPageProxy.messages.in:
+
+        * UIProcess/WebProcessPool.cpp:
+        (WebKit::WebProcessPool::processForNavigation):
+        Do process swapping on cross-origin window.open() if the corresponding setting is
+        enabled.
+
+        * WebProcess/WebCoreSupport/WebFrameLoaderClient.cpp:
+        (WebKit::WebFrameLoaderClient::didCreateWindow):
+        * WebProcess/WebCoreSupport/WebFrameLoaderClient.h:
+
+        * WebProcess/WebPage/WebPage.cpp:
+        (WebKit::WebPage::frameBecameRemote):
+        This is called when process swapping has happened due to a window.open() navigation
+        cross-origin, when a Frame / Window has been constructed in the new process. We do
+        the following:
+        - Construct a RemoteFrame / RemoteWindow using the provided global identifiers to
+          represent the Frame / Window in the new process.
+        - We transfer the WindowProxies from the old Frame's WindowProxyController to the
+          new RemoteFrame's WindowProxyController.
+        - We update the window proxied by those WindowProxies to be the new RemoteWindow.
+        - We detach the old Frame as it is now remote and represented by the new RemoteFrame
+          object we constructed.
+        - If the old frame was the main frame (always the case currently), we close the page
+          as it is no longer needed. The new RemoteFrame is currently owned by the RemoteWindow
+          which is kept alive by its JS wrappers.
+
+        * WebProcess/WebPage/WebPage.h:
+        * WebProcess/WebPage/WebPage.messages.in:
+
 2018-04-18  Carlos Garcia Campos  <cgarcia@igalia.com>
 
         Unreviewed. Update OptionsGTK.cmake and NEWS for 2.21.1 release.
index 0934ba3..bacffa7 100644 (file)
@@ -128,6 +128,7 @@ Ref<ProcessPoolConfiguration> ProcessPoolConfiguration::copy()
 #endif
     copy->m_presentingApplicationPID = this->m_presentingApplicationPID;
     copy->m_processSwapsOnNavigation = this->m_processSwapsOnNavigation;
+    copy->m_processSwapsOnWindowOpenWithOpener = this->m_processSwapsOnWindowOpenWithOpener;
 
     return copy;
 }
index 831146b..3dc2b18 100644 (file)
@@ -138,6 +138,9 @@ public:
     bool processSwapsOnNavigation() const { return m_processSwapsOnNavigation; }
     void setProcessSwapsOnNavigation(bool swaps) { m_processSwapsOnNavigation = swaps; }
 
+    bool processSwapsOnWindowOpenWithOpener() const { return m_processSwapsOnWindowOpenWithOpener; }
+    void setProcessSwapsOnWindowOpenWithOpener(bool swaps) { m_processSwapsOnWindowOpenWithOpener = swaps; }
+
 private:
     bool m_shouldHaveLegacyDataStore { false };
 
@@ -170,6 +173,7 @@ private:
     bool m_shouldCaptureAudioInUIProcess { false };
     ProcessID m_presentingApplicationPID { getCurrentProcessID() };
     bool m_processSwapsOnNavigation { false };
+    bool m_processSwapsOnWindowOpenWithOpener { false };
 
 #if PLATFORM(IOS)
     WTF::String m_ctDataConnectionServiceType;
index 1554a94..f7896ce 100644 (file)
@@ -167,3 +167,13 @@ void WKContextConfigurationSetProcessSwapsOnNavigation(WKContextConfigurationRef
 {
     toImpl(configuration)->setProcessSwapsOnNavigation(swaps);
 }
+
+bool WKContextConfigurationProcessSwapsOnWindowOpenWithOpener(WKContextConfigurationRef configuration)
+{
+    return toImpl(configuration)->processSwapsOnWindowOpenWithOpener();
+}
+
+void WKContextConfigurationSetProcessSwapsOnWindowOpenWithOpener(WKContextConfigurationRef configuration, bool swaps)
+{
+    toImpl(configuration)->setProcessSwapsOnWindowOpenWithOpener(swaps);
+}
index d24eee5..77ae171 100644 (file)
@@ -71,6 +71,9 @@ WK_EXPORT void WKContextConfigurationSetShouldCaptureAudioInUIProcess(WKContextC
 WK_EXPORT bool WKContextConfigurationProcessSwapsOnNavigation(WKContextConfigurationRef configuration);
 WK_EXPORT void WKContextConfigurationSetProcessSwapsOnNavigation(WKContextConfigurationRef configuration, bool swaps);
 
+WK_EXPORT bool WKContextConfigurationProcessSwapsOnWindowOpenWithOpener(WKContextConfigurationRef configuration);
+WK_EXPORT void WKContextConfigurationSetProcessSwapsOnWindowOpenWithOpener(WKContextConfigurationRef configuration, bool swaps);
+
 #ifdef __cplusplus
 }
 #endif
index 29f4390..2a13728 100644 (file)
@@ -58,6 +58,7 @@ WK_CLASS_AVAILABLE(macosx(10.10), ios(8.0))
 #endif
 @property (nonatomic) pid_t presentingApplicationPID WK_API_AVAILABLE(macosx(10.13), ios(11.0));
 @property (nonatomic) BOOL processSwapsOnNavigation WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
+@property (nonatomic) BOOL processSwapsOnWindowOpenWithOpener WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
 
 @end
 
index 17fa6f1..cd29da8 100644 (file)
     return _processPoolConfiguration->processSwapsOnNavigation();
 }
 
+- (void)setProcessSwapsOnWindowOpenWithOpener:(BOOL)swaps
+{
+    _processPoolConfiguration->setProcessSwapsOnWindowOpenWithOpener(swaps);
+}
+
+- (BOOL)processSwapsOnWindowOpenWithOpener
+{
+    return _processPoolConfiguration->processSwapsOnWindowOpenWithOpener();
+}
+
 #if PLATFORM(IOS)
 - (NSString *)CTDataConnectionServiceType
 {
index 0e02763..4df4cd6 100644 (file)
 #include <WebCore/FloatRect.h>
 #include <WebCore/FocusDirection.h>
 #include <WebCore/FrameLoader.h>
+#include <WebCore/GlobalFrameIdentifier.h>
+#include <WebCore/GlobalWindowIdentifier.h>
 #include <WebCore/JSDOMBinding.h>
 #include <WebCore/JSDOMExceptionHandling.h>
 #include <WebCore/LengthBox.h>
@@ -2413,6 +2415,11 @@ void WebPageProxy::continueNavigationInNewProcess(API::Navigation& navigation, R
 {
     LOG(Loading, "Continuing navigation %" PRIu64 " '%s' in a new web process", navigation.navigationID(), navigation.loggingString().utf8().data());
 
+    Ref<WebProcessProxy> previousProcess = m_process.copyRef();
+    std::optional<uint64_t> navigatedFrameIdentifierInPreviousProcess;
+    if (m_mainFrame)
+        navigatedFrameIdentifierInPreviousProcess = m_mainFrame->frameID();
+
     ASSERT(m_process.ptr() != process.ptr());
     attachToProcessForNavigation(WTFMove(process));
 
@@ -2443,6 +2450,15 @@ void WebPageProxy::continueNavigationInNewProcess(API::Navigation& navigation, R
             didReceiveServerRedirectForProvisionalLoadForFrame(m_mainFrame->frameID(), navigation->navigationID(), WTFMove(request), { });
         };
     }
+
+    if (!navigation.isCrossOriginWindowOpenNavigation() || !navigatedFrameIdentifierInPreviousProcess)
+        return;
+
+    m_mainFrameWindowCreationHandler = [this, previousProcess = WTFMove(previousProcess), navigatedFrameIdentifierInPreviousProcess = *navigatedFrameIdentifierInPreviousProcess](const GlobalWindowIdentifier& windowIdentifier) {
+        ASSERT(m_mainFrame);
+        GlobalFrameIdentifier navigatedFrameIdentifierInNewProcess { pageID(), m_mainFrame->frameID() };
+        previousProcess->send(Messages::WebPage::FrameBecameRemote(navigatedFrameIdentifierInPreviousProcess, navigatedFrameIdentifierInNewProcess, windowIdentifier), pageID());
+    };
 }
 
 void WebPageProxy::setUserAgent(String&& userAgent)
@@ -3271,6 +3287,14 @@ void WebPageProxy::didCreateSubframe(uint64_t frameID)
     m_process->frameCreated(frameID, subFrame.get());
 }
 
+void WebPageProxy::didCreateWindow(uint64_t frameID, GlobalWindowIdentifier&& windowIdentifier)
+{
+    if (m_mainFrame && m_mainFrame->frameID() == frameID) {
+        if (auto mainFrameWindowCreationHandler = WTFMove(m_mainFrameWindowCreationHandler))
+            mainFrameWindowCreationHandler(windowIdentifier);
+    }
+}
+
 double WebPageProxy::estimatedProgress() const
 {
     return m_pageLoadState.estimatedProgress();
index d3618d2..233f13a 100644 (file)
@@ -177,6 +177,7 @@ struct ApplicationManifest;
 struct DictionaryPopupInfo;
 struct ExceptionDetails;
 struct FileChooserSettings;
+struct GlobalWindowIdentifier;
 struct MediaStreamRequest;
 struct SecurityOriginData;
 struct TextAlternativeWithRange;
@@ -1338,6 +1339,7 @@ private:
 
     void didCreateMainFrame(uint64_t frameID);
     void didCreateSubframe(uint64_t frameID);
+    void didCreateWindow(uint64_t frameID, WebCore::GlobalWindowIdentifier&&);
 
     void didStartProvisionalLoadForFrame(uint64_t frameID, uint64_t navigationID, WebCore::URL&&, WebCore::URL&& unreachableURL, const UserData&);
     void didReceiveServerRedirectForProvisionalLoadForFrame(uint64_t frameID, uint64_t navigationID, WebCore::ResourceRequest&&, const UserData&);
@@ -1780,6 +1782,7 @@ private:
 
     RefPtr<WebFrameProxy> m_mainFrame;
     Function<void()> m_mainFrameCreationHandler;
+    Function<void(const WebCore::GlobalWindowIdentifier&)> m_mainFrameWindowCreationHandler;
 
     RefPtr<WebFrameProxy> m_focusedFrame;
     RefPtr<WebFrameProxy> m_frameSetLargestFrame;
index 2304a70..53fb183 100644 (file)
@@ -115,6 +115,8 @@ messages -> WebPageProxy {
     DidCreateMainFrame(uint64_t frameID)
     DidCreateSubframe(uint64_t frameID)
 
+    DidCreateWindow(uint64_t frameID, struct WebCore::GlobalWindowIdentifier windowIdentifier)
+
     # Frame load messages
     DidStartProvisionalLoadForFrame(uint64_t frameID, uint64_t navigationID, WebCore::URL url, WebCore::URL unreachableURL, WebKit::UserData userData)
     DidReceiveServerRedirectForProvisionalLoadForFrame(uint64_t frameID, uint64_t navigationID, WebCore::ResourceRequest request, WebKit::UserData userData)
index 63f7fb0..ab9c8b0 100644 (file)
@@ -1985,15 +1985,18 @@ Ref<WebProcessProxy> WebProcessPool::processForNavigation(WebPageProxy& page, co
     if (!page.process().hasCommittedAnyProvisionalLoads())
         return page.process();
 
-    // FIXME: We should support process swap when a window has an opener.
-    if (navigation.opener())
-        return page.process();
-
     if (navigation.isCrossOriginWindowOpenNavigation()) {
+        if (navigation.opener() && !m_configuration->processSwapsOnWindowOpenWithOpener())
+            return page.process();
+
         action = PolicyAction::Ignore;
         return createNewWebProcess(page.websiteDataStore());
     }
 
+    // FIXME: We should support process swap when a window has an opener.
+    if (navigation.opener())
+        return page.process();
+
     auto targetURL = navigation.currentRequest().url();
     auto url = URL { ParsedURLString, page.pageLoadState().url() };
     if (!url.isValid() || url.isEmpty() || url.isBlankURL() || protocolHostAndPortAreEqual(url, targetURL))
index 0ab1131..a4eeaca 100644 (file)
@@ -1876,6 +1876,15 @@ void WebFrameLoaderClient::finishedLoadingIcon(uint64_t callbackIdentifier, Shar
     }
 }
 
+void WebFrameLoaderClient::didCreateWindow(DOMWindow& window)
+{
+    auto* webPage = m_frame->page();
+    if (!webPage)
+        return;
+
+    webPage->send(Messages::WebPageProxy::DidCreateWindow(m_frame->frameID(), window.identifier()));
+}
+
 #if ENABLE(APPLICATION_MANIFEST)
 void WebFrameLoaderClient::finishedLoadingApplicationManifest(uint64_t callbackIdentifier, const std::optional<WebCore::ApplicationManifest>& manifest)
 {
index 768bf7c..7a7ef2a 100644 (file)
@@ -269,6 +269,8 @@ private:
     void getLoadDecisionForIcons(const Vector<std::pair<WebCore::LinkIcon&, uint64_t>>&) final;
     void finishedLoadingIcon(uint64_t callbackIdentifier, WebCore::SharedBuffer*) final;
 
+    void didCreateWindow(WebCore::DOMWindow&) final;
+
 #if ENABLE(APPLICATION_MANIFEST)
     void finishedLoadingApplicationManifest(uint64_t, const std::optional<WebCore::ApplicationManifest>&) final;
 #endif
index 953da7d..20dcc1c 100644 (file)
 #include <WebCore/PrintContext.h>
 #include <WebCore/PromisedBlobInfo.h>
 #include <WebCore/Range.h>
+#include <WebCore/RemoteDOMWindow.h>
+#include <WebCore/RemoteFrame.h>
 #include <WebCore/RenderLayer.h>
 #include <WebCore/RenderTheme.h>
 #include <WebCore/RenderTreeAsText.h>
@@ -5871,6 +5873,32 @@ void WebPage::setIsSuspended(bool suspended)
     m_isSuspended = suspended;
 }
 
+void WebPage::frameBecameRemote(uint64_t frameID, GlobalFrameIdentifier&& remoteFrameIdentifier, GlobalWindowIdentifier&& remoteWindowIdentifier)
+{
+    RefPtr<WebFrame> frame = WebProcess::singleton().webFrame(frameID);
+    if (!frame)
+        return;
+
+    if (frame->page() != this)
+        return;
+
+    auto remoteFrame = RemoteFrame::create(WTFMove(remoteFrameIdentifier));
+    auto remoteWindow = RemoteDOMWindow::create(remoteFrame.copyRef(), WTFMove(remoteWindowIdentifier));
+    UNUSED_PARAM(remoteWindow);
+
+    auto windowProxies = frame->coreFrame()->windowProxyController().releaseWindowProxies();
+    remoteFrame->windowProxyController().setWindowProxies(WTFMove(windowProxies));
+    remoteFrame->windowProxyController().setDOMWindowForWindowProxy(remoteWindow.ptr());
+
+    auto* coreFrame = frame->coreFrame();
+    coreFrame->setView(nullptr);
+    coreFrame->willDetachPage();
+    coreFrame->detachFromPage();
+
+    if (frame->isMainFrame())
+        close();
+}
+
 #if HAVE(CFNETWORK_STORAGE_PARTITIONING)
 static uint64_t nextRequestStorageAccessContextId()
 {
index a291b81..5300b7e 100644 (file)
@@ -156,6 +156,8 @@ enum class TextIndicatorPresentationTransition : uint8_t;
 
 struct CompositionUnderline;
 struct DictationAlternative;
+struct GlobalFrameIdentifier;
+struct GlobalWindowIdentifier;
 struct Highlight;
 struct KeypressCommand;
 struct PromisedBlobInfo;
@@ -1383,6 +1385,8 @@ private:
     void didReceivePasswordForQuickLookDocument(const String&);
 #endif
 
+    void frameBecameRemote(uint64_t frameID, WebCore::GlobalFrameIdentifier&& remoteFrameIdentifier, WebCore::GlobalWindowIdentifier&& remoteWindowIdentifier);
+
     void registerURLSchemeHandler(uint64_t identifier, const String& scheme);
 
     void urlSchemeTaskDidPerformRedirection(uint64_t handlerIdentifier, uint64_t taskIdentifier, WebCore::ResourceResponse&&, WebCore::ResourceRequest&&);
index e0ff521..cc70a9a 100644 (file)
@@ -484,6 +484,8 @@ messages -> WebPage LegacyReceiver {
     DidReceivePasswordForQuickLookDocument(String password)
 #endif
 
+    FrameBecameRemote(uint64_t frameID, struct WebCore::GlobalFrameIdentifier remoteFrameIdentifier, struct WebCore::GlobalWindowIdentifier remoteWindowIdentifier)
+
     RegisterURLSchemeHandler(uint64_t identifier, String scheme)
 
     URLSchemeTaskDidPerformRedirection(uint64_t handlerIdentifier, uint64_t taskIdentifier, WebCore::ResourceResponse response, WebCore::ResourceRequest request)
index c1c65df..a520b61 100644 (file)
@@ -1,3 +1,39 @@
+2018-04-18  Chris Dumez  <cdumez@apple.com>
+
+        Add support for converting a local window to a remote window
+        https://bugs.webkit.org/show_bug.cgi?id=184515
+        <rdar://problem/39011318>
+
+        Reviewed by Ryosuke Niwa.
+
+        * MiniBrowser/mac/AppDelegate.m:
+        (defaultConfiguration):
+        * MiniBrowser/mac/SettingsController.h:
+        * MiniBrowser/mac/SettingsController.m:
+        (-[SettingsController _populateMenu]):
+        (-[SettingsController validateMenuItem:]):
+        (-[SettingsController processSwapOnWindowOpenWithOpenerEnabled]):
+        (-[SettingsController toggleProcessSwapOnWindowOpenWithOpener:]):
+        Add menu entry in minibrowser to turn on process swap on cross-origin window.open().
+
+        * TestWebKitAPI/Tests/WebKitCocoa/ProcessSwapOnNavigation.mm:
+        Turn on process swap on cross-origin window.open() for corresponding test and update
+        test to expect that a new WebProcess is created.
+
+        * WebKitTestRunner/InjectedBundle/InjectedBundlePage.cpp:
+        (WTR::InjectedBundlePage::decidePolicyForResponse):
+        Add null checks for injectedBundle.testRunner(). When we swap process on navigation,
+        the InjectedBundlePage::decidePolicyForResponse() gets called in the new process.
+        In this new process, we have constructed a InjectedBundlePage for the page but we
+        have not initialized the InjectedBundle members such as testRunner.
+
+        * WebKitTestRunner/TestController.cpp:
+        (WTR::TestController::createWebViewWithOptions):
+        (WTR::updateTestOptionsFromTestHeader):
+        * WebKitTestRunner/TestOptions.h:
+        (WTR::TestOptions::hasSameInitializationOptions const):
+        Add a way for layout tests to turn on process swap on navigation via a test header.
+
 2018-04-18  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         Unreviewed, reland r230697, r230720, and r230724.
index 61b9e8b..3a369a4 100644 (file)
@@ -100,6 +100,8 @@ static WKWebViewConfiguration *defaultConfiguration()
             processConfiguration.maximumProcessCount = 1;
         if ([SettingsController shared].processSwapOnNavigationEnabled)
             processConfiguration.processSwapsOnNavigation = true;
+        if ([SettingsController shared].processSwapOnWindowOpenWithOpenerEnabled)
+            processConfiguration.processSwapsOnWindowOpenWithOpener = true;
         
         configuration.processPool = [[[WKProcessPool alloc] _initWithConfiguration:processConfiguration] autorelease];
 
index 8dad064..84db32c 100644 (file)
@@ -60,6 +60,7 @@
 @property (nonatomic, readonly) BOOL usesGameControllerFramework;
 @property (nonatomic, readonly) BOOL networkCacheSpeculativeRevalidationDisabled;
 @property (nonatomic, readonly) BOOL processSwapOnNavigationEnabled;
+@property (nonatomic, readonly) BOOL processSwapOnWindowOpenWithOpenerEnabled;
 
 @property (nonatomic, readonly) NSString *defaultURL;
 
index d64ef41..84f0190 100644 (file)
@@ -71,6 +71,7 @@ static NSString * const UseRemoteLayerTreeDrawingAreaPreferenceKey = @"WebKit2Us
 static NSString * const PerWindowWebProcessesDisabledKey = @"PerWindowWebProcessesDisabled";
 static NSString * const NetworkCacheSpeculativeRevalidationDisabledKey = @"NetworkCacheSpeculativeRevalidationDisabled";
 static NSString * const ProcessSwapOnNavigationKey = @"ProcessSwapOnNavigation";
+static NSString * const ProcessSwapOnWindowOpenWithOpenerKey = @"ProcessSwapOnWindowOpenWithOpener";
 
 typedef NS_ENUM(NSInteger, DebugOverylayMenuItemTag) {
     NonFastScrollableRegionOverlayTag = 100,
@@ -181,6 +182,7 @@ typedef NS_ENUM(NSInteger, DebugOverylayMenuItemTag) {
     [self _addItemWithTitle:@"Use GameController.framework on macOS (Restart required)" action:@selector(toggleUsesGameControllerFramework:) indented:YES];
     [self _addItemWithTitle:@"Disable network cache speculative revalidation" action:@selector(toggleNetworkCacheSpeculativeRevalidationDisabled:) indented:YES];
     [self _addItemWithTitle:@"Enable Process Swap on Navigation" action:@selector(toggleProcessSwapOnNavigation:) indented:YES];
+    [self _addItemWithTitle:@"Enable Process Swap on window.open() with an opener" action:@selector(toggleProcessSwapOnWindowOpenWithOpener:) indented:YES];
 
     NSMenuItem *debugOverlaysSubmenuItem = [[NSMenuItem alloc] initWithTitle:@"Debug Overlays" action:nil keyEquivalent:@""];
     NSMenu *debugOverlaysMenu = [[NSMenu alloc] initWithTitle:@"Debug Overlays"];
@@ -274,6 +276,8 @@ typedef NS_ENUM(NSInteger, DebugOverylayMenuItemTag) {
         [menuItem setState:[self networkCacheSpeculativeRevalidationDisabled] ? NSControlStateValueOn : NSControlStateValueOff];
     else if (action == @selector(toggleProcessSwapOnNavigation:))
         [menuItem setState:[self processSwapOnNavigationEnabled] ? NSControlStateValueOn : NSControlStateValueOff];
+    else if (action == @selector(toggleProcessSwapOnWindowOpenWithOpener:))
+        [menuItem setState:[self processSwapOnWindowOpenWithOpenerEnabled] ? NSControlStateValueOn : NSControlStateValueOff];
     else if (action == @selector(toggleUseUISideCompositing:))
         [menuItem setState:[self useUISideCompositing] ? NSControlStateValueOn : NSControlStateValueOff];
     else if (action == @selector(togglePerWindowWebProcessesDisabled:))
@@ -499,6 +503,16 @@ typedef NS_ENUM(NSInteger, DebugOverylayMenuItemTag) {
     [self _toggleBooleanDefault:ProcessSwapOnNavigationKey];
 }
 
+- (BOOL)processSwapOnWindowOpenWithOpenerEnabled
+{
+    return [[NSUserDefaults standardUserDefaults] boolForKey:ProcessSwapOnWindowOpenWithOpenerKey];
+}
+
+- (void)toggleProcessSwapOnWindowOpenWithOpener:(id)sender
+{
+    [self _toggleBooleanDefault:ProcessSwapOnWindowOpenWithOpenerKey];
+}
+
 - (BOOL)isSpaceReservedForBanners
 {
     return [[NSUserDefaults standardUserDefaults] boolForKey:ReserveSpaceForBannersPreferenceKey];
index 2299c05..b1f1a79 100644 (file)
@@ -409,6 +409,7 @@ TEST(ProcessSwap, CrossOriginWindowOpenWithOpener)
 {
     auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
     processPoolConfiguration.get().processSwapsOnNavigation = YES;
+    processPoolConfiguration.get().processSwapsOnWindowOpenWithOpener = YES;
     auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
 
     auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
@@ -443,8 +444,7 @@ TEST(ProcessSwap, CrossOriginWindowOpenWithOpener)
     auto pid2 = [createdWebView _webProcessIdentifier];
     EXPECT_TRUE(!!pid2);
 
-    // FIXME: This should eventually be false once we support process swapping when there is an opener.
-    EXPECT_EQ(pid1, pid2);
+    EXPECT_NE(pid1, pid2);
 }
 
 TEST(ProcessSwap, SameOriginWindowOpenNoOpener)
index fb01438..436c39d 100644 (file)
@@ -1386,7 +1386,7 @@ WKBundlePagePolicyAction InjectedBundlePage::decidePolicyForNewWindowAction(WKBu
 WKBundlePagePolicyAction InjectedBundlePage::decidePolicyForResponse(WKBundlePageRef page, WKBundleFrameRef, WKURLResponseRef response, WKURLRequestRef, WKTypeRef*)
 {
     auto& injectedBundle = InjectedBundle::singleton();
-    if (injectedBundle.testRunner()->isPolicyDelegateEnabled() && WKURLResponseIsAttachment(response)) {
+    if (injectedBundle.testRunner() && injectedBundle.testRunner()->isPolicyDelegateEnabled() && WKURLResponseIsAttachment(response)) {
         StringBuilder stringBuilder;
         WKRetainPtr<WKStringRef> filename = adoptWK(WKURLResponseCopySuggestedFilename(response));
         stringBuilder.appendLiteral("Policy delegate: resource is an attachment, suggested file name \'");
@@ -1395,7 +1395,7 @@ WKBundlePagePolicyAction InjectedBundlePage::decidePolicyForResponse(WKBundlePag
         InjectedBundle::singleton().outputText(stringBuilder.toString());
     }
 
-    if (injectedBundle.testRunner()->shouldDecideResponsePolicyAfterDelay())
+    if (injectedBundle.testRunner() && injectedBundle.testRunner()->shouldDecideResponsePolicyAfterDelay())
         return WKBundlePagePolicyActionPassThrough;
 
     WKRetainPtr<WKStringRef> mimeType = adoptWK(WKURLResponseCopyMIMEType(response));
index 5f1bdba..6965cdd 100644 (file)
@@ -497,6 +497,11 @@ void TestController::createWebViewWithOptions(const TestOptions& options)
         WKArrayAppendItem(overrideLanguages.get(), adoptWK(WKStringCreateWithUTF8CString(language.utf8().data())).get());
     WKContextConfigurationSetOverrideLanguages(contextConfiguration.get(), overrideLanguages.get());
 
+    if (options.enableProcessSwapOnNavigation) {
+        WKContextConfigurationSetProcessSwapsOnNavigation(contextConfiguration.get(), true);
+        WKContextConfigurationSetProcessSwapsOnWindowOpenWithOpener(contextConfiguration.get(), true);
+    }
+
     auto configuration = generatePageConfiguration(contextConfiguration.get());
 
     // Some preferences (notably mock scroll bars setting) currently cannot be re-applied to an existing view, so we need to set them now.
@@ -1096,6 +1101,8 @@ static void updateTestOptionsFromTestHeader(TestOptions& testOptions, const std:
             testOptions.allowCrossOriginSubresourcesToAskForCredentials = parseBooleanTestHeaderValue(value);
         if (key == "enableCSSAnimationsAndCSSTransitionsBackedByWebAnimations")
             testOptions.enableCSSAnimationsAndCSSTransitionsBackedByWebAnimations = parseBooleanTestHeaderValue(value);
+        if (key == "enableProcessSwapOnNavigation")
+            testOptions.enableProcessSwapOnNavigation = parseBooleanTestHeaderValue(value);
         pairStart = pairEnd + 1;
     }
 }
index 36b53ca..bf89a07 100644 (file)
@@ -56,6 +56,7 @@ struct TestOptions {
     bool dumpJSConsoleLogInStdErr { false };
     bool allowCrossOriginSubresourcesToAskForCredentials { false };
     bool enableCSSAnimationsAndCSSTransitionsBackedByWebAnimations { false };
+    bool enableProcessSwapOnNavigation { false };
 
     float deviceScaleFactor { 1 };
     Vector<String> overrideLanguages;
@@ -86,7 +87,8 @@ struct TestOptions {
             || dumpJSConsoleLogInStdErr != options.dumpJSConsoleLogInStdErr
             || applicationManifest != options.applicationManifest
             || allowCrossOriginSubresourcesToAskForCredentials != options.allowCrossOriginSubresourcesToAskForCredentials
-            || enableCSSAnimationsAndCSSTransitionsBackedByWebAnimations != options.enableCSSAnimationsAndCSSTransitionsBackedByWebAnimations)
+            || enableCSSAnimationsAndCSSTransitionsBackedByWebAnimations != options.enableCSSAnimationsAndCSSTransitionsBackedByWebAnimations
+            || enableProcessSwapOnNavigation != options.enableProcessSwapOnNavigation)
             return false;
 
         return true;