Title: [142114] trunk
Revision
142114
Author
[email protected]
Date
2013-02-07 07:00:13 -0800 (Thu, 07 Feb 2013)

Log Message

Web Inspector: support _javascript_ variable mutation in protocol and V8 bindings
https://bugs.webkit.org/show_bug.cgi?id=107829

Source/WebCore:

A new command is added to protocol description and the call is passed through
debugger agent through injected script and debugger script down to V8 mirror
API. JSC bindings got a thorw exception stub.

Only declarative _javascript_ scopes are supported (local, closure, catch). Other
scopes (global, with) are not supported by V8 and not supported by protocol, because
manual approach (direct property assigment) is available for them in form of evaluate
commands and is more desirable because of a complex nature of operation (it can throw
exception in several cases such as exception in setter function).

Patch by Peter Rybin <[email protected]> on 2013-02-07
Reviewed by Pavel Feldman.

Test: inspector-protocol/debugger-setVariableValue.html

* bindings/js/JSInjectedScriptHostCustom.cpp:
(WebCore::JSInjectedScriptHost::setFunctionVariableValue):
(WebCore):
* bindings/js/JSJavaScriptCallFrameCustom.cpp:
(WebCore::JSJavaScriptCallFrame::setVariableValue):
(WebCore):
* bindings/v8/DebuggerScript.js:
(.):
* bindings/v8/_javascript_CallFrame.cpp:
(WebCore::_javascript_CallFrame::setVariableValue):
(WebCore):
* bindings/v8/_javascript_CallFrame.h:
(_javascript_CallFrame):
* bindings/v8/ScriptDebugServer.cpp:
(WebCore::ScriptDebugServer::setFunctionVariableValue):
(WebCore):
* bindings/v8/ScriptDebugServer.h:
(ScriptDebugServer):
* bindings/v8/custom/V8InjectedScriptHostCustom.cpp:
(WebCore::V8InjectedScriptHost::setFunctionVariableValueCallback):
(WebCore):
* bindings/v8/custom/V8JavaScriptCallFrameCustom.cpp:
(WebCore::V8JavaScriptCallFrame::setVariableValueCallback):
(WebCore):
* inspector/InjectedScript.cpp:
(WebCore::InjectedScript::setVariableValue):
(WebCore):
* inspector/InjectedScript.h:
(InjectedScript):
* inspector/InjectedScriptHost.idl:
* inspector/InjectedScriptSource.js:
(.):
* inspector/Inspector.json:
* inspector/InspectorDebuggerAgent.cpp:
(WebCore::InspectorDebuggerAgent::getFunctionDetails):
(WebCore::InspectorDebuggerAgent::setVariableValue):
(WebCore):
* inspector/InspectorDebuggerAgent.h:
(InspectorDebuggerAgent):
* inspector/_javascript_CallFrame.idl:

LayoutTests:

Patch by Peter Rybin <[email protected]> on 2013-02-07
Reviewed by Pavel Feldman.

* inspector-protocol/debugger-setVariableValue-expected.txt: Added.
* inspector-protocol/debugger-setVariableValue.html: Added.
* inspector/console/command-line-api-expected.txt:
* platform/chromium/inspector-protocol/debugger-setVariableValue-expected.txt: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (142113 => 142114)


--- trunk/LayoutTests/ChangeLog	2013-02-07 14:56:30 UTC (rev 142113)
+++ trunk/LayoutTests/ChangeLog	2013-02-07 15:00:13 UTC (rev 142114)
@@ -1,3 +1,15 @@
+2013-02-07  Peter Rybin  <[email protected]>
+
+        Web Inspector: support _javascript_ variable mutation in protocol and V8 bindings
+        https://bugs.webkit.org/show_bug.cgi?id=107829
+
+        Reviewed by Pavel Feldman.
+
+        * inspector-protocol/debugger-setVariableValue-expected.txt: Added.
+        * inspector-protocol/debugger-setVariableValue.html: Added.
+        * inspector/console/command-line-api-expected.txt:
+        * platform/chromium/inspector-protocol/debugger-setVariableValue-expected.txt: Added.
+
 2013-02-07  Yury Semikhatsky  <[email protected]>
 
         Web Inspector: reduce number of native memory instrumentation categories

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


--- trunk/LayoutTests/inspector/console/command-line-api-expected.txt	2013-02-07 14:56:30 UTC (rev 142113)
+++ trunk/LayoutTests/inspector/console/command-line-api-expected.txt	2013-02-07 15:00:13 UTC (rev 142114)
@@ -1,4 +1,4 @@
-CONSOLE MESSAGE: line 1060: The console function $() has changed from $=getElementById(id) to $=querySelector(selector). You might try $("#%s")
+CONSOLE MESSAGE: line 1121: The console function $() has changed from $=getElementById(id) to $=querySelector(selector). You might try $("#%s")
 Tests that command line api works.
 
 

Added: trunk/LayoutTests/inspector-protocol/debugger-setVariableValue-expected.txt (0 => 142114)


