Web Inspector: $0 stops working after navigating to a different domain
https://bugs.webkit.org/show_bug.cgi?id=147962

Patch by Joseph Pecoraro <pecoraro@apple.com> on 2015-11-09
Reviewed by Brian Burg.

Source/JavaScriptCore:

Extract the per-GlobalObject cache of JSValue wrappers for
InjectedScriptHost objects to be reused by WebCore for its
CommandLineAPIHost objects injected into multiple contexts.

* CMakeLists.txt:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters:
* JavaScriptCore.xcodeproj/project.pbxproj:
Add new files.

* inspector/PerGlobalObjectWrapperWorld.h:
* inspector/PerGlobalObjectWrapperWorld.cpp:
(Inspector::PerGlobalObjectWrapperWorld::getWrapper):
(Inspector::PerGlobalObjectWrapperWorld::addWrapper):
(Inspector::PerGlobalObjectWrapperWorld::clearAllWrappers):
Hold a bunch of per-global-object wrappers for an object
that will outlive the global object. This inspector does this
for host objects that it exposes into scripts it injects into
each execution context created by the page.

* inspector/InjectedScriptHost.cpp:
(Inspector::InjectedScriptHost::wrapper):
(Inspector::InjectedScriptHost::clearAllWrappers):
(Inspector::InjectedScriptHost::jsWrapper): Deleted.
(Inspector::clearWrapperFromValue): Deleted.
(Inspector::InjectedScriptHost::clearWrapper): Deleted.
Extract and simplify the Per-GlobalObject wrapping into a class.
Simplify object construction as well.

* inspector/InjectedScriptHost.h:
* inspector/InjectedScriptManager.cpp:
(Inspector::InjectedScriptManager::createInjectedScript):
(Inspector::InjectedScriptManager::discardInjectedScripts):
Make discarding virtual so subclasses may also discard injected scripts.

* inspector/JSInjectedScriptHost.cpp:
(Inspector::JSInjectedScriptHost::JSInjectedScriptHost):
(Inspector::JSInjectedScriptHost::releaseImpl): Deleted.
(Inspector::JSInjectedScriptHost::~JSInjectedScriptHost): Deleted.
(Inspector::toJS): Deleted.
(Inspector::toJSInjectedScriptHost): Deleted.
* inspector/JSInjectedScriptHost.h:
(Inspector::JSInjectedScriptHost::create):
(Inspector::JSInjectedScriptHost::impl):
Update this code originally copied from older generated bindings to
be more like new generated bindings and remove some now unused code.

Source/WebCore:

Test: http/tests/inspector/console/cross-domain-inspected-node-access.html

The inspector backend injects the CommandLineAPI Source with a
corresponding CommandLineAPIHost into each execution context
created by the page (main frame, sub frames, etc).

When creating the JSValue wrapper for the CommandLineAPIHost using
the generated toJS(...) DOM bindings, we were using the cached
CommandLineAPIHost wrapper values in the single DOMWrapperWorld shared
across all frames. This meant that the first time the wrapper was
needed it was created in context A. But when needed for context B
it was using the wrapper created in context A. Using this wrapper
in context B was producing unexpected cross-origin warnings.

The solution taken here, is to create a new JSValue wrapper for
the CommandLineAPIHost per execution context. This way each time
the CommandLineAPIHost wrapper is used in a frame, it is using
the one created for that frame.

The C++ host object being wrapped has a lifetime equivalent to
the Page. It does not change in this patch. The wrapper values
are cleared on page navigation or when the page is closed, and
will be garbage collected.

* WebCore.vcxproj/WebCore.vcxproj:
* WebCore.vcxproj/WebCore.vcxproj.filters:
* ForwardingHeaders/inspector/PerGlobalObjectWrapperWorld.h: Added.
New forwarding header.

* inspector/CommandLineAPIHost.h:
* inspector/CommandLineAPIHost.cpp:
(WebCore::CommandLineAPIHost::CommandLineAPIHost):
(WebCore::CommandLineAPIHost::wrapper):
Cached JSValue wrappers per GlobalObject.

(WebCore::CommandLineAPIHost::clearAllWrappers):
Clear any wrappers we have, including the $0 value itself
which we weren't explicitly clearing previously.

* inspector/CommandLineAPIModule.cpp:
(WebCore::CommandLineAPIModule::host):
Simplify creating the wrapper.

* inspector/WebInjectedScriptManager.h:
* inspector/WebInjectedScriptManager.cpp:
(WebCore::WebInjectedScriptManager::discardInjectedScripts):
When the main frame window object clears, also clear the
CommandLineAPI wrappers we may have created. Also take this
opportunity to clear any $0 value that may have pointed
to a value in the previous page.

LayoutTests:

* TestExpectations:
* http/tests/inspector/console/access-inspected-object-expected.txt: Removed.
* http/tests/inspector/console/access-inspected-object.html: Removed.
* http/tests/inspector/console/cross-domain-inspected-node-access-expected.txt: Added.
* http/tests/inspector/console/cross-domain-inspected-node-access.html: Added.
Rewrite the old test with the new testing infrastructure.
Test this particular case of cross origin CommandLineAPI usage ($0).

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@192186 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index 5e8b34d..e28f4f8 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,3 +1,18 @@
+2015-11-09  Joseph Pecoraro  <pecoraro@apple.com>
+
+        Web Inspector: $0 stops working after navigating to a different domain
+        https://bugs.webkit.org/show_bug.cgi?id=147962
+
+        Reviewed by Brian Burg.
+
+        * TestExpectations:
+        * http/tests/inspector/console/access-inspected-object-expected.txt: Removed.
+        * http/tests/inspector/console/access-inspected-object.html: Removed.
+        * http/tests/inspector/console/cross-domain-inspected-node-access-expected.txt: Added.
+        * http/tests/inspector/console/cross-domain-inspected-node-access.html: Added.
+        Rewrite the old test with the new testing infrastructure.
+        Test this particular case of cross origin CommandLineAPI usage ($0).
+
 2015-11-09  Ryan Haddad  <ryanhaddad@apple.com>
 
         Marking crypto/subtle/rsa-export-generated-keys.html as slow on mac
diff --git a/LayoutTests/TestExpectations b/LayoutTests/TestExpectations
index dedc03d..126d9c4 100644
--- a/LayoutTests/TestExpectations
+++ b/LayoutTests/TestExpectations
@@ -120,7 +120,6 @@
 webkit.org/b/129057 media/controls-styling-strict.html [ Pass Failure ]
 
 # These tests will be rewritten, just skip them until that time.
-webkit.org/b/148036 http/tests/inspector/console/ [ Skip ]
 webkit.org/b/148036 http/tests/inspector/css/ [ Skip ]
 webkit.org/b/148036 http/tests/inspector/page/ [ Skip ]
 webkit.org/b/148036 http/tests/inspector/replay/ [ Skip ]
