Bug 1841018 - [bidi] Implement "browsingContext.traverseHistory" command. r=webdriver-reviewers,whimboo

Differential Revision: https://phabricator.services.mozilla.com/D193895
This commit is contained in:
Alexandra Borovova 2023-11-24 09:25:00 +00:00
parent 15c1a58822
commit 120294a016
3 changed files with 91 additions and 2 deletions

View File

@ -238,6 +238,9 @@ export function executeSoon(fn) {
* @param {Condition} func
* Function to run off the main thread.
* @param {object=} options
* @param {string=} options.errorMessage
* Message to use to send a warning if ``timeout`` is over.
* Defaults to `PollPromise timed out`.
* @param {number=} options.timeout
* Desired timeout if wanted. If 0 or less than the runtime evaluation
* time of ``func``, ``func`` is guaranteed to run at least once.
@ -257,7 +260,12 @@ export function executeSoon(fn) {
* @throws {RangeError}
* If `timeout` or `interval` are not unsigned integers.
*/
export function PollPromise(func, { timeout = null, interval = 10 } = {}) {
export function PollPromise(func, options = {}) {
const {
errorMessage = "PollPromise timed out",
interval = 10,
timeout = null,
} = options;
const timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
if (typeof func != "function") {
@ -311,6 +319,9 @@ export function PollPromise(func, { timeout = null, interval = 10 } = {}) {
timer.init(evalFn, interval, TYPE_REPEATING_SLACK);
}).then(
res => {
if (Number.isInteger(timeout)) {
lazy.logger.warn(`${errorMessage} after ${timeout} ms`);
}
timer.cancel();
return res;
},

View File

@ -27,6 +27,7 @@ const ERRORS = new Set([
"NoSuchElementError",
"NoSuchFrameError",
"NoSuchHandleError",
"NoSuchHistoryEntryError",
"NoSuchInterceptError",
"NoSuchNodeError",
"NoSuchScriptError",
@ -585,6 +586,21 @@ class NoSuchHandleError extends WebDriverError {
}
}
/**
* The entry of the history could not be found.
*
* @param {string=} message
* Optional string describing error situation.
* @param {object=} data
* Additional error data helpful in diagnosing the error.
*/
class NoSuchHistoryEntryError extends WebDriverError {
constructor(message, data = {}) {
super(message, data);
this.status = "no such history entry";
}
}
/**
* Tried to remove an unknown network intercept.
*
@ -803,6 +819,7 @@ const STATUSES = new Map([
["no such element", NoSuchElementError],
["no such frame", NoSuchFrameError],
["no such handle", NoSuchHandleError],
["no such history entry", NoSuchHistoryEntryError],
["no such intercept", NoSuchInterceptError],
["no such node", NoSuchNodeError],
["no such script", NoSuchScriptError],

View File

@ -22,6 +22,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
"chrome://remote/content/shared/NavigationManager.sys.mjs",
NavigationListener:
"chrome://remote/content/shared/listeners/NavigationListener.sys.mjs",
PollPromise: "chrome://remote/content/shared/Sync.sys.mjs",
pprint: "chrome://remote/content/shared/Format.sys.mjs",
print: "chrome://remote/content/shared/PDF.sys.mjs",
ProgressListener: "chrome://remote/content/shared/Navigate.sys.mjs",
@ -89,6 +90,8 @@ export const OriginType = {
viewport: "viewport",
};
const TIMEOUT_SET_HISTORY_INDEX = 1000;
/**
* Enum of user prompt types supported by the browsingContext.handleUserPrompt
* command, these types can be retrieved from `dialog.args.promptType`.
@ -1003,7 +1006,7 @@ class BrowsingContextModule extends Module {
*
* @throws {InvalidArgumentError}
* Raised if an argument is of an invalid type or value.
* @throws UnsupportedOperationError
* @throws {UnsupportedOperationError}
* Raised when the command is called on Android.
*/
async setViewport(options = {}) {
@ -1080,6 +1083,64 @@ class BrowsingContextModule extends Module {
});
}
/**
* Traverses the history of a given context by a given delta.
*
* @param {object=} options
* @param {string} options.context
* Id of the browsing context.
* @param {number} options.delta
* The number of steps we have to traverse.
*
* @throws {InvalidArgumentError}
* Raised if an argument is of an invalid type or value.
* @throws {NoSuchFrameException}
* When a context is not available.
* @throws {NoSuchHistoryEntryError}
* When a requested history entry does not exist.
*/
async traverseHistory(options = {}) {
const { context: contextId, delta } = options;
lazy.assert.string(
contextId,
`Expected "context" to be a string, got ${contextId}`
);
const context = this.#getBrowsingContext(contextId);
lazy.assert.integer(delta);
const sessionHistory = context.sessionHistory;
const allSteps = sessionHistory.count;
const currentIndex = sessionHistory.index;
const targetIndex = currentIndex + delta;
const validEntry = targetIndex >= 0 && targetIndex < allSteps;
if (!validEntry) {
throw new lazy.error.NoSuchHistoryEntryError(
`History entry with delta ${delta} not found`
);
}
context.goToIndex(targetIndex);
// On some platforms the requested index isn't set immediately.
await lazy.PollPromise(
(resolve, reject) => {
if (sessionHistory.index == targetIndex) {
resolve();
} else {
reject();
}
},
{
errorMessage: `History was not updated for index "${targetIndex}"`,
timeout: TIMEOUT_SET_HISTORY_INDEX,
}
);
}
/**
* Start and await a navigation on the provided BrowsingContext. Returns a
* promise which resolves when the navigation is done according to the provided