--- trunk/LayoutTests/inspector-protocol/debugger-setVariableValue-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/inspector-protocol/debugger-setVariableValue-expected.txt	2013-02-07 15:00:13 UTC (rev 142114)
@@ -0,0 +1,4 @@
+Closure returns: {"type":"string","value":"ttttrue52013"}
+ (expected: 'ttttrue52013')
+Protocol command 'Debugger.setVariableValue' failed
+

Added: trunk/LayoutTests/inspector-protocol/debugger-setVariableValue.html (0 => 142114)


--- trunk/LayoutTests/inspector-protocol/debugger-setVariableValue.html	                        (rev 0)
+++ trunk/LayoutTests/inspector-protocol/debugger-setVariableValue.html	2013-02-07 15:00:13 UTC (rev 142114)
@@ -0,0 +1,181 @@
+<html>
+<head>
+<script type="text/_javascript_" src=""
+<script>
+
+function test()
+{
+    // A general-purpose engine for sending a sequence of protocol commands.
+    // The clients provide requests and response handlers, while the engine catches
+    // errors and makes sure that once there's nothing to do completeTest() is called.
+    // @param step is an object with command, params and callback fields 
+    function runRequestSeries(step) {
+        processStep(step);
+
+        function processStep(s) {
+            try {
+                processStepOrFail(s);
+            } catch (e) {
+                InspectorTest.log(e.stack);
+                InspectorTest.completeTest();
+            }
+        }
+        
+        function processStepOrFail(s) {
+            if (!s) {
+                InspectorTest.completeTest();
+                return;
+            }
+            if (!s.command) {
+                // A simple loopback step.
+                var next = s.callback();
+                processStep(next);
+                return;
+            }
+
+            var innerCallback = function(response) {
+                var next;
+                if ("error" in response) {
+                    if (!("errorHandler" in s)) {
+                        // Error message is not logged intentionally, it may be platform-specific.
+                        InspectorTest.log("Protocol command '" + s.command + "' failed");
+                        InspectorTest.completeTest();
+                        return;
+                    }
+                    try {
+                        next = s.errorHandler(response.error);
+                    } catch (e) {
+                        InspectorTest.log(e.stack);
+                        InspectorTest.completeTest();
+                        return;
+                    }
+                } else {
+                    try {
+                        next = s.callback(response.result);
+                    } catch (e) {
+                        InspectorTest.log(e.stack);
+                        InspectorTest.completeTest();
+                        return;
+                    }
+                }
+                processStep(next);
+            }
+            InspectorTest.sendCommand(s.command, s.params, innerCallback);
+        }
+    }
+    
+    var firstStep = { callback: enableDebugger };
+
+    runRequestSeries(firstStep);
+    
+    function enableDebugger() {
+        return { command: "Debugger.enable", params: {}, callback: evalFunction };
+    }
+    
+    // Testing function/closure scopes.
+    
+    function evalFunction(response) {
+        var _expression_ = "(function(p){var r=5;with({year:2013}){return function Closure(q){return p+q+r+year};}})('ttt')";
+        return { command: "Runtime.evaluate", params: {_expression_: _expression_}, callback: callbackEvalFunction };
+    }
+    
+    function callbackEvalFunction(result) {
+        var id = result.result.objectId;
+        if (id === undefined)
+            throw new Error("objectId is expected");
+        return createCheckFunctionStepChain(id);
+    }
+
+    function createCheckFunctionStepChain(functionObjectId) {
+        var params = {
+            objectId: functionObjectId,
+            functionDeclaration: "function(){return this(true);}"
+        };
+        return {
+            command: "Runtime.callFunctionOn", params: params, callback: callbackLogClosureEval
+        };
+
+	    function callbackLogClosureEval(result) {
+	        InspectorTest.log("Closure returns: " + JSON.stringify(result.result));
+	        InspectorTest.log(" (expected: 'ttttrue52013')");
+	        
+	        var params = {
+	            functionObjectId: functionObjectId,
+	            scopeNumber: 1,
+	            variableName: "r",
+	            newValue: { value: 4 }
+	        };
+	        return {
+	            command: "Debugger.setVariableValue", params: params, callback: setVariableCallback
+	        };
+	    }
+	    
+	    function setVariableCallback() {
+	        InspectorTest.log("Debugger.setVariableValue OK");
+	        
+	        var params = {
+	            objectId: functionObjectId,
+	            functionDeclaration: "function(){return this(true);}"
+	        };
+	        return {
+	            command: "Runtime.callFunctionOn", params: params, callback: callbackLogClosureEval2
+	        };
+	        
+	    }
+
+	    function callbackLogClosureEval2(result) {
+	        InspectorTest.log("Closure returns: " + JSON.stringify(result.result));
+	        InspectorTest.log(" (expected: 'ttttrue42013')");
+	        
+	        var params = {
+	            // No target is specified
+	            scopeNumber: 1,
+	            variableName: "r",
+	            newValue: { value: 4 }
+	        };
+	        return {
+	            command: "Debugger.setVariableValue", params: params, errorHandler: setVariableErrorCallback3
+	        };
+	    }
+	    
+	    function setVariableErrorCallback3(error) {
+	        InspectorTest.log("Expected error: " + JSON.stringify(error));
+
+	        var params = {
+	            functionObjectId: functionObjectId,
+	            scopeNumber: 100, // Wrong scope number
+	            variableName: "r",
+	            newValue: { value: 4 }
+	        };
+	        return {
+	            command: "Debugger.setVariableValue", params: params, errorHandler: setVariableErrorCallback4
+	        };
+	    }
+	    
+	    function setVariableErrorCallback4(error) {
+	        InspectorTest.log("Expected error");
+
+	        var params = {
+	            functionObjectId: functionObjectId,
+	            scopeNumber: 1,
+	            variableName: "bad", // Wrong variable name
+	            newValue: { value: 4 }
+	        };
+	        return {
+	            command: "Debugger.setVariableValue", params: params, errorHandler: setVariableErrorCallback5
+	        };
+	    }
+	    
+	    function setVariableErrorCallback5(error) {
+	        InspectorTest.log("Expected error");
+	        
+	        // End of test.
+	        return;
+	    }
+    }
+}
+</script>
+</head>
+<body _onLoad_="runTest();">
+</body>
+</html>

