Skip to content

Commit

Permalink
Bug 1750541 - [bidi] Implement basic support for "script.callFunction…
Browse files Browse the repository at this point in the history
…" command r=webdriver-reviewers,whimboo

Depends on D150245

Differential Revision: https://phabricator.services.mozilla.com/D150822
  • Loading branch information
juliandescottes committed Jul 6, 2022
1 parent 25cf447 commit 87ebacd
Show file tree
Hide file tree
Showing 4 changed files with 184 additions and 187 deletions.
124 changes: 111 additions & 13 deletions remote/webdriver-bidi/modules/root/script.jsm
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,90 @@ class ScriptModule extends Module {
* @property {RemoteValue} result
*/

/**
* 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]
* @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
* The target for the evaluation, which either matches the definition for
* a RealmTarget or for ContextTarget.
* @param {RemoteValue=} this
* The value of the this keyword for the function call. [unsupported]
*
* @returns {ScriptEvaluateResult}
*
* @throws {InvalidArgumentError}
* If any of the arguments has not the expected type.
* @throws {NoSuchFrameError}
* If the target cannot be found.
*/
async callFunction(options = {}) {
const {
arguments: commandArguments = null,
awaitPromise,
functionDeclaration,
resultOwnership = OwnershipModel.None,
target = {},
this: thisParameter = null,
} = options;

lazy.assert.string(
functionDeclaration,
`Expected "functionDeclaration" to be a string, got ${functionDeclaration}`
);

lazy.assert.boolean(
awaitPromise,
`Expected "awaitPromise" to be a boolean, got ${awaitPromise}`
);

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`
);
}

const { contextId, realmId, sandbox } = this.#assertTarget(target);
const realm = this.#getRealmInfoFromTarget({ contextId, realmId, sandbox });
const evaluationResult = await this.messageHandler.forwardCommand({
moduleName: "script",
commandName: "callFunctionDeclaration",
destination: {
type: lazy.WindowGlobalMessageHandler.type,
id: realm.context.id,
},
params: {
awaitPromise,
commandArguments,
functionDeclaration,
},
});

return this.#buildReturnValue(evaluationResult, realm);
}

/**
* Evaluate a provided expression in the provided target, which is either a
* realm or a browsing context.
Expand Down Expand Up @@ -162,14 +246,37 @@ class ScriptModule extends Module {
`Expected "awaitPromise" to be a boolean, got ${awaitPromise}`
);

this.#assertResultOwnership(resultOwnership);

const { contextId, realmId, sandbox } = this.#assertTarget(target);
const realm = this.#getRealmInfoFromTarget({ contextId, realmId, sandbox });
const evaluationResult = await this.messageHandler.forwardCommand({
moduleName: "script",
commandName: "evaluateExpression",
destination: {
type: lazy.WindowGlobalMessageHandler.type,
id: realm.context.id,
},
params: {
awaitPromise,
expression: source,
},
});

return this.#buildReturnValue(evaluationResult, realm);
}

#assertResultOwnership(resultOwnership) {
if (![OwnershipModel.None, OwnershipModel.Root].includes(resultOwnership)) {
throw new lazy.error.InvalidArgumentError(
`Expected "resultOwnership" to be one of ${Object.values(
OwnershipModel
)}, got ${resultOwnership}`
);
}
}

#assertTarget(target) {
lazy.assert.object(
target,
`Expected "target" to be an object, got ${target}`
Expand All @@ -180,6 +287,7 @@ class ScriptModule extends Module {
realm: realmId = null,
sandbox = null,
} = target;

if (contextId != null) {
lazy.assert.string(
contextId,
Expand Down Expand Up @@ -207,20 +315,10 @@ class ScriptModule extends Module {
throw new lazy.error.InvalidArgumentError(`No context or realm provided`);
}

const realm = this.#getRealmInfoFromTarget({ contextId, realmId, sandbox });
const evaluationResult = await this.messageHandler.forwardCommand({
moduleName: "script",
commandName: "evaluateExpression",
destination: {
type: lazy.WindowGlobalMessageHandler.type,
id: realm.context.id,
},
params: {
awaitPromise,
expression: source,
},
});
return { contextId, realmId, sandbox };
}

#buildReturnValue(evaluationResult, realm) {
const rv = { realm: realm.realm };
switch (evaluationResult.evaluationStatus) {
// TODO: Compare with EvaluationStatus.Normal after Bug 1774444 is fixed.
Expand Down
115 changes: 73 additions & 42 deletions remote/webdriver-bidi/modules/windowglobal/script.jsm
Original file line number Diff line number Diff line change
Expand Up @@ -94,48 +94,7 @@ class ScriptModule extends Module {
};
}

#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
// waiveXrays on Maps/Sets/... and then unwaiveXrays on entries but since
// we serialize with maxDepth=1, calling waiveXrays once on the root
// object allows to return correctly serialized values.
return Cu.waiveXrays(rawObject);
}

// If maybeDebuggerObject was not a Debugger.Object, it is a primitive value
// which can be used as is.
return maybeDebuggerObject;
}

/**
* Evaluate a provided expression 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 {string} expression
* The expression to evaluate.
*
* @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 evaluateExpression(options) {
const { awaitPromise, expression } = options;
const rv = this.#global.executeInGlobal(expression, {
url: this.messageHandler.window.document.baseURI,
});

async #buildReturnValue(rv, awaitPromise) {
let evaluationStatus, exception, result, stack;
if ("return" in rv) {
evaluationStatus = EvaluationStatus.Normal;
Expand Down Expand Up @@ -184,6 +143,78 @@ class ScriptModule extends Module {
);
}
}

#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
// waiveXrays on Maps/Sets/... and then unwaiveXrays on entries but since
// we serialize with maxDepth=1, calling waiveXrays once on the root
// object allows to return correctly serialized values.
return Cu.waiveXrays(rawObject);
}

// If maybeDebuggerObject was not a Debugger.Object, it is a primitive value
// which can be used as is.
return maybeDebuggerObject;
}

/**
* 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
* 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,
});
return this.#buildReturnValue(rv, awaitPromise);
}

/**
* Evaluate a provided expression 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 {string} expression
* The expression to evaluate.
*
* @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 evaluateExpression(options) {
const { awaitPromise, expression } = options;
const rv = this.#global.executeInGlobal(expression, {
url: this.messageHandler.window.document.baseURI,
});

return this.#buildReturnValue(rv, awaitPromise);
}
}

const script = ScriptModule;
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
[call_function.py]
[test_exception]
expected: FAIL

[test_invalid_function]
expected: FAIL

[test_arrow_function]
expected: FAIL

[test_arguments]
expected: FAIL

Expand All @@ -23,14 +17,8 @@
[test_remote_value_argument]
expected: FAIL

[test_async_arrow_await_promise[True\]]
expected: FAIL

[test_async_arrow_await_promise[False\]]
expected: FAIL

[test_async_classic_await_promise[True\]]
expected: FAIL

[test_async_classic_await_promise[False\]]
expected: FAIL
Loading

0 comments on commit 87ebacd

Please sign in to comment.