diff --git a/LayoutTests/http/tests/inspector/console/access-inspected-object-expected.txt b/LayoutTests/http/tests/inspector/console/access-inspected-object-expected.txt
deleted file mode 100644
index db11e59..0000000
--- a/LayoutTests/http/tests/inspector/console/access-inspected-object-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-CONSOLE MESSAGE: line 52: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a frame with origin "http://localhost:8000". Protocols, domains, and ports must match.
-Test that code evaluated in the main frame cannot access $0 that resolves into a node in a frame from a different domain. Bug 105423.
-
- 
diff --git a/LayoutTests/http/tests/inspector/console/access-inspected-object.html b/LayoutTests/http/tests/inspector/console/access-inspected-object.html
deleted file mode 100644
index e537ddd..0000000
--- a/LayoutTests/http/tests/inspector/console/access-inspected-object.html
+++ /dev/null
@@ -1,75 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../resources/protocol-test.js"></script>
-<script>
-if (window.testRunner) {
-    testRunner.dumpAsText();
-    testRunner.waitUntilDone();
-}
-
-function test()
-{
-    InspectorProtocol.sendCommand("DOM.getDocument", {}, didGetDocument);
-
-    function didGetDocument(messageObject)
-    {
-        InspectorProtocol.sendCommand("DOM.querySelector", {
-                "nodeId": messageObject.result.root.nodeId,
-                "selector": "iframe#myframe"
-            }, didFindIframe);
-        InspectorProtocol.eventHandler["DOM.setChildNodes"] = iframeRequestHandler;
-    }
-
-    function didFindIframe(messageObject)
-    {
-        if (messageObject.error) {
-            ProtocolTest.log("FAIL: " + messageObject.error);
-            ProtocolTest.completeTest();
-        }
-    }
-
-    function iframeRequestHandler(messageObject)
-    {
-        var node = messageObject.params.nodes[0];
-        if (!node || node.nodeName !== "IFRAME")
-            return;
-        InspectorProtocol.eventHandler["DOM.setChildNodes"] = null;
-        InspectorProtocol.sendCommand("DOM.querySelector", {
-                "nodeId": node.contentDocument.nodeId,
-                "selector": "div#rootDiv"
-            }, didFindDiv);
-    }
-
-    function didFindDiv(messageObject)
-    {
-        InspectorProtocol.sendCommand("Console.enable", {});
-        InspectorProtocol.sendCommand("Console.addInspectedNode", {
-                "nodeId": messageObject.result.nodeId
-            }, didAddInspectedNode);
-    }
-
-    function didAddInspectedNode(messageObject)
-    {
-        InspectorProtocol.sendCommand("Runtime.evaluate", {
-                "expression": "$0",
-                "includeCommandLineAPI": true
-            }, didEvaluate);
-    }
-
-    function didEvaluate(messageObject)
-    {
-        if (messageObject.result.wasThrown)
-            ProtocolTest.log("FAIL: unexpected exception: " + JSON.stringify(messageObject, null, 2));
-        if (messageObject.result.result.value !== null)
-            ProtocolTest.log("FAIL: unexpected value: " + JSON.stringify(messageObject, null, 2));
-        ProtocolTest.completeTest();
-    }
-}
-
-</script>
-</head>
-<body>
-<p>Test that code evaluated in the main frame cannot access $0 that resolves into a node in a frame from a different domain. <a href="https://bugs.webkit.org/show_bug.cgi?id=105423">Bug 105423.</p>
-<iframe id="myframe" src="http://localhost:8000/inspector/page/resources/test-page.html" onload="runTest()"></iframe>
-</body>
-</html>
diff --git a/LayoutTests/http/tests/inspector/console/cross-domain-inspected-node-access-expected.txt b/LayoutTests/http/tests/inspector/console/cross-domain-inspected-node-access-expected.txt
new file mode 100644
index 0000000..b62f4de
--- /dev/null
+++ b/LayoutTests/http/tests/inspector/console/cross-domain-inspected-node-access-expected.txt
@@ -0,0 +1,23 @@
+CONSOLE MESSAGE: line 52: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a frame with origin "http://localhost:8000". Protocols, domains, and ports must match.
+CONSOLE MESSAGE: line 52: Blocked a frame with origin "http://localhost:8000" from accessing a frame with origin "http://127.0.0.1:8000". Protocols, domains, and ports must match.
+Test that code evaluated in the main frame cannot access $0 that resolves to a node in a frame from a different domain. Bug 105423.
+
+
+
+== Running test suite: CommandLineAPI.$0.cross-frame
+-- Running test case: AttemptCrossFrame$0AccessFromMainFrame
+Setting $0 to node within subframe.
+PASS: MainFrame access to $0 node in subframe should be null.
+
+-- Running test case: AttemptSameFrame$0AccessFromMainFrame
+Setting $0 to node within the main frame.
+PASS: MainFrame access to $0 node in main frame should be a node.
+
+-- Running test case: AttemptCrossFrame$0AccessFromSubFrame
+Setting $0 to node within the main frame.
+PASS: SubFrame access to $0 node in main frame should be null.
+
+-- Running test case: AttemptSameFrame$0AccessFromSubFrame
+Setting $0 to node within the subframe.
+PASS: SubFrame access to $0 node in sub frame should be a node.
+
diff --git a/LayoutTests/http/tests/inspector/console/cross-domain-inspected-node-access.html b/LayoutTests/http/tests/inspector/console/cross-domain-inspected-node-access.html
new file mode 100644
index 0000000..7963d4b
--- /dev/null
+++ b/LayoutTests/http/tests/inspector/console/cross-domain-inspected-node-access.html
@@ -0,0 +1,97 @@
+<!doctype html>
+<html>
+<head>
+<script type="text/javascript" src="../resources/inspector-test.js"></script>
+<script>
+function test()
+{
+    let suite = InspectorTest.createAsyncSuite("CommandLineAPI.$0.cross-frame");
+
+    let nodeInMainFrameId;
+    let nodeInSubFrameId;
+
+    suite.addTestCase({
+        name: "AttemptCrossFrame$0AccessFromMainFrame",
+        description: "Should not be able to access $0 node in different domain subframe from the main frame.",
+        test: (resolve, reject) => {
+            InspectorTest.log("Setting $0 to node within subframe.");
+            ConsoleAgent.addInspectedNode(nodeInSubFrameId);
+            RuntimeAgent.evaluate.invoke({expression: "$0", includeCommandLineAPI: true}, (error, remoteObjectPayload, wasThrown) => {    
+                InspectorTest.assert(!error, "Should not be a protocol error.");
+                InspectorTest.assert(!wasThrown, "Should not be an exception.");
+                let remoteObject = WebInspector.RemoteObject.fromPayload(remoteObjectPayload);
+                InspectorTest.expectThat(remoteObject.value === null, "MainFrame access to $0 node in subframe should be null.");
+                resolve();
+            });
+        }
+    });
+
+    suite.addTestCase({
+        name: "AttemptSameFrame$0AccessFromMainFrame",
+        description: "Should be able to access $0 node in the same frame.",
+        test: (resolve, reject) => {
+            InspectorTest.log("Setting $0 to node within the main frame.");
+            ConsoleAgent.addInspectedNode(nodeInMainFrameId);
+            RuntimeAgent.evaluate.invoke({expression: "$0", includeCommandLineAPI: true}, (error, remoteObjectPayload, wasThrown) => {
+                InspectorTest.assert(!error, "Should not be a protocol error.");
+                InspectorTest.assert(!wasThrown, "Should not be an exception.");
+                let remoteObject = WebInspector.RemoteObject.fromPayload(remoteObjectPayload);
+                InspectorTest.expectThat(remoteObject.isNode(), "MainFrame access to $0 node in main frame should be a node.");
+                resolve();
+            });
+        }
+    });
+
+    suite.addTestCase({
+        name: "AttemptCrossFrame$0AccessFromSubFrame",
+        description: "Should not be able to access $0 node in different domain main frame from the subframe.",
+        test: (resolve, reject) => {
+            InspectorTest.log("Setting $0 to node within the main frame.");
+            ConsoleAgent.addInspectedNode(nodeInMainFrameId);
+            const childFrame = WebInspector.frameResourceManager.mainFrame.childFrames[0];
+            RuntimeAgent.evaluate.invoke({expression: "$0", includeCommandLineAPI: true, contextId: childFrame.pageExecutionContext.id}, (error, remoteObjectPayload, wasThrown) => {    
+                InspectorTest.assert(!error, "Should not be a protocol error.");
+                InspectorTest.assert(!wasThrown, "Should not be an exception.");
+                let remoteObject = WebInspector.RemoteObject.fromPayload(remoteObjectPayload);
+                InspectorTest.expectThat(remoteObject.value === null, "SubFrame access to $0 node in main frame should be null.");
+                resolve();
+            });
+        }
+    });
+
+    suite.addTestCase({
+        name: "AttemptSameFrame$0AccessFromSubFrame",
+        description: "Should be able to access $0 node in the same frame.",
+        test: (resolve, reject) => {
+            InspectorTest.log("Setting $0 to node within the subframe.");
+            ConsoleAgent.addInspectedNode(nodeInSubFrameId);
+            const childFrame = WebInspector.frameResourceManager.mainFrame.childFrames[0];
+            RuntimeAgent.evaluate.invoke({expression: "$0", includeCommandLineAPI: true, contextId: childFrame.pageExecutionContext.id}, (error, remoteObjectPayload, wasThrown) => {
+                InspectorTest.assert(!error, "Should not be a protocol error.");
+                InspectorTest.assert(!wasThrown, "Should not be an exception.");
+                let remoteObject = WebInspector.RemoteObject.fromPayload(remoteObjectPayload);
+                InspectorTest.expectThat(remoteObject.isNode(), "SubFrame access to $0 node in sub frame should be a node.");
+                resolve();
+            });
+        }
+    });
+
+    WebInspector.domTreeManager.requestDocument((documentNode) => {
+        WebInspector.domTreeManager.querySelector(documentNode.id, "iframe#myframe", (nodeId) => {
+            let iframeNode = WebInspector.domTreeManager.nodeForId(nodeId);
+            let iframeDocumentNodeId = iframeNode.children[0].id;
+            WebInspector.domTreeManager.querySelector(iframeDocumentNodeId, "div#rootDiv", (nodeId) => {
+                nodeInMainFrameId = iframeNode.id;
+                nodeInSubFrameId = nodeId;
+                suite.runTestCasesAndFinish();
+            });
+        });
+    });
+}
+</script>
+</head>
+<body>
+<p>Test that code evaluated in the main frame cannot access $0 that resolves to a node in a frame from a different domain. <a href="https://bugs.webkit.org/show_bug.cgi?id=105423">Bug 105423.</a></p>
+<iframe id="myframe" src="http://localhost:8000/inspector/page/resources/test-page.html" onload="runTest()"></iframe>
+</body>
+</html>
diff --git a/Source/JavaScriptCore/CMakeLists.txt b/Source/JavaScriptCore/CMakeLists.txt
index dbc96cc..4e0922d 100644
--- a/Source/JavaScriptCore/CMakeLists.txt
+++ b/Source/JavaScriptCore/CMakeLists.txt
@@ -398,6 +398,7 @@
     inspector/JSJavaScriptCallFrame.cpp
     inspector/JSJavaScriptCallFramePrototype.cpp
     inspector/JavaScriptCallFrame.cpp