Added: trunk/LayoutTests/platform/chromium/inspector-protocol/debugger-setVariableValue-expected.txt (0 => 142114)


--- trunk/LayoutTests/platform/chromium/inspector-protocol/debugger-setVariableValue-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/platform/chromium/inspector-protocol/debugger-setVariableValue-expected.txt	2013-02-07 15:00:13 UTC (rev 142114)
@@ -0,0 +1,9 @@
+Closure returns: {"type":"string","value":"ttttrue52013"}
+ (expected: 'ttttrue52013')
+Debugger.setVariableValue OK
+Closure returns: {"type":"string","value":"ttttrue42013"}
+ (expected: 'ttttrue42013')
+Expected error: {"code":-32000,"message":"Either call frame or function object must be specified"}
+Expected error
+Expected error
+

Modified: trunk/Source/WebCore/ChangeLog (142113 => 142114)


--- trunk/Source/WebCore/ChangeLog	2013-02-07 14:56:30 UTC (rev 142113)
+++ trunk/Source/WebCore/ChangeLog	2013-02-07 15:00:13 UTC (rev 142114)
@@ -1,3 +1,63 @@
+2013-02-07  Peter Rybin  <[email protected]>
+
+        Web Inspector: support _javascript_ variable mutation in protocol and V8 bindings
+        https://bugs.webkit.org/show_bug.cgi?id=107829
+
+        A new command is added to protocol description and the call is passed through
+        debugger agent through injected script and debugger script down to V8 mirror
+        API. JSC bindings got a thorw exception stub.
+
+        Only declarative _javascript_ scopes are supported (local, closure, catch). Other
+        scopes (global, with) are not supported by V8 and not supported by protocol, because
+        manual approach (direct property assigment) is available for them in form of evaluate
+        commands and is more desirable because of a complex nature of operation (it can throw
+        exception in several cases such as exception in setter function).
+
+        Reviewed by Pavel Feldman.
+
+        Test: inspector-protocol/debugger-setVariableValue.html
+
+        * bindings/js/JSInjectedScriptHostCustom.cpp:
+        (WebCore::JSInjectedScriptHost::setFunctionVariableValue):
+        (WebCore):
+        * bindings/js/JSJavaScriptCallFrameCustom.cpp:
+        (WebCore::JSJavaScriptCallFrame::setVariableValue):
+        (WebCore):
+        * bindings/v8/DebuggerScript.js:
+        (.):
+        * bindings/v8/_javascript_CallFrame.cpp:
+        (WebCore::_javascript_CallFrame::setVariableValue):
+        (WebCore):
+        * bindings/v8/_javascript_CallFrame.h:
+        (_javascript_CallFrame):
+        * bindings/v8/ScriptDebugServer.cpp:
+        (WebCore::ScriptDebugServer::setFunctionVariableValue):
+        (WebCore):
+        * bindings/v8/ScriptDebugServer.h:
+        (ScriptDebugServer):
+        * bindings/v8/custom/V8InjectedScriptHostCustom.cpp:
+        (WebCore::V8InjectedScriptHost::setFunctionVariableValueCallback):
+        (WebCore):
+        * bindings/v8/custom/V8JavaScriptCallFrameCustom.cpp:
+        (WebCore::V8JavaScriptCallFrame::setVariableValueCallback):
+        (WebCore):
+        * inspector/InjectedScript.cpp:
+        (WebCore::InjectedScript::setVariableValue):
+        (WebCore):
+        * inspector/InjectedScript.h:
+        (InjectedScript):
+        * inspector/InjectedScriptHost.idl:
+        * inspector/InjectedScriptSource.js:
+        (.):
+        * inspector/Inspector.json:
+        * inspector/InspectorDebuggerAgent.cpp:
+        (WebCore::InspectorDebuggerAgent::getFunctionDetails):
+        (WebCore::InspectorDebuggerAgent::setVariableValue):
+        (WebCore):
+        * inspector/InspectorDebuggerAgent.h:
+        (InspectorDebuggerAgent):
+        * inspector/_javascript_CallFrame.idl:
+
 2013-02-07  Caio Marcelo de Oliveira Filho  <[email protected]>
 
         [CoordinatedGraphics] Use ScrollingCoordinator to track fixed layers

