1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 ChromeUtils.defineESModuleGetters(lazy, {
8 AppInfo: "chrome://remote/content/shared/AppInfo.sys.mjs",
9 error: "chrome://remote/content/shared/webdriver/Errors.sys.mjs",
10 EventPromise: "chrome://remote/content/shared/Sync.sys.mjs",
11 MessageManagerDestroyedPromise:
12 "chrome://remote/content/marionette/sync.sys.mjs",
13 TabManager: "chrome://remote/content/shared/TabManager.sys.mjs",
14 windowManager: "chrome://remote/content/shared/WindowManager.sys.mjs",
18 export const browser = {};
21 * Variations of Marionette contexts.
23 * Choosing a context through the <tt>Marionette:SetContext</tt>
24 * command directs all subsequent browsing context scoped commands
27 * @class Marionette.Context
29 export class Context {
31 * Gets the correct context from a string.
34 * Context string serialisation.
40 * If <var>s</var> is not a context.
42 static fromString(s) {
45 return Context.Chrome;
48 return Context.Content;
51 throw new TypeError(`Unknown context: ${s}`);
56 Context.Chrome = "chrome";
57 Context.Content = "content";
60 * Creates a browsing context wrapper.
62 * Browsing contexts handle interactions with the browser, according to
63 * the current environment.
65 browser.Context = class {
67 * @param {ChromeWindow} window
68 * ChromeWindow that contains the top-level browsing context.
69 * @param {GeckoDriver} driver
70 * Reference to driver instance.
72 constructor(window, driver) {
76 // In Firefox this is <xul:tabbrowser> (not <xul:browser>!)
77 // and MobileTabBrowser in GeckoView.
78 this.tabBrowser = lazy.TabManager.getTabBrowser(this.window);
80 // Used to set curFrameId upon new session
81 this.newSession = true;
83 // A reference to the tab corresponding to the current window handle,
84 // if any. Specifically, this.tab refers to the last tab that Marionette
85 // switched to in this browser window. Note that this may not equal the
86 // currently selected tab. For example, if Marionette switches to tab
87 // A, and then clicks on a button that opens a new tab B in the same
88 // browser window, this.tab will still point to tab A, despite tab B
89 // being the currently selected tab.
94 * Returns the content browser for the currently selected tab.
95 * If there is no tab selected, null will be returned.
97 get contentBrowser() {
99 return lazy.TabManager.getBrowserForTab(this.tab);
102 this.driver.isReftestBrowser(this.tabBrowser)
104 return this.tabBrowser;
110 get messageManager() {
111 if (this.contentBrowser) {
112 return this.contentBrowser.messageManager;
119 * Checks if the browsing context has been discarded.
121 * The browsing context will have been discarded if the content
122 * browser, represented by the <code><xul:browser></code>,
126 * True if browsing context has been discarded, false otherwise.
129 return this.contentBrowser === null;
133 * Gets the position and dimensions of the top-level browsing context.
135 * @returns {Map.<string, number>}
136 * Object with |x|, |y|, |width|, and |height| properties.
140 x: this.window.screenX,
141 y: this.window.screenY,
142 width: this.window.outerWidth,
143 height: this.window.outerHeight,
148 * Close the current window.
151 * A promise which is resolved when the current window has been closed.
153 async closeWindow() {
154 return lazy.windowManager.closeWindow(this.window);
158 * Focus the current window.
161 * A promise which is resolved when the current window has been focused.
163 async focusWindow() {
164 await lazy.windowManager.focusWindow(this.window);
166 // Also focus the currently selected tab if present.
167 this.contentBrowser?.focus();
171 * Open a new browser window.
174 * A promise resolving to the newly created chrome window.
176 openBrowserWindow(focus = false, isPrivate = false) {
177 return lazy.windowManager.openBrowserWindow({
178 openerWindow: this.window,
185 * Close the current tab.
188 * A promise which is resolved when the current tab has been closed.
190 * @throws UnsupportedOperationError
191 * If tab handling for the current application isn't supported.
194 // If the current window is not a browser then close it directly. Do the
195 // same if only one remaining tab is open, or no tab selected at all.
197 // Note: For GeckoView there will always be a single tab only. But for
198 // consistency with other platforms a specific condition has been added
199 // below as well even it's not really used.
202 !this.tabBrowser.tabs ||
203 this.tabBrowser.tabs.length === 1 ||
206 return this.closeWindow();
209 let destroyed = new lazy.MessageManagerDestroyedPromise(
214 if (lazy.AppInfo.isAndroid) {
215 await lazy.TabManager.removeTab(this.tab);
216 } else if (lazy.AppInfo.isFirefox) {
217 tabClosed = new lazy.EventPromise(this.tab, "TabClose");
218 await this.tabBrowser.removeTab(this.tab);
220 throw new lazy.error.UnsupportedOperationError(
221 `closeTab() not supported for ${lazy.AppInfo.name}`
225 return Promise.all([destroyed, tabClosed]);
229 * Open a new tab in the currently selected chrome window.
231 async openTab(focus = false) {
234 // Bug 1795841 - For Firefox the TabManager cannot be used yet. As such
235 // handle opening a tab differently for Android.
236 if (lazy.AppInfo.isAndroid) {
237 tab = await lazy.TabManager.addTab({ focus, window: this.window });
238 } else if (lazy.AppInfo.isFirefox) {
239 const opened = new lazy.EventPromise(this.window, "TabOpen");
240 this.window.BrowserCommands.openTab({ url: "about:blank" });
243 tab = this.tabBrowser.selectedTab;
245 // The new tab is always selected by default. If focus is not wanted,
246 // the previously tab needs to be selected again.
248 await lazy.TabManager.selectTab(this.tab);
251 throw new lazy.error.UnsupportedOperationError(
252 `openTab() not supported for ${lazy.AppInfo.name}`
260 * Set the current tab.
262 * @param {number=} index
263 * Tab index to switch to. If the parameter is undefined,
264 * the currently selected tab will be used.
265 * @param {ChromeWindow=} window
266 * Switch to this window before selecting the tab.
267 * @param {boolean=} focus
268 * A boolean value which determins whether to focus
269 * the window. Defaults to true.
274 * @throws UnsupportedOperationError
275 * If tab handling for the current application isn't supported.
277 async switchToTab(index, window = undefined, focus = true) {
279 this.window = window;
280 this.tabBrowser = lazy.TabManager.getTabBrowser(this.window);
283 if (!this.tabBrowser || this.driver.isReftestBrowser(this.tabBrowser)) {
287 if (typeof index == "undefined") {
288 this.tab = this.tabBrowser.selectedTab;
290 this.tab = this.tabBrowser.tabs[index];
294 await lazy.TabManager.selectTab(this.tab);
297 // By accessing the content browser's message manager a new browsing
298 // context is created for browserless tabs, which is needed to successfully
299 // run the WebDriver's is browsing context open step. This is temporary
300 // until we find a better solution on bug 1812258.
307 * Registers a new frame, and sets its current frame id to this frame
308 * if it is not already assigned, and if a) we already have a session
309 * or b) we're starting a new session and it is the right start frame.
312 if (!this.tabBrowser) {
316 // If we're setting up a new session on Firefox, we only process the
317 // registration for this frame if it belongs to the current tab.