+    inspector/PerGlobalObjectWrapperWorld.cpp
     inspector/ScriptArguments.cpp
     inspector/ScriptCallFrame.cpp
     inspector/ScriptCallStack.cpp
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index c6e3b1f..4abe449 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,57 @@
+2015-11-09  Joseph Pecoraro  <pecoraro@apple.com>
+
+        Web Inspector: $0 stops working after navigating to a different domain
+        https://bugs.webkit.org/show_bug.cgi?id=147962
+
+        Reviewed by Brian Burg.
+
+        Extract the per-GlobalObject cache of JSValue wrappers for
+        InjectedScriptHost objects to be reused by WebCore for its
+        CommandLineAPIHost objects injected into multiple contexts.
+
+        * CMakeLists.txt:
+        * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
+        * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        Add new files.
+
+        * inspector/PerGlobalObjectWrapperWorld.h:
+        * inspector/PerGlobalObjectWrapperWorld.cpp:
+        (Inspector::PerGlobalObjectWrapperWorld::getWrapper):
+        (Inspector::PerGlobalObjectWrapperWorld::addWrapper):
+        (Inspector::PerGlobalObjectWrapperWorld::clearAllWrappers):
+        Hold a bunch of per-global-object wrappers for an object
+        that will outlive the global object. This inspector does this
+        for host objects that it exposes into scripts it injects into
+        each execution context created by the page.
+
+        * inspector/InjectedScriptHost.cpp:
+        (Inspector::InjectedScriptHost::wrapper):
+        (Inspector::InjectedScriptHost::clearAllWrappers):
+        (Inspector::InjectedScriptHost::jsWrapper): Deleted.
+        (Inspector::clearWrapperFromValue): Deleted.
+        (Inspector::InjectedScriptHost::clearWrapper): Deleted.
+        Extract and simplify the Per-GlobalObject wrapping into a class.
+        Simplify object construction as well.
+
+        * inspector/InjectedScriptHost.h:
+        * inspector/InjectedScriptManager.cpp:
+        (Inspector::InjectedScriptManager::createInjectedScript):
+        (Inspector::InjectedScriptManager::discardInjectedScripts):
+        Make discarding virtual so subclasses may also discard injected scripts.
+
+        * inspector/JSInjectedScriptHost.cpp:
+        (Inspector::JSInjectedScriptHost::JSInjectedScriptHost):
+        (Inspector::JSInjectedScriptHost::releaseImpl): Deleted.
+        (Inspector::JSInjectedScriptHost::~JSInjectedScriptHost): Deleted.
+        (Inspector::toJS): Deleted.
+        (Inspector::toJSInjectedScriptHost): Deleted.
+        * inspector/JSInjectedScriptHost.h:
+        (Inspector::JSInjectedScriptHost::create):
+        (Inspector::JSInjectedScriptHost::impl):
+        Update this code originally copied from older generated bindings to
+        be more like new generated bindings and remove some now unused code.
+
 2015-11-08  Filip Pizlo  <fpizlo@apple.com>
 
         B3 should be able to compile a program with a double constant
diff --git a/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj b/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj
index 57cbbba..c89aee2 100644
--- a/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj
+++ b/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj
@@ -608,6 +608,7 @@
     <ClCompile Include="..\inspector\JSJavaScriptCallFrame.cpp" />
     <ClCompile Include="..\inspector\JSJavaScriptCallFramePrototype.cpp" />
     <ClCompile Include="..\inspector\JavaScriptCallFrame.cpp" />
+    <ClCompile Include="..\inspector\PerGlobalObjectWrapperWorld.cpp" />
     <ClCompile Include="..\inspector\ScriptArguments.cpp" />
     <ClCompile Include="..\inspector\ScriptCallFrame.cpp" />
     <ClCompile Include="..\inspector\ScriptCallStack.cpp" />
@@ -1418,6 +1419,7 @@
     <ClInclude Include="..\inspector\JSJavaScriptCallFrame.h" />
     <ClInclude Include="..\inspector\JSJavaScriptCallFramePrototype.h" />
     <ClInclude Include="..\inspector\JavaScriptCallFrame.h" />
+    <ClInclude Include="..\inspector\PerGlobalObjectWrapperWorld.h" />
     <ClInclude Include="..\inspector\ScriptArguments.h" />
     <ClInclude Include="..\inspector\ScriptBreakpoint.h" />
     <ClInclude Include="..\inspector\ScriptCallFrame.h" />
diff --git a/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters b/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters
index fcab45f..e23cf23 100644
--- a/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters
+++ b/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters
@@ -1731,6 +1731,9 @@
     <ClCompile Include="..\inspector\JSJavaScriptCallFramePrototype.cpp">
       <Filter>inspector</Filter>
     </ClCompile>