Modified: trunk/Source/WebCore/bindings/js/JSInjectedScriptHostCustom.cpp (142113 => 142114)


--- trunk/Source/WebCore/bindings/js/JSInjectedScriptHostCustom.cpp	2013-02-07 14:56:30 UTC (rev 142113)
+++ trunk/Source/WebCore/bindings/js/JSInjectedScriptHostCustom.cpp	2013-02-07 15:00:13 UTC (rev 142114)
@@ -307,6 +307,13 @@
     return result;
 }
 
+JSValue JSInjectedScriptHost::setFunctionVariableValue(JSC::ExecState* exec)
+{
+    // FIXME: implement this. https://bugs.webkit.org/show_bug.cgi?id=107830
+    throwError(exec, createTypeError(exec, "Variable value mutation is not supported"));
+    return jsUndefined();
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(INSPECTOR)

Modified: trunk/Source/WebCore/bindings/js/JSJavaScriptCallFrameCustom.cpp (142113 => 142114)


--- trunk/Source/WebCore/bindings/js/JSJavaScriptCallFrameCustom.cpp	2013-02-07 14:56:30 UTC (rev 142113)
+++ trunk/Source/WebCore/bindings/js/JSJavaScriptCallFrameCustom.cpp	2013-02-07 15:00:13 UTC (rev 142114)
@@ -130,6 +130,13 @@
     return jsUndefined();
 }
 
+JSValue JSJavaScriptCallFrame::setVariableValue(JSC::ExecState* exec)
+{
+    // FIXME: implement this. https://bugs.webkit.org/show_bug.cgi?id=107830
+    throwError(exec, createTypeError(exec, "Variable value mutation is not supported"));
+    return jsUndefined();
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(_javascript__DEBUGGER)

Modified: trunk/Source/WebCore/bindings/v8/DebuggerScript.js (142113 => 142114)


--- trunk/Source/WebCore/bindings/v8/DebuggerScript.js	2013-02-07 14:56:30 UTC (rev 142113)
+++ trunk/Source/WebCore/bindings/v8/DebuggerScript.js	2013-02-07 15:00:13 UTC (rev 142114)
@@ -93,6 +93,23 @@
     return result;
 }
 
+DebuggerScript.setFunctionVariableValue = function(functionValue, scopeIndex, variableName, newValue)
+{
+    var mirror = MakeMirror(functionValue);
+    if (!mirror.isFunction())
+        throw new Error("Function value has incorrect type");
+    return DebuggerScript._setScopeVariableValue(mirror, scopeIndex, variableName, newValue);
+}
+
+DebuggerScript._setScopeVariableValue = function(scopeHolder, scopeIndex, variableName, newValue)
+{
+    var scopeMirror = scopeHolder.scope(scopeIndex);
+    if (!scopeMirror)
+        throw new Error("Incorrect scope index");
+    scopeMirror.setVariableValue(variableName, newValue);
+    return undefined;
+}
+
 DebuggerScript.getScripts = function(contextData)
 {
     var result = [];
@@ -295,6 +312,11 @@
         return Debug.LiveEdit.RestartFrame(frameMirror);
     }
 
+    function setVariableValue(scopeNumber, variableName, newValue)
+    {
+        return DebuggerScript._setScopeVariableValue(frameMirror, scopeNumber, variableName, newValue);
+    }
+
     return {
         "sourceID": sourceID,
         "line": location ? location.line : 0,
@@ -305,7 +327,8 @@
         "scopeType": scopeType,
         "evaluate": evaluate,
         "caller": callerFrame,
-        "restart": restart
+        "restart": restart,
+        "setVariableValue": setVariableValue
     };
 }
 

Modified: trunk/Source/WebCore/bindings/v8/_javascript_CallFrame.cpp (142113 => 142114)


--- trunk/Source/WebCore/bindings/v8/_javascript_CallFrame.cpp	2013-02-07 14:56:30 UTC (rev 142113)
+++ trunk/Source/WebCore/bindings/v8/_javascript_CallFrame.cpp	2013-02-07 15:00:13 UTC (rev 142114)
@@ -134,6 +134,17 @@
     return result;
 }
 
