tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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)