+    <ClCompile Include="..\inspector\PerGlobalObjectWrapperWorld.cpp">
+      <Filter>inspector</Filter>
+    </ClCompile>
     <ClCompile Include="..\profiler\ProfilerJettisonReason.cpp">
       <Filter>profiler</Filter>
     </ClCompile>
@@ -4327,6 +4330,9 @@
     <ClInclude Include="..\inspector\JSJavaScriptCallFrame.h">
       <Filter>inspector</Filter>
     </ClInclude>
+    <ClInclude Include="..\inspector\PerGlobalObjectWrapperWorld.h">
+      <Filter>inspector</Filter>
+    </ClInclude>
     <ClInclude Include="..\runtime\JSCInlines.h">
       <Filter>runtime</Filter>
     </ClInclude>
diff --git a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
index 24401d1..31ce73f 100644
--- a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
+++ b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
@@ -1522,6 +1522,8 @@
 		A59455921824744700CC3843 /* JSGlobalObjectDebuggable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A59455901824744700CC3843 /* JSGlobalObjectDebuggable.cpp */; };
 		A59455931824744700CC3843 /* JSGlobalObjectDebuggable.h in Headers */ = {isa = PBXBuildFile; fileRef = A59455911824744700CC3843 /* JSGlobalObjectDebuggable.h */; };
 		A5945595182479EB00CC3843 /* InspectorFrontendChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = A5945594182479EB00CC3843 /* InspectorFrontendChannel.h */; settings = {ATTRIBUTES = (Private, ); }; };
+		A5AB49DC1BEC8082007020FB /* PerGlobalObjectWrapperWorld.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A5AB49DA1BEC8079007020FB /* PerGlobalObjectWrapperWorld.cpp */; };
+		A5AB49DD1BEC8086007020FB /* PerGlobalObjectWrapperWorld.h in Headers */ = {isa = PBXBuildFile; fileRef = A5AB49DB1BEC8079007020FB /* PerGlobalObjectWrapperWorld.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		A5B6A74D18C6DBA600F11E91 /* ConsoleClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A5B6A74C18C6DBA600F11E91 /* ConsoleClient.cpp */; };
 		A5BA15E8182340B300A82E69 /* RemoteInspector.h in Headers */ = {isa = PBXBuildFile; fileRef = A5BA15E1182340B300A82E69 /* RemoteInspector.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		A5BA15E9182340B300A82E69 /* RemoteInspector.mm in Sources */ = {isa = PBXBuildFile; fileRef = A5BA15E2182340B300A82E69 /* RemoteInspector.mm */; };
@@ -1942,8 +1944,8 @@
 		E49DC16D12EF295300184A1F /* SourceProviderCacheItem.h in Headers */ = {isa = PBXBuildFile; fileRef = E49DC14912EF261A00184A1F /* SourceProviderCacheItem.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		FE0D4A061AB8DD0A002F54BF /* ExecutionTimeLimitTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE0D4A041AB8DD0A002F54BF /* ExecutionTimeLimitTest.cpp */; };
 		FE0D4A091ABA2437002F54BF /* GlobalContextWithFinalizerTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE0D4A071ABA2437002F54BF /* GlobalContextWithFinalizerTest.cpp */; };
-		FE1220271BE7F58C0039E6F2 /* JITAddGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = FE1220261BE7F5640039E6F2 /* JITAddGenerator.h */; settings = {ASSET_TAGS = (); }; };
-		FE1220281BE7F5910039E6F2 /* JITAddGenerator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE1220251BE7F5640039E6F2 /* JITAddGenerator.cpp */; settings = {ASSET_TAGS = (); }; };
+		FE1220271BE7F58C0039E6F2 /* JITAddGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = FE1220261BE7F5640039E6F2 /* JITAddGenerator.h */; };
+		FE1220281BE7F5910039E6F2 /* JITAddGenerator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE1220251BE7F5640039E6F2 /* JITAddGenerator.cpp */; };
 		FE1C0FFD1B193E9800B53FCA /* Exception.h in Headers */ = {isa = PBXBuildFile; fileRef = FE1C0FFC1B193E9800B53FCA /* Exception.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		FE1C0FFF1B194FD100B53FCA /* Exception.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE1C0FFE1B194FD100B53FCA /* Exception.cpp */; };
 		FE20CE9D15F04A9500DF3430 /* LLIntCLoop.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE20CE9B15F04A9500DF3430 /* LLIntCLoop.cpp */; };