+v8::Handle<v8::Value> _javascript_CallFrame::setVariableValue(int scopeNumber, const String& variableName, v8::Handle<v8::Value> newValue)
+{
+    v8::Handle<v8::Function> setVariableValueFunction = v8::Handle<v8::Function>::Cast(m_callFrame.get()->Get(v8::String::NewSymbol("setVariableValue")));
+    v8::Handle<v8::Value> argv[] = {
+        v8::Handle<v8::Value>(v8::Integer::New(scopeNumber)),
+        v8String(variableName, m_debuggerContext->GetIsolate()),
+        newValue
+    };
+    return setVariableValueFunction->Call(m_callFrame.get(), 3, argv);
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(_javascript__DEBUGGER)

Modified: trunk/Source/WebCore/bindings/v8/_javascript_CallFrame.h (142113 => 142114)


--- trunk/Source/WebCore/bindings/v8/_javascript_CallFrame.h	2013-02-07 14:56:30 UTC (rev 142113)
+++ trunk/Source/WebCore/bindings/v8/_javascript_CallFrame.h	2013-02-07 15:00:13 UTC (rev 142114)
@@ -61,6 +61,7 @@
     
     v8::Handle<v8::Value> evaluate(const String& _expression_);
     v8::Handle<v8::Value> restart();
+    v8::Handle<v8::Value> setVariableValue(int scopeNumber, const String& variableName, v8::Handle<v8::Value> newValue);
     
 private:
     _javascript_CallFrame(v8::Handle<v8::Context> debuggerContext, v8::Handle<v8::Object> callFrame);

Modified: trunk/Source/WebCore/bindings/v8/ScriptDebugServer.cpp (142113 => 142114)


--- trunk/Source/WebCore/bindings/v8/ScriptDebugServer.cpp	2013-02-07 14:56:30 UTC (rev 142113)
+++ trunk/Source/WebCore/bindings/v8/ScriptDebugServer.cpp	2013-02-07 15:00:13 UTC (rev 142114)
@@ -557,7 +557,22 @@
     return callDebuggerMethod("getInternalProperties", 1, argv);
 }
 
+v8::Local<v8::Value> ScriptDebugServer::setFunctionVariableValue(v8::Handle<v8::Value> functionValue, int scopeNumber, const String& variableName, v8::Handle<v8::Value> newValue)
+{
+    v8::Local<v8::Context> debuggerContext = v8::Debug::GetDebugContext();
+    if (m_debuggerScript.get().IsEmpty())
+        return *(v8::ThrowException(v8::String::New("Debugging is not enabled.")));
 
+    v8::Handle<v8::Value> argv[] = {
+        functionValue,
+        v8::Handle<v8::Value>(v8::Integer::New(scopeNumber)),
+        v8String(variableName, debuggerContext->GetIsolate()),
+        newValue
+    };
+    return callDebuggerMethod("setFunctionVariableValue", 4, argv);
+}
+
+
 bool ScriptDebugServer::isPaused()
 {
     return !m_executionState.get().IsEmpty();

Modified: trunk/Source/WebCore/bindings/v8/ScriptDebugServer.h (142113 => 142114)


--- trunk/Source/WebCore/bindings/v8/ScriptDebugServer.h	2013-02-07 14:56:30 UTC (rev 142113)
+++ trunk/Source/WebCore/bindings/v8/ScriptDebugServer.h	2013-02-07 15:00:13 UTC (rev 142114)
@@ -101,7 +101,9 @@
 
     v8::Local<v8::Value> functionScopes(v8::Handle<v8::Function>);
     v8::Local<v8::Value> getInternalProperties(v8::Handle<v8::Object>&);
+    v8::Local<v8::Value> setFunctionVariableValue(v8::Handle<v8::Value> functionValue, int scopeNumber, const String& variableName, v8::Handle<v8::Value> newValue);
 
+
     virtual void compileScript(ScriptState*, const String& _expression_, const String& sourceURL, String* scriptId, String* exceptionMessage);
     virtual void clearCompiledScripts();
     virtual void runScript(ScriptState*, const String& scriptId, ScriptValue* result, bool* wasThrown, String* exceptionMessage);

Modified: trunk/Source/WebCore/bindings/v8/custom/V8InjectedScriptHostCustom.cpp (142113 => 142114)


--- trunk/Source/WebCore/bindings/v8/custom/V8InjectedScriptHostCustom.cpp	2013-02-07 14:56:30 UTC (rev 142113)
+++ trunk/Source/WebCore/bindings/v8/custom/V8InjectedScriptHostCustom.cpp	2013-02-07 15:00:13 UTC (rev 142114)
@@ -321,6 +321,19 @@
     return script->Run();
 }
 
+v8::Handle<v8::Value> V8InjectedScriptHost::setFunctionVariableValueCallback(const v8::Arguments& args)
+{
+    v8::Handle<v8::Value> functionValue = args[0];
+    int scopeIndex = args[1]->Int32Value();
+    String variableName = toWebCoreStringWithUndefinedOrNullCheck(args[2]);
+    v8::Handle<v8::Value> newValue = args[3];
+
+    InjectedScriptHost* host = V8InjectedScriptHost::toNative(args.Holder());
+    ScriptDebugServer& debugServer = host->scriptDebugServer();
+    return debugServer.setFunctionVariableValue(functionValue, scopeIndex, variableName, newValue);
+}
+
+
 } // namespace WebCore
 
 #endif // ENABLE(INSPECTOR)

Modified: trunk/Source/WebCore/bindings/v8/custom/V8JavaScriptCallFrameCustom.cpp (142113 => 142114)


--- trunk/Source/WebCore/bindings/v8/custom/V8JavaScriptCallFrameCustom.cpp	2013-02-07 14:56:30 UTC (rev 142113)
+++ trunk/Source/WebCore/bindings/v8/custom/V8JavaScriptCallFrameCustom.cpp	2013-02-07 15:00:13 UTC (rev 142114)
@@ -50,6 +50,15 @@
     return impl->restart();
 }
 
