Title: [200533] trunk
Revision
200533
Author
commit-qu...@webkit.org
Date
2016-05-06 16:32:28 -0700 (Fri, 06 May 2016)

Log Message

Web Inspector: Console: Variables defined with let/const aren't accessible outside of console's scope
https://bugs.webkit.org/show_bug.cgi?id=150752
<rdar://problem/23343385>

Patch by Joseph Pecoraro <pecor...@apple.com> on 2016-05-06
Reviewed by Mark Lam.

Source/_javascript_Core:

This approach allows Web Inspector to hang a "Scope Extension", a
WithObjectScope, off the GlobalObject. When resolving identifiers
in fails to resolve anything in the normal scope chain, consult
the scope extension.

This allows us to eliminate the `with (commandLineAPI) { ... }`
block in global console evaluations, and instead makes it a full
program evaluation, with the commandLineAPI available and safely
shadowed by actual variables as expected.

* inspector/InjectedScriptSource.js:
(InjectedScript.prototype._evaluateOn):
Use the new evaluateWithScopeExtension and provide the CommandLineAPI
object as the scope extension object.

(BasicCommandLineAPI):
(BasicCommandLineAPI.inScopeVariables): Deleted.
Simplify now that we don't need to check for variable shadowing ourselves.

* inspector/JSInjectedScriptHost.cpp:
(Inspector::JSInjectedScriptHost::evaluateWithScopeExtension):
* inspector/JSInjectedScriptHost.h:
* inspector/JSInjectedScriptHostPrototype.cpp:
(Inspector::JSInjectedScriptHostPrototype::finishCreation):
(Inspector::jsInjectedScriptHostPrototypeFunctionEvaluateWithScopeExtension):
Provide a new InjectedScriptHost method to evaluate a program
with a scope extension.

* runtime/Completion.cpp:
(JSC::evaluateWithScopeExtension):
* runtime/Completion.h:
General JSC::evaluate function to evaluate a program with a scope extension.

* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::setGlobalScopeExtension):
(JSC::JSGlobalObject::clearGlobalScopeExtension):
(JSC::JSGlobalObject::visitChildren):
* runtime/JSGlobalObject.h:
(JSC::JSGlobalObject::globalScopeExtension):
Hang a scope extension off the global object.

* runtime/JSScope.cpp:
(JSC::JSScope::resolve):
Consult the scope extension when resolve fails to find anything normally.

Source/WebCore:

Test: inspector/runtime/evaluate-CommandLineAPI.html

* inspector/CommandLineAPIModuleSource.js:
(bind):
(this.member.toString):
(CommandLineAPI):
(CommandLineAPIImpl.prototype):
(slice): Deleted.
(bound): Deleted.
(bound.toString): Deleted.
(inScopeVariables): Deleted.
(customToStringMethod): Deleted.
Simplify now that we don't need to do our own variable shadow checking.

LayoutTests:

* inspector/runtime/evaluate-CommandLineAPI-expected.txt: Added.
* inspector/runtime/evaluate-CommandLineAPI.html: Added.
New test covering the different cases of global evaluation with the
CommandLineAPI as a scope extension.

* http/tests/inspector/console/cross-domain-inspected-node-access-expected.txt:
* inspector/console/command-line-api-expected.txt:
* inspector/debugger/js-stacktrace-expected.txt:
* inspector/debugger/js-stacktrace.html:
* inspector/model/stack-trace-expected.txt:
* platform/mac/inspector/model/remote-object-expected.txt:
Update expectations now that global evaluations are treated as
program's [global code] and not evals [eval code]. Also some
line number changes.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (200532 => 200533)


--- trunk/LayoutTests/ChangeLog	2016-05-06 23:21:16 UTC (rev 200532)
+++ trunk/LayoutTests/ChangeLog	2016-05-06 23:32:28 UTC (rev 200533)
@@ -1,3 +1,26 @@
+2016-05-06  Joseph Pecoraro  <pecor...@apple.com>
+
+        Web Inspector: Console: Variables defined with let/const aren't accessible outside of console's scope
+        https://bugs.webkit.org/show_bug.cgi?id=150752
+        <rdar://problem/23343385>
+
+        Reviewed by Mark Lam.
+
+        * inspector/runtime/evaluate-CommandLineAPI-expected.txt: Added.
+        * inspector/runtime/evaluate-CommandLineAPI.html: Added.
+        New test covering the different cases of global evaluation with the
+        CommandLineAPI as a scope extension.
+
+        * http/tests/inspector/console/cross-domain-inspected-node-access-expected.txt:
+        * inspector/console/command-line-api-expected.txt:
+        * inspector/debugger/js-stacktrace-expected.txt:
+        * inspector/debugger/js-stacktrace.html:
+        * inspector/model/stack-trace-expected.txt:
+        * platform/mac/inspector/model/remote-object-expected.txt:
+        Update expectations now that global evaluations are treated as
+        program's [global code] and not evals [eval code]. Also some
+        line number changes.
+
 2016-05-06  Tim Horton  <timothy_hor...@apple.com>
 
         <attachment> element should understand UTIs

Modified: trunk/LayoutTests/http/tests/inspector/console/cross-domain-inspected-node-access-expected.txt (200532 => 200533)


--- trunk/LayoutTests/http/tests/inspector/console/cross-domain-inspected-node-access-expected.txt	2016-05-06 23:21:16 UTC (rev 200532)
+++ trunk/LayoutTests/http/tests/inspector/console/cross-domain-inspected-node-access-expected.txt	2016-05-06 23:32:28 UTC (rev 200533)
@@ -1,5 +1,5 @@
-CONSOLE MESSAGE: line 57: 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 57: 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.
+CONSOLE MESSAGE: line 42: 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 42: 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.
 
 

Modified: trunk/LayoutTests/inspector/console/command-line-api-expected.txt (200532 => 200533)


--- trunk/LayoutTests/inspector/console/command-line-api-expected.txt	2016-05-06 23:21:16 UTC (rev 200532)
+++ trunk/LayoutTests/inspector/console/command-line-api-expected.txt	2016-05-06 23:32:28 UTC (rev 200533)
@@ -1,4 +1,4 @@
-CONSOLE MESSAGE: line 27: The console function $() has changed from $=getElementById(id) to $=querySelector(selector). You might try $("#%s")
+CONSOLE MESSAGE: line 12: The console function $() has changed from $=getElementById(id) to $=querySelector(selector). You might try $("#%s")
 Tests that command line api works.
 
 

