window_manager.py (8422B)
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 3 # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 5 import sys 6 7 from marionette_driver import Wait 8 9 10 class WindowManagerMixin(object): 11 def setUp(self): 12 super(WindowManagerMixin, self).setUp() 13 14 self.start_window = self.marionette.current_chrome_window_handle 15 self.start_windows = self.marionette.chrome_window_handles 16 17 self.start_tab = self.marionette.current_window_handle 18 self.start_tabs = self.marionette.window_handles 19 20 def tearDown(self): 21 if len(self.marionette.chrome_window_handles) > len(self.start_windows): 22 raise Exception("Not all windows as opened by the test have been closed") 23 24 if len(self.marionette.window_handles) > len(self.start_tabs): 25 raise Exception("Not all tabs as opened by the test have been closed") 26 27 super(WindowManagerMixin, self).tearDown() 28 29 def close_all_tabs(self): 30 current_window_handles = self.marionette.window_handles 31 32 # If the start tab is not present anymore, use the next one of the list 33 if self.start_tab not in current_window_handles: 34 self.start_tab = current_window_handles[0] 35 36 current_window_handles.remove(self.start_tab) 37 for handle in current_window_handles: 38 self.marionette.switch_to_window(handle) 39 self.marionette.close() 40 41 self.marionette.switch_to_window(self.start_tab) 42 43 def close_all_windows(self): 44 current_chrome_window_handles = self.marionette.chrome_window_handles 45 46 # If the start window is not present anymore, use the next one of the list 47 if self.start_window not in current_chrome_window_handles: 48 self.start_window = current_chrome_window_handles[0] 49 current_chrome_window_handles.remove(self.start_window) 50 51 for handle in current_chrome_window_handles: 52 self.marionette.switch_to_window(handle) 53 self.marionette.close_chrome_window() 54 55 self.marionette.switch_to_window(self.start_window) 56 57 def open_tab(self, callback=None, focus=False): 58 current_tabs = self.marionette.window_handles 59 60 try: 61 if callable(callback): 62 callback() 63 else: 64 result = self.marionette.open(type="tab", focus=focus) 65 if result["type"] != "tab": 66 raise Exception( 67 "Newly opened browsing context is of type {} and not tab.".format( 68 result["type"] 69 ) 70 ) 71 except Exception: 72 exc_cls, exc, tb = sys.exc_info() 73 raise exc_cls( 74 "Failed to trigger opening a new tab: {}".format(exc) 75 ).with_traceback(tb) 76 else: 77 Wait(self.marionette).until( 78 lambda mn: len(mn.window_handles) == len(current_tabs) + 1, 79 message="No new tab has been opened", 80 ) 81 82 [new_tab] = list(set(self.marionette.window_handles) - set(current_tabs)) 83 84 return new_tab 85 86 def open_window(self, callback=None, focus=False, private=False): 87 current_windows = self.marionette.chrome_window_handles 88 current_tabs = self.marionette.window_handles 89 90 def loaded(handle): 91 with self.marionette.using_context("chrome"): 92 return self.marionette.execute_async_script( 93 """ 94 const [handle, resolve] = arguments; 95 96 const { NavigableManager } = ChromeUtils.importESModule( 97 "chrome://remote/content/shared/NavigableManager.sys.mjs" 98 ); 99 const { windowManager } = ChromeUtils.importESModule( 100 "chrome://remote/content/shared/WindowManager.sys.mjs" 101 ); 102 103 const browsingContext = 104 NavigableManager.getBrowsingContextById(handle); 105 const window = 106 windowManager.getChromeWindowForBrowsingContext(browsingContext); 107 108 (async function() { 109 if (window) { 110 await windowManager.waitForChromeWindowLoaded(window); 111 } 112 113 resolve(); 114 })(); 115 """, 116 script_args=[handle], 117 ) 118 119 try: 120 if callable(callback): 121 callback(focus) 122 else: 123 result = self.marionette.open( 124 type="window", focus=focus, private=private 125 ) 126 if result["type"] != "window": 127 raise Exception( 128 "Newly opened browsing context is of type {} and not window.".format( 129 result["type"] 130 ) 131 ) 132 except Exception: 133 exc_cls, exc, tb = sys.exc_info() 134 raise exc_cls( 135 "Failed to trigger opening a new window: {}".format(exc) 136 ).with_traceback(tb) 137 else: 138 Wait(self.marionette).until( 139 lambda mn: len(mn.chrome_window_handles) == len(current_windows) + 1, 140 message="No new window has been opened", 141 ) 142 143 [new_window] = list( 144 set(self.marionette.chrome_window_handles) - set(current_windows) 145 ) 146 147 # Before continuing ensure the window has been completed loading 148 loaded(new_window) 149 150 # Bug 1507771 - Return the correct handle based on the currently selected context 151 # as long as "WebDriver:NewWindow" is not handled separtely in chrome context 152 context = self.marionette._send_message( 153 "Marionette:GetContext", key="value" 154 ) 155 if context == "chrome": 156 return new_window 157 elif context == "content": 158 [new_tab] = list( 159 set(self.marionette.window_handles) - set(current_tabs) 160 ) 161 return new_tab 162 163 def open_chrome_window(self, url, focus=False): 164 """Open a new chrome window with the specified chrome URL. 165 166 Can be replaced with "WebDriver:NewWindow" once the command 167 supports opening generic chrome windows beside browsers (bug 1507771). 168 """ 169 170 def open_with_js(focus): 171 with self.marionette.using_context("chrome"): 172 self.marionette.execute_async_script( 173 """ 174 let [url, focus, resolve] = arguments; 175 176 function waitForEvent(target, type, args) { 177 return new Promise(resolve => { 178 let params = Object.assign({once: true}, args); 179 target.addEventListener(type, event => { 180 dump(`** Received DOM event ${event.type} for ${event.target}\n`); 181 resolve(); 182 }, params); 183 }); 184 } 185 186 function waitForFocus(win) { 187 return Promise.all([ 188 waitForEvent(win, "activate"), 189 waitForEvent(win, "focus", {capture: true}), 190 ]); 191 } 192 193 (async function() { 194 // Open a window, wait for it to receive focus 195 let win = window.openDialog(url, null, "chrome,centerscreen"); 196 let focused = waitForFocus(win); 197 198 win.focus(); 199 await focused; 200 201 // The new window shouldn't get focused. As such set the 202 // focus back to the opening window. 203 if (!focus && Services.focus.activeWindow != window) { 204 let focused = waitForFocus(window); 205 window.focus(); 206 await focused; 207 } 208 209 resolve(win.docShell.browsingContext.id); 210 })(); 211 """, 212 script_args=(url, focus), 213 ) 214 215 with self.marionette.using_context("chrome"): 216 return self.open_window(callback=open_with_js, focus=focus)