+v8::Handle<v8::Value> V8JavaScriptCallFrame::setVariableValueCallback(const v8::Arguments& args)
+{
+    _javascript_CallFrame* impl = V8JavaScriptCallFrame::toNative(args.Holder());
+    int scopeIndex = args[0]->Int32Value();
+    String variableName = toWebCoreStringWithUndefinedOrNullCheck(args[1]);
+    v8::Handle<v8::Value> newValue = args[2];
+    return impl->setVariableValue(scopeIndex, variableName, newValue);
+}
+
 v8::Handle<v8::Value> V8JavaScriptCallFrame::scopeChainAccessorGetter(v8::Local<v8::String> name, const v8::AccessorInfo& info)
 {
     _javascript_CallFrame* impl = V8JavaScriptCallFrame::toNative(info.Holder());

Modified: trunk/Source/WebCore/inspector/InjectedScript.cpp (142113 => 142114)


--- trunk/Source/WebCore/inspector/InjectedScript.cpp	2013-02-07 14:56:30 UTC (rev 142113)
+++ trunk/Source/WebCore/inspector/InjectedScript.cpp	2013-02-07 15:00:13 UTC (rev 142114)
@@ -116,6 +116,36 @@
     *errorString = "Internal error";
 }
 
+void InjectedScript::setVariableValue(ErrorString* errorString, const ScriptValue& callFrames, const String* callFrameIdOpt, const String* functionObjectIdOpt, int scopeNumber, const String& variableName, const String& newValueStr)
+{
+    ScriptFunctionCall function(injectedScriptObject(), "setVariableValue");
+    if (callFrameIdOpt) {
+        function.appendArgument(callFrames);
+        function.appendArgument(*callFrameIdOpt);
+    } else {
+        function.appendArgument(false);
+        function.appendArgument(false);
+    }
+    if (functionObjectIdOpt)
+        function.appendArgument(*functionObjectIdOpt);
+    else
+        function.appendArgument(false);
+    function.appendArgument(scopeNumber);
+    function.appendArgument(variableName);
+    function.appendArgument(newValueStr);
+    RefPtr<InspectorValue> resultValue;
+    makeCall(function, &resultValue);
+    if (!resultValue) {
+        *errorString = "Internal error";
+        return;
+    }
+    if (resultValue->type() == InspectorValue::TypeString) {
+        resultValue->asString(errorString);
+        return;
+    }
+    // Normal return.
+}
+
 void InjectedScript::getFunctionDetails(ErrorString* errorString, const String& functionId, RefPtr<FunctionDetails>* result)
 {
     ScriptFunctionCall function(injectedScriptObject(), "getFunctionDetails");

Modified: trunk/Source/WebCore/inspector/InjectedScript.h (142113 => 142114)


--- trunk/Source/WebCore/inspector/InjectedScript.h	2013-02-07 14:56:30 UTC (rev 142113)
+++ trunk/Source/WebCore/inspector/InjectedScript.h	2013-02-07 15:00:13 UTC (rev 142114)
@@ -80,6 +80,7 @@
                              RefPtr<TypeBuilder::Runtime::RemoteObject>* result,
                              TypeBuilder::OptOutput<bool>* wasThrown);
     void restartFrame(ErrorString*, const ScriptValue& callFrames, const String& callFrameId, RefPtr<InspectorObject>* result);
+    void setVariableValue(ErrorString*, const ScriptValue& callFrames, const String* callFrameIdOpt, const String* functionObjectIdOpt, int scopeNumber, const String& variableName, const String& newValueStr);
     void getFunctionDetails(ErrorString*, const String& functionId, RefPtr<TypeBuilder::Debugger::FunctionDetails>* result);
     void getProperties(ErrorString*, const String& objectId, bool ownProperties, RefPtr<TypeBuilder::Array<TypeBuilder::Runtime::PropertyDescriptor> >* result);
     void getInternalProperties(ErrorString*, const String& objectId, RefPtr<TypeBuilder::Array<TypeBuilder::Runtime::InternalPropertyDescriptor> >* result);

Modified: trunk/Source/WebCore/inspector/InjectedScriptHost.idl (142113 => 142114)


--- trunk/Source/WebCore/inspector/InjectedScriptHost.idl	2013-02-07 14:56:30 UTC (rev 142113)
+++ trunk/Source/WebCore/inspector/InjectedScriptHost.idl	2013-02-07 15:00:13 UTC (rev 142114)
@@ -49,4 +49,6 @@
     [Custom] DOMString databaseId(in DOMObject database);
     [Custom] DOMString storageId(in DOMObject storage);
     [Custom] DOMObject evaluate(in DOMString text);
+    // Only declarative scope (local, with and catch) is accepted. Returns undefined. 
+    [Custom] DOMObject setFunctionVariableValue(in DOMObject functionObject, in int scopeIndex, in DOMString variableName, DOMObject newValue);
 };

Modified: trunk/Source/WebCore/inspector/InjectedScriptSource.js (142113 => 142114)