Modified: trunk/LayoutTests/inspector/debugger/js-stacktrace-expected.txt (200532 => 200533)


--- trunk/LayoutTests/inspector/debugger/js-stacktrace-expected.txt	2016-05-06 23:21:16 UTC (rev 200532)
+++ trunk/LayoutTests/inspector/debugger/js-stacktrace-expected.txt	2016-05-06 23:32:28 UTC (rev 200533)
@@ -19,7 +19,7 @@
     {
         "lineNumber": 0,
         "columnNumber": 14,
-        "functionName": "Eval Code",
+        "functionName": "Global Code",
         "nativeCode": false,
         "programCode": true
     }
@@ -40,7 +40,7 @@
         "columnNumber": 21
     },
     {
-        "functionName": "eval code",
+        "functionName": "global code",
         "url": "",
         "lineNumber": 0,
         "columnNumber": 0
@@ -73,7 +73,7 @@
     {
         "lineNumber": 0,
         "columnNumber": 28,
-        "functionName": "Eval Code",
+        "functionName": "Global Code",
         "nativeCode": false,
         "programCode": true
     }
@@ -100,7 +100,7 @@
         "columnNumber": 20
     },
     {
-        "functionName": "eval code",
+        "functionName": "global code",
         "url": "",
         "lineNumber": 0,
         "columnNumber": 0

Modified: trunk/LayoutTests/inspector/debugger/js-stacktrace.html (200532 => 200533)


--- trunk/LayoutTests/inspector/debugger/js-stacktrace.html	2016-05-06 23:21:16 UTC (rev 200532)
+++ trunk/LayoutTests/inspector/debugger/js-stacktrace.html	2016-05-06 23:32:28 UTC (rev 200533)
@@ -67,7 +67,7 @@
         if (error)
             InspectorTest.log(error);
 
-        var stackTrace = stripPayloadAfterEval(WebInspector.StackTrace._parseStackTrace(result.value));
+        var stackTrace = stripPayloadAfterGlobalCode(WebInspector.StackTrace._parseStackTrace(result.value));
         stackTrace = stripFilePaths(stackTrace);
 
         InspectorTest.log(JSON.stringify(stackTrace, null, 4));
@@ -80,7 +80,7 @@
         if (error)
             InspectorTest.log(error);
 
-        var stackTrace = stripPayloadAfterEval(WebInspector.StackTrace._parseStackTrace(result.value));
+        var stackTrace = stripPayloadAfterGlobalCode(WebInspector.StackTrace._parseStackTrace(result.value));
         stackTrace = stripFilePaths(stackTrace);
 
         InspectorTest.log(JSON.stringify(stackTrace, null, 4));
@@ -93,7 +93,7 @@
         if (error)
             InspectorTest.log(error);
 
-        var stackTrace = stripPayloadAfterEval(WebInspector.StackTrace._parseStackTrace(result.value));
+        var stackTrace = stripPayloadAfterGlobalCode(WebInspector.StackTrace._parseStackTrace(result.value));
         stackTrace = stripFilePaths(stackTrace);
 
         InspectorTest.log(JSON.stringify(stackTrace, null, 4));
@@ -120,12 +120,12 @@
         return stackTrace.slice(0, index);
     }
 
