mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-19 00:05:36 +00:00
Bug 1504756 - [marionette] Added "WebDriver:NewWindow" command to open a new top-level browsing context. r=ato
The patch adds the end-point for the recently defined `New Window` command (https://github.com/w3c/webdriver/issues/1138). It allows to open a new top-level browsing context as tab or as window. Depends on D13662 Differential Revision: https://phabricator.services.mozilla.com/D13663 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
ea87c4dedf
commit
c631c98202
@ -14,6 +14,7 @@ const {
|
||||
const {
|
||||
MessageManagerDestroyedPromise,
|
||||
waitForEvent,
|
||||
waitForObserverTopic,
|
||||
} = ChromeUtils.import("chrome://marionette/content/sync.js", {});
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["browser", "Context", "WindowState"];
|
||||
@ -71,11 +72,11 @@ this.Context = Context;
|
||||
*/
|
||||
browser.getBrowserForTab = function(tab) {
|
||||
// Fennec
|
||||
if ("browser" in tab) {
|
||||
if (tab && "browser" in tab) {
|
||||
return tab.browser;
|
||||
|
||||
// Firefox
|
||||
} else if ("linkedBrowser" in tab) {
|
||||
} else if (tab && "linkedBrowser" in tab) {
|
||||
return tab.linkedBrowser;
|
||||
}
|
||||
|
||||
@ -297,6 +298,51 @@ browser.Context = class {
|
||||
return Promise.all([destroyed, unloaded]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a new browser window.
|
||||
*
|
||||
* @return {Promise}
|
||||
* A promise resolving to the newly created chrome window.
|
||||
*/
|
||||
async openBrowserWindow(focus = false) {
|
||||
switch (this.driver.appName) {
|
||||
case "firefox":
|
||||
// Open new browser window, and wait until it is fully loaded.
|
||||
// Also wait for the window to be focused and activated to prevent a
|
||||
// race condition when promptly focusing to the original window again.
|
||||
let win = this.window.OpenBrowserWindow();
|
||||
|
||||
let activated = waitForEvent(win, "activate");
|
||||
let focused = waitForEvent(win, "focus", {capture: true});
|
||||
let startup = waitForObserverTopic("browser-delayed-startup-finished",
|
||||
subject => subject == win);
|
||||
|
||||
// Bug 1509380 - Missing focus/activate event when Firefox is not
|
||||
// the top-most application. As such wait for the next tick, and
|
||||
// manually focus the newly opened window.
|
||||
win.setTimeout(() => win.focus(), 0);
|
||||
|
||||
await Promise.all([activated, focused, startup]);
|
||||
|
||||
if (!focus) {
|
||||
// The new window shouldn't get focused. As such set the
|
||||
// focus back to the currently selected window.
|
||||
activated = waitForEvent(this.window, "activate");
|
||||
focused = waitForEvent(this.window, "focus", {capture: true});
|
||||
|
||||
this.window.focus();
|
||||
|
||||
await Promise.all([activated, focused]);
|
||||
}
|
||||
|
||||
return win;
|
||||
|
||||
default:
|
||||
throw new UnsupportedOperationError(
|
||||
`openWindow() not supported in ${this.driver.appName}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the current tab.
|
||||
*
|
||||
@ -319,32 +365,58 @@ browser.Context = class {
|
||||
let destroyed = new MessageManagerDestroyedPromise(this.messageManager);
|
||||
let tabClosed;
|
||||
|
||||
if (this.tabBrowser.closeTab) {
|
||||
// Fennec
|
||||
tabClosed = waitForEvent(this.tabBrowser.deck, "TabClose");
|
||||
this.tabBrowser.closeTab(this.tab);
|
||||
switch (this.driver.appName) {
|
||||
case "fennec":
|
||||
// Fennec
|
||||
tabClosed = waitForEvent(this.tabBrowser.deck, "TabClose");
|
||||
this.tabBrowser.closeTab(this.tab);
|
||||
break;
|
||||
|
||||
} else if (this.tabBrowser.removeTab) {
|
||||
// Firefox
|
||||
tabClosed = waitForEvent(this.tab, "TabClose");
|
||||
this.tabBrowser.removeTab(this.tab);
|
||||
case "firefox":
|
||||
tabClosed = waitForEvent(this.tab, "TabClose");
|
||||
this.tabBrowser.removeTab(this.tab);
|
||||
break;
|
||||
|
||||
} else {
|
||||
throw new UnsupportedOperationError(
|
||||
`closeTab() not supported in ${this.driver.appName}`);
|
||||
default:
|
||||
throw new UnsupportedOperationError(
|
||||
`closeTab() not supported in ${this.driver.appName}`);
|
||||
}
|
||||
|
||||
return Promise.all([destroyed, tabClosed]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a tab with given URI.
|
||||
*
|
||||
* @param {string} uri
|
||||
* URI to open.
|
||||
* Open a new tab in the currently selected chrome window.
|
||||
*/
|
||||
addTab(uri) {
|
||||
return this.tabBrowser.addTab(uri, true);
|
||||
async openTab(focus = false) {
|
||||
let tab = null;
|
||||
let tabOpened = waitForEvent(this.window, "TabOpen");
|
||||
|
||||
switch (this.driver.appName) {
|
||||
case "fennec":
|
||||
tab = this.tabBrowser.addTab(null, {selected: focus});
|
||||
break;
|
||||
|
||||
case "firefox":
|
||||
this.window.BrowserOpenTab();
|
||||
tab = this.tabBrowser.selectedTab;
|
||||
|
||||
// The new tab is always selected by default. If focus is not wanted,
|
||||
// the previously tab needs to be selected again.
|
||||
if (!focus) {
|
||||
this.tabBrowser.selectedTab = this.tab;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new UnsupportedOperationError(
|
||||
`openTab() not supported in ${this.driver.appName}`);
|
||||
}
|
||||
|
||||
await tabOpened;
|
||||
|
||||
return tab;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -378,16 +450,18 @@ browser.Context = class {
|
||||
this.tab = this.tabBrowser.tabs[index];
|
||||
|
||||
if (focus) {
|
||||
if (this.tabBrowser.selectTab) {
|
||||
// Fennec
|
||||
this.tabBrowser.selectTab(this.tab);
|
||||
switch (this.driver.appName) {
|
||||
case "fennec":
|
||||
this.tabBrowser.selectTab(this.tab);
|
||||
break;
|
||||
|
||||
} else if ("selectedTab" in this.tabBrowser) {
|
||||
// Firefox
|
||||
this.tabBrowser.selectedTab = this.tab;
|
||||
case "firefox":
|
||||
this.tabBrowser.selectedTab = this.tab;
|
||||
break;
|
||||
|
||||
} else {
|
||||
throw new UnsupportedOperationError("switchToTab() not supported");
|
||||
default:
|
||||
throw new UnsupportedOperationError(
|
||||
`switchToTab() not supported in ${this.driver.appName}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -108,13 +108,12 @@ const globalMessageManager = Services.mm;
|
||||
*
|
||||
* @class GeckoDriver
|
||||
*
|
||||
* @param {string} appId
|
||||
* Unique identifier of the application.
|
||||
* @param {MarionetteServer} server
|
||||
* The instance of Marionette server.
|
||||
*/
|
||||
this.GeckoDriver = function(appId, server) {
|
||||
this.appId = appId;
|
||||
this.GeckoDriver = function(server) {
|
||||
this.appId = Services.appinfo.ID;
|
||||
this.appName = Services.appinfo.name.toLowerCase();
|
||||
this._server = server;
|
||||
|
||||
this.sessionID = null;
|
||||
@ -1294,6 +1293,7 @@ GeckoDriver.prototype.getIdForBrowser = function(browser) {
|
||||
if (browser === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let permKey = browser.permanentKey;
|
||||
if (this._browserIds.has(permKey)) {
|
||||
return this._browserIds.get(permKey);
|
||||
@ -2709,6 +2709,66 @@ GeckoDriver.prototype.deleteCookie = async function(cmd) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Open a new top-level browsing context.
|
||||
*
|
||||
* @param {string=} type
|
||||
* Optional type of the new top-level browsing context. Can be one of
|
||||
* `tab` or `window`. Defaults to `tab`.
|
||||
* @param {boolean=} focus
|
||||
* Optional flag if the new top-level browsing context should be opened
|
||||
* in foreground (focused) or background (not focused). Defaults to false.
|
||||
*
|
||||
* @return {Object.<string, string>}
|
||||
* Handle and type of the new browsing context.
|
||||
*/
|
||||
GeckoDriver.prototype.newWindow = async function(cmd) {
|
||||
assert.open(this.getCurrentWindow(Context.Content));
|
||||
await this._handleUserPrompts();
|
||||
|
||||
let focus = false;
|
||||
if (typeof cmd.parameters.focus != "undefined") {
|
||||
focus = assert.boolean(cmd.parameters.focus,
|
||||
pprint`Expected "focus" to be a boolean, got ${cmd.parameters.focus}`);
|
||||
}
|
||||
|
||||
let type;
|
||||
if (typeof cmd.parameters.type != "undefined") {
|
||||
type = assert.string(cmd.parameters.type,
|
||||
pprint`Expected "type" to be a string, got ${cmd.parameters.type}`);
|
||||
}
|
||||
|
||||
// If an invalid or no type has been specified default to a tab.
|
||||
if (typeof type == "undefined" || !["tab", "window"].includes(type)) {
|
||||
type = "tab";
|
||||
}
|
||||
|
||||
let contentBrowser;
|
||||
|
||||
switch (type) {
|
||||
case "window":
|
||||
let win = await this.curBrowser.openBrowserWindow(focus);
|
||||
contentBrowser = browser.getTabBrowser(win).selectedBrowser;
|
||||
break;
|
||||
|
||||
default:
|
||||
// To not fail if a new type gets added in the future, make opening
|
||||
// a new tab the default action.
|
||||
let tab = await this.curBrowser.openTab(focus);
|
||||
contentBrowser = browser.getBrowserForTab(tab);
|
||||
}
|
||||
|
||||
// Even with the framescript registered, the browser might not be known to
|
||||
// the parent process yet. Wait until it is available.
|
||||
// TODO: Fix by using `Browser:Init` or equivalent on bug 1311041
|
||||
let windowId = await new PollPromise((resolve, reject) => {
|
||||
let id = this.getIdForBrowser(contentBrowser);
|
||||
this.windowHandles.includes(id) ? resolve(id) : reject();
|
||||
});
|
||||
|
||||
return {handle: windowId.toString(), type};
|
||||
};
|
||||
|
||||
/**
|
||||
* Close the currently selected tab/window.
|
||||
*
|
||||
@ -3549,6 +3609,7 @@ GeckoDriver.prototype.commands = {
|
||||
"WebDriver:MaximizeWindow": GeckoDriver.prototype.maximizeWindow,
|
||||
"WebDriver:Navigate": GeckoDriver.prototype.get,
|
||||
"WebDriver:NewSession": GeckoDriver.prototype.newSession,
|
||||
"WebDriver:NewWindow": GeckoDriver.prototype.newWindow,
|
||||
"WebDriver:PerformActions": GeckoDriver.prototype.performActions,
|
||||
"WebDriver:Refresh": GeckoDriver.prototype.refresh,
|
||||
"WebDriver:ReleaseActions": GeckoDriver.prototype.releaseActions,
|
||||
|
@ -11,7 +11,6 @@ const ServerSocket = CC(
|
||||
"nsIServerSocket",
|
||||
"initSpecialConnection");
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
ChromeUtils.import("chrome://marionette/content/assert.js");
|
||||
@ -74,7 +73,7 @@ class TCPListener {
|
||||
*/
|
||||
driverFactory() {
|
||||
MarionettePrefs.contentListener = false;
|
||||
return new GeckoDriver(Services.appinfo.ID, this);
|
||||
return new GeckoDriver(this);
|
||||
}
|
||||
|
||||
set acceptConnections(value) {
|
||||
|
Loading…
Reference in New Issue
Block a user