--- trunk/Source/WebCore/inspector/InjectedScriptSource.js	2013-02-07 14:56:30 UTC (rev 142113)
+++ trunk/Source/WebCore/inspector/InjectedScriptSource.js	2013-02-07 15:00:13 UTC (rev 142114)
@@ -427,21 +427,13 @@
             var resolvedArgs = [];
             args = InjectedScriptHost.evaluate(args);
             for (var i = 0; i < args.length; ++i) {
-                objectId = args[i].objectId;
-                if (objectId) {
-                    var parsedArgId = this._parseObjectId(objectId);
-                    if (!parsedArgId || parsedArgId["injectedScriptId"] !== injectedScriptId)
-                        return "Arguments should belong to the same _javascript_ world as the target object.";
-
-                    var resolvedArg = this._objectForId(parsedArgId);
-                    if (!this._isDefined(resolvedArg))
-                        return "Could not find object with given id";
-
-                    resolvedArgs.push(resolvedArg);
-                } else if ("value" in args[i])
-                    resolvedArgs.push(args[i].value);
-                else
-                    resolvedArgs.push(undefined);
+                var resolvedCallArgument;
+                try {
+                    resolvedCallArgument = this._resolveCallArgument(args[i]);
+                } catch (e) {
+                    return String(e);
+                }
+                resolvedArgs.push(resolvedCallArgument)
             }
         }
 
@@ -457,7 +449,31 @@
             return this._createThrownValue(e, objectGroup);
         }
     },
+    
+    /**
+     * Resolves a value from CallArgument description.
+     * @param {RuntimeAgent.CallArgument} callArgumentJson
+     * @return {*} resolved value
+     * @throw {string} error message
+     */
+    _resolveCallArgument: function(callArgumentJson) {
+        var objectId = callArgumentJson.objectId;
+        if (objectId) {
+            var parsedArgId = this._parseObjectId(objectId);
+            if (!parsedArgId || parsedArgId["injectedScriptId"] !== injectedScriptId)
+                throw "Arguments should belong to the same _javascript_ world as the target object.";
 
+            var resolvedArg = this._objectForId(parsedArgId);
+            if (!this._isDefined(resolvedArg))
+                throw "Could not find object with given id";
+
+            return resolvedArg;
+        } else if ("value" in callArgumentJson)
+            return callArgumentJson.value;
+        else
+            return undefined;
+    },
+
     /**
      * @param {Function} evalFunction
      * @param {Object} object
@@ -576,7 +592,52 @@
     },
 
     /**
+     * Either callFrameId or functionObjectId must be specified.
      * @param {Object} topCallFrame
+     * @param {string|boolean} callFrameId or false
+     * @param {string|boolean} functionObjectId or false
+     * @param {integer} scopeNumber
+     * @param {string} variableName
+     * @param {string} newValueJsonString RuntimeAgent.CallArgument structure serialized as string 
+     * @return {string|undefined} undefined if success or an error message 
+     */
+    setVariableValue: function(topCallFrame, callFrameId, functionObjectId, scopeNumber, variableName, newValueJsonString)
+    {   
+        var setter;
+        if (callFrameId) {
+            var callFrame = this._callFrameForId(topCallFrame, callFrameId);
+            if (!callFrame)
+                return "Could not find call frame with given id";
+            setter = callFrame.setVariableValue.bind(callFrame);    
+        } else {
+            var parsedFunctionId = this._parseObjectId(functionObjectId);
+            var func = this._objectForId(parsedFunctionId);
+            if (typeof func !== "function")
+                return "Cannot resolve function by id.";
+            setter = InjectedScriptHost.setFunctionVariableValue.bind(InjectedScriptHost, func); 
+        }
+        var newValueJson;
+        try {
+            newValueJson = InjectedScriptHost.evaluate("(" + newValueJsonString + ")");
+        } catch (e) {
+            return "Failed to parse new value JSON " + newValueJsonString + " : " + e;
+        }
+        var resolvedValue;
+        try {
+            resolvedValue = this._resolveCallArgument(newValueJson);
+        } catch (e) {
+            return String(e);
+        }
+        try {
+            setter(scopeNumber, variableName, resolvedValue);
+        } catch (e) {
+            return "Failed to change variable value: " + e;
+        }
+        return undefined;
+    },
+
+    /**
+     * @param {Object} topCallFrame
      * @param {string} callFrameId
      * @return {Object}
      */

Modified: trunk/Source/WebCore/inspector/Inspector.json (142113 => 142114)


--- trunk/Source/WebCore/inspector/Inspector.json	2013-02-07 14:56:30 UTC (rev 142113)
+++ trunk/Source/WebCore/inspector/Inspector.json	2013-02-07 15:00:13 UTC (rev 142114)
@@ -2933,6 +2933,18 @@
                 ],
                 "hidden": true,
                 "description": "Sets overlay message."