-    function stripPayloadAfterEval(payload)
+    function stripPayloadAfterGlobalCode(payload)
     {
         var index = 0;
         for (var frame of payload) {
             index++;
-            if (frame.functionName === "eval code")
+            if (frame.functionName === "global code")
                 break;
         }
         return payload.slice(0, index);

Modified: trunk/LayoutTests/inspector/model/stack-trace-expected.txt (200532 => 200533)


--- trunk/LayoutTests/inspector/model/stack-trace-expected.txt	2016-05-06 23:21:16 UTC (rev 200532)
+++ trunk/LayoutTests/inspector/model/stack-trace-expected.txt	2016-05-06 23:32:28 UTC (rev 200533)
@@ -15,8 +15,8 @@
   4: Global Code (inline-script.js:2) - nativeCode (false) programCode (true)
   5: appendChild - nativeCode (true) programCode (false)
   6: triggerConsoleMessage (stack-trace.html:9) - nativeCode (false) programCode (false)
-  7: Eval Code (Anonymous Script 2 (line 1)) - nativeCode (false) programCode (true)
-  8: eval - nativeCode (true) programCode (false)
+  7: Global Code (Anonymous Script 2 (line 1)) - nativeCode (false) programCode (true)
+  8: evaluateWithScopeExtension - nativeCode (true) programCode (false)
   9: _evaluateOn - nativeCode (true) programCode (false)
   10: _evaluateAndWrap - nativeCode (true) programCode (false)
   11: evaluate - nativeCode (true) programCode (false)

Added: trunk/LayoutTests/inspector/runtime/evaluate-CommandLineAPI-expected.txt (0 => 200533)


--- trunk/LayoutTests/inspector/runtime/evaluate-CommandLineAPI-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/inspector/runtime/evaluate-CommandLineAPI-expected.txt	2016-05-06 23:32:28 UTC (rev 200533)
@@ -0,0 +1,36 @@
+Tests for the Runtime.evaluate with the Command Line API.
+
+
+== Running test suite: Runtime.evaluate.CommandLineAPI
+-- Running test case: AccessCommandLineAPI
+PASS: CommandLineAPI `keys` can be accessed.
+PASS: CommandLineAPI `keys` should work with a simple object.
+
+-- Running test case: AccessGlobalVariable
+PASS: Should be able to access var in global scope.
+PASS: Should be able to access let in global scope.
+PASS: Should be able to access const in global scope.
+
+-- Running test case: CreateGlobalVariable
+PASS: Should be able to access created var in global scope.
+PASS: Should be able to access created let in global scope.
+PASS: Should be able to access created const in global scope.
+
+-- Running test case: CreateGlobalClass
+PASS: Should be able to access class created in earlier evaluation.
+
+-- Running test case: ExpectedExceptionCreatingDuplicateLexicalGlobalVariables
+PASS: Should be an exception defining a lexical global multiple times.
+
+-- Running test case: NonStrictAndStrictEvaluations
+PASS: Non-strict evaluation. Should be able to access arguments.callee.
+PASS: Script evaluation. Should not be able to access arguments.callee.
+
+-- Running test case: CommandLineAPIDoesNotShadowVariables
+PASS: `keys` is currently CommandLineAPI function.
+PASS: Creating global `keys` variable should be okay.
+PASS: Global `keys` variable should not be shadowed by CommandLineAPI function.
+
+-- Running test case: CommandLineAPIDoesNotShadowGlobalObjectProperties
+PASS: `values` should be `window.values` and not shadowed by CommandLineAPI `values` function.
+

Added: trunk/LayoutTests/inspector/runtime/evaluate-CommandLineAPI.html (0 => 200533)


--- trunk/LayoutTests/inspector/runtime/evaluate-CommandLineAPI.html	                        (rev 0)
+++ trunk/LayoutTests/inspector/runtime/evaluate-CommandLineAPI.html	2016-05-06 23:32:28 UTC (rev 200533)
@@ -0,0 +1,179 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src=""
+<script>
+var varGlobalVariable = 1;
+let letGlobalVariable = 2;
+const constGlobalVariable = 3;
+
+function test()
+{
+    const objectGroup = "test";
+    const includeCommandLineAPI = true;
+    const returnByValue = true;
+
+    function testEvaluate(_expression_, callback) {
+        RuntimeAgent.evaluate.invoke({_expression_, objectGroup, includeCommandLineAPI, returnByValue}, (error, resultValue, wasThrown) => {
+            InspectorTest.assert(!error, "Should not be a protocol error.");
+            InspectorTest.assert(!wasThrown, "Should not be a runtime error.");
+            if (callback)
+                callback(resultValue.value);
+        });
+    }
+
+    function testEvaluateThrow(_expression_, callback) {
+        RuntimeAgent.evaluate.invoke({_expression_, objectGroup, includeCommandLineAPI, returnByValue}, (error, resultValue, wasThrown) => {
+            InspectorTest.assert(!error, "Should not be a protocol error.");
+            InspectorTest.assert(wasThrown, "Should be a runtime error.");
+            if (callback)
+                callback(wasThrown);
+        });
+    }
+
+    let suite = InspectorTest.createAsyncSuite("Runtime.evaluate.CommandLineAPI");
+
+    suite.addTestCase({
+        name: "AccessCommandLineAPI",
+        description: "Test evaluate can access CommandLineAPI methods.",
+        test: (resolve, reject) => {
+            testEvaluate("keys.toString()", (resultValue) => {
+                InspectorTest.expectThat(resultValue.includes("[Command Line API]"), "CommandLineAPI `keys` can be accessed.");
+            });
+            testEvaluate("keys({a:1, b:2})", (resultValue) => {
+                InspectorTest.expectThat(resultValue.length === 2 && resultValue[0] === "a" && resultValue[1] === "b", "CommandLineAPI `keys` should work with a simple object.");
+                resolve();
+            });
+        }
+    });
+
+    suite.addTestCase({
+        name: "AccessGlobalVariable",
+        description: "Test evaluate can access global variables.",
+        test: (resolve, reject) => {
+            testEvaluate("varGlobalVariable", (resultValue) => {
+                InspectorTest.expectThat(resultValue === 1, "Should be able to access var in global scope.");
+            });
+            testEvaluate("letGlobalVariable", (resultValue) => {
+                InspectorTest.expectThat(resultValue === 2, "Should be able to access let in global scope.");
+            });
+            testEvaluate("constGlobalVariable", (resultValue) => {
+                InspectorTest.expectThat(resultValue === 3, "Should be able to access const in global scope.");
+                resolve();
+            });
+        }
+    });
+
+    suite.addTestCase({
+        name: "CreateGlobalVariable",
+        description: "Test evaluate can create global variables.",
+        test: (resolve, reject) => {
+            testEvaluate(`
+                var createdVarGlobalVariable = 1;
+                let createdLetGlobalVariable = 2;
+                const createdConstGlobalVariable = 3;
+            `);
+            testEvaluate("createdVarGlobalVariable", (resultValue) => {
+                InspectorTest.expectThat(resultValue === 1, "Should be able to access created var in global scope.");
+            });
+            testEvaluate("createdLetGlobalVariable", (resultValue) => {
+                InspectorTest.expectThat(resultValue === 2, "Should be able to access created let in global scope.");
+            });
+            testEvaluate("createdConstGlobalVariable", (resultValue) => {
+                InspectorTest.expectThat(resultValue === 3, "Should be able to access created const in global scope.");
+                resolve();
+            });
+        }
+    });
+
+    suite.addTestCase({
+        name: "CreateGlobalClass",
+        description: "Test evaluate can create a class.",
+        test: (resolve, reject) => {
+            testEvaluate(`
+                class Foo {
+                    static staticMethod() {
+                        return 42;
+                    }
+                }
+            `);
+            testEvaluate("Foo.staticMethod()", (resultValue) => {
+                InspectorTest.expectThat(resultValue === 42, "Should be able to access class created in earlier evaluation.");
+                resolve();
+            });
+        }
+    });
+
+    suite.addTestCase({
+        name: "ExpectedExceptionCreatingDuplicateLexicalGlobalVariables",
+        description: "Test evaluate produces expected exception creating duplicate lexical global variables across evaluations.",
+        test: (resolve, reject) => {
+            testEvaluate(`let duplicateGlobalVariable = 1`);
+            testEvaluateThrow(`let duplicateGlobalVariable = 1`, (wasThrown) => {
+                InspectorTest.expectThat(wasThrown, "Should be an exception defining a lexical global multiple times.");
+                resolve();
+            });
+        }
+    });
+
+    suite.addTestCase({
+        name: "NonStrictAndStrictEvaluations",
+        description: "Test evaluate can run strict and non-strict programs.",
+        test: (resolve, reject) => {
+            testEvaluate(`
+                // Not strict, this is okay.
+                (function f() {
+                    return arguments.callee.name;
+                })();
+            `, (resultValue) => {
+                InspectorTest.expectThat(resultValue === "f", "Non-strict evaluation. Should be able to access arguments.callee.");
+            });
+            testEvaluateThrow(`"use strict";
+                // Strict, this throw an exception.
+                (function() {
+                    return arguments.callee;
+                })();`
+            , (wasThrown) => {
+                InspectorTest.expectThat(wasThrown, "Script evaluation. Should not be able to access arguments.callee.");
+                resolve();
+            });
+        }
+    });
+
+    suite.addTestCase({
+        name: "CommandLineAPIDoesNotShadowVariables",
+        description: "Test CommandLineAPI does not shadow global variables.",
+        test: (resolve, reject) => {
+            testEvaluate("keys.toString()", (resultValue) => {
+                InspectorTest.expectThat(resultValue.includes("[Command Line API]"), "`keys` is currently CommandLineAPI function.");
+            });
+            testEvaluate("var keys = 123; keys", (resultValue) => {
+                InspectorTest.expectThat(resultValue === 123, "Creating global `keys` variable should be okay.");
+            });
+            testEvaluate("keys", (resultValue) => {
+                InspectorTest.expectThat(resultValue === 123, "Global `keys` variable should not be shadowed by CommandLineAPI function.");
+                resolve();
+            });
+        }
+    });
+
+    suite.addTestCase({
+        name: "CommandLineAPIDoesNotShadowGlobalObjectProperties",
+        description: "Test CommandLineAPI does not shadow global object properties.",
+        test: (resolve, reject) => {
+            testEvaluate("values.toString()", (resultValue) => {
+                InspectorTest.expectThat(resultValue === "[object HTMLDivElement]", "`values` should be `window.values` and not shadowed by CommandLineAPI `values` function.");
+                resolve();
+            });
+        }
+    });
+
+    suite.runTestCasesAndFinish();
+}
+</script>
+</head>
+<body _onload_="runTest()">
+<div id="values"></div> <!-- This introduces the named property `window.values` on the window global object. -->
+<p>Tests for the Runtime.evaluate with the Command Line API.</p>
+</body>
+</html>

Modified: trunk/LayoutTests/platform/mac/inspector/model/remote-object-expected.txt (200532 => 200533)


--- trunk/LayoutTests/platform/mac/inspector/model/remote-object-expected.txt	2016-05-06 23:21:16 UTC (rev 200532)
+++ trunk/LayoutTests/platform/mac/inspector/model/remote-object-expected.txt	2016-05-06 23:32:28 UTC (rev 200533)
@@ -2328,7 +2328,7 @@
         "_listeners": null,
         "_name": "stack",
         "_type": "string",
-        "_value": "eval code\neval@[native code]\n_evaluateOn\n_evaluateAndWrap\nevaluate"
+        "_value": "global code\nevaluateWithScopeExtension@[native code]\n_evaluateOn\n_evaluateAndWrap\nevaluate"
       }
     ],
     "_entries": null
