Source code
Revision control
Copy as Markdown
Other Tools
# 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
import sys
from marionette_driver import Wait
from six import reraise
class WindowManagerMixin(object):
def setUp(self):
super(WindowManagerMixin, self).setUp()
self.start_window = self.marionette.current_chrome_window_handle
self.start_windows = self.marionette.chrome_window_handles
self.start_tab = self.marionette.current_window_handle
self.start_tabs = self.marionette.window_handles
def tearDown(self):
if len(self.marionette.chrome_window_handles) > len(self.start_windows):
raise Exception("Not all windows as opened by the test have been closed")
if len(self.marionette.window_handles) > len(self.start_tabs):
raise Exception("Not all tabs as opened by the test have been closed")
super(WindowManagerMixin, self).tearDown()
def close_all_tabs(self):
current_window_handles = self.marionette.window_handles
# If the start tab is not present anymore, use the next one of the list
if self.start_tab not in current_window_handles:
self.start_tab = current_window_handles[0]
current_window_handles.remove(self.start_tab)
for handle in current_window_handles:
self.marionette.switch_to_window(handle)
self.marionette.close()
self.marionette.switch_to_window(self.start_tab)
def close_all_windows(self):
current_chrome_window_handles = self.marionette.chrome_window_handles
# If the start window is not present anymore, use the next one of the list
if self.start_window not in current_chrome_window_handles:
self.start_window = current_chrome_window_handles[0]
current_chrome_window_handles.remove(self.start_window)
for handle in current_chrome_window_handles:
self.marionette.switch_to_window(handle)
self.marionette.close_chrome_window()
self.marionette.switch_to_window(self.start_window)
def open_tab(self, callback=None, focus=False):
current_tabs = self.marionette.window_handles
try:
if callable(callback):
callback()
else:
result = self.marionette.open(type="tab", focus=focus)
if result["type"] != "tab":
raise Exception(
"Newly opened browsing context is of type {} and not tab.".format(
result["type"]
)
)
except Exception:
exc_cls, exc, tb = sys.exc_info()
reraise(
exc_cls,
exc_cls("Failed to trigger opening a new tab: {}".format(exc)),
tb,
)
else:
Wait(self.marionette).until(
lambda mn: len(mn.window_handles) == len(current_tabs) + 1,
message="No new tab has been opened",
)
[new_tab] = list(set(self.marionette.window_handles) - set(current_tabs))
return new_tab
def open_window(self, callback=None, focus=False, private=False):
current_windows = self.marionette.chrome_window_handles
current_tabs = self.marionette.window_handles
def loaded(handle):
with self.marionette.using_context("chrome"):
return self.marionette.execute_script(
"""
const { windowManager } = ChromeUtils.importESModule(
"chrome://remote/content/shared/WindowManager.sys.mjs"
);
const win = windowManager.findWindowByHandle(arguments[0]).win;
return win.document.readyState == "complete";
""",
script_args=[handle],
)
try:
if callable(callback):
callback(focus)
else:
result = self.marionette.open(
type="window", focus=focus, private=private
)
if result["type"] != "window":
raise Exception(
"Newly opened browsing context is of type {} and not window.".format(
result["type"]
)
)
except Exception:
exc_cls, exc, tb = sys.exc_info()
reraise(
exc_cls,
exc_cls("Failed to trigger opening a new window: {}".format(exc)),
tb,
)
else:
Wait(self.marionette).until(
lambda mn: len(mn.chrome_window_handles) == len(current_windows) + 1,
message="No new window has been opened",
)
[new_window] = list(
set(self.marionette.chrome_window_handles) - set(current_windows)
)
# Before continuing ensure the window has been completed loading
Wait(self.marionette).until(
lambda _: loaded(new_window),
message="Window with handle '{}'' did not finish loading".format(
new_window
),
)
# as long as "WebDriver:NewWindow" is not handled separtely in chrome context
context = self.marionette._send_message(
"Marionette:GetContext", key="value"
)
if context == "chrome":
return new_window
elif context == "content":
[new_tab] = list(
set(self.marionette.window_handles) - set(current_tabs)
)
return new_tab
def open_chrome_window(self, url, focus=False):
"""Open a new chrome window with the specified chrome URL.
Can be replaced with "WebDriver:NewWindow" once the command
"""
def open_with_js(focus):
with self.marionette.using_context("chrome"):
self.marionette.execute_async_script(
"""
let [url, focus, resolve] = arguments;
function waitForEvent(target, type, args) {
return new Promise(resolve => {
let params = Object.assign({once: true}, args);
target.addEventListener(type, event => {
dump(`** Received DOM event ${event.type} for ${event.target}\n`);
resolve();
}, params);
});
}
function waitForFocus(win) {
return Promise.all([
waitForEvent(win, "activate"),
waitForEvent(win, "focus", {capture: true}),
]);
}
(async function() {
// Open a window, wait for it to receive focus
let win = window.openDialog(url, null, "chrome,centerscreen");
let focused = waitForFocus(win);
win.focus();
await focused;
// The new window shouldn't get focused. As such set the
// focus back to the opening window.
if (!focus && Services.focus.activeWindow != window) {
let focused = waitForFocus(window);
window.focus();
await focused;
}
resolve(win.docShell.browsingContext.id);
})();
""",
script_args=(url, focus),
)
with self.marionette.using_context("chrome"):
return self.open_window(callback=open_with_js, focus=focus)