mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-28 19:37:32 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
|
|
@ -0,0 +1,388 @@
|
|||
# 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/.
|
||||
|
||||
from marionette_driver import (
|
||||
By, Wait
|
||||
)
|
||||
|
||||
from marionette_driver.errors import NoSuchElementException
|
||||
|
||||
import firefox_puppeteer.errors as errors
|
||||
|
||||
from firefox_puppeteer.api.security import Security
|
||||
from firefox_puppeteer.ui.base import UIBaseLib, DOMElement
|
||||
|
||||
|
||||
class TabBar(UIBaseLib):
|
||||
"""Wraps the tabs toolbar DOM element inside a browser window."""
|
||||
|
||||
# Properties for visual elements of the tabs toolbar #
|
||||
|
||||
@property
|
||||
def menupanel(self):
|
||||
"""A :class:`MenuPanel` instance which represents the menu panel
|
||||
at the far right side of the tabs toolbar.
|
||||
|
||||
:returns: :class:`MenuPanel` instance.
|
||||
"""
|
||||
return MenuPanel(self.marionette, self.window)
|
||||
|
||||
@property
|
||||
def newtab_button(self):
|
||||
"""The DOM element which represents the new tab button.
|
||||
|
||||
:returns: Reference to the new tab button.
|
||||
"""
|
||||
return self.toolbar.find_element(By.ANON_ATTRIBUTE, {'anonid': 'tabs-newtab-button'})
|
||||
|
||||
@property
|
||||
def tabs(self):
|
||||
"""List of all the :class:`Tab` instances of the current browser window.
|
||||
|
||||
:returns: List of :class:`Tab` instances.
|
||||
"""
|
||||
tabs = self.toolbar.find_elements(By.TAG_NAME, 'tab')
|
||||
|
||||
return [Tab(self.marionette, self.window, tab) for tab in tabs]
|
||||
|
||||
@property
|
||||
def toolbar(self):
|
||||
"""The DOM element which represents the tab toolbar.
|
||||
|
||||
:returns: Reference to the tabs toolbar.
|
||||
"""
|
||||
return self.element
|
||||
|
||||
# Properties for helpers when working with the tabs toolbar #
|
||||
|
||||
@property
|
||||
def selected_index(self):
|
||||
"""The index of the currently selected tab.
|
||||
|
||||
:return: Index of the selected tab.
|
||||
"""
|
||||
return int(self.toolbar.get_property('selectedIndex'))
|
||||
|
||||
@property
|
||||
def selected_tab(self):
|
||||
"""A :class:`Tab` instance of the currently selected tab.
|
||||
|
||||
:returns: :class:`Tab` instance.
|
||||
"""
|
||||
return self.tabs[self.selected_index]
|
||||
|
||||
# Methods for helpers when working with the tabs toolbar #
|
||||
|
||||
def close_all_tabs(self, exceptions=None):
|
||||
"""Forces closing of all open tabs.
|
||||
|
||||
There is an optional `exceptions` list, which can be used to exclude
|
||||
specific tabs from being closed.
|
||||
|
||||
:param exceptions: Optional, list of :class:`Tab` instances not to close.
|
||||
"""
|
||||
# Get handles from tab exceptions, and find those which can be closed
|
||||
for tab in self.tabs:
|
||||
if tab not in exceptions:
|
||||
tab.close(force=True)
|
||||
|
||||
def close_tab(self, tab=None, trigger='menu', force=False):
|
||||
"""Closes the tab by using the specified trigger.
|
||||
|
||||
By default the currently selected tab will be closed. If another :class:`Tab`
|
||||
is specified, that one will be closed instead. Also when the tab is closed, a
|
||||
:func:`switch_to` call is automatically performed, so that the new selected
|
||||
tab becomes active.
|
||||
|
||||
:param tab: Optional, the :class:`Tab` instance to close. Defaults to
|
||||
the currently selected tab.
|
||||
|
||||
:param trigger: Optional, method to close the current tab. This can
|
||||
be a string with one of `menu` or `shortcut`, or a callback which gets triggered
|
||||
with the :class:`Tab` as parameter. Defaults to `menu`.
|
||||
|
||||
:param force: Optional, forces the closing of the window by using the Gecko API.
|
||||
Defaults to `False`.
|
||||
"""
|
||||
tab = tab or self.selected_tab
|
||||
tab.close(trigger, force)
|
||||
|
||||
def open_tab(self, trigger='menu'):
|
||||
"""Opens a new tab in the current browser window.
|
||||
|
||||
If the tab opens in the foreground, a call to :func:`switch_to` will
|
||||
automatically be performed. But if it opens in the background, the current
|
||||
tab will keep its focus.
|
||||
|
||||
:param trigger: Optional, method to open the new tab. This can
|
||||
be a string with one of `menu`, `button` or `shortcut`, or a callback
|
||||
which gets triggered with the current :class:`Tab` as parameter.
|
||||
Defaults to `menu`.
|
||||
|
||||
:returns: :class:`Tab` instance for the opened tab.
|
||||
"""
|
||||
start_handles = self.marionette.window_handles
|
||||
|
||||
# Prepare action which triggers the opening of the browser window
|
||||
if callable(trigger):
|
||||
trigger(self.selected_tab)
|
||||
elif trigger == 'button':
|
||||
self.window.tabbar.newtab_button.click()
|
||||
elif trigger == 'menu':
|
||||
self.window.menubar.select_by_id('file-menu',
|
||||
'menu_newNavigatorTab')
|
||||
elif trigger == 'shortcut':
|
||||
self.window.send_shortcut(self.window.localize_entity('tabCmd.commandkey'),
|
||||
accel=True)
|
||||
# elif - need to add other cases
|
||||
else:
|
||||
raise ValueError('Unknown opening method: "%s"' % trigger)
|
||||
|
||||
# TODO: Needs to be replaced with event handling code (bug 1121705)
|
||||
Wait(self.marionette).until(
|
||||
lambda mn: len(mn.window_handles) == len(start_handles) + 1,
|
||||
message='No new tab has been opened.')
|
||||
|
||||
handles = self.marionette.window_handles
|
||||
[new_handle] = list(set(handles) - set(start_handles))
|
||||
[new_tab] = [tab for tab in self.tabs if tab.handle == new_handle]
|
||||
|
||||
# if the new tab is the currently selected tab, switch to it
|
||||
if new_tab == self.selected_tab:
|
||||
new_tab.switch_to()
|
||||
|
||||
return new_tab
|
||||
|
||||
def switch_to(self, target):
|
||||
"""Switches the context to the specified tab.
|
||||
|
||||
:param target: The tab to switch to. `target` can be an index, a :class:`Tab`
|
||||
instance, or a callback that returns True in the context of the desired tab.
|
||||
|
||||
:returns: Instance of the selected :class:`Tab`.
|
||||
"""
|
||||
start_handle = self.marionette.current_window_handle
|
||||
|
||||
if isinstance(target, int):
|
||||
return self.tabs[target].switch_to()
|
||||
elif isinstance(target, Tab):
|
||||
return target.switch_to()
|
||||
if callable(target):
|
||||
for tab in self.tabs:
|
||||
tab.switch_to()
|
||||
if target(tab):
|
||||
return tab
|
||||
|
||||
self.marionette.switch_to_window(start_handle)
|
||||
raise errors.UnknownTabError("No tab found for '{}'".format(target))
|
||||
|
||||
raise ValueError("The 'target' parameter must either be an index or a callable")
|
||||
|
||||
@staticmethod
|
||||
def get_handle_for_tab(marionette, tab_element):
|
||||
"""Retrieves the marionette handle for the given :class:`Tab` instance.
|
||||
|
||||
:param marionette: An instance of the Marionette client.
|
||||
|
||||
:param tab_element: The DOM element corresponding to a tab inside the tabs toolbar.
|
||||
|
||||
:returns: `handle` of the tab.
|
||||
"""
|
||||
# TODO: This introduces coupling with marionette's window handles
|
||||
# implementation. To avoid this, the capacity to get the XUL
|
||||
# element corresponding to the active window according to
|
||||
# marionette or a similar ability should be added to marionette.
|
||||
handle = marionette.execute_script("""
|
||||
let win = arguments[0].linkedBrowser;
|
||||
if (!win) {
|
||||
return null;
|
||||
}
|
||||
return win.outerWindowID.toString();
|
||||
""", script_args=[tab_element])
|
||||
|
||||
return handle
|
||||
|
||||
|
||||
class Tab(UIBaseLib):
|
||||
"""Wraps a tab DOM element."""
|
||||
|
||||
def __init__(self, marionette, window, element):
|
||||
super(Tab, self).__init__(marionette, window, element)
|
||||
|
||||
self._security = Security(self.marionette)
|
||||
self._handle = None
|
||||
|
||||
# Properties for visual elements of tabs #
|
||||
|
||||
@property
|
||||
def close_button(self):
|
||||
"""The DOM element which represents the tab close button.
|
||||
|
||||
:returns: Reference to the tab close button.
|
||||
"""
|
||||
return self.tab_element.find_element(By.ANON_ATTRIBUTE, {'anonid': 'close-button'})
|
||||
|
||||
@property
|
||||
def tab_element(self):
|
||||
"""The inner tab DOM element.
|
||||
|
||||
:returns: Tab DOM element.
|
||||
"""
|
||||
return self.element
|
||||
|
||||
# Properties for backend values
|
||||
|
||||
@property
|
||||
def location(self):
|
||||
"""Returns the current URL
|
||||
|
||||
:returns: Current URL
|
||||
"""
|
||||
self.switch_to()
|
||||
|
||||
return self.marionette.execute_script("""
|
||||
return arguments[0].linkedBrowser.currentURI.spec;
|
||||
""", script_args=[self.tab_element])
|
||||
|
||||
@property
|
||||
def certificate(self):
|
||||
"""The SSL certificate assiciated with the loaded web page.
|
||||
|
||||
:returns: Certificate details as JSON blob.
|
||||
"""
|
||||
self.switch_to()
|
||||
|
||||
return self._security.get_certificate_for_page(self.tab_element)
|
||||
|
||||
# Properties for helpers when working with tabs #
|
||||
|
||||
@property
|
||||
def handle(self):
|
||||
"""The `handle` of the content window.
|
||||
|
||||
:returns: content window `handle`.
|
||||
"""
|
||||
# If no handle has been set yet, wait until it is available
|
||||
if not self._handle:
|
||||
self._handle = Wait(self.marionette).until(
|
||||
lambda mn: TabBar.get_handle_for_tab(mn, self.element),
|
||||
message='Tab handle could not be found.')
|
||||
|
||||
return self._handle
|
||||
|
||||
@property
|
||||
def selected(self):
|
||||
"""Checks if the tab is selected.
|
||||
|
||||
:return: `True` if the tab is selected.
|
||||
"""
|
||||
return self.marionette.execute_script("""
|
||||
return arguments[0].hasAttribute('selected');
|
||||
""", script_args=[self.tab_element])
|
||||
|
||||
# Methods for helpers when working with tabs #
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.handle == other.handle
|
||||
|
||||
def close(self, trigger='menu', force=False):
|
||||
"""Closes the tab by using the specified trigger.
|
||||
|
||||
When the tab is closed a :func:`switch_to` call is automatically performed, so that
|
||||
the new selected tab becomes active.
|
||||
|
||||
:param trigger: Optional, method in how to close the tab. This can
|
||||
be a string with one of `button`, `menu` or `shortcut`, or a callback which
|
||||
gets triggered with the current :class:`Tab` as parameter. Defaults to `menu`.
|
||||
|
||||
:param force: Optional, forces the closing of the window by using the Gecko API.
|
||||
Defaults to `False`.
|
||||
"""
|
||||
handle = self.handle
|
||||
start_handles = self.marionette.window_handles
|
||||
|
||||
self.switch_to()
|
||||
|
||||
if force:
|
||||
self.marionette.close()
|
||||
elif callable(trigger):
|
||||
trigger(self)
|
||||
elif trigger == 'button':
|
||||
self.close_button.click()
|
||||
elif trigger == 'menu':
|
||||
self.window.menubar.select_by_id('file-menu', 'menu_close')
|
||||
elif trigger == 'shortcut':
|
||||
self.window.send_shortcut(self.window.localize_entity('closeCmd.key'),
|
||||
accel=True)
|
||||
else:
|
||||
raise ValueError('Unknown closing method: "%s"' % trigger)
|
||||
|
||||
Wait(self.marionette).until(
|
||||
lambda _: len(self.window.tabbar.tabs) == len(start_handles) - 1,
|
||||
message='Tab with handle "%s" has not been closed.' % handle)
|
||||
|
||||
# Ensure to switch to the window handle which represents the new selected tab
|
||||
self.window.tabbar.selected_tab.switch_to()
|
||||
|
||||
def select(self):
|
||||
"""Selects the tab and sets the focus to it."""
|
||||
self.tab_element.click()
|
||||
self.switch_to()
|
||||
|
||||
# Bug 1121705: Maybe we have to wait for TabSelect event
|
||||
Wait(self.marionette).until(
|
||||
lambda _: self.selected,
|
||||
message='Tab with handle "%s" could not be selected.' % self.handle)
|
||||
|
||||
def switch_to(self):
|
||||
"""Switches the context of Marionette to this tab.
|
||||
|
||||
Please keep in mind that calling this method will not select the tab.
|
||||
Use the :func:`~Tab.select` method instead.
|
||||
"""
|
||||
self.marionette.switch_to_window(self.handle)
|
||||
|
||||
|
||||
class MenuPanel(UIBaseLib):
|
||||
|
||||
@property
|
||||
def popup(self):
|
||||
"""
|
||||
:returns: The :class:`MenuPanelElement`.
|
||||
"""
|
||||
popup = self.marionette.find_element(By.ID, 'PanelUI-popup')
|
||||
return self.MenuPanelElement(popup)
|
||||
|
||||
class MenuPanelElement(DOMElement):
|
||||
"""Wraps the menu panel."""
|
||||
_buttons = None
|
||||
|
||||
@property
|
||||
def buttons(self):
|
||||
"""
|
||||
:returns: A list of all the clickable buttons in the menu panel.
|
||||
"""
|
||||
if not self._buttons:
|
||||
self._buttons = (self.find_element(By.ID, 'PanelUI-multiView')
|
||||
.find_element(By.ANON_ATTRIBUTE,
|
||||
{'anonid': 'viewContainer'})
|
||||
.find_elements(By.TAG_NAME,
|
||||
'toolbarbutton'))
|
||||
return self._buttons
|
||||
|
||||
def click(self, target=None):
|
||||
"""
|
||||
Overrides HTMLElement.click to provide a target to click.
|
||||
|
||||
:param target: The label associated with the button to click on,
|
||||
e.g., `New Private Window`.
|
||||
"""
|
||||
if not target:
|
||||
return DOMElement.click(self)
|
||||
|
||||
for button in self.buttons:
|
||||
if button.get_attribute('label') == target:
|
||||
return button.click()
|
||||
raise NoSuchElementException('Could not find "{}"" in the '
|
||||
'menu panel UI'.format(target))
|
||||
Loading…
Add table
Add a link
Reference in a new issue