@@ -2372,7 +2372,7 @@
         "_listeners": null,
         "_name": "stack",
         "_type": "string",
-        "_value": "eval@[native code]\neval code\neval@[native code]\n_evaluateOn\n_evaluateAndWrap\nevaluate"
+        "_value": "eval@[native code]\nglobal code\nevaluateWithScopeEx…ative code]\n_evaluateOn\n_evaluateAndWrap\nevaluate"
       }
     ],
     "_entries": null

Modified: trunk/Source/_javascript_Core/ChangeLog (200532 => 200533)


--- trunk/Source/_javascript_Core/ChangeLog	2016-05-06 23:21:16 UTC (rev 200532)
+++ trunk/Source/_javascript_Core/ChangeLog	2016-05-06 23:32:28 UTC (rev 200533)
@@ -1,3 +1,56 @@
+2016-05-06  Joseph Pecoraro  <pecor...@apple.com>
+
+        Web Inspector: Console: Variables defined with let/const aren't accessible outside of console's scope
+        https://bugs.webkit.org/show_bug.cgi?id=150752
+        <rdar://problem/23343385>
+
+        Reviewed by Mark Lam.
+
+        This approach allows Web Inspector to hang a "Scope Extension", a
+        WithObjectScope, off the GlobalObject. When resolving identifiers
+        in fails to resolve anything in the normal scope chain, consult
+        the scope extension.
+
+        This allows us to eliminate the `with (commandLineAPI) { ... }`
+        block in global console evaluations, and instead makes it a full
+        program evaluation, with the commandLineAPI available and safely
+        shadowed by actual variables as expected.
+
+        * inspector/InjectedScriptSource.js:
+        (InjectedScript.prototype._evaluateOn):
+        Use the new evaluateWithScopeExtension and provide the CommandLineAPI
+        object as the scope extension object.
+
+        (BasicCommandLineAPI):
+        (BasicCommandLineAPI.inScopeVariables): Deleted.
+        Simplify now that we don't need to check for variable shadowing ourselves.
+
+        * inspector/JSInjectedScriptHost.cpp:
+        (Inspector::JSInjectedScriptHost::evaluateWithScopeExtension):
+        * inspector/JSInjectedScriptHost.h:
+        * inspector/JSInjectedScriptHostPrototype.cpp:
+        (Inspector::JSInjectedScriptHostPrototype::finishCreation):
+        (Inspector::jsInjectedScriptHostPrototypeFunctionEvaluateWithScopeExtension):
+        Provide a new InjectedScriptHost method to evaluate a program
+        with a scope extension.
+
+        * runtime/Completion.cpp:
+        (JSC::evaluateWithScopeExtension):
+        * runtime/Completion.h:
+        General JSC::evaluate function to evaluate a program with a scope extension.
+
+        * runtime/JSGlobalObject.cpp:
+        (JSC::JSGlobalObject::setGlobalScopeExtension):
+        (JSC::JSGlobalObject::clearGlobalScopeExtension):
+        (JSC::JSGlobalObject::visitChildren):
+        * runtime/JSGlobalObject.h:
+        (JSC::JSGlobalObject::globalScopeExtension):
+        Hang a scope extension off the global object.
+
+        * runtime/JSScope.cpp:
+        (JSC::JSScope::resolve):
+        Consult the scope extension when resolve fails to find anything normally.
+
 2016-05-06  Mark Lam  <mark....@apple.com>
 
         Add JSC options reportBaselineCompileTimes and reportDFGCompileTimes.