@@ -1955,7 +1957,7 @@
 		FE3913541B794F6E00EDAF71 /* LiveObjectList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE3913521B794AC900EDAF71 /* LiveObjectList.cpp */; };
 		FE3913551B794F8A00EDAF71 /* LiveObjectData.h in Headers */ = {isa = PBXBuildFile; fileRef = FE3913511B794AC900EDAF71 /* LiveObjectData.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		FE3913561B794F8F00EDAF71 /* LiveObjectList.h in Headers */ = {isa = PBXBuildFile; fileRef = FE3913531B794AC900EDAF71 /* LiveObjectList.h */; settings = {ATTRIBUTES = (Private, ); }; };
-		FE4238901BE18C3C00514737 /* JITSubGenerator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE42388F1BE18C1200514737 /* JITSubGenerator.cpp */; settings = {ASSET_TAGS = (); }; };
+		FE4238901BE18C3C00514737 /* JITSubGenerator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE42388F1BE18C1200514737 /* JITSubGenerator.cpp */; };
 		FE4BFF2B1AD476E700088F87 /* FunctionOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE4BFF291AD476E700088F87 /* FunctionOverrides.cpp */; };
 		FE4BFF2C1AD476E700088F87 /* FunctionOverrides.h in Headers */ = {isa = PBXBuildFile; fileRef = FE4BFF2A1AD476E700088F87 /* FunctionOverrides.h */; };
 		FE4D55B81AE716CA0052E459 /* IterationStatus.h in Headers */ = {isa = PBXBuildFile; fileRef = FE4D55B71AE716CA0052E459 /* IterationStatus.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -3604,6 +3606,8 @@
 		A59455901824744700CC3843 /* JSGlobalObjectDebuggable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSGlobalObjectDebuggable.cpp; sourceTree = "<group>"; };
 		A59455911824744700CC3843 /* JSGlobalObjectDebuggable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSGlobalObjectDebuggable.h; sourceTree = "<group>"; };
 		A5945594182479EB00CC3843 /* InspectorFrontendChannel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InspectorFrontendChannel.h; sourceTree = "<group>"; };
+		A5AB49DA1BEC8079007020FB /* PerGlobalObjectWrapperWorld.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PerGlobalObjectWrapperWorld.cpp; sourceTree = "<group>"; };
+		A5AB49DB1BEC8079007020FB /* PerGlobalObjectWrapperWorld.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PerGlobalObjectWrapperWorld.h; sourceTree = "<group>"; };
 		A5B6A74C18C6DBA600F11E91 /* ConsoleClient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ConsoleClient.cpp; sourceTree = "<group>"; };
 		A5BA15E1182340B300A82E69 /* RemoteInspector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RemoteInspector.h; sourceTree = "<group>"; };
 		A5BA15E2182340B300A82E69 /* RemoteInspector.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RemoteInspector.mm; sourceTree = "<group>"; };
@@ -6429,6 +6433,8 @@
 				A503FA16188E0FB000110F14 /* JSJavaScriptCallFrame.h */,
 				A503FA17188E0FB000110F14 /* JSJavaScriptCallFramePrototype.cpp */,
 				A503FA18188E0FB000110F14 /* JSJavaScriptCallFramePrototype.h */,
+				A5AB49DA1BEC8079007020FB /* PerGlobalObjectWrapperWorld.cpp */,
+				A5AB49DB1BEC8079007020FB /* PerGlobalObjectWrapperWorld.h */,
 				A5FD0065189AFE9C00633231 /* ScriptArguments.cpp */,
 				A5FD0066189AFE9C00633231 /* ScriptArguments.h */,
 				A503FA1F188EFF6800110F14 /* ScriptBreakpoint.h */,
@@ -7231,6 +7237,7 @@
 				BC18C41E0E16F5CD00B34460 /* JSContextRef.h in Headers */,
 				A5EA70EE19F5B5C40098F5EC /* JSContextRefInspectorSupport.h in Headers */,
 				A5D2E665195E174000A518E7 /* JSContextRefInternal.h in Headers */,
+				A5AB49DD1BEC8086007020FB /* PerGlobalObjectWrapperWorld.h in Headers */,
 				148CD1D8108CF902008163C6 /* JSContextRefPrivate.h in Headers */,
 				A72028B81797601E0098028C /* JSCTestRunnerUtils.h in Headers */,
 				0F2B66EC17B6B5AB00A7AE3F /* JSDataView.h in Headers */,
@@ -8720,6 +8727,7 @@
 				A7E5AB371799E4B200D2833D /* LLVMDisassembler.cpp in Sources */,
 				14469DDE107EC7E700650446 /* Lookup.cpp in Sources */,
 				0F4680CC14BBB17A00BFE272 /* LowLevelInterpreter.cpp in Sources */,
+				A5AB49DC1BEC8082007020FB /* PerGlobalObjectWrapperWorld.cpp in Sources */,
 				14B723B212D7DA46003BD5ED /* MachineStackMarker.cpp in Sources */,
 				0FEB3ECF16237F6C00AB67AD /* MacroAssembler.cpp in Sources */,
 				86C568E011A213EE0007F7F0 /* MacroAssemblerARM.cpp in Sources */,
diff --git a/Source/JavaScriptCore/inspector/InjectedScriptHost.cpp b/Source/JavaScriptCore/inspector/InjectedScriptHost.cpp
index 6351472..657ba19 100644
--- a/Source/JavaScriptCore/inspector/InjectedScriptHost.cpp
+++ b/Source/JavaScriptCore/inspector/InjectedScriptHost.cpp
@@ -26,6 +26,7 @@
 #include "config.h"
 #include "InjectedScriptHost.h"
 
+#include "JSCInlines.h"
 #include "JSInjectedScriptHost.h"
 
 using namespace JSC;
@@ -36,44 +37,23 @@
 {
 }
 
-JSValue InjectedScriptHost::jsWrapper(ExecState* exec, JSGlobalObject* globalObject)
+JSValue InjectedScriptHost::wrapper(ExecState* exec, JSGlobalObject* globalObject)
 {
-    auto key = std::make_pair(exec, globalObject);
-    auto it = m_wrappers.find(key);
-    if (it != m_wrappers.end())
-        return it->value.get();
+    JSValue value = m_wrappers.getWrapper(globalObject);
+    if (value)
+        return value;
 
-    JSValue jsValue = toJS(exec, globalObject, this);
-    if (!jsValue.isObject())
-        return jsValue;
+    JSObject* prototype = JSInjectedScriptHost::createPrototype(exec->vm(), globalObject);
+    Structure* structure = JSInjectedScriptHost::createStructure(exec->vm(), globalObject, prototype);
+    JSInjectedScriptHost* injectedScriptHost = JSInjectedScriptHost::create(exec->vm(), structure, Ref<InjectedScriptHost>(*this));
+    m_wrappers.addWrapper(globalObject, injectedScriptHost);
 
-    JSObject* jsObject = jsValue.toObject(exec, globalObject);
-    Strong<JSObject> wrapper(exec->vm(), jsObject);
-    m_wrappers.add(key, wrapper);
-
-    return jsValue;
-}
-
-static void clearWrapperFromValue(JSValue value)
-{
-    JSInjectedScriptHost* jsInjectedScriptHost = toJSInjectedScriptHost(value);
-    ASSERT(jsInjectedScriptHost);
-    if (jsInjectedScriptHost)
-        jsInjectedScriptHost->releaseImpl();
-}
-
-void InjectedScriptHost::clearWrapper(ExecState* exec, JSGlobalObject* globalObject)
-{
-    auto key = std::make_pair(exec, globalObject);
-    clearWrapperFromValue(m_wrappers.take(key).get());
+    return injectedScriptHost;
 }
 
 void InjectedScriptHost::clearAllWrappers()
 {
-    for (auto& wrapper : m_wrappers)
-        clearWrapperFromValue(wrapper.value.get());
-
-    m_wrappers.clear();
+    m_wrappers.clearAllWrappers();
 }
 
 } // namespace Inspector
diff --git a/Source/JavaScriptCore/inspector/InjectedScriptHost.h b/Source/JavaScriptCore/inspector/InjectedScriptHost.h
index ae530a2..27de142 100644
--- a/Source/JavaScriptCore/inspector/InjectedScriptHost.h
+++ b/Source/JavaScriptCore/inspector/InjectedScriptHost.h
@@ -27,8 +27,7 @@
 #define InjectedScriptHost_h
 
 #include "JSCJSValueInlines.h"
-#include "Strong.h"
-#include "StrongInlines.h"
+#include "inspector/PerGlobalObjectWrapperWorld.h"
 #include <wtf/HashMap.h>
 #include <wtf/RefCounted.h>
 
@@ -42,12 +41,11 @@
     virtual JSC::JSValue subtype(JSC::ExecState*, JSC::JSValue) { return JSC::jsUndefined(); }
     virtual bool isHTMLAllCollection(JSC::JSValue) { return false; }
 
-    JSC::JSValue jsWrapper(JSC::ExecState*, JSC::JSGlobalObject*);
-    void clearWrapper(JSC::ExecState*, JSC::JSGlobalObject*);
+    JSC::JSValue wrapper(JSC::ExecState*, JSC::JSGlobalObject*);
     void clearAllWrappers();
 
 private:
-    HashMap<std::pair<JSC::ExecState*, JSC::JSGlobalObject*>, JSC::Strong<JSC::JSObject>> m_wrappers;
+    PerGlobalObjectWrapperWorld m_wrappers;
 };
 
 } // namespace Inspector
diff --git a/Source/JavaScriptCore/inspector/InjectedScriptManager.cpp b/Source/JavaScriptCore/inspector/InjectedScriptManager.cpp
index e25f9e3..812ad3d 100644
--- a/Source/JavaScriptCore/inspector/InjectedScriptManager.cpp
+++ b/Source/JavaScriptCore/inspector/InjectedScriptManager.cpp
@@ -60,6 +60,13 @@
     discardInjectedScripts();
 }
 
+void InjectedScriptManager::discardInjectedScripts()
+{
+    m_injectedScriptHost->clearAllWrappers();
+    m_idToInjectedScript.clear();
+    m_scriptStateToId.clear();
+}
+
 InjectedScriptHost* InjectedScriptManager::injectedScriptHost()
 {
     return m_injectedScriptHost.get();
@@ -107,13 +114,6 @@
     return m_idToInjectedScript.get(injectedScriptId);
 }
 
