mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-16 23:05:42 +00:00
Bug 1747222 - [remote] Move browsing context APIs from browser.js and WindowManager to TabManager r=webdriver-reviewers,whimboo
Depends on D135414 Differential Revision: https://phabricator.services.mozilla.com/D135415
This commit is contained in:
parent
7d8052e4a0
commit
892ae10673
@ -15,6 +15,7 @@ remote.jar:
|
|||||||
# shared modules (all protocols)
|
# shared modules (all protocols)
|
||||||
content/shared/Format.jsm (shared/Format.jsm)
|
content/shared/Format.jsm (shared/Format.jsm)
|
||||||
content/shared/Log.jsm (shared/Log.jsm)
|
content/shared/Log.jsm (shared/Log.jsm)
|
||||||
|
content/shared/MobileTabBrowser.jsm (shared/MobileTabBrowser.jsm)
|
||||||
content/shared/Navigate.jsm (shared/Navigate.jsm)
|
content/shared/Navigate.jsm (shared/Navigate.jsm)
|
||||||
content/shared/RecommendedPreferences.jsm (shared/RecommendedPreferences.jsm)
|
content/shared/RecommendedPreferences.jsm (shared/RecommendedPreferences.jsm)
|
||||||
content/shared/Stack.jsm (shared/Stack.jsm)
|
content/shared/Stack.jsm (shared/Stack.jsm)
|
||||||
|
@ -14,6 +14,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
|||||||
AppInfo: "chrome://remote/content/marionette/appinfo.js",
|
AppInfo: "chrome://remote/content/marionette/appinfo.js",
|
||||||
error: "chrome://remote/content/shared/webdriver/Errors.jsm",
|
error: "chrome://remote/content/shared/webdriver/Errors.jsm",
|
||||||
MessageManagerDestroyedPromise: "chrome://remote/content/marionette/sync.js",
|
MessageManagerDestroyedPromise: "chrome://remote/content/marionette/sync.js",
|
||||||
|
TabManager: "chrome://remote/content/shared/TabManager.jsm",
|
||||||
waitForEvent: "chrome://remote/content/marionette/sync.js",
|
waitForEvent: "chrome://remote/content/marionette/sync.js",
|
||||||
WebElementEventTarget: "chrome://remote/content/marionette/dom.js",
|
WebElementEventTarget: "chrome://remote/content/marionette/dom.js",
|
||||||
windowManager: "chrome://remote/content/shared/WindowManager.jsm",
|
windowManager: "chrome://remote/content/shared/WindowManager.jsm",
|
||||||
@ -61,91 +62,6 @@ Context.Chrome = "chrome";
|
|||||||
Context.Content = "content";
|
Context.Content = "content";
|
||||||
this.Context = Context;
|
this.Context = Context;
|
||||||
|
|
||||||
// GeckoView shim for Desktop's gBrowser
|
|
||||||
class MobileTabBrowser {
|
|
||||||
constructor(window) {
|
|
||||||
this.window = window;
|
|
||||||
}
|
|
||||||
|
|
||||||
get tabs() {
|
|
||||||
return [this.window.tab];
|
|
||||||
}
|
|
||||||
|
|
||||||
get selectedTab() {
|
|
||||||
return this.window.tab;
|
|
||||||
}
|
|
||||||
|
|
||||||
set selectedTab(tab) {
|
|
||||||
if (tab != this.selectedTab) {
|
|
||||||
throw new Error("GeckoView only supports a single tab");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Synthesize a custom TabSelect event to indicate that a tab has been
|
|
||||||
// selected even when we don't change it.
|
|
||||||
const event = this.window.CustomEvent("TabSelect", {
|
|
||||||
bubbles: true,
|
|
||||||
cancelable: false,
|
|
||||||
detail: {
|
|
||||||
previousTab: this.selectedTab,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
this.window.document.dispatchEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
get selectedBrowser() {
|
|
||||||
return this.selectedTab.linkedBrowser;
|
|
||||||
}
|
|
||||||
|
|
||||||
addEventListener() {
|
|
||||||
this.window.addEventListener(...arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
removeEventListener() {
|
|
||||||
this.window.removeEventListener(...arguments);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the <code><xul:browser></code> for the specified tab.
|
|
||||||
*
|
|
||||||
* @param {Tab} tab
|
|
||||||
* The tab whose browser needs to be returned.
|
|
||||||
*
|
|
||||||
* @return {Browser}
|
|
||||||
* The linked browser for the tab or null if no browser can be found.
|
|
||||||
*/
|
|
||||||
browser.getBrowserForTab = function(tab) {
|
|
||||||
if (tab && "linkedBrowser" in tab) {
|
|
||||||
return tab.linkedBrowser;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the tab browser for the specified chrome window.
|
|
||||||
*
|
|
||||||
* @param {ChromeWindow} win
|
|
||||||
* Window whose <code>tabbrowser</code> needs to be accessed.
|
|
||||||
*
|
|
||||||
* @return {Tab}
|
|
||||||
* Tab browser or null if it's not a browser window.
|
|
||||||
*/
|
|
||||||
browser.getTabBrowser = function(window) {
|
|
||||||
// GeckoView
|
|
||||||
if (AppInfo.isAndroid) {
|
|
||||||
return new MobileTabBrowser(window);
|
|
||||||
// Firefox
|
|
||||||
} else if ("gBrowser" in window) {
|
|
||||||
return window.gBrowser;
|
|
||||||
// Thunderbird
|
|
||||||
} else if (window.document.getElementById("tabmail")) {
|
|
||||||
return window.document.getElementById("tabmail");
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a browsing context wrapper.
|
* Creates a browsing context wrapper.
|
||||||
*
|
*
|
||||||
@ -165,7 +81,7 @@ browser.Context = class {
|
|||||||
|
|
||||||
// In Firefox this is <xul:tabbrowser> (not <xul:browser>!)
|
// In Firefox this is <xul:tabbrowser> (not <xul:browser>!)
|
||||||
// and MobileTabBrowser in GeckoView.
|
// and MobileTabBrowser in GeckoView.
|
||||||
this.tabBrowser = browser.getTabBrowser(this.window);
|
this.tabBrowser = TabManager.getTabBrowser(this.window);
|
||||||
|
|
||||||
// Used to set curFrameId upon new session
|
// Used to set curFrameId upon new session
|
||||||
this.newSession = true;
|
this.newSession = true;
|
||||||
@ -186,7 +102,7 @@ browser.Context = class {
|
|||||||
*/
|
*/
|
||||||
get contentBrowser() {
|
get contentBrowser() {
|
||||||
if (this.tab) {
|
if (this.tab) {
|
||||||
return browser.getBrowserForTab(this.tab);
|
return TabManager.getBrowserForTab(this.tab);
|
||||||
} else if (
|
} else if (
|
||||||
this.tabBrowser &&
|
this.tabBrowser &&
|
||||||
this.driver.isReftestBrowser(this.tabBrowser)
|
this.driver.isReftestBrowser(this.tabBrowser)
|
||||||
@ -374,7 +290,7 @@ browser.Context = class {
|
|||||||
|
|
||||||
if (window) {
|
if (window) {
|
||||||
this.window = window;
|
this.window = window;
|
||||||
this.tabBrowser = browser.getTabBrowser(this.window);
|
this.tabBrowser = TabManager.getTabBrowser(this.window);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.tabBrowser) {
|
if (!this.tabBrowser) {
|
||||||
|
@ -48,6 +48,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
|||||||
registerCommandsActor:
|
registerCommandsActor:
|
||||||
"chrome://remote/content/marionette/actors/MarionetteCommandsParent.jsm",
|
"chrome://remote/content/marionette/actors/MarionetteCommandsParent.jsm",
|
||||||
RemoteAgent: "chrome://remote/content/components/RemoteAgent.jsm",
|
RemoteAgent: "chrome://remote/content/components/RemoteAgent.jsm",
|
||||||
|
TabManager: "chrome://remote/content/shared/TabManager.jsm",
|
||||||
TimedPromise: "chrome://remote/content/marionette/sync.js",
|
TimedPromise: "chrome://remote/content/marionette/sync.js",
|
||||||
Timeouts: "chrome://remote/content/shared/webdriver/Capabilities.jsm",
|
Timeouts: "chrome://remote/content/shared/webdriver/Capabilities.jsm",
|
||||||
UnhandledPromptBehavior:
|
UnhandledPromptBehavior:
|
||||||
@ -442,11 +443,11 @@ GeckoDriver.prototype.newSession = async function(cmd) {
|
|||||||
this.dialogObserver.add(this.handleModalDialog.bind(this));
|
this.dialogObserver.add(this.handleModalDialog.bind(this));
|
||||||
|
|
||||||
for (let win of windowManager.windows) {
|
for (let win of windowManager.windows) {
|
||||||
const tabBrowser = browser.getTabBrowser(win);
|
const tabBrowser = TabManager.getTabBrowser(win);
|
||||||
|
|
||||||
if (tabBrowser) {
|
if (tabBrowser) {
|
||||||
for (const tab of tabBrowser.tabs) {
|
for (const tab of tabBrowser.tabs) {
|
||||||
const contentBrowser = browser.getBrowserForTab(tab);
|
const contentBrowser = TabManager.getBrowserForTab(tab);
|
||||||
this.registerBrowser(contentBrowser);
|
this.registerBrowser(contentBrowser);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -489,7 +490,7 @@ GeckoDriver.prototype.newSession = async function(cmd) {
|
|||||||
* Chrome window to register event listeners for.
|
* Chrome window to register event listeners for.
|
||||||
*/
|
*/
|
||||||
GeckoDriver.prototype.registerListenersForWindow = function(win) {
|
GeckoDriver.prototype.registerListenersForWindow = function(win) {
|
||||||
const tabBrowser = browser.getTabBrowser(win);
|
const tabBrowser = TabManager.getTabBrowser(win);
|
||||||
|
|
||||||
// Listen for any kind of top-level process switch
|
// Listen for any kind of top-level process switch
|
||||||
tabBrowser?.addEventListener("XULFrameLoaderCreated", this);
|
tabBrowser?.addEventListener("XULFrameLoaderCreated", this);
|
||||||
@ -502,7 +503,7 @@ GeckoDriver.prototype.registerListenersForWindow = function(win) {
|
|||||||
* Chrome window to unregister event listeners for.
|
* Chrome window to unregister event listeners for.
|
||||||
*/
|
*/
|
||||||
GeckoDriver.prototype.unregisterListenersForWindow = function(win) {
|
GeckoDriver.prototype.unregisterListenersForWindow = function(win) {
|
||||||
const tabBrowser = browser.getTabBrowser(win);
|
const tabBrowser = TabManager.getTabBrowser(win);
|
||||||
|
|
||||||
tabBrowser?.removeEventListener("XULFrameLoaderCreated", this);
|
tabBrowser?.removeEventListener("XULFrameLoaderCreated", this);
|
||||||
};
|
};
|
||||||
@ -984,7 +985,7 @@ GeckoDriver.prototype.getWindowHandle = function() {
|
|||||||
if (this.context == Context.Chrome) {
|
if (this.context == Context.Chrome) {
|
||||||
return windowManager.getIdForWindow(this.curBrowser.window);
|
return windowManager.getIdForWindow(this.curBrowser.window);
|
||||||
}
|
}
|
||||||
return windowManager.getIdForBrowser(this.curBrowser.contentBrowser);
|
return TabManager.getIdForBrowser(this.curBrowser.contentBrowser);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1005,7 +1006,7 @@ GeckoDriver.prototype.getWindowHandles = function() {
|
|||||||
if (this.context == Context.Chrome) {
|
if (this.context == Context.Chrome) {
|
||||||
return windowManager.chromeWindowHandles.map(String);
|
return windowManager.chromeWindowHandles.map(String);
|
||||||
}
|
}
|
||||||
return windowManager.windowHandles.map(String);
|
return TabManager.allBrowserUniqueIds.map(String);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1171,7 +1172,7 @@ GeckoDriver.prototype.setWindowHandle = async function(
|
|||||||
if (!winProperties.hasTabBrowser) {
|
if (!winProperties.hasTabBrowser) {
|
||||||
this.currentSession.contentBrowsingContext = null;
|
this.currentSession.contentBrowsingContext = null;
|
||||||
} else {
|
} else {
|
||||||
const tabBrowser = browser.getTabBrowser(winProperties.win);
|
const tabBrowser = TabManager.getTabBrowser(winProperties.win);
|
||||||
|
|
||||||
// For chrome windows such as a reftest window, `getTabBrowser` is not
|
// For chrome windows such as a reftest window, `getTabBrowser` is not
|
||||||
// a tabbrowser, it is the content browser which should be used here.
|
// a tabbrowser, it is the content browser which should be used here.
|
||||||
@ -2010,21 +2011,21 @@ GeckoDriver.prototype.newWindow = async function(cmd) {
|
|||||||
switch (type) {
|
switch (type) {
|
||||||
case "window":
|
case "window":
|
||||||
let win = await this.curBrowser.openBrowserWindow(focus, isPrivate);
|
let win = await this.curBrowser.openBrowserWindow(focus, isPrivate);
|
||||||
contentBrowser = browser.getTabBrowser(win).selectedBrowser;
|
contentBrowser = TabManager.getTabBrowser(win).selectedBrowser;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// To not fail if a new type gets added in the future, make opening
|
// To not fail if a new type gets added in the future, make opening
|
||||||
// a new tab the default action.
|
// a new tab the default action.
|
||||||
let tab = await this.curBrowser.openTab(focus);
|
let tab = await this.curBrowser.openTab(focus);
|
||||||
contentBrowser = browser.getBrowserForTab(tab);
|
contentBrowser = TabManager.getBrowserForTab(tab);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actors need the new window to be loaded to safely execute queries.
|
// Actors need the new window to be loaded to safely execute queries.
|
||||||
// Wait until the initial page load has been finished.
|
// Wait until the initial page load has been finished.
|
||||||
await waitForInitialNavigationCompleted(contentBrowser.browsingContext);
|
await waitForInitialNavigationCompleted(contentBrowser.browsingContext);
|
||||||
|
|
||||||
const id = windowManager.getIdForBrowser(contentBrowser);
|
const id = TabManager.getIdForBrowser(contentBrowser);
|
||||||
|
|
||||||
return { handle: id.toString(), type };
|
return { handle: id.toString(), type };
|
||||||
};
|
};
|
||||||
@ -2050,29 +2051,17 @@ GeckoDriver.prototype.close = async function() {
|
|||||||
assert.open(this.getBrowsingContext({ context: Context.Content, top: true }));
|
assert.open(this.getBrowsingContext({ context: Context.Content, top: true }));
|
||||||
await this._handleUserPrompts();
|
await this._handleUserPrompts();
|
||||||
|
|
||||||
let nwins = 0;
|
|
||||||
|
|
||||||
for (let win of windowManager.windows) {
|
|
||||||
// For browser windows count the tabs. Otherwise take the window itself.
|
|
||||||
let tabbrowser = browser.getTabBrowser(win);
|
|
||||||
if (tabbrowser && tabbrowser.tabs) {
|
|
||||||
nwins += tabbrowser.tabs.length;
|
|
||||||
} else {
|
|
||||||
nwins += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there is only one window left, do not close it. Instead return
|
// If there is only one window left, do not close it. Instead return
|
||||||
// a faked empty array of window handles. This will instruct geckodriver
|
// a faked empty array of window handles. This will instruct geckodriver
|
||||||
// to terminate the application.
|
// to terminate the application.
|
||||||
if (nwins === 1) {
|
if (TabManager.getTabCount() === 1) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.curBrowser.closeTab();
|
await this.curBrowser.closeTab();
|
||||||
this.currentSession.contentBrowsingContext = null;
|
this.currentSession.contentBrowsingContext = null;
|
||||||
|
|
||||||
return windowManager.windowHandles.map(String);
|
return TabManager.allBrowserUniqueIds.map(String);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
51
remote/shared/MobileTabBrowser.jsm
Normal file
51
remote/shared/MobileTabBrowser.jsm
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var EXPORTED_SYMBOLS = ["MobileTabBrowser"];
|
||||||
|
|
||||||
|
// GeckoView shim for Desktop's gBrowser
|
||||||
|
class MobileTabBrowser {
|
||||||
|
constructor(window) {
|
||||||
|
this.window = window;
|
||||||
|
}
|
||||||
|
|
||||||
|
get tabs() {
|
||||||
|
return [this.window.tab];
|
||||||
|
}
|
||||||
|
|
||||||
|
get selectedTab() {
|
||||||
|
return this.window.tab;
|
||||||
|
}
|
||||||
|
|
||||||
|
set selectedTab(tab) {
|
||||||
|
if (tab != this.selectedTab) {
|
||||||
|
throw new Error("GeckoView only supports a single tab");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Synthesize a custom TabSelect event to indicate that a tab has been
|
||||||
|
// selected even when we don't change it.
|
||||||
|
const event = this.window.CustomEvent("TabSelect", {
|
||||||
|
bubbles: true,
|
||||||
|
cancelable: false,
|
||||||
|
detail: {
|
||||||
|
previousTab: this.selectedTab,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this.window.document.dispatchEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
get selectedBrowser() {
|
||||||
|
return this.selectedTab.linkedBrowser;
|
||||||
|
}
|
||||||
|
|
||||||
|
addEventListener() {
|
||||||
|
this.window.addEventListener(...arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeEventListener() {
|
||||||
|
this.window.removeEventListener(...arguments);
|
||||||
|
}
|
||||||
|
}
|
@ -12,12 +12,94 @@ var { XPCOMUtils } = ChromeUtils.import(
|
|||||||
|
|
||||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||||
Services: "resource://gre/modules/Services.jsm",
|
Services: "resource://gre/modules/Services.jsm",
|
||||||
|
|
||||||
|
MobileTabBrowser: "chrome://remote/content/shared/MobileTabBrowser.jsm",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Maps browser's permanentKey to uuid: WeakMap.<Object, string>
|
||||||
|
const browserUniqueIds = new WeakMap();
|
||||||
|
|
||||||
var TabManager = {
|
var TabManager = {
|
||||||
get gBrowser() {
|
get gBrowser() {
|
||||||
const window = Services.wm.getMostRecentWindow("navigator:browser");
|
const window = Services.wm.getMostRecentWindow("navigator:browser");
|
||||||
return window.gBrowser;
|
return this.getTabBrowser(window);
|
||||||
|
},
|
||||||
|
|
||||||
|
get windows() {
|
||||||
|
return Services.wm.getEnumerator(null);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array of unique browser ids (UUIDs) for all content browsers of all
|
||||||
|
* windows.
|
||||||
|
*
|
||||||
|
* TODO: Similarly to getBrowserById, we should improve the performance of
|
||||||
|
* this getter in Bug 1750065.
|
||||||
|
*
|
||||||
|
* @return {Array<String>}
|
||||||
|
* Array of UUIDs for all content browsers.
|
||||||
|
*/
|
||||||
|
get allBrowserUniqueIds() {
|
||||||
|
const browserIds = [];
|
||||||
|
|
||||||
|
for (const win of this.windows) {
|
||||||
|
const tabBrowser = this.getTabBrowser(win);
|
||||||
|
|
||||||
|
// Only return handles for browser windows
|
||||||
|
if (tabBrowser && tabBrowser.tabs) {
|
||||||
|
for (const tab of tabBrowser.tabs) {
|
||||||
|
const contentBrowser = this.getBrowserForTab(tab);
|
||||||
|
const winId = this.getIdForBrowser(contentBrowser);
|
||||||
|
if (winId !== null) {
|
||||||
|
browserIds.push(winId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return browserIds;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the <code><xul:browser></code> for the specified tab.
|
||||||
|
*
|
||||||
|
* @param {Tab} tab
|
||||||
|
* The tab whose browser needs to be returned.
|
||||||
|
*
|
||||||
|
* @return {xul:browser}
|
||||||
|
* The linked browser for the tab or null if no browser can be found.
|
||||||
|
*/
|
||||||
|
getBrowserForTab(tab) {
|
||||||
|
if (tab && "linkedBrowser" in tab) {
|
||||||
|
return tab.linkedBrowser;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the tab browser for the specified chrome window.
|
||||||
|
*
|
||||||
|
* @param {ChromeWindow} win
|
||||||
|
* Window whose <code>tabbrowser</code> needs to be accessed.
|
||||||
|
*
|
||||||
|
* @return {Tab}
|
||||||
|
* Tab browser or null if it's not a browser window.
|
||||||
|
*/
|
||||||
|
getTabBrowser(win) {
|
||||||
|
// GeckoView
|
||||||
|
// TODO: Migrate to AppInfo.isAndroid after AppInfo moves to shared/
|
||||||
|
if (Services.appinfo.OS === "Android") {
|
||||||
|
return new MobileTabBrowser(win);
|
||||||
|
// Firefox
|
||||||
|
} else if ("gBrowser" in win) {
|
||||||
|
return win.gBrowser;
|
||||||
|
// Thunderbird
|
||||||
|
} else if (win.document.getElementById("tabmail")) {
|
||||||
|
return win.document.getElementById("tabmail");
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
addTab({ userContextId }) {
|
addTab({ userContextId }) {
|
||||||
@ -30,6 +112,84 @@ var TabManager = {
|
|||||||
return tab;
|
return tab;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a the browser element corresponding to the provided unique id,
|
||||||
|
* previously generated via getIdForBrowser.
|
||||||
|
*
|
||||||
|
* TODO: To avoid creating strong references on browser elements and
|
||||||
|
* potentially leaking those elements, this method loops over all windows and
|
||||||
|
* all tabs. It should be replaced by a faster implementation in Bug 1750065.
|
||||||
|
*
|
||||||
|
* @param {String} id
|
||||||
|
* A browser unique id created by getIdForBrowser.
|
||||||
|
* @return {xul:browser}
|
||||||
|
* The <xul:browser> corresponding to the provided id. Will return null if
|
||||||
|
* no matching browser element is found.
|
||||||
|
*/
|
||||||
|
getBrowserById(id) {
|
||||||
|
for (const win of this.windows) {
|
||||||
|
const tabBrowser = this.getTabBrowser(win);
|
||||||
|
if (tabBrowser && tabBrowser.tabs) {
|
||||||
|
for (let i = 0; i < tabBrowser.tabs.length; ++i) {
|
||||||
|
const contentBrowser = this.getBrowserForTab(tabBrowser.tabs[i]);
|
||||||
|
if (this.getIdForBrowser(contentBrowser) == id) {
|
||||||
|
return contentBrowser;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the unique id for the given xul browser element. The id is a
|
||||||
|
* dynamically generated uuid associated with the permanentKey property of the
|
||||||
|
* given browser element.
|
||||||
|
*
|
||||||
|
* @param {xul:browser} browserElement
|
||||||
|
* The <xul:browser> for which we want to retrieve the id.
|
||||||
|
* @return {String} The unique id for this browser.
|
||||||
|
*/
|
||||||
|
getIdForBrowser(browserElement) {
|
||||||
|
if (browserElement === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const key = browserElement.permanentKey;
|
||||||
|
if (!browserUniqueIds.has(key)) {
|
||||||
|
const uuid = Services.uuid.generateUUID().toString();
|
||||||
|
browserUniqueIds.set(key, uuid.substring(1, uuid.length - 1));
|
||||||
|
}
|
||||||
|
return browserUniqueIds.get(key);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the unique id for the browser element owning the provided browsing
|
||||||
|
* context.
|
||||||
|
*
|
||||||
|
* @param {BrowsingContext} browsingContext
|
||||||
|
* The browsing context for which we want to retrieve the (browser) uuid.
|
||||||
|
* @return {String} The unique id for the browser owning the browsing context.
|
||||||
|
*/
|
||||||
|
getBrowserIdForBrowsingContext(browsingContext) {
|
||||||
|
const contentBrowser = browsingContext.top.embedderElement;
|
||||||
|
return this.getIdForBrowser(contentBrowser);
|
||||||
|
},
|
||||||
|
|
||||||
|
getTabCount() {
|
||||||
|
let count = 0;
|
||||||
|
for (const win of this.windows) {
|
||||||
|
// For browser windows count the tabs. Otherwise take the window itself.
|
||||||
|
const tabbrowser = this.getTabBrowser(win);
|
||||||
|
if (tabbrowser?.tabs) {
|
||||||
|
count += tabbrowser.tabs.length;
|
||||||
|
} else {
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
},
|
||||||
|
|
||||||
removeTab(tab) {
|
removeTab(tab) {
|
||||||
this.gBrowser.removeTab(tab);
|
this.gBrowser.removeTab(tab);
|
||||||
},
|
},
|
||||||
|
@ -14,8 +14,8 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
|||||||
Services: "resource://gre/modules/Services.jsm",
|
Services: "resource://gre/modules/Services.jsm",
|
||||||
|
|
||||||
AppInfo: "chrome://remote/content/marionette/appinfo.js",
|
AppInfo: "chrome://remote/content/marionette/appinfo.js",
|
||||||
browser: "chrome://remote/content/marionette/browser.js",
|
|
||||||
error: "chrome://remote/content/shared/webdriver/Errors.jsm",
|
error: "chrome://remote/content/shared/webdriver/Errors.jsm",
|
||||||
|
TabManager: "chrome://remote/content/shared/TabManager.jsm",
|
||||||
TimedPromise: "chrome://remote/content/marionette/sync.js",
|
TimedPromise: "chrome://remote/content/marionette/sync.js",
|
||||||
waitForEvent: "chrome://remote/content/marionette/sync.js",
|
waitForEvent: "chrome://remote/content/marionette/sync.js",
|
||||||
waitForObserverTopic: "chrome://remote/content/marionette/sync.js",
|
waitForObserverTopic: "chrome://remote/content/marionette/sync.js",
|
||||||
@ -28,32 +28,10 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
|||||||
*/
|
*/
|
||||||
class WindowManager {
|
class WindowManager {
|
||||||
constructor() {
|
constructor() {
|
||||||
// Maps browser's permanentKey to uuid: WeakMap.<Object, string>
|
|
||||||
this._windowHandles = new WeakMap();
|
|
||||||
// Maps ChromeWindow to uuid: WeakMap.<Object, string>
|
// Maps ChromeWindow to uuid: WeakMap.<Object, string>
|
||||||
this._chromeWindowHandles = new WeakMap();
|
this._chromeWindowHandles = new WeakMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
get windowHandles() {
|
|
||||||
const windowHandles = [];
|
|
||||||
|
|
||||||
for (const win of this.windows) {
|
|
||||||
const tabBrowser = browser.getTabBrowser(win);
|
|
||||||
|
|
||||||
// Only return handles for browser windows
|
|
||||||
if (tabBrowser && tabBrowser.tabs) {
|
|
||||||
for (const tab of tabBrowser.tabs) {
|
|
||||||
const winId = this.getIdForBrowser(browser.getBrowserForTab(tab));
|
|
||||||
if (winId !== null) {
|
|
||||||
windowHandles.push(winId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return windowHandles;
|
|
||||||
}
|
|
||||||
|
|
||||||
get chromeWindowHandles() {
|
get chromeWindowHandles() {
|
||||||
const chromeWindowHandles = [];
|
const chromeWindowHandles = [];
|
||||||
|
|
||||||
@ -88,11 +66,11 @@ class WindowManager {
|
|||||||
|
|
||||||
// Otherwise check if the chrome window has a tab browser, and that it
|
// Otherwise check if the chrome window has a tab browser, and that it
|
||||||
// contains a tab with the wanted window handle.
|
// contains a tab with the wanted window handle.
|
||||||
const tabBrowser = browser.getTabBrowser(win);
|
const tabBrowser = TabManager.getTabBrowser(win);
|
||||||
if (tabBrowser && tabBrowser.tabs) {
|
if (tabBrowser && tabBrowser.tabs) {
|
||||||
for (let i = 0; i < tabBrowser.tabs.length; ++i) {
|
for (let i = 0; i < tabBrowser.tabs.length; ++i) {
|
||||||
let contentBrowser = browser.getBrowserForTab(tabBrowser.tabs[i]);
|
let contentBrowser = TabManager.getBrowserForTab(tabBrowser.tabs[i]);
|
||||||
let contentWindowId = this.getIdForBrowser(contentBrowser);
|
let contentWindowId = TabManager.getIdForBrowser(contentBrowser);
|
||||||
|
|
||||||
if (contentWindowId == handle) {
|
if (contentWindowId == handle) {
|
||||||
return this.getWindowProperties(win, { tabIndex: i });
|
return this.getWindowProperties(win, { tabIndex: i });
|
||||||
@ -104,35 +82,6 @@ class WindowManager {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve a the browser element corresponding to the provided unique id,
|
|
||||||
* previously generated via getIdForBrowser.
|
|
||||||
*
|
|
||||||
* TODO: To avoid creating strong references on browser elements and
|
|
||||||
* potentially leaking those elements, this method loops over all windows and
|
|
||||||
* all tabs. It should be replaced by a faster implementation in Bug 1750065.
|
|
||||||
*
|
|
||||||
* @param {String} id
|
|
||||||
* A browser unique id created by getIdForBrowser.
|
|
||||||
* @return {xul:browser}
|
|
||||||
* The <xul:browser> corresponding to the provided id. Will return null if
|
|
||||||
* no matching browser element is found.
|
|
||||||
*/
|
|
||||||
getBrowserById(id) {
|
|
||||||
for (const win of this.windows) {
|
|
||||||
const tabBrowser = browser.getTabBrowser(win);
|
|
||||||
if (tabBrowser && tabBrowser.tabs) {
|
|
||||||
for (let i = 0; i < tabBrowser.tabs.length; ++i) {
|
|
||||||
const contentBrowser = browser.getBrowserForTab(tabBrowser.tabs[i]);
|
|
||||||
if (this.getIdForBrowser(contentBrowser) == id) {
|
|
||||||
return contentBrowser;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A set of properties describing a window and that should allow to uniquely
|
* A set of properties describing a window and that should allow to uniquely
|
||||||
* identify it. The described window can either be a Chrome Window or a
|
* identify it. The described window can either be a Chrome Window or a
|
||||||
@ -166,46 +115,11 @@ class WindowManager {
|
|||||||
return {
|
return {
|
||||||
win,
|
win,
|
||||||
id: this.getIdForWindow(win),
|
id: this.getIdForWindow(win),
|
||||||
hasTabBrowser: !!browser.getTabBrowser(win),
|
hasTabBrowser: !!TabManager.getTabBrowser(win),
|
||||||
tabIndex: options.tabIndex,
|
tabIndex: options.tabIndex,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves an id for the given xul browser element. The id is a dynamically
|
|
||||||
* generated uuid associated with the permanentKey property of the given
|
|
||||||
* browser element.
|
|
||||||
*
|
|
||||||
* @param {xul:browser} browserElement
|
|
||||||
* The <xul:browser> for which we want to retrieve the id.
|
|
||||||
* @return {String} The unique id for this browser.
|
|
||||||
*/
|
|
||||||
getIdForBrowser(browserElement) {
|
|
||||||
if (browserElement === null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const key = browserElement.permanentKey;
|
|
||||||
if (!this._windowHandles.has(key)) {
|
|
||||||
const uuid = Services.uuid.generateUUID().toString();
|
|
||||||
this._windowHandles.set(key, uuid.substring(1, uuid.length - 1));
|
|
||||||
}
|
|
||||||
return this._windowHandles.get(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve an id for the browser element owning the provided browsing
|
|
||||||
* context.
|
|
||||||
*
|
|
||||||
* @param {BrowsingContext} browsingContext
|
|
||||||
* The browsing context for which we want to retrieve the (browser) uuid.
|
|
||||||
* @return {String} The unique id for the browser owning the browsing context.
|
|
||||||
*/
|
|
||||||
getBrowserIdForBrowsingContext(browsingContext) {
|
|
||||||
const contentBrowser = browsingContext.top.embedderElement;
|
|
||||||
return this.getIdForBrowser(contentBrowser);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves an id for the given chrome window. The id is a dynamically
|
* Retrieves an id for the given chrome window. The id is a dynamically
|
||||||
* generated uuid associated with the window object.
|
* generated uuid associated with the window object.
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const { windowManager } = ChromeUtils.import(
|
const { TabManager } = ChromeUtils.import(
|
||||||
"chrome://remote/content/shared/WindowManager.jsm"
|
"chrome://remote/content/shared/TabManager.jsm"
|
||||||
);
|
);
|
||||||
|
|
||||||
const COM_TEST_PAGE = "https://example.com/document-builder.sjs?html=COM";
|
const COM_TEST_PAGE = "https://example.com/document-builder.sjs?html=COM";
|
||||||
@ -80,7 +80,7 @@ function sendBroadcastForTopBrowsingContext(
|
|||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
type: CONTEXT_DESCRIPTOR_TYPES.TOP_BROWSING_CONTEXT,
|
type: CONTEXT_DESCRIPTOR_TYPES.TOP_BROWSING_CONTEXT,
|
||||||
id: windowManager.getBrowserIdForBrowsingContext(topBrowsingContext),
|
id: TabManager.getBrowserIdForBrowsingContext(topBrowsingContext),
|
||||||
},
|
},
|
||||||
rootMessageHandler
|
rootMessageHandler
|
||||||
);
|
);
|
||||||
|
@ -19,7 +19,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
|||||||
"chrome://remote/content/shared/messagehandler/transports/FrameContextUtils.jsm",
|
"chrome://remote/content/shared/messagehandler/transports/FrameContextUtils.jsm",
|
||||||
MessageHandlerFrameActor:
|
MessageHandlerFrameActor:
|
||||||
"chrome://remote/content/shared/messagehandler/transports/js-window-actors/MessageHandlerFrameActor.jsm",
|
"chrome://remote/content/shared/messagehandler/transports/js-window-actors/MessageHandlerFrameActor.jsm",
|
||||||
windowManager: "chrome://remote/content/shared/WindowManager.jsm",
|
TabManager: "chrome://remote/content/shared/TabManager.jsm",
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -119,7 +119,7 @@ class FrameTransport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (type === CONTEXT_DESCRIPTOR_TYPES.TOP_BROWSING_CONTEXT) {
|
if (type === CONTEXT_DESCRIPTOR_TYPES.TOP_BROWSING_CONTEXT) {
|
||||||
const { browserId } = windowManager.getBrowserById(id);
|
const { browserId } = TabManager.getBrowserById(id);
|
||||||
return this._getBrowsingContexts({ browserId });
|
return this._getBrowsingContexts({ browserId });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user