Modified: trunk/Source/_javascript_Core/inspector/InjectedScriptSource.js (200532 => 200533)


--- trunk/Source/_javascript_Core/inspector/InjectedScriptSource.js	2016-05-06 23:21:16 UTC (rev 200532)
+++ trunk/Source/_javascript_Core/inspector/InjectedScriptSource.js	2016-05-06 23:32:28 UTC (rev 200533)
@@ -31,7 +31,8 @@
 
 (function (InjectedScriptHost, inspectedGlobalObject, injectedScriptId) {
 
-// Protect against Object overwritten by the user code.
+// FIXME: <https://webkit.org/b/152294> Web Inspector: Parse InjectedScriptSource as a built-in to get guaranteed non-user-overriden built-ins
+
 var Object = {}.constructor;
 
 function toString(obj)
@@ -519,32 +520,12 @@
             return result;
         }
 
-        // When not evaluating on a call frame we use a 'with' statement to allow var and function statements to leak
-        // into the global scope. This allow them to stick around between evaluations.
-
-        try {
-            if (commandLineAPI) {
-                if (inspectedGlobalObject.console)
-                    inspectedGlobalObject.console.__commandLineAPI = commandLineAPI;
-                else
-                    inspectedGlobalObject.__commandLineAPI = commandLineAPI;
-                _expression_ = "with ((this && (this.console ? this.console.__commandLineAPI : this.__commandLineAPI)) || {}) { " + _expression_ + "\n}";
-            }
-
-            var result = evalFunction.call(inspectedGlobalObject, _expression_);
-
-            if (saveResult)
-                this._saveResult(result);
-
-            return result;
-        } finally {
-            if (commandLineAPI) {
-                if (inspectedGlobalObject.console)
-                    delete inspectedGlobalObject.console.__commandLineAPI;
-                else
-                    delete inspectedGlobalObject.__commandLineAPI;
-            }
-        }
+        // When not evaluating on a call frame, we evaluate as a program
+        // with the Command Line API as a scope extension object.
+        var result = InjectedScriptHost.evaluateWithScopeExtension(_expression_, commandLineAPI);
+        if (saveResult)
+            this._saveResult(result);
+        return result;
     },
 
     wrapCallFrames: function(callFrame)
@@ -1411,38 +1392,16 @@
 
 function BasicCommandLineAPI(callFrame)
 {
-    function inScopeVariables(member)
-    {
-        if (!callFrame)
-            return false;
-
-        var scopeChain = callFrame.scopeChain;
-        for (var i = 0; i < scopeChain.length; ++i) {
-            if (member in scopeChain[i])
-                return true;
-        }
-        return false;
-    }
-
     this.$_ = injectedScript._lastResult;
     this.$exception = injectedScript._exceptionValue;
 
     // $1-$99
-    for (var i = 1; i <= injectedScript._savedResults.length; ++i) {
-        var member = "$" + i;
-        if (member in inspectedGlobalObject)
-            continue;
+    for (var i = 1; i <= injectedScript._savedResults.length; ++i)
         this.__defineGetter__("$" + i, bind(injectedScript._savedResult, injectedScript, i));
-    }
 
     // Command Line API methods.
-    for (var i = 0; i < BasicCommandLineAPI.methods.length; ++i) {
-        var method = BasicCommandLineAPI.methods[i];
-        var name = method.name;
-        if (name in inspectedGlobalObject || inScopeVariables(name))
-            continue;
-        this[name] = method;
-    }
+    for (var i = 0; i < BasicCommandLineAPI.methods.length; ++i)
+        this[method.name] = method;
 }
 
 BasicCommandLineAPI.methods = [

Modified: trunk/Source/_javascript_Core/inspector/JSInjectedScriptHost.cpp (200532 => 200533)


--- trunk/Source/_javascript_Core/inspector/JSInjectedScriptHost.cpp	2016-05-06 23:21:16 UTC (rev 200532)
+++ trunk/Source/_javascript_Core/inspector/JSInjectedScriptHost.cpp	2016-05-06 23:32:28 UTC (rev 200533)
@@ -27,6 +27,7 @@
 #include "JSInjectedScriptHost.h"
 
 #include "BuiltinNames.h"
+#include "Completion.h"
 #include "DateInstance.h"
 #include "DirectArguments.h"
 #include "Error.h"
@@ -37,6 +38,7 @@
 #include "JSBoundFunction.h"
 #include "JSCInlines.h"
 #include "JSFunction.h"
+#include "JSGlobalObjectFunctions.h"
 #include "JSInjectedScriptHostPrototype.h"
 #include "JSMap.h"
 #include "JSMapIterator.h"
@@ -48,6 +50,7 @@
 #include "JSTypedArrays.h"
 #include "JSWeakMap.h"
 #include "JSWeakSet.h"
+#include "JSWithScope.h"
 #include "ObjectConstructor.h"
 #include "ProxyObject.h"
 #include "RegExpObject.h"
@@ -91,6 +94,20 @@
     return globalObject->evalFunction();
 }
 