-void InjectedScriptManager::discardInjectedScripts()
-{
-    m_injectedScriptHost->clearAllWrappers();
-    m_idToInjectedScript.clear();
-    m_scriptStateToId.clear();
-}
-
 void InjectedScriptManager::releaseObjectGroup(const String& objectGroup)
 {
     for (auto& injectedScript : m_idToInjectedScript.values())
@@ -151,7 +151,7 @@
         return Deprecated::ScriptObject();
 
     MarkedArgumentBuffer args;
-    args.append(m_injectedScriptHost->jsWrapper(scriptState, globalObject));
+    args.append(m_injectedScriptHost->wrapper(scriptState, globalObject));
     args.append(globalThisValue);
     args.append(jsNumber(id));
 
diff --git a/Source/JavaScriptCore/inspector/InjectedScriptManager.h b/Source/JavaScriptCore/inspector/InjectedScriptManager.h
index 4a39db3..e18d7cd 100644
--- a/Source/JavaScriptCore/inspector/InjectedScriptManager.h
+++ b/Source/JavaScriptCore/inspector/InjectedScriptManager.h
@@ -54,6 +54,7 @@
     virtual ~InjectedScriptManager();
 
     virtual void disconnect();
+    virtual void discardInjectedScripts();
 
     InjectedScriptHost* injectedScriptHost();
     InspectorEnvironment& inspectorEnvironment() const { return m_environment; }
@@ -62,7 +63,6 @@
     InjectedScript injectedScriptForId(int);
     int injectedScriptIdFor(JSC::ExecState*);
     InjectedScript injectedScriptForObjectId(const String& objectId);
-    void discardInjectedScripts();
     void releaseObjectGroup(const String& objectGroup);
     void clearExceptionValue();
 
diff --git a/Source/JavaScriptCore/inspector/JSInjectedScriptHost.cpp b/Source/JavaScriptCore/inspector/JSInjectedScriptHost.cpp
index 420df7e..8b5fc56 100644
--- a/Source/JavaScriptCore/inspector/JSInjectedScriptHost.cpp
+++ b/Source/JavaScriptCore/inspector/JSInjectedScriptHost.cpp
@@ -60,9 +60,9 @@
 
 const ClassInfo JSInjectedScriptHost::s_info = { "InjectedScriptHost", &Base::s_info, 0, CREATE_METHOD_TABLE(JSInjectedScriptHost) };
 
-JSInjectedScriptHost::JSInjectedScriptHost(VM& vm, Structure* structure, PassRefPtr<InjectedScriptHost> impl)
+JSInjectedScriptHost::JSInjectedScriptHost(VM& vm, Structure* structure, Ref<InjectedScriptHost>&& impl)
     : JSDestructibleObject(vm, structure)
-    , m_impl(impl.leakRef())
+    , m_wrapped(WTF::move(impl))
 {
 }
 
@@ -83,17 +83,6 @@
     thisObject->JSInjectedScriptHost::~JSInjectedScriptHost();
 }
 
-void JSInjectedScriptHost::releaseImpl()
-{
-    if (auto impl = std::exchange(m_impl, nullptr))
-        impl->deref();
-}
-
-JSInjectedScriptHost::~JSInjectedScriptHost()
-{
-    releaseImpl();
-}
-
 JSValue JSInjectedScriptHost::evaluate(ExecState* exec) const
 {
     JSGlobalObject* globalObject = exec->lexicalGlobalObject();
@@ -484,21 +473,4 @@
     return array;
 }
 
-JSValue toJS(ExecState* exec, JSGlobalObject* globalObject, InjectedScriptHost* impl)
-{
-    if (!impl)
-        return jsNull();
-
-    JSObject* prototype = JSInjectedScriptHost::createPrototype(exec->vm(), globalObject);
-    Structure* structure = JSInjectedScriptHost::createStructure(exec->vm(), globalObject, prototype);
-    JSInjectedScriptHost* injectedScriptHost = JSInjectedScriptHost::create(exec->vm(), structure, impl);
-
-    return injectedScriptHost;
-}
-
-JSInjectedScriptHost* toJSInjectedScriptHost(JSValue value)
-{
-    return value.inherits(JSInjectedScriptHost::info()) ? jsCast<JSInjectedScriptHost*>(value) : nullptr;
-}
-
 } // namespace Inspector
diff --git a/Source/JavaScriptCore/inspector/JSInjectedScriptHost.h b/Source/JavaScriptCore/inspector/JSInjectedScriptHost.h
index 8689151..c7e16d2 100644
--- a/Source/JavaScriptCore/inspector/JSInjectedScriptHost.h
+++ b/Source/JavaScriptCore/inspector/JSInjectedScriptHost.h
@@ -28,10 +28,6 @@
 
 #include "JSDestructibleObject.h"
 
-namespace JSC {
-class WeakMapData;
-}
-
 namespace Inspector {
 
 class InjectedScriptHost;
@@ -48,9 +44,9 @@
         return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
     }
 
-    static JSInjectedScriptHost* create(JSC::VM& vm, JSC::Structure* structure, PassRefPtr<InjectedScriptHost> impl)
+    static JSInjectedScriptHost* create(JSC::VM& vm, JSC::Structure* structure, Ref<InjectedScriptHost>&& impl)
     {
-        JSInjectedScriptHost* instance = new (NotNull, JSC::allocateCell<JSInjectedScriptHost>(vm.heap)) JSInjectedScriptHost(vm, structure, impl);
+        JSInjectedScriptHost* instance = new (NotNull, JSC::allocateCell<JSInjectedScriptHost>(vm.heap)) JSInjectedScriptHost(vm, structure, WTF::move(impl));
         instance->finishCreation(vm);
         return instance;
     }
@@ -58,8 +54,7 @@
     static JSC::JSObject* createPrototype(JSC::VM&, JSC::JSGlobalObject*);
     static void destroy(JSC::JSCell*);
 
-    InjectedScriptHost& impl() const { return *m_impl; }
-    void releaseImpl();
+    InjectedScriptHost& impl() const { return const_cast<InjectedScriptHost&>(m_wrapped.get()); }
 
     // Attributes.
     JSC::JSValue evaluate(JSC::ExecState*) const;
@@ -80,15 +75,11 @@
     void finishCreation(JSC::VM&);
 
 private:
-    JSInjectedScriptHost(JSC::VM&, JSC::Structure*, PassRefPtr<InjectedScriptHost>);
-    ~JSInjectedScriptHost();
+    JSInjectedScriptHost(JSC::VM&, JSC::Structure*, Ref<InjectedScriptHost>&&);
 
-    InjectedScriptHost* m_impl;
+    Ref<InjectedScriptHost> m_wrapped;
 };
 
-JSC::JSValue toJS(JSC::ExecState*, JSC::JSGlobalObject*, InjectedScriptHost*);
-JSInjectedScriptHost* toJSInjectedScriptHost(JSC::JSValue);
-
 } // namespace Inspector
 
 #endif // !defined(JSInjectedScriptHost_h)