+            },
+            {
+                "name": "setVariableValue",
+                "parameters": [
+                    { "name": "callFrameId", "$ref": "CallFrameId", "optional": true, "description": "Id of callframe that holds variable." },
+                    { "name": "functionObjectId", "$ref": "Runtime.RemoteObjectId", "optional": true, "description": "Object id of closure (function) that holds variable." },
+                    { "name": "scopeNumber", "type": "integer", "description": "0-based number of scope as was listed in scope chain. Only 'local', 'closure' and 'catch' scope types are allowed. Other scopes could be manipulated manually." },
+                    { "name": "variableName", "type": "string", "description": "Variable name." },
+                    { "name": "newValue", "$ref": "Runtime.CallArgument", "description": "New variable value." }
+                ],
+                "hidden": true,
+                "description": "Changes value of variable in a callframe or a closure. Either callframe or function must be specified. Object-based scopes are not supported and must be mutated manually."
             }
         ],
         "events": [

Modified: trunk/Source/WebCore/inspector/InspectorDebuggerAgent.cpp (142113 => 142114)


--- trunk/Source/WebCore/inspector/InspectorDebuggerAgent.cpp	2013-02-07 14:56:30 UTC (rev 142113)
+++ trunk/Source/WebCore/inspector/InspectorDebuggerAgent.cpp	2013-02-07 15:00:13 UTC (rev 142114)
@@ -411,7 +411,7 @@
 {
     InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(functionId);
     if (injectedScript.hasNoValue()) {
-        *errorString = "Inspected frame has gone";
+        *errorString = "Function object id is obsolete";
         return;
     }
     injectedScript.getFunctionDetails(errorString, functionId, &details);
@@ -580,6 +580,30 @@
 {
 }
 
+void InspectorDebuggerAgent::setVariableValue(ErrorString* errorString, const String* callFrameId, const String* functionObjectId, int scopeNumber, const String& variableName, const RefPtr<InspectorObject>& newValue)
+{
+    InjectedScript injectedScript;
+    if (callFrameId) {
+        injectedScript = m_injectedScriptManager->injectedScriptForObjectId(*callFrameId);
+        if (injectedScript.hasNoValue()) {
+            *errorString = "Inspected frame has gone";
+            return;
+        }
+    } else if (functionObjectId) {
+        injectedScript = m_injectedScriptManager->injectedScriptForObjectId(*functionObjectId);
+        if (injectedScript.hasNoValue()) {
+            *errorString = "Function object id cannot be resolved";
+            return;
+        }
+    } else {
+        *errorString = "Either call frame or function object must be specified";
+        return;
+    }
+    String newValueString = newValue->toJSONString();
+
+    injectedScript.setVariableValue(errorString, m_currentCallStack, callFrameId, functionObjectId, scopeNumber, variableName, newValueString);
+}
+
 void InspectorDebuggerAgent::scriptExecutionBlockedByCSP(const String& directiveText)
 {
     if (scriptDebugServer().pauseOnExceptionsState() != ScriptDebugServer::DontPauseOnExceptions) {

Modified: trunk/Source/WebCore/inspector/InspectorDebuggerAgent.h (142113 => 142114)


--- trunk/Source/WebCore/inspector/InspectorDebuggerAgent.h	2013-02-07 14:56:30 UTC (rev 142113)
+++ trunk/Source/WebCore/inspector/InspectorDebuggerAgent.h	2013-02-07 15:00:13 UTC (rev 142114)
@@ -113,6 +113,7 @@
     void compileScript(ErrorString*, const String& _expression_, const String& sourceURL, TypeBuilder::OptOutput<TypeBuilder::Debugger::ScriptId>*, TypeBuilder::OptOutput<String>* syntaxErrorMessage);
     void runScript(ErrorString*, const TypeBuilder::Debugger::ScriptId&, const int* executionContextId, const String* objectGroup, const bool* doNotPauseOnExceptionsAndMuteConsole, RefPtr<TypeBuilder::Runtime::RemoteObject>& result, TypeBuilder::OptOutput<bool>* wasThrown);
     virtual void setOverlayMessage(ErrorString*, const String*);
+    virtual void setVariableValue(ErrorString*, const String* in_callFrame, const String* in_functionObjectId, int in_scopeNumber, const String& in_variableName, const RefPtr<InspectorObject>& in_newValue);
 
     void schedulePauseOnNextStatement(InspectorFrontend::Debugger::Reason::Enum breakReason, PassRefPtr<InspectorObject> data);
     void cancelPauseOnNextStatement();

Modified: trunk/Source/WebCore/inspector/_javascript_CallFrame.idl (142113 => 142114)


--- trunk/Source/WebCore/inspector/_javascript_CallFrame.idl	2013-02-07 14:56:30 UTC (rev 142113)
+++ trunk/Source/WebCore/inspector/_javascript_CallFrame.idl	2013-02-07 15:00:13 UTC (rev 142114)
@@ -39,6 +39,9 @@
 
     [Custom] void evaluate(in DOMString script);
     [Custom] DOMObject restart();
+    
+    // Only declarative scope (local, with and catch) is accepted. Returns undefined. 
+    [Custom] DOMObject setVariableValue(in int scopeIndex, in DOMString variableName, DOMObject newValue);
 
     readonly attribute _javascript_CallFrame caller;
     readonly attribute long sourceID;
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to