+JSValue JSInjectedScriptHost::evaluateWithScopeExtension(ExecState* exec)
+{
+    JSValue scriptValue = exec->argument(0);
+    if (!scriptValue.isString())
+        return throwTypeError(exec, "InjectedScriptHost.evaluateWithScopeExtension first argument must be a string.");
+
+    String program = scriptValue.toString(exec)->value(exec);
+    if (exec->hadException())
+        return jsUndefined();
+
+    JSObject* scopeExtension = exec->argument(1).getObject();
+    return JSC::evaluateWithScopeExtension(exec, makeSource(program), scopeExtension);
+}
+
 JSValue JSInjectedScriptHost::internalConstructorName(ExecState* exec)
 {
     if (exec->argumentCount() < 1)

Modified: trunk/Source/_javascript_Core/inspector/JSInjectedScriptHost.h (200532 => 200533)


--- trunk/Source/_javascript_Core/inspector/JSInjectedScriptHost.h	2016-05-06 23:21:16 UTC (rev 200532)
+++ trunk/Source/_javascript_Core/inspector/JSInjectedScriptHost.h	2016-05-06 23:32:28 UTC (rev 200533)
@@ -60,6 +60,7 @@
     JSC::JSValue evaluate(JSC::ExecState*) const;
 
     // Functions.
+    JSC::JSValue evaluateWithScopeExtension(JSC::ExecState*);
     JSC::JSValue internalConstructorName(JSC::ExecState*);
     JSC::JSValue isHTMLAllCollection(JSC::ExecState*);
     JSC::JSValue subtype(JSC::ExecState*);

Modified: trunk/Source/_javascript_Core/inspector/JSInjectedScriptHostPrototype.cpp (200532 => 200533)


--- trunk/Source/_javascript_Core/inspector/JSInjectedScriptHostPrototype.cpp	2016-05-06 23:21:16 UTC (rev 200532)
+++ trunk/Source/_javascript_Core/inspector/JSInjectedScriptHostPrototype.cpp	2016-05-06 23:32:28 UTC (rev 200533)
@@ -48,6 +48,7 @@
 static EncodedJSValue JSC_HOST_CALL jsInjectedScriptHostPrototypeFunctionWeakSetSize(ExecState*);
 static EncodedJSValue JSC_HOST_CALL jsInjectedScriptHostPrototypeFunctionWeakSetEntries(ExecState*);
 static EncodedJSValue JSC_HOST_CALL jsInjectedScriptHostPrototypeFunctionIteratorEntries(ExecState*);
+static EncodedJSValue JSC_HOST_CALL jsInjectedScriptHostPrototypeFunctionEvaluateWithScopeExtension(ExecState*);
 
 static EncodedJSValue JSC_HOST_CALL jsInjectedScriptHostPrototypeAttributeEvaluate(ExecState*);
 
@@ -69,6 +70,7 @@
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("weakSetSize", jsInjectedScriptHostPrototypeFunctionWeakSetSize, DontEnum, 1);
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("weakSetEntries", jsInjectedScriptHostPrototypeFunctionWeakSetEntries, DontEnum, 1);
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("iteratorEntries", jsInjectedScriptHostPrototypeFunctionIteratorEntries, DontEnum, 1);
+    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("evaluateWithScopeExtension", jsInjectedScriptHostPrototypeFunctionEvaluateWithScopeExtension, DontEnum, 1);
 
     JSC_NATIVE_GETTER("evaluate", jsInjectedScriptHostPrototypeAttributeEvaluate, DontEnum | Accessor);
 }
@@ -80,7 +82,6 @@
     if (!castedThis)
         return throwVMTypeError(exec);
 
-    ASSERT_GC_OBJECT_INHERITS(castedThis, JSInjectedScriptHost::info());
     return JSValue::encode(castedThis->evaluate(exec));
 }
 
@@ -91,7 +92,6 @@
     if (!castedThis)
         return throwVMTypeError(exec);
 
-    ASSERT_GC_OBJECT_INHERITS(castedThis, JSInjectedScriptHost::info());
     return JSValue::encode(castedThis->internalConstructorName(exec));
 }
 
@@ -102,7 +102,6 @@
     if (!castedThis)
         return throwVMTypeError(exec);
 
-    ASSERT_GC_OBJECT_INHERITS(castedThis, JSInjectedScriptHost::info());
     return JSValue::encode(castedThis->isHTMLAllCollection(exec));
 }
 
@@ -113,7 +112,6 @@
     if (!castedThis)
         return throwVMTypeError(exec);
 
-    ASSERT_GC_OBJECT_INHERITS(castedThis, JSInjectedScriptHost::info());
     return JSValue::encode(castedThis->weakMapSize(exec));
 }
 
@@ -124,7 +122,6 @@
     if (!castedThis)
         return throwVMTypeError(exec);
 
-    ASSERT_GC_OBJECT_INHERITS(castedThis, JSInjectedScriptHost::info());
     return JSValue::encode(castedThis->weakMapEntries(exec));
 }
 
@@ -135,7 +132,6 @@
     if (!castedThis)
         return throwVMTypeError(exec);
 
-    ASSERT_GC_OBJECT_INHERITS(castedThis, JSInjectedScriptHost::info());
     return JSValue::encode(castedThis->weakSetSize(exec));
 }
 
@@ -146,7 +142,6 @@
     if (!castedThis)
         return throwVMTypeError(exec);
 
-    ASSERT_GC_OBJECT_INHERITS(castedThis, JSInjectedScriptHost::info());
     return JSValue::encode(castedThis->weakSetEntries(exec));
 }
 
@@ -157,10 +152,19 @@
     if (!castedThis)
         return throwVMTypeError(exec);
 
-    ASSERT_GC_OBJECT_INHERITS(castedThis, JSInjectedScriptHost::info());
     return JSValue::encode(castedThis->iteratorEntries(exec));
 }
 
+EncodedJSValue JSC_HOST_CALL jsInjectedScriptHostPrototypeFunctionEvaluateWithScopeExtension(ExecState* exec)
+{
+    JSValue thisValue = exec->thisValue();
+    JSInjectedScriptHost* castedThis = jsDynamicCast<JSInjectedScriptHost*>(thisValue);
+    if (!castedThis)
+        return throwVMTypeError(exec);
+
+    return JSValue::encode(castedThis->evaluateWithScopeExtension(exec));
+}
+
 EncodedJSValue JSC_HOST_CALL jsInjectedScriptHostPrototypeFunctionSubtype(ExecState* exec)
 {
     JSValue thisValue = exec->thisValue();
@@ -168,7 +172,6 @@
     if (!castedThis)
         return throwVMTypeError(exec);
 
-    ASSERT_GC_OBJECT_INHERITS(castedThis, JSInjectedScriptHost::info());
     return JSValue::encode(castedThis->subtype(exec));
 }
 
@@ -179,7 +182,6 @@
     if (!castedThis)
         return throwVMTypeError(exec);
 
-    ASSERT_GC_OBJECT_INHERITS(castedThis, JSInjectedScriptHost::info());
     return JSValue::encode(castedThis->functionDetails(exec));
 }
 