diff --git a/Source/JavaScriptCore/inspector/PerGlobalObjectWrapperWorld.cpp b/Source/JavaScriptCore/inspector/PerGlobalObjectWrapperWorld.cpp
new file mode 100644
index 0000000..c9b7abd
--- /dev/null
+++ b/Source/JavaScriptCore/inspector/PerGlobalObjectWrapperWorld.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2013 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. ``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
+ * 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.
+ */
+
+#include "config.h"
+#include "PerGlobalObjectWrapperWorld.h"
+
+using namespace JSC;
+
+namespace Inspector {
+
+JSValue PerGlobalObjectWrapperWorld::getWrapper(JSGlobalObject* globalObject)
+{
+    auto it = m_wrappers.find(globalObject);
+    if (it != m_wrappers.end())
+        return it->value.get();
+    return JSValue();
+}
+
+void PerGlobalObjectWrapperWorld::addWrapper(JSGlobalObject* globalObject, JSObject* object)
+{
+    Strong<JSObject> wrapper(globalObject->vm(), object);
+    m_wrappers.add(globalObject, wrapper);
+}
+
+void PerGlobalObjectWrapperWorld::clearAllWrappers()
+{
+    m_wrappers.clear();
+}
+
+} // namespace Inspector
diff --git a/Source/JavaScriptCore/inspector/PerGlobalObjectWrapperWorld.h b/Source/JavaScriptCore/inspector/PerGlobalObjectWrapperWorld.h
new file mode 100644
index 0000000..bdd7db0
--- /dev/null
+++ b/Source/JavaScriptCore/inspector/PerGlobalObjectWrapperWorld.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2015 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. ``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
+ * 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.
+ */
+
+#ifndef PerGlobalObjectWrapperWorld_h
+#define PerGlobalObjectWrapperWorld_h
+
+#include "JSCJSValueInlines.h"
+#include "Strong.h"
+#include "StrongInlines.h"
+#include <wtf/HashMap.h>
+
+namespace Inspector {
+
+class JS_EXPORT_PRIVATE PerGlobalObjectWrapperWorld {
+public:
+    JSC::JSValue getWrapper(JSC::JSGlobalObject*);
+    void addWrapper(JSC::JSGlobalObject*, JSC::JSObject*);
+    void clearAllWrappers();
+
+private:
+    HashMap<JSC::JSGlobalObject*, JSC::Strong<JSC::JSObject>> m_wrappers;
+};
+
+} // namespace Inspector
+
+#endif // !defined(PerGlobalObjectWrapperWorld_h)
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 0eafbfb..f311a3d 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,61 @@
+2015-11-09  Joseph Pecoraro  <pecoraro@apple.com>
+
+        Web Inspector: $0 stops working after navigating to a different domain
+        https://bugs.webkit.org/show_bug.cgi?id=147962
+
+        Reviewed by Brian Burg.
+
+        Test: http/tests/inspector/console/cross-domain-inspected-node-access.html
+
+        The inspector backend injects the CommandLineAPI Source with a
+        corresponding CommandLineAPIHost into each execution context
+        created by the page (main frame, sub frames, etc).
+
+        When creating the JSValue wrapper for the CommandLineAPIHost using
+        the generated toJS(...) DOM bindings, we were using the cached
+        CommandLineAPIHost wrapper values in the single DOMWrapperWorld shared
+        across all frames. This meant that the first time the wrapper was
+        needed it was created in context A. But when needed for context B
+        it was using the wrapper created in context A. Using this wrapper
+        in context B was producing unexpected cross-origin warnings.
+
+        The solution taken here, is to create a new JSValue wrapper for
+        the CommandLineAPIHost per execution context. This way each time
+        the CommandLineAPIHost wrapper is used in a frame, it is using
+        the one created for that frame.
+
+        The C++ host object being wrapped has a lifetime equivalent to
+        the Page. It does not change in this patch. The wrapper values
+        are cleared on page navigation or when the page is closed, and
+        will be garbage collected.
+
+        * WebCore.vcxproj/WebCore.vcxproj:
+        * WebCore.vcxproj/WebCore.vcxproj.filters:
+        * ForwardingHeaders/inspector/PerGlobalObjectWrapperWorld.h: Added.
+        New forwarding header.
+
+        * inspector/CommandLineAPIHost.h:
+        * inspector/CommandLineAPIHost.cpp:
+        (WebCore::CommandLineAPIHost::CommandLineAPIHost):
+        (WebCore::CommandLineAPIHost::wrapper):
+        Cached JSValue wrappers per GlobalObject.
+
+        (WebCore::CommandLineAPIHost::clearAllWrappers):
+        Clear any wrappers we have, including the $0 value itself
+        which we weren't explicitly clearing previously.
+
+        * inspector/CommandLineAPIModule.cpp:
+        (WebCore::CommandLineAPIModule::host):
+        Simplify creating the wrapper.
+
+        * inspector/WebInjectedScriptManager.h:
+        * inspector/WebInjectedScriptManager.cpp:
+        (WebCore::WebInjectedScriptManager::discardInjectedScripts):
+        When the main frame window object clears, also clear the
+        CommandLineAPI wrappers we may have created. Also take this
+        opportunity to clear any $0 value that may have pointed
+        to a value in the previous page.
+
 2015-11-09  Per Arne Vollan  <peavo@outlook.com>
 
         [WinCairo][Video][MediaFoundation] Video should be rendered in provided graphics context.
diff --git a/Source/WebCore/ForwardingHeaders/inspector/PerGlobalObjectWrapperWorld.h b/Source/WebCore/ForwardingHeaders/inspector/PerGlobalObjectWrapperWorld.h
new file mode 100644
index 0000000..ee94720
--- /dev/null
+++ b/Source/WebCore/ForwardingHeaders/inspector/PerGlobalObjectWrapperWorld.h
@@ -0,0 +1,4 @@
+#ifndef WebCore_FWD_PerGlobalObjectWrapperWorld_h
+#define WebCore_FWD_PerGlobalObjectWrapperWorld_h
+#include <JavaScriptCore/PerGlobalObjectWrapperWorld.h>
+#endif
diff --git a/Source/WebCore/WebCore.vcxproj/WebCore.vcxproj b/Source/WebCore/WebCore.vcxproj/WebCore.vcxproj
index 782f1a3..2116117 100644
--- a/Source/WebCore/WebCore.vcxproj/WebCore.vcxproj
+++ b/Source/WebCore/WebCore.vcxproj/WebCore.vcxproj
@@ -20435,6 +20435,7 @@
     <ClInclude Include="..\ForwardingHeaders\inspector\InspectorProtocolObjects.h" />
     <ClInclude Include="..\ForwardingHeaders\inspector\InspectorProtocolTypes.h" />
     <ClInclude Include="..\ForwardingHeaders\inspector\InspectorValues.h" />
+    <ClInclude Include="..\ForwardingHeaders\inspector\PerGlobalObjectWrapperWorld.h" />
     <ClInclude Include="..\ForwardingHeaders\inspector\ScriptArguments.h" />
     <ClInclude Include="..\ForwardingHeaders\inspector\ScriptBreakpoint.h" />
     <ClInclude Include="..\ForwardingHeaders\inspector\ScriptCallFrame.h" />
diff --git a/Source/WebCore/WebCore.vcxproj/WebCore.vcxproj.filters b/Source/WebCore/WebCore.vcxproj/WebCore.vcxproj.filters
index 2e41190..666d1e6 100644
--- a/Source/WebCore/WebCore.vcxproj/WebCore.vcxproj.filters
+++ b/Source/WebCore/WebCore.vcxproj/WebCore.vcxproj.filters
@@ -11782,6 +11782,9 @@
     <ClInclude Include="..\ForwardingHeaders\inspector\InspectorValues.h">
       <Filter>ForwardingHeaders\inspector</Filter>
     </ClInclude>
+    <ClInclude Include="..\ForwardingHeaders\inspector\PerGlobalObjectWrapperWorld.h">
+      <Filter>ForwardingHeaders\inspector</Filter>
+    </ClInclude>
     <ClInclude Include="..\ForwardingHeaders\inspector\ScriptArguments.h">
       <Filter>ForwardingHeaders\inspector</Filter>
     </ClInclude>
diff --git a/Source/WebCore/inspector/CommandLineAPIHost.cpp b/Source/WebCore/inspector/CommandLineAPIHost.cpp
index c1f1fab..0a12e6c 100644
--- a/Source/WebCore/inspector/CommandLineAPIHost.cpp
+++ b/Source/WebCore/inspector/CommandLineAPIHost.cpp
@@ -32,24 +32,22 @@
 #include "CommandLineAPIHost.h"
 
 #include "Database.h"
-#include "Element.h"
-#include "Frame.h"
-#include "FrameLoader.h"
-#include "HTMLFrameOwnerElement.h"
 #include "InspectorDOMAgent.h"
 #include "InspectorDOMStorageAgent.h"
 #include "InspectorDatabaseAgent.h"
-#include <inspector/InspectorFrontendDispatchers.h>
+#include "JSCommandLineAPIHost.h"
+#include "JSDOMGlobalObject.h"
 #include "Pasteboard.h"
 #include "Storage.h"
