Bug 1770492 - [bidi] Support basic "arguments" for "script.callFunction" command r=webdriver-reviewers,whimboo
authorJulian Descottes <jdescottes@mozilla.com>
Tue, 12 Jul 2022 16:19:31 +0000
changeset 623671 f2734a746b1a072c9a9c94e7160503071a8af642
parent 623670 29be5540259989e22b15d11c9e49c7471071b636
child 623672 8d4d296089fc49eb7e19bc126ee83cdd6c8457ac
push id39973
push userncsoregi@mozilla.com
push dateWed, 13 Jul 2022 03:48:16 +0000
treeherdermozilla-central@9bdc06e5e8a2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerswebdriver-reviewers, whimboo
bugs1770492
milestone104.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 1770492 - [bidi] Support basic "arguments" for "script.callFunction" command r=webdriver-reviewers,whimboo Depends on D151529 Differential Revision: https://phabricator.services.mozilla.com/D150823
remote/webdriver-bidi/modules/root/script.jsm
remote/webdriver-bidi/modules/windowglobal/script.jsm
testing/web-platform/meta/webdriver/tests/bidi/script/call_function/invalid.py.ini
--- a/remote/webdriver-bidi/modules/root/script.jsm
+++ b/remote/webdriver-bidi/modules/root/script.jsm
@@ -122,17 +122,17 @@ class ScriptModule extends Module {
    */
 
   /**
    * Calls a provided function with given arguments and scope in the provided
    * target, which is either a realm or a browsing context.
    *
    * @param {Object=} options
    * @param {Array<RemoteValue>=} arguments
-   *     The arguments to pass to the function call. [unsupported]
+   *     The arguments to pass to the function call.
    * @param {boolean} awaitPromise
    *     Determines if the command should wait for the return value of the
    *     expression to resolve, if this return value is a Promise.
    * @param {string} functionDeclaration
    *     The expression to evaluate.
    * @param {OwnershipModel=} resultOwnership [unsupported]
    *     The ownership model to use for the results of this evaluation.
    * @param {Object} target
@@ -173,19 +173,16 @@ class ScriptModule extends Module {
 
     this.#assertResultOwnership(resultOwnership);
 
     if (commandArguments != null) {
       lazy.assert.array(
         commandArguments,
         `Expected "arguments" to be an array, got ${commandArguments}`
       );
-      throw new lazy.error.UnsupportedOperationError(
-        `"arguments" parameter is not supported yet`
-      );
     }
 
     if (thisParameter != null) {
       throw new lazy.error.UnsupportedOperationError(
         `"this" parameter is not supported yet`
       );
     }
 
--- a/remote/webdriver-bidi/modules/windowglobal/script.jsm
+++ b/remote/webdriver-bidi/modules/windowglobal/script.jsm
@@ -13,16 +13,17 @@ const { XPCOMUtils } = ChromeUtils.impor
 const { Module } = ChromeUtils.import(
   "chrome://remote/content/shared/messagehandler/Module.jsm"
 );
 
 const lazy = {};
 XPCOMUtils.defineLazyModuleGetters(lazy, {
   addDebuggerToGlobal: "resource://gre/modules/jsdebugger.jsm",
 
+  deserialize: "chrome://remote/content/webdriver-bidi/RemoteValue.jsm",
   error: "chrome://remote/content/shared/webdriver/Errors.jsm",
   getFramesFromStack: "chrome://remote/content/shared/Stack.jsm",
   isChromeFrame: "chrome://remote/content/shared/Stack.jsm",
   serialize: "chrome://remote/content/webdriver-bidi/RemoteValue.jsm",
 });
 
 XPCOMUtils.defineLazyGetter(lazy, "dbg", () => {
   // eslint-disable-next-line mozilla/reject-globalThis-modification
@@ -139,16 +140,24 @@ class ScriptModule extends Module {
         };
       default:
         throw new lazy.error.UnsupportedOperationError(
           `Unsupported completion value for expression evaluation`
         );
     }
   }
 
+  #cloneAsDebuggerObject(obj) {
+    // To use an object created in the priviledged Debugger compartment from the
+    // the content compartment, we need to first clone it into the target
+    // compartment and then retrieve the corresponding Debugger.Object wrapper.
+    const proxyObject = Cu.cloneInto(obj, this.messageHandler.window);
+    return this.#global.makeDebuggeeValue(proxyObject);
+  }
+
   #toRawObject(maybeDebuggerObject) {
     if (maybeDebuggerObject instanceof Debugger.Object) {
       // Retrieve the referent for the provided Debugger.object.
       // See https://firefox-source-docs.mozilla.org/devtools-user/debugger-api/debugger.object/index.html
       const rawObject = maybeDebuggerObject.unsafeDereference();
 
       // TODO: Getters for Maps and Sets iterators return "Opaque" objects and
       // are not iterable. RemoteValue.jsm' serializer should handle calling
@@ -165,33 +174,51 @@ class ScriptModule extends Module {
 
   /**
    * Call a function in the current window global.
    *
    * @param {Object} options
    * @param {boolean} awaitPromise
    *     Determines if the command should wait for the return value of the
    *     expression to resolve, if this return value is a Promise.
-   * @param {Array<RemoteValue>} commandArguments
+   * @param {Array<RemoteValue>=} commandArguments
    *     The arguments to pass to the function call.
    * @param {string} functionDeclaration
    *     The body of the function to call.
    *
    * @return {Object}
    *     - evaluationStatus {EvaluationStatus} One of "normal", "throw".
    *     - exceptionDetails {ExceptionDetails=} the details of the exception if
    *     the evaluation status was "throw".
    *     - result {RemoteValue=} the result of the evaluation serialized as a
    *     RemoteValue if the evaluation status was "normal".
    */
   async callFunctionDeclaration(options) {
-    const { awaitPromise, functionDeclaration } = options;
-    const rv = this.#global.executeInGlobal(`(${functionDeclaration})()`, {
-      url: this.messageHandler.window.document.baseURI,
-    });
+    const {
+      awaitPromise,
+      commandArguments = null,
+      functionDeclaration,
+    } = options;
+
+    const deserializedArguments =
+      commandArguments != null
+        ? commandArguments.map(a => lazy.deserialize(a))
+        : [];
+    const expression = `(${functionDeclaration}).apply(null, __bidi_args)`;
+
+    const rv = this.#global.executeInGlobalWithBindings(
+      expression,
+      {
+        __bidi_args: this.#cloneAsDebuggerObject(deserializedArguments),
+      },
+      {
+        url: this.messageHandler.window.document.baseURI,
+      }
+    );
+
     return this.#buildReturnValue(rv, awaitPromise);
   }
 
   /**
    * Evaluate a provided expression in the current window global.
    *
    * @param {Object} options
    * @param {boolean} awaitPromise
--- a/testing/web-platform/meta/webdriver/tests/bidi/script/call_function/invalid.py.ini
+++ b/testing/web-platform/meta/webdriver/tests/bidi/script/call_function/invalid.py.ini
@@ -11,23 +11,8 @@
   [test_params_this_invalid_type[42\]]
     expected: FAIL
 
   [test_params_this_invalid_type[this3\]]
     expected: FAIL
 
   [test_params_this_invalid_type[this4\]]
     expected: FAIL
-
-  [test_params_single_argument_invalid_type[False\]]
-    expected: FAIL
-
-  [test_params_single_argument_invalid_type[SOME_STRING\]]
-    expected: FAIL
-
-  [test_params_single_argument_invalid_type[42\]]
-    expected: FAIL
-
-  [test_params_single_argument_invalid_type[argument3\]]
-    expected: FAIL
-
-  [test_params_single_argument_invalid_type[argument4\]]
-    expected: FAIL