@@ -190,7 +192,6 @@
     if (!castedThis)
         return throwVMTypeError(exec);
 
-    ASSERT_GC_OBJECT_INHERITS(castedThis, JSInjectedScriptHost::info());
     return JSValue::encode(castedThis->getInternalProperties(exec));
 }
 

Modified: trunk/Source/_javascript_Core/runtime/Completion.cpp (200532 => 200533)


--- trunk/Source/_javascript_Core/runtime/Completion.cpp	2016-05-06 23:21:16 UTC (rev 200532)
+++ trunk/Source/_javascript_Core/runtime/Completion.cpp	2016-05-06 23:32:28 UTC (rev 200533)
@@ -35,6 +35,7 @@
 #include "JSInternalPromiseDeferred.h"
 #include "JSLock.h"
 #include "JSModuleRecord.h"
+#include "JSWithScope.h"
 #include "ModuleAnalyzer.h"
 #include "ModuleLoaderObject.h"
 #include "Parser.h"
@@ -121,6 +122,28 @@
     return evaluate(exec, source, thisValue, returnedException);
 }
 
+JSValue evaluateWithScopeExtension(ExecState* exec, const SourceCode& source, JSObject* scopeExtensionObject)
+{
+    JSGlobalObject* globalObject = exec->vmEntryGlobalObject();
+
+    if (scopeExtensionObject) {
+        JSScope* ignoredPreviousScope = globalObject->globalScope();
+        globalObject->setGlobalScopeExtension(JSWithScope::create(exec->vm(), globalObject, scopeExtensionObject, ignoredPreviousScope));
+    }
+
+    NakedPtr<Exception> exception;
+    JSValue returnValue = JSC::evaluate(globalObject->globalExec(), source, globalObject, exception);
+
+    // Don't swallow the exception.
+    if (exception)
+        globalObject->vm().restorePreviousException(exception);
+
+    if (scopeExtensionObject)
+        globalObject->clearGlobalScopeExtension();
+
+    return returnValue;
+}
+
 static Symbol* createSymbolForEntryPointModule(VM& vm)
 {
     // Generate the unique key for the source-provided module.

Modified: trunk/Source/_javascript_Core/runtime/Completion.h (200532 => 200533)


--- trunk/Source/_javascript_Core/runtime/Completion.h	2016-05-06 23:21:16 UTC (rev 200532)
+++ trunk/Source/_javascript_Core/runtime/Completion.h	2016-05-06 23:32:28 UTC (rev 200533)
@@ -31,7 +31,7 @@
 
 class Exception;
 class ExecState;
-class JSScope;
+class JSObject;
 class ParserError;
 class SourceCode;
 class VM;
@@ -55,6 +55,8 @@
     return profiledEvaluate(exec, reason, sourceCode, thisValue, unused);
 }
 
+JS_EXPORT_PRIVATE JSValue evaluateWithScopeExtension(ExecState*, const SourceCode&, JSObject* scopeExtension);
+
 // Load the module source and evaluate it.
 JS_EXPORT_PRIVATE JSInternalPromise* loadAndEvaluateModule(ExecState*, const String& moduleName);
 JS_EXPORT_PRIVATE JSInternalPromise* loadAndEvaluateModule(ExecState*, const SourceCode&);

Modified: trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp (200532 => 200533)


--- trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp	2016-05-06 23:21:16 UTC (rev 200532)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp	2016-05-06 23:32:28 UTC (rev 200533)
@@ -875,6 +875,16 @@
     addGlobalVar(propertyName);
 }
 