-#include "markup.h"
 #include <bindings/ScriptValue.h>
 #include <inspector/InspectorValues.h>
 #include <inspector/agents/InspectorAgent.h>
 #include <inspector/agents/InspectorConsoleAgent.h>
+#include <runtime/JSCInlines.h>
 #include <wtf/RefPtr.h>
 #include <wtf/StdLibExtras.h>
 
+using namespace JSC;
 using namespace Inspector;
 
 namespace WebCore {
@@ -60,13 +58,8 @@
 }
 
 CommandLineAPIHost::CommandLineAPIHost()
-    : m_inspectorAgent(nullptr)
-    , m_consoleAgent(nullptr)
-    , m_domAgent(nullptr)
-    , m_domStorageAgent(nullptr)
-    , m_databaseAgent(nullptr)
+    : m_inspectedObject(std::make_unique<InspectableObject>())
 {
-    m_inspectedObject = std::make_unique<InspectableObject>();
 }
 
 CommandLineAPIHost::~CommandLineAPIHost()
@@ -143,4 +136,24 @@
     return String();
 }
 
+JSValue CommandLineAPIHost::wrapper(ExecState* exec, JSDOMGlobalObject* globalObject)
+{
+    JSValue value = m_wrappers.getWrapper(globalObject);
+    if (value)
+        return value;
+
+    JSObject* prototype = JSCommandLineAPIHost::createPrototype(exec->vm(), globalObject);
+    Structure* structure = JSCommandLineAPIHost::createStructure(exec->vm(), globalObject, prototype);
+    JSCommandLineAPIHost* commandLineAPIHost = JSCommandLineAPIHost::create(structure, globalObject, Ref<CommandLineAPIHost>(*this));
+    m_wrappers.addWrapper(globalObject, commandLineAPIHost);
+
+    return commandLineAPIHost;
+}
+
+void CommandLineAPIHost::clearAllWrappers()
+{
+    m_wrappers.clearAllWrappers();
+    m_inspectedObject = std::make_unique<InspectableObject>();
+}
+
 } // namespace WebCore
diff --git a/Source/WebCore/inspector/CommandLineAPIHost.h b/Source/WebCore/inspector/CommandLineAPIHost.h
index b1231ae..7bfca53 100644
--- a/Source/WebCore/inspector/CommandLineAPIHost.h
+++ b/Source/WebCore/inspector/CommandLineAPIHost.h
@@ -31,7 +31,7 @@
 #define CommandLineAPIHost_h
 
 #include "ScriptState.h"
-#include <runtime/ConsoleTypes.h>
+#include <inspector/PerGlobalObjectWrapperWorld.h>
 #include <wtf/RefCounted.h>
 #include <wtf/Vector.h>
 #include <wtf/text/WTFString.h>
@@ -40,6 +40,10 @@
 class ScriptValue;
 }
 
+namespace JSC {
+class JSValue;
+}
+
 namespace Inspector {
 class InspectorAgent;
 class InspectorConsoleAgent;
@@ -53,6 +57,7 @@
 class InspectorDOMAgent;
 class InspectorDOMStorageAgent;
 class InspectorDatabaseAgent;
+class JSDOMGlobalObject;
 class Node;
 class Storage;
 
@@ -97,16 +102,20 @@
     String databaseIdImpl(Database*);
     String storageIdImpl(Storage*);
 
+    JSC::JSValue wrapper(JSC::ExecState*, JSDOMGlobalObject*);
+    void clearAllWrappers();
+
 private:
     CommandLineAPIHost();
 
-    Inspector::InspectorAgent* m_inspectorAgent;
-    Inspector::InspectorConsoleAgent* m_consoleAgent;
-    InspectorDOMAgent* m_domAgent;
-    InspectorDOMStorageAgent* m_domStorageAgent;
-    InspectorDatabaseAgent* m_databaseAgent;
+    Inspector::InspectorAgent* m_inspectorAgent {nullptr};
+    Inspector::InspectorConsoleAgent* m_consoleAgent {nullptr};
+    InspectorDOMAgent* m_domAgent {nullptr};
+    InspectorDOMStorageAgent* m_domStorageAgent {nullptr};
+    InspectorDatabaseAgent* m_databaseAgent {nullptr};
 
     std::unique_ptr<InspectableObject> m_inspectedObject; // $0
+    Inspector::PerGlobalObjectWrapperWorld m_wrappers;
 };
 
 } // namespace WebCore
diff --git a/Source/WebCore/inspector/CommandLineAPIModule.cpp b/Source/WebCore/inspector/CommandLineAPIModule.cpp
index a9d621d..de82a76 100644
--- a/Source/WebCore/inspector/CommandLineAPIModule.cpp
+++ b/Source/WebCore/inspector/CommandLineAPIModule.cpp
@@ -27,8 +27,7 @@
 #include "CommandLineAPIModule.h"
 
 #include "CommandLineAPIModuleSource.h"
-#include "DOMWrapperWorld.h"
-#include "JSCommandLineAPIHost.h"
+#include "JSDOMGlobalObject.h"
 #include "WebInjectedScriptManager.h"
 #include <inspector/InjectedScript.h>
 
@@ -53,13 +52,14 @@
     return StringImpl::createWithoutCopying(CommandLineAPIModuleSource_js, sizeof(CommandLineAPIModuleSource_js));
 }
 
-JSC::JSValue CommandLineAPIModule::host(InjectedScriptManager* injectedScriptManager, JSC::ExecState* exec) const
+JSValue CommandLineAPIModule::host(InjectedScriptManager* injectedScriptManager, ExecState* exec) const
 {
     // CommandLineAPIModule should only ever be used by a WebInjectedScriptManager.
     WebInjectedScriptManager* pageInjectedScriptManager = static_cast<WebInjectedScriptManager*>(injectedScriptManager);
     ASSERT(pageInjectedScriptManager->commandLineAPIHost());
+
     JSDOMGlobalObject* globalObject = jsCast<JSDOMGlobalObject*>(exec->lexicalGlobalObject());
-    return toJS(exec, globalObject, pageInjectedScriptManager->commandLineAPIHost());
+    return pageInjectedScriptManager->commandLineAPIHost()->wrapper(exec, globalObject);
 }
 
 } // namespace WebCore
diff --git a/Source/WebCore/inspector/WebInjectedScriptManager.cpp b/Source/WebCore/inspector/WebInjectedScriptManager.cpp
index 1813756..7bf9b7e 100644
--- a/Source/WebCore/inspector/WebInjectedScriptManager.cpp
+++ b/Source/WebCore/inspector/WebInjectedScriptManager.cpp
@@ -47,6 +47,13 @@
     m_commandLineAPIHost = nullptr;
 }
 
+void WebInjectedScriptManager::discardInjectedScripts()
+{
+    InjectedScriptManager::discardInjectedScripts();
+
+    m_commandLineAPIHost->clearAllWrappers();
+}
+
 void WebInjectedScriptManager::didCreateInjectedScript(const Inspector::InjectedScript& injectedScript)
 {
     CommandLineAPIModule::injectIfNeeded(this, injectedScript);
diff --git a/Source/WebCore/inspector/WebInjectedScriptManager.h b/Source/WebCore/inspector/WebInjectedScriptManager.h
index 2b5f077..305dbe4 100644
--- a/Source/WebCore/inspector/WebInjectedScriptManager.h
+++ b/Source/WebCore/inspector/WebInjectedScriptManager.h
@@ -42,6 +42,7 @@
     CommandLineAPIHost* commandLineAPIHost() const { return m_commandLineAPIHost.get(); }
 
     virtual void disconnect() override;
+    virtual void discardInjectedScripts() override;
 
     void discardInjectedScriptsFor(DOMWindow*);