Bug 1548098 - Implement Runtime.callFunctionOn's returnByValue argument. r=remote-protocol-reviewers,ato
authorAlexandre Poirot <poirot.alex@gmail.com>
Mon, 13 May 2019 16:10:22 +0000
changeset 532447 55505348c64ff409345e16a6f18c7ae411b02b1f
parent 532446 a0cb021737218a16e04aa0fced67284010f50118
child 532448 087726ba0d15387d6a18b152a1687c2b562b1806
push id11268
push usercsabou@mozilla.com
push dateTue, 14 May 2019 15:24:22 +0000
treeherdermozilla-beta@5fb7fcd568d6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersremote-protocol-reviewers, ato
bugs1548098
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1548098 - Implement Runtime.callFunctionOn's returnByValue argument. r=remote-protocol-reviewers,ato Differential Revision: https://phabricator.services.mozilla.com/D30265
remote/domains/content/Runtime.jsm
remote/domains/content/runtime/ExecutionContext.jsm
remote/test/browser/browser_runtime_callFunctionOn.js
--- a/remote/domains/content/Runtime.jsm
+++ b/remote/domains/content/Runtime.jsm
@@ -83,17 +83,20 @@ class Runtime extends ContentProcessDoma
       throw new Error(`Unable to find execution context with id: ${request.executionContextId}`);
     }
     if (typeof(request.functionDeclaration) != "string") {
       throw new Error("Expect 'functionDeclaration' attribute to be passed and be a string");
     }
     if (request.arguments && !Array.isArray(request.arguments)) {
       throw new Error("Expect 'arguments' to be an array");
     }
-    return context.callFunctionOn(request.functionDeclaration, request.arguments);
+    if (request.returnByValue && typeof(request.returnByValue) != "boolean") {
+      throw new Error("Expect 'returnByValue' to be a boolean");
+    }
+    return context.callFunctionOn(request.functionDeclaration, request.arguments, request.returnByValue);
   }
 
   get _debugger() {
     if (this.__debugger) {
       return this.__debugger;
     }
     this.__debugger = new Debugger();
     return this.__debugger;
--- a/remote/domains/content/runtime/ExecutionContext.jsm
+++ b/remote/domains/content/runtime/ExecutionContext.jsm
@@ -85,17 +85,17 @@ class ExecutionContext {
     // If that isn't an Error, consider the exception as a JS value
     return {
       exceptionDetails: {
         exception: this._toRemoteObject(exception),
       },
     };
   }
 
-  async callFunctionOn(functionDeclaration, callArguments = []) {
+  async callFunctionOn(functionDeclaration, callArguments = [], returnByValue = false) {
     // First evaluate the function
     const fun = this._debuggee.executeInGlobal("(" + functionDeclaration + ")");
     if (!fun) {
       return {
         exceptionDetails: {
           text: "Evaluation terminated!",
         },
       };
@@ -108,30 +108,41 @@ class ExecutionContext {
     // into JS values
     const args = callArguments.map(arg => this._fromCallArgument(arg));
 
     // Finally, call the function with these arguments
     const rv = fun.return.apply(null, args);
     if (rv.throw) {
       return this._returnError(rv.throw);
     }
+    if (returnByValue) {
+      return {
+        result: {
+          value: this._serialize(rv.return),
+        },
+      };
+    }
+
     return {
       result: this._toRemoteObject(rv.return),
     };
   }
 
   /**
    * Convert a given `Debugger.Object` to a JSON string.
    *
    * @param {Debugger.Object} obj
    *  The object to convert
    * @return {String}
    *  The JSON string
    */
   _serialize(obj) {
+    if (typeof(obj) == "undefined") {
+      return undefined;
+    }
     const result = this._debuggee.executeInGlobalWithBindings("JSON.stringify(e)", {e: obj});
     if (result.throw) {
       throw new Error("Object is not serializable");
     }
     return JSON.parse(result.return);
   }
 
   /**
--- a/remote/test/browser/browser_runtime_callFunctionOn.js
+++ b/remote/test/browser/browser_runtime_callFunctionOn.js
@@ -26,16 +26,17 @@ add_task(async function() {
     },
   });
   ok(true, "CDP client has been instantiated");
 
   const firstContext = await testRuntimeEnable(client);
   const contextId = firstContext.id;
   await testObjectReferences(client, contextId);
   await testExceptions(client, contextId);
+  await testReturnByValue(client, contextId);
 
   await client.close();
   ok(true, "The client is closed");
 
   BrowserTestUtils.removeTab(gBrowser.selectedTab);
 
   await RemoteAgent.close();
 });
@@ -109,8 +110,39 @@ async function testExceptions({ Runtime 
 
   // Test error when calling the function
   ({ exceptionDetails } = await Runtime.callFunctionOn({
     executionContextId,
     functionDeclaration: "() => doesNotExists()",
   }));
   is(exceptionDetails.text, "doesNotExists is not defined", "Exception message is passed to the client");
 }
+
+async function testReturnByValue({ Runtime }, executionContextId) {
+  const values = [
+    42,
+    "42",
+    42.00,
+    true,
+    false,
+    null,
+    { foo: true },
+    { foo: { bar: 42, str: "str", array: [1, 2, 3] } },
+    [ 42, "42", true ],
+    [ { foo: true } ],
+  ];
+  for (const value of values) {
+    const { result } = await Runtime.callFunctionOn({
+      executionContextId,
+      functionDeclaration: "() => (" + JSON.stringify(value) + ")",
+      returnByValue: true,
+    });
+    Assert.deepEqual(result.value, value, "The returned value is the same than the input value");
+  }
+
+  // Test undefined individually as JSON.stringify doesn't return a string
+  const { result } = await Runtime.callFunctionOn({
+    executionContextId,
+    functionDeclaration: "() => {}",
+    returnByValue: true,
+  });
+  is(result.value, undefined, "The returned value is undefined");
+}