+void JSGlobalObject::setGlobalScopeExtension(JSScope* scope)
+{
+    m_globalScopeExtension.set(vm(), this, scope);
+}
+
+void JSGlobalObject::clearGlobalScopeExtension()
+{
+    m_globalScopeExtension.clear();
+}
+
 static inline JSObject* lastInPrototypeChain(JSObject* object)
 {
     JSObject* o = object;
@@ -1034,6 +1044,7 @@
     visitor.append(&thisObject->m_globalThis);
 
     visitor.append(&thisObject->m_globalLexicalEnvironment);
+    visitor.append(&thisObject->m_globalScopeExtension);
     visitor.append(&thisObject->m_globalCallee);
     visitor.append(&thisObject->m_regExpConstructor);
     visitor.append(&thisObject->m_errorConstructor);

Modified: trunk/Source/_javascript_Core/runtime/JSGlobalObject.h (200532 => 200533)


--- trunk/Source/_javascript_Core/runtime/JSGlobalObject.h	2016-05-06 23:21:16 UTC (rev 200532)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObject.h	2016-05-06 23:32:28 UTC (rev 200533)
@@ -217,6 +217,7 @@
     WriteBarrier<JSObject> m_globalThis;
 
     WriteBarrier<JSGlobalLexicalEnvironment> m_globalLexicalEnvironment;
+    WriteBarrier<JSScope> m_globalScopeExtension;
     WriteBarrier<JSObject> m_globalCallee;
     WriteBarrier<RegExpConstructor> m_regExpConstructor;
     WriteBarrier<ErrorConstructor> m_errorConstructor;
@@ -455,6 +456,10 @@
     JSScope* globalScope() { return m_globalLexicalEnvironment.get(); }
     JSGlobalLexicalEnvironment* globalLexicalEnvironment() { return m_globalLexicalEnvironment.get(); }
 
+    JSScope* globalScopeExtension() { return m_globalScopeExtension.get(); }
+    void setGlobalScopeExtension(JSScope*);
+    void clearGlobalScopeExtension();
+
     // The following accessors return pristine values, even if a script 
     // replaces the global object's associated property.
 

Modified: trunk/Source/_javascript_Core/runtime/JSScope.cpp (200532 => 200533)


--- trunk/Source/_javascript_Core/runtime/JSScope.cpp	2016-05-06 23:21:16 UTC (rev 200532)
+++ trunk/Source/_javascript_Core/runtime/JSScope.cpp	2016-05-06 23:32:28 UTC (rev 200533)
@@ -203,8 +203,18 @@
         JSScope* scope = it.scope();
         JSObject* object = it.get();
 
-        if (++it == end) // Global scope.
+        // Global scope.
+        if (++it == end) {
+            JSScope* globalScopeExtension = scope->globalObject()->globalScopeExtension();
+            if (UNLIKELY(globalScopeExtension)) {
+                if (object->hasProperty(exec, ident))
+                    return object;
+                JSObject* extensionScopeObject = JSScope::objectAtScope(globalScopeExtension);
+                if (extensionScopeObject->hasProperty(exec, ident))
+                    return extensionScopeObject;
+            }
             return object;
+        }
 
         if (object->hasProperty(exec, ident)) {
             if (!isUnscopable(exec, scope, object, ident))

Modified: trunk/Source/WebCore/ChangeLog (200532 => 200533)


--- trunk/Source/WebCore/ChangeLog	2016-05-06 23:21:16 UTC (rev 200532)
+++ trunk/Source/WebCore/ChangeLog	2016-05-06 23:32:28 UTC (rev 200533)
@@ -1,3 +1,25 @@
+2016-05-06  Joseph Pecoraro  <pecor...@apple.com>
+
+        Web Inspector: Console: Variables defined with let/const aren't accessible outside of console's scope
+        https://bugs.webkit.org/show_bug.cgi?id=150752
+        <rdar://problem/23343385>
+
+        Reviewed by Mark Lam.
+
+        Test: inspector/runtime/evaluate-CommandLineAPI.html
+
+        * inspector/CommandLineAPIModuleSource.js:
+        (bind):
+        (this.member.toString):
+        (CommandLineAPI):
+        (CommandLineAPIImpl.prototype):
+        (slice): Deleted.
+        (bound): Deleted.
+        (bound.toString): Deleted.
+        (inScopeVariables): Deleted.
+        (customToStringMethod): Deleted.
+        Simplify now that we don't need to do our own variable shadow checking.
+
 2016-05-06  Tim Horton  <timothy_hor...@apple.com>
 
         <attachment> element should understand UTIs

Modified: trunk/Source/WebCore/inspector/CommandLineAPIModuleSource.js (200532 => 200533)


--- trunk/Source/WebCore/inspector/CommandLineAPIModuleSource.js	2016-05-06 23:21:16 UTC (rev 200532)
+++ trunk/Source/WebCore/inspector/CommandLineAPIModuleSource.js	2016-05-06 23:32:28 UTC (rev 200533)
@@ -37,40 +37,13 @@
  */
 (function (InjectedScriptHost, inspectedWindow, injectedScriptId, injectedScript, CommandLineAPIHost) {
 
-/**
- * @param {Arguments} array
- * @param {number=} index
- * @return {Array.<*>}
- */
-function slice(array, index)
-{
-    var result = [];
-    for (var i = index || 0; i < array.length; ++i)
-        result.push(array[i]);
-    return result;
-}
+// FIXME: <https://webkit.org/b/152294> Web Inspector: Parse InjectedScriptSource as a built-in to get guaranteed non-user-overriden built-ins
 
-/**
- * Please use this bind, not the one from Function.prototype
- * @param {function(...)} func
- * @param {Object} thisObject
- * @param {...number} var_args
- */
-function bind(func, thisObject, var_args)
+function bind(func, thisObject, ...outerArgs)
 {
-    var args = slice(arguments, 2);
-
-    /**
-     * @param {...number} var_args
-     */
-    function bound(var_args)
-    {
-        return func.apply(thisObject, args.concat(slice(arguments)));
+    return function(...innerArgs) {
+        return func.apply(thisObject, outerArgs.concat(innerArgs));
     }
-    bound.toString = function() {
-        return "bound: " + func;
-    };
-    return bound;
 }
 
 /**
@@ -80,55 +53,21 @@
  */
 function CommandLineAPI(commandLineAPIImpl, callFrame)
 {
-    /**
-     * @param {string} member
-     * @return {boolean}
-     */
-    function inScopeVariables(member)
-    {
-        if (!callFrame)
-            return false;
+    this.$_ = injectedScript._lastResult;
+    this.$exception = injectedScript._exceptionValue;
 
-        var scopeChain = callFrame.scopeChain;
-        for (var i = 0; i < scopeChain.length; ++i) {
-            if (member in scopeChain[i])
-                return true;
-        }
-        return false;
-    }
-
-    /**
-     * @param {string} name The name of the method for which a toString method should be generated.
-     * @return {function():string}
-     */
-    function customToStringMethod(name)
-    {
-        return function () { return "function " + name + "() { [Command Line API] }"; };
-    }
-
-    for (var i = 0; i < CommandLineAPI.members_.length; ++i) {
-        var member = CommandLineAPI.members_[i];
-        if (member in inspectedWindow || inScopeVariables(member))
-            continue;
-
-        this[member] = bind(commandLineAPIImpl[member], commandLineAPIImpl);
-        this[member].toString = customToStringMethod(member);
-    }
-
     // $0
     this.__defineGetter__("$0", bind(commandLineAPIImpl._inspectedObject, commandLineAPIImpl));
 
     // $1-$99
-    for (var i = 1; i <= injectedScript._savedResults.length; ++i) {
-        var member = "$" + i;
-        if (member in inspectedWindow || inScopeVariables(member))
-            continue;
-
+    for (var i = 1; i <= injectedScript._savedResults.length; ++i)
         this.__defineGetter__("$" + i, bind(injectedScript._savedResult, injectedScript, i));
-    }
 
-    this.$_ = injectedScript._lastResult;
-    this.$exception = injectedScript._exceptionValue;
+    // Command Line API methods.
+    for (let member of CommandLineAPI.members_) {
+        this[member] = bind(commandLineAPIImpl[member], commandLineAPIImpl);
+        this[member].toString = function() { return "function " + member + "() { [Command Line API] }" };
+    }
 }
 
 /**
@@ -177,8 +116,8 @@
     $$: function (selector, start)
     {
         if (this._canQuerySelectorOnNode(start))
-            return slice(start.querySelectorAll(selector));
-        return slice(inspectedWindow.document.querySelectorAll(selector));
+            return Array.from(start.querySelectorAll(selector));
+        return Array.from(inspectedWindow.document.querySelectorAll(selector));
     },
 
     /**
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to