diff options
author | Matt A. Tobin <email@mattatobin.com> | 2021-11-29 17:19:16 -0500 |
---|---|---|
committer | Matt A. Tobin <email@mattatobin.com> | 2021-11-29 17:19:16 -0500 |
commit | 8e9b2397c44650adfefa50e1e0088bb4fd252f52 (patch) | |
tree | e12bdf6414743ac994b135db358ee50cb3342eba /testing | |
parent | e2cba763b662cee7d3f4da0887e61b746a677ab7 (diff) | |
download | aura-central-8e9b2397c44650adfefa50e1e0088bb4fd252f52.tar.gz |
No Issue - Remove Marionette
Diffstat (limited to 'testing')
455 files changed, 0 insertions, 60169 deletions
diff --git a/testing/firefox-ui/.flake8 b/testing/firefox-ui/.flake8 deleted file mode 100644 index ad0819adf..000000000 --- a/testing/firefox-ui/.flake8 +++ /dev/null @@ -1,3 +0,0 @@ -[flake8] -max-line-length = 99 -exclude = __init__.py, diff --git a/testing/firefox-ui/harness/MANIFEST.in b/testing/firefox-ui/harness/MANIFEST.in deleted file mode 100644 index cf628b039..000000000 --- a/testing/firefox-ui/harness/MANIFEST.in +++ /dev/null @@ -1,2 +0,0 @@ -exclude MANIFEST.in -include requirements.txt diff --git a/testing/firefox-ui/harness/firefox_ui_harness/__init__.py b/testing/firefox-ui/harness/firefox_ui_harness/__init__.py deleted file mode 100644 index 02ae10cdf..000000000 --- a/testing/firefox-ui/harness/firefox_ui_harness/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -# 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/. - -__version__ = '1.4.0' - -import cli_functional -import cli_update diff --git a/testing/firefox-ui/harness/firefox_ui_harness/arguments/__init__.py b/testing/firefox-ui/harness/firefox_ui_harness/arguments/__init__.py deleted file mode 100644 index 57dc77f60..000000000 --- a/testing/firefox-ui/harness/firefox_ui_harness/arguments/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# 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 firefox_ui_harness.arguments.base import FirefoxUIArguments -from firefox_ui_harness.arguments.update import UpdateArguments diff --git a/testing/firefox-ui/harness/firefox_ui_harness/arguments/base.py b/testing/firefox-ui/harness/firefox_ui_harness/arguments/base.py deleted file mode 100644 index e6427e764..000000000 --- a/testing/firefox-ui/harness/firefox_ui_harness/arguments/base.py +++ /dev/null @@ -1,18 +0,0 @@ -# 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_harness import BaseMarionetteArguments - - -class FirefoxUIBaseArguments(object): - name = 'Firefox UI Tests' - args = [] - - -class FirefoxUIArguments(BaseMarionetteArguments): - - def __init__(self, **kwargs): - super(FirefoxUIArguments, self).__init__(**kwargs) - - self.register_argument_container(FirefoxUIBaseArguments()) diff --git a/testing/firefox-ui/harness/firefox_ui_harness/arguments/update.py b/testing/firefox-ui/harness/firefox_ui_harness/arguments/update.py deleted file mode 100644 index 9b6d3e5e0..000000000 --- a/testing/firefox-ui/harness/firefox_ui_harness/arguments/update.py +++ /dev/null @@ -1,65 +0,0 @@ -# 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 base import FirefoxUIArguments - - -class UpdateBaseArguments(object): - name = 'Firefox UI Update Tests' - args = [ - [['--update-allow-mar-channel'], { - 'dest': 'update_mar_channels', - 'default': [], - 'action': 'append', - 'metavar': 'MAR_CHANNEL', - 'help': 'Additional MAR channel to be allowed for updates, ' - 'e.g. "firefox-mozilla-beta" for updating a release ' - 'build to the latest beta build.' - }], - [['--update-channel'], { - 'dest': 'update_channel', - 'metavar': 'CHANNEL', - 'help': 'Channel to use for the update check.' - }], - [['--update-direct-only'], { - 'dest': 'update_direct_only', - 'default': False, - 'action': 'store_true', - 'help': 'Only perform a direct update' - }], - [['--update-fallback-only'], { - 'dest': 'update_fallback_only', - 'default': False, - 'action': 'store_true', - 'help': 'Only perform a fallback update' - }], - [['--update-override-url'], { - 'dest': 'update_override_url', - 'metavar': 'URL', - 'help': 'Force specified URL to use for update checks.' - }], - [['--update-target-version'], { - 'dest': 'update_target_version', - 'metavar': 'VERSION', - 'help': 'Version of the updated build.' - }], - [['--update-target-buildid'], { - 'dest': 'update_target_buildid', - 'metavar': 'BUILD_ID', - 'help': 'Build ID of the updated build.' - }], - ] - - def verify_usage_handler(self, args): - if args.update_direct_only and args.update_fallback_only: - raise ValueError('Arguments --update-direct-only and --update-fallback-only ' - 'are mutually exclusive.') - - -class UpdateArguments(FirefoxUIArguments): - - def __init__(self, **kwargs): - super(UpdateArguments, self).__init__(**kwargs) - - self.register_argument_container(UpdateBaseArguments()) diff --git a/testing/firefox-ui/harness/firefox_ui_harness/cli_functional.py b/testing/firefox-ui/harness/firefox_ui_harness/cli_functional.py deleted file mode 100644 index e83d88b51..000000000 --- a/testing/firefox-ui/harness/firefox_ui_harness/cli_functional.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -# 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_harness.runtests import cli as mn_cli - -from firefox_ui_harness.arguments import FirefoxUIArguments -from firefox_ui_harness.runners import FirefoxUITestRunner - - -def cli(args=None): - mn_cli(runner_class=FirefoxUITestRunner, - parser_class=FirefoxUIArguments, - args=args, - ) - - -if __name__ == '__main__': - cli() diff --git a/testing/firefox-ui/harness/firefox_ui_harness/cli_update.py b/testing/firefox-ui/harness/firefox_ui_harness/cli_update.py deleted file mode 100644 index 27446a099..000000000 --- a/testing/firefox-ui/harness/firefox_ui_harness/cli_update.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python - -# 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_harness.runtests import cli as mn_cli - -from firefox_ui_harness.arguments import UpdateArguments -from firefox_ui_harness.runners import UpdateTestRunner - - -def cli(args=None): - mn_cli(runner_class=UpdateTestRunner, - parser_class=UpdateArguments, - args=args, - ) - - -if __name__ == '__main__': - cli() diff --git a/testing/firefox-ui/harness/firefox_ui_harness/runners/__init__.py b/testing/firefox-ui/harness/firefox_ui_harness/runners/__init__.py deleted file mode 100644 index 9022a45b8..000000000 --- a/testing/firefox-ui/harness/firefox_ui_harness/runners/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# 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 firefox_ui_harness.runners.base import FirefoxUITestRunner -from firefox_ui_harness.runners.update import UpdateTestRunner diff --git a/testing/firefox-ui/harness/firefox_ui_harness/runners/base.py b/testing/firefox-ui/harness/firefox_ui_harness/runners/base.py deleted file mode 100644 index 66c2a5308..000000000 --- a/testing/firefox-ui/harness/firefox_ui_harness/runners/base.py +++ /dev/null @@ -1,45 +0,0 @@ -# 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/. - -import os -import shutil -import tempfile - -import mozfile -import mozinfo - -from marionette_harness import BaseMarionetteTestRunner, MarionetteTestCase - - -class FirefoxUITestRunner(BaseMarionetteTestRunner): - - def __init__(self, **kwargs): - super(FirefoxUITestRunner, self).__init__(**kwargs) - - # select the appropriate GeckoInstance - self.app = 'fxdesktop' - - self.test_handlers = [MarionetteTestCase] - - def duplicate_application(self, application_folder): - """Creates a copy of the specified binary.""" - - if self.workspace: - target_folder = os.path.join(self.workspace_path, 'application.copy') - else: - target_folder = tempfile.mkdtemp('.application.copy') - - self.logger.info('Creating a copy of the application at "%s".' % target_folder) - mozfile.remove(target_folder) - shutil.copytree(application_folder, target_folder) - - return target_folder - - def get_application_folder(self, binary): - """Returns the directory of the application.""" - if mozinfo.isMac: - end_index = binary.find('.app') + 4 - return binary[:end_index] - else: - return os.path.dirname(binary) diff --git a/testing/firefox-ui/harness/firefox_ui_harness/runners/update.py b/testing/firefox-ui/harness/firefox_ui_harness/runners/update.py deleted file mode 100644 index fe4936f68..000000000 --- a/testing/firefox-ui/harness/firefox_ui_harness/runners/update.py +++ /dev/null @@ -1,101 +0,0 @@ -# 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/. - -import sys - -import mozfile -import mozinstall - -from firefox_ui_harness.runners import FirefoxUITestRunner -from firefox_ui_harness.testcases import UpdateTestCase - - -DEFAULT_PREFS = { - # Bug 1355026: Re-enable when support for the new simplified UI update is available - 'app.update.doorhanger': False, - 'app.update.log': True, - 'startup.homepage_override_url': 'about:blank', -} - - -class UpdateTestRunner(FirefoxUITestRunner): - - def __init__(self, **kwargs): - super(UpdateTestRunner, self).__init__(**kwargs) - - self.original_bin = self.bin - - self.prefs.update(DEFAULT_PREFS) - - # In case of overriding the update URL, set the appropriate preference - override_url = kwargs.pop('update_override_url', None) - if override_url: - self.prefs.update({'app.update.url.override': override_url}) - - self.run_direct_update = not kwargs.pop('update_fallback_only', False) - self.run_fallback_update = not kwargs.pop('update_direct_only', False) - - self.test_handlers = [UpdateTestCase] - - def run_tests(self, tests): - # Used to store the last occurred exception because we execute - # run_tests() multiple times - self.exc_info = None - - failed = 0 - source_folder = self.get_application_folder(self.original_bin) - - results = {} - - def _run_tests(tags): - application_folder = None - - try: - # Backup current tags - test_tags = self.test_tags - - application_folder = self.duplicate_application(source_folder) - self.bin = mozinstall.get_binary(application_folder, 'Firefox') - - self.test_tags = tags - super(UpdateTestRunner, self).run_tests(tests) - - except Exception: - self.exc_info = sys.exc_info() - self.logger.error('Failure during execution of the update test.', - exc_info=self.exc_info) - - finally: - self.test_tags = test_tags - - self.logger.info('Removing copy of the application at "%s"' % application_folder) - try: - mozfile.remove(application_folder) - except IOError as e: - self.logger.error('Cannot remove copy of application: "%s"' % str(e)) - - # Run direct update tests if wanted - if self.run_direct_update: - _run_tests(tags=['direct']) - failed += self.failed - results['Direct'] = False if self.failed else True - - # Run fallback update tests if wanted - if self.run_fallback_update: - _run_tests(tags=['fallback']) - failed += self.failed - results['Fallback'] = False if self.failed else True - - self.logger.info("Summary of update tests:") - for test_type, result in results.iteritems(): - self.logger.info("\t%s update test ran and %s" % - (test_type, 'PASSED' if result else 'FAILED')) - - # Combine failed tests for all run_test() executions - self.failed = failed - - # If exceptions happened, re-throw the last one - if self.exc_info: - ex_type, exception, tb = self.exc_info - raise ex_type, exception, tb diff --git a/testing/firefox-ui/harness/firefox_ui_harness/testcases.py b/testing/firefox-ui/harness/firefox_ui_harness/testcases.py deleted file mode 100644 index abcbd6555..000000000 --- a/testing/firefox-ui/harness/firefox_ui_harness/testcases.py +++ /dev/null @@ -1,420 +0,0 @@ -# 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/. - -import os -import pprint -from datetime import datetime - -import mozfile - -from firefox_puppeteer import PuppeteerMixin -from firefox_puppeteer.api.software_update import SoftwareUpdate -from firefox_puppeteer.ui.update_wizard import UpdateWizardDialog -from marionette_driver import Wait -from marionette_driver.errors import NoSuchWindowException -from marionette_harness import MarionetteTestCase - - -class UpdateTestCase(PuppeteerMixin, MarionetteTestCase): - - TIMEOUT_UPDATE_APPLY = 300 - TIMEOUT_UPDATE_CHECK = 30 - TIMEOUT_UPDATE_DOWNLOAD = 720 - - # For the old update wizard, the errors are displayed inside the dialog. For the - # handling of updates in the about window the errors are displayed in new dialogs. - # When the old wizard is open we have to set the preference, so the errors will be - # shown as expected, otherwise we would have unhandled modal dialogs when errors are - # raised. See: - # http://mxr.mozilla.org/mozilla-central/source/toolkit/mozapps/update/nsUpdateService.js?rev=a9240b1eb2fb#4813 - # http://mxr.mozilla.org/mozilla-central/source/toolkit/mozapps/update/nsUpdateService.js?rev=a9240b1eb2fb#4756 - PREF_APP_UPDATE_ALTWINDOWTYPE = 'app.update.altwindowtype' - - def __init__(self, *args, **kwargs): - super(UpdateTestCase, self).__init__(*args, **kwargs) - - self.update_channel = kwargs.pop('update_channel') - self.update_mar_channels = set(kwargs.pop('update_mar_channels')) - - self.target_buildid = kwargs.pop('update_target_buildid') - self.target_version = kwargs.pop('update_target_version') - - def setUp(self, is_fallback=False): - super(UpdateTestCase, self).setUp() - - self.software_update = SoftwareUpdate(self.marionette) - self.download_duration = None - - # If a custom update channel has to be set, force a restart of - # Firefox to actually get it applied as a default pref. Use the clean - # option to force a non in_app restart, which would allow Firefox to - # dump the logs to the console. - if self.update_channel: - self.software_update.update_channel = self.update_channel - self.restart(clean=True) - - self.assertEqual(self.software_update.update_channel, self.update_channel) - - # If requested modify the list of allowed MAR channels - if self.update_mar_channels: - self.software_update.mar_channels.add_channels(self.update_mar_channels) - - self.assertTrue(self.update_mar_channels.issubset( - self.software_update.mar_channels.channels), - 'Allowed MAR channels have been set: expected "{}" in "{}"'.format( - ', '.join(self.update_mar_channels), - ', '.join(self.software_update.mar_channels.channels))) - - # Ensure that there exists no already partially downloaded update - self.remove_downloaded_update() - - # Dictionary which holds the information for each update - self.update_status = { - 'build_pre': self.software_update.build_info, - 'build_post': None, - 'fallback': is_fallback, - 'patch': {}, - 'success': False, - } - - # Check if the user has permissions to run the update - self.assertTrue(self.software_update.allowed, - 'Current user has permissions to update the application.') - - def tearDown(self): - try: - self.browser.tabbar.close_all_tabs([self.browser.tabbar.selected_tab]) - - # Add content of the update log file for detailed failures when applying an update - self.update_status['update_log'] = self.read_update_log() - - # Print results for now until we have treeherder integration - output = pprint.pformat(self.update_status) - self.logger.info('Update test results: \n{}'.format(output)) - finally: - super(UpdateTestCase, self).tearDown() - - # Ensure that no trace of an partially downloaded update remain - self.remove_downloaded_update() - - @property - def patch_info(self): - """ Returns information about the active update in the queue. - - :returns: A dictionary with information about the active patch - """ - patch = self.software_update.patch_info - patch['download_duration'] = self.download_duration - - return patch - - def check_for_updates(self, about_window, timeout=TIMEOUT_UPDATE_CHECK): - """Clicks on "Check for Updates" button, and waits for check to complete. - - :param about_window: Instance of :class:`AboutWindow`. - :param timeout: How long to wait for the update check to finish. Optional, - defaults to 60s. - - :returns: True, if an update is available. - """ - self.assertEqual(about_window.deck.selected_panel, - about_window.deck.check_for_updates) - - about_window.deck.check_for_updates.button.click() - Wait(self.marionette, timeout=self.TIMEOUT_UPDATE_CHECK).until( - lambda _: about_window.deck.selected_panel not in - (about_window.deck.check_for_updates, about_window.deck.checking_for_updates), - message='Check for updates has been finished.') - - return about_window.deck.selected_panel != about_window.deck.no_updates_found - - def check_update_applied(self): - """Check that the update has been applied correctly""" - self.update_status['build_post'] = self.software_update.build_info - - # Ensure that the target version is the same or higher. No downgrade - # should have happened. - version_check = self.marionette.execute_script(""" - Components.utils.import("resource://gre/modules/Services.jsm"); - - return Services.vc.compare(arguments[0], arguments[1]); - """, script_args=(self.update_status['build_post']['version'], - self.update_status['build_pre']['version'])) - - self.assertGreaterEqual(version_check, 0, - 'A downgrade from version {} to {} is not allowed'.format( - self.update_status['build_pre']['version'], - self.update_status['build_post']['version'])) - - self.assertNotEqual(self.update_status['build_post']['buildid'], - self.update_status['build_pre']['buildid'], - 'The staged update to buildid {} has not been applied'.format( - self.update_status['patch']['buildid'])) - - self.assertEqual(self.update_status['build_post']['buildid'], - self.update_status['patch']['buildid'], - 'Unexpected target buildid after applying the patch, {} != {}'.format( - self.update_status['build_post']['buildid'], - self.update_status['patch']['buildid'])) - - self.assertEqual(self.update_status['build_post']['locale'], - self.update_status['build_pre']['locale'], - 'Unexpected change of the locale from {} to {}'.format( - self.update_status['build_pre']['locale'], - self.update_status['build_post']['locale'])) - - self.assertEqual(self.update_status['build_post']['disabled_addons'], - self.update_status['build_pre']['disabled_addons'], - 'Application-wide addons have been unexpectedly disabled: {}'.format( - ', '.join(set(self.update_status['build_pre']['locale']) - - set(self.update_status['build_post']['locale'])) - )) - - if self.target_version: - self.assertEqual(self.update_status['build_post']['version'], - self.target_version, - 'Current target version {} does not match expected version {}'.format( - self.update_status['build_post']['version'], self.target_version)) - - if self.target_buildid: - self.assertEqual(self.update_status['build_post']['buildid'], - self.target_buildid, - 'Current target buildid {} does not match expected buildid {}'.format( - self.update_status['build_post']['buildid'], self.target_buildid)) - - self.update_status['success'] = True - - def check_update_not_applied(self): - """Check that the update has not been applied due to a forced invalidation of the patch""" - build_info = self.software_update.build_info - - # Ensure that the version has not been changed - version_check = self.marionette.execute_script(""" - Components.utils.import("resource://gre/modules/Services.jsm"); - - return Services.vc.compare(arguments[0], arguments[1]); - """, script_args=(build_info['version'], - self.update_status['build_pre']['version'])) - - self.assertEqual(version_check, 0, - 'An update from version {} to {} has been unexpectedly applied'.format( - self.update_status['build_pre']['version'], - build_info['version'])) - - # Check that the build id of the source build and the current build are identical - self.assertEqual(build_info['buildid'], - self.update_status['build_pre']['buildid'], - 'The build id has been unexpectedly changed from {} to {}'.format( - self.update_status['build_pre']['buildid'], build_info['buildid'])) - - def download_update(self, window, wait_for_finish=True, timeout=TIMEOUT_UPDATE_DOWNLOAD): - """ Download the update patch. - - :param window: Instance of :class:`AboutWindow` or :class:`UpdateWizardDialog`. - :param wait_for_finish: If True the function has to wait for the download to be finished. - Optional, default to `True`. - :param timeout: How long to wait for the download to finish. Optional, default to 360s. - """ - - def download_via_update_wizard(dialog): - """ Download the update via the old update wizard dialog. - - :param dialog: Instance of :class:`UpdateWizardDialog`. - """ - self.marionette.set_pref(self.PREF_APP_UPDATE_ALTWINDOWTYPE, dialog.window_type) - - try: - # If updates have already been found, proceed to download - if dialog.wizard.selected_panel in [dialog.wizard.updates_found_basic, - dialog.wizard.error_patching, - ]: - dialog.select_next_page() - - # If incompatible add-on are installed, skip over the wizard page - # TODO: Remove once we no longer support version Firefox 45.0ESR - if self.puppeteer.utils.compare_version(self.puppeteer.appinfo.version, - '49.0a1') == -1: - if dialog.wizard.selected_panel == dialog.wizard.incompatible_list: - dialog.select_next_page() - - # Updates were stored in the cache, so no download is necessary - if dialog.wizard.selected_panel in [dialog.wizard.finished, - dialog.wizard.finished_background, - ]: - pass - - # Download the update - elif dialog.wizard.selected_panel == dialog.wizard.downloading: - if wait_for_finish: - start_time = datetime.now() - self.wait_for_download_finished(dialog, timeout) - self.download_duration = (datetime.now() - start_time).total_seconds() - - Wait(self.marionette).until(lambda _: ( - dialog.wizard.selected_panel in [dialog.wizard.finished, - dialog.wizard.finished_background, - ]), - message='Final wizard page has been selected.') - - else: - raise Exception('Invalid wizard page for downloading an update: {}'.format( - dialog.wizard.selected_panel)) - - finally: - self.marionette.clear_pref(self.PREF_APP_UPDATE_ALTWINDOWTYPE) - - # The old update wizard dialog has to be handled differently. It's necessary - # for fallback updates and invalid add-on versions. - if isinstance(window, UpdateWizardDialog): - download_via_update_wizard(window) - return - - if window.deck.selected_panel == window.deck.download_and_install: - window.deck.download_and_install.button.click() - - # Wait for the download to start - Wait(self.marionette).until(lambda _: ( - window.deck.selected_panel != window.deck.download_and_install), - message='Download of the update has been started.') - - if wait_for_finish: - start_time = datetime.now() - self.wait_for_download_finished(window, timeout) - self.download_duration = (datetime.now() - start_time).total_seconds() - - def download_and_apply_available_update(self, force_fallback=False): - """Checks, downloads, and applies an available update. - - :param force_fallback: Optional, if `True` invalidate current update status. - Defaults to `False`. - """ - # Open the about window and check for updates - about_window = self.browser.open_about_window() - - try: - update_available = self.check_for_updates(about_window) - self.assertTrue(update_available, - "Available update has been found") - - # Download update and wait until it has been applied - self.download_update(about_window) - self.wait_for_update_applied(about_window) - - finally: - self.update_status['patch'] = self.patch_info - - if force_fallback: - # Set the downloaded update into failed state - self.software_update.force_fallback() - - # Restart Firefox to apply the downloaded update - self.restart() - - def download_and_apply_forced_update(self): - self.check_update_not_applied() - - # The update wizard dialog opens automatically after the restart but with a short delay - dialog = Wait(self.marionette, ignored_exceptions=[NoSuchWindowException]).until( - lambda _: self.puppeteer.windows.switch_to(lambda win: type(win) is UpdateWizardDialog) - ) - - # In case of a broken complete update the about window has to be used - if self.update_status['patch']['is_complete']: - about_window = None - try: - self.assertEqual(dialog.wizard.selected_panel, - dialog.wizard.error) - dialog.close() - - # Open the about window and check for updates - about_window = self.browser.open_about_window() - update_available = self.check_for_updates(about_window) - self.assertTrue(update_available, - 'Available update has been found') - - # Download update and wait until it has been applied - self.download_update(about_window) - self.wait_for_update_applied(about_window) - - finally: - if about_window: - self.update_status['patch'] = self.patch_info - - else: - try: - self.assertEqual(dialog.wizard.selected_panel, - dialog.wizard.error_patching) - - # Start downloading the fallback update - self.download_update(dialog) - - finally: - self.update_status['patch'] = self.patch_info - - # Restart Firefox to apply the update - self.restart() - - def read_update_log(self): - """Read the content of the update log file for the last update attempt.""" - path = os.path.join(os.path.dirname(self.software_update.staging_directory), - 'last-update.log') - try: - with open(path, 'rb') as f: - return f.read().splitlines() - except IOError as exc: - self.logger.warning(str(exc)) - return None - - def remove_downloaded_update(self): - """Remove an already downloaded update from the update staging directory. - - Hereby not only remove the update subdir but everything below 'updates'. - """ - path = os.path.dirname(self.software_update.staging_directory) - self.logger.info('Clean-up update staging directory: {}'.format(path)) - mozfile.remove(path) - - def wait_for_download_finished(self, window, timeout=TIMEOUT_UPDATE_DOWNLOAD): - """ Waits until download is completed. - - :param window: Instance of :class:`AboutWindow` or :class:`UpdateWizardDialog`. - :param timeout: How long to wait for the download to finish. Optional, - default to 360 seconds. - """ - # The old update wizard dialog has to be handled differently. It's necessary - # for fallback updates and invalid add-on versions. - if isinstance(window, UpdateWizardDialog): - Wait(self.marionette, timeout=timeout).until( - lambda _: window.wizard.selected_panel != window.wizard.downloading, - message='Download has been completed.') - - self.assertNotIn(window.wizard.selected_panel, - [window.wizard.error, window.wizard.error_extra]) - return - - Wait(self.marionette, timeout=timeout).until( - lambda _: window.deck.selected_panel not in - (window.deck.download_and_install, window.deck.downloading), - message='Download has been completed.') - - self.assertNotEqual(window.deck.selected_panel, - window.deck.download_failed) - - def wait_for_update_applied(self, about_window, timeout=TIMEOUT_UPDATE_APPLY): - """ Waits until the downloaded update has been applied. - - :param about_window: Instance of :class:`AboutWindow`. - :param timeout: How long to wait for the update to apply. Optional, - default to 300 seconds - """ - Wait(self.marionette, timeout=timeout).until( - lambda _: about_window.deck.selected_panel == about_window.deck.apply, - message='Final wizard page has been selected.') - - # Wait for update to be staged because for update tests we modify the update - # status file to enforce the fallback update. If we modify the file before - # Firefox does, Firefox will override our change and we will have no fallback update. - Wait(self.marionette, timeout=timeout).until( - lambda _: 'applied' in self.software_update.active_update.state, - message='Update has been applied.') diff --git a/testing/firefox-ui/harness/requirements.txt b/testing/firefox-ui/harness/requirements.txt deleted file mode 100644 index 54114debb..000000000 --- a/testing/firefox-ui/harness/requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -firefox-puppeteer >= 52.1.0, <53.0.0 -marionette-harness >= 4.0.0 -mozfile >= 1.2 -mozinfo >= 0.8 -mozinstall >= 1.12 diff --git a/testing/firefox-ui/harness/setup.py b/testing/firefox-ui/harness/setup.py deleted file mode 100644 index 1799d5057..000000000 --- a/testing/firefox-ui/harness/setup.py +++ /dev/null @@ -1,44 +0,0 @@ -# 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/. - -import os -import re -from setuptools import setup, find_packages - -THIS_DIR = os.path.dirname(os.path.realpath(__name__)) - - -def read(*parts): - with open(os.path.join(THIS_DIR, *parts)) as f: - return f.read() - - -def get_version(): - return re.findall("__version__ = '([\d\.]+)'", - read('firefox_ui_harness', '__init__.py'), re.M)[0] - -long_description = """Custom Marionette runner classes and entry scripts for Firefox Desktop -specific Marionette tests. -""" - -setup(name='firefox-ui-harness', - version=get_version(), - description="Firefox UI Harness", - long_description=long_description, - classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers - keywords='mozilla', - author='Auto-tools', - author_email='tools-marionette@lists.mozilla.org', - url='https://wiki.mozilla.org/Auto-tools/Projects/Marionette/Harnesses/FirefoxUI', - license='MPL', - packages=find_packages(), - include_package_data=True, - zip_safe=False, - install_requires=read('requirements.txt').splitlines(), - entry_points=""" - [console_scripts] - firefox-ui-functional = firefox_ui_harness.cli_functional:cli - firefox-ui-update = firefox_ui_harness.cli_update:cli - """, - ) diff --git a/testing/firefox-ui/mach_commands.py b/testing/firefox-ui/mach_commands.py deleted file mode 100644 index 368b673a2..000000000 --- a/testing/firefox-ui/mach_commands.py +++ /dev/null @@ -1,120 +0,0 @@ -# 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 __future__ import absolute_import, unicode_literals - -import os -import sys - -from mozbuild.base import ( - MachCommandBase, - MachCommandConditions as conditions, -) - -from mach.decorators import ( - Command, - CommandProvider, -) - - -def setup_argument_parser_functional(): - from firefox_ui_harness.arguments.base import FirefoxUIArguments - from mozlog.structured import commandline - parser = FirefoxUIArguments() - commandline.add_logging_group(parser) - return parser - - -def setup_argument_parser_update(): - from firefox_ui_harness.arguments.update import UpdateArguments - from mozlog.structured import commandline - parser = UpdateArguments() - commandline.add_logging_group(parser) - return parser - - -def run_firefox_ui_test(testtype=None, topsrcdir=None, **kwargs): - from mozlog.structured import commandline - from argparse import Namespace - import firefox_ui_harness - - if testtype == 'functional': - parser = setup_argument_parser_functional() - else: - parser = setup_argument_parser_update() - - test_types = { - 'functional': { - 'default_tests': [ - os.path.join('puppeteer', 'manifest.ini'), - os.path.join('functional', 'manifest.ini'), - ], - 'cli_module': firefox_ui_harness.cli_functional, - }, - 'update': { - 'default_tests': [ - os.path.join('update', 'manifest.ini'), - ], - 'cli_module': firefox_ui_harness.cli_update, - } - } - - fxui_dir = os.path.join(topsrcdir, 'testing', 'firefox-ui') - - # Set the resources path which is used to serve test data via wptserve - if not kwargs['server_root']: - kwargs['server_root'] = os.path.join(fxui_dir, 'resources') - - # If called via "mach test" a dictionary of tests is passed in - if 'test_objects' in kwargs: - tests = [] - for obj in kwargs['test_objects']: - tests.append(obj['file_relpath']) - kwargs['tests'] = tests - elif not kwargs.get('tests'): - # If no tests have been selected, set default ones - kwargs['tests'] = [os.path.join(fxui_dir, 'tests', test) - for test in test_types[testtype]['default_tests']] - - kwargs['logger'] = commandline.setup_logging('Firefox UI - {} Tests'.format(testtype), - {"mach": sys.stdout}) - - args = Namespace() - - for k, v in kwargs.iteritems(): - setattr(args, k, v) - - parser.verify_usage(args) - - failed = test_types[testtype]['cli_module'].cli(args=vars(args)) - - if failed > 0: - return 1 - else: - return 0 - - -@CommandProvider -class MachCommands(MachCommandBase): - """Mach command provider for Firefox ui tests.""" - - @Command('firefox-ui-functional', category='testing', - conditions=[conditions.is_firefox], - description='Run the functional test suite of Firefox UI tests.', - parser=setup_argument_parser_functional, - ) - def run_firefox_ui_functional(self, **kwargs): - kwargs['binary'] = kwargs['binary'] or self.get_binary_path('app') - return run_firefox_ui_test(testtype='functional', - topsrcdir=self.topsrcdir, **kwargs) - - @Command('firefox-ui-update', category='testing', - conditions=[conditions.is_firefox], - description='Run the update test suite of Firefox UI tests.', - parser=setup_argument_parser_update, - ) - def run_firefox_ui_update(self, **kwargs): - kwargs['binary'] = kwargs['binary'] or self.get_binary_path('app') - return run_firefox_ui_test(testtype='update', - topsrcdir=self.topsrcdir, **kwargs) diff --git a/testing/firefox-ui/moz.build b/testing/firefox-ui/moz.build deleted file mode 100644 index dd7311c03..000000000 --- a/testing/firefox-ui/moz.build +++ /dev/null @@ -1,11 +0,0 @@ -# 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/. - -FIREFOX_UI_FUNCTIONAL_MANIFESTS += ["tests/functional/manifest.ini"] -FIREFOX_UI_UPDATE_MANIFESTS += ["tests/update/manifest.ini"] -# TODO: Move to testing/marionette/puppeteer/firefox -PUPPETEER_FIREFOX_MANIFESTS += ["tests/puppeteer/manifest.ini"] - -with Files("**"): - BUG_COMPONENT = ("Testing", "Firefox UI Tests") diff --git a/testing/firefox-ui/resources/addons/extensions/restartless_addon_signed.xpi b/testing/firefox-ui/resources/addons/extensions/restartless_addon_signed.xpi Binary files differdeleted file mode 100644 index ed86213e7..000000000 --- a/testing/firefox-ui/resources/addons/extensions/restartless_addon_signed.xpi +++ /dev/null diff --git a/testing/firefox-ui/resources/addons/extensions/restartless_addon_unsigned.xpi b/testing/firefox-ui/resources/addons/extensions/restartless_addon_unsigned.xpi Binary files differdeleted file mode 100644 index d0768103d..000000000 --- a/testing/firefox-ui/resources/addons/extensions/restartless_addon_unsigned.xpi +++ /dev/null diff --git a/testing/firefox-ui/resources/cookies/cookie_single.html b/testing/firefox-ui/resources/cookies/cookie_single.html deleted file mode 100644 index d4a02b45b..000000000 --- a/testing/firefox-ui/resources/cookies/cookie_single.html +++ /dev/null @@ -1,16 +0,0 @@ -<!DOCTYPE html> -<html lang="en" dir="ltr"> -<head> -<script type="text/javascript"> - function setCookie() - { - var date = new Date(); - date.setDate(new Date().getDate() + 36); - document.cookie = "litmus_1=true;expires=" + date.toGMTString(); - } -</script> -</head> - -<body onload="setCookie()"> -</body> -</html> diff --git a/testing/firefox-ui/resources/images/firefox_favicon.ico b/testing/firefox-ui/resources/images/firefox_favicon.ico Binary files differdeleted file mode 100644 index 2c2f81768..000000000 --- a/testing/firefox-ui/resources/images/firefox_favicon.ico +++ /dev/null diff --git a/testing/firefox-ui/resources/images/mozilla_favicon.ico b/testing/firefox-ui/resources/images/mozilla_favicon.ico Binary files differdeleted file mode 100644 index d44438903..000000000 --- a/testing/firefox-ui/resources/images/mozilla_favicon.ico +++ /dev/null diff --git a/testing/firefox-ui/resources/images/mozilla_logo.jpg b/testing/firefox-ui/resources/images/mozilla_logo.jpg Binary files differdeleted file mode 100644 index 231b385ee..000000000 --- a/testing/firefox-ui/resources/images/mozilla_logo.jpg +++ /dev/null diff --git a/testing/firefox-ui/resources/layout/mozilla.html b/testing/firefox-ui/resources/layout/mozilla.html deleted file mode 100644 index 9224533bf..000000000 --- a/testing/firefox-ui/resources/layout/mozilla.html +++ /dev/null @@ -1,45 +0,0 @@ -<!DOCTYPE html> -<html lang="en" dir="ltr"> -<head> - <title>Mozilla</title> - <link rel="shortcut icon" type="image/ico" href="../images/mozilla_favicon.ico" /> -</head> - -<body> - <a href="mozilla.html"> - <img id="mozilla_logo" src="../images/mozilla_logo.jpg" /> - </a> - - <a href="#community">Community</a> | - <a href="#project">Project</a> | - <a href="#organization">Organization</a> - - <div id="content"> - <h1 id="page-title"> - <strong>We believe</strong> that the internet should be public, - open and accessible. - </h1> - - <h2><a name="community">Community</a></h2> - <p id="community"> - We're a global community of thousands who believe in the power - of technology to enrich people's lives. - <a href="mozilla_community.html">More</a> - </p> - - <h2><a name="project">Project</a></h2> - <p id="project"> - We're an open source project whose code is used for some of the - Internet's most innovative applications. - <a href="mozilla_projects.html">More</a> - </p> - - <h2><a name="organization">Organization</a></h2> - <p id="organization"> - We're a public benefit organization dedicated to making the - Internet better for everyone. - <a href="mozilla_mission.html">More</a> - </p> - </div> -</body> -</html> diff --git a/testing/firefox-ui/resources/layout/mozilla_community.html b/testing/firefox-ui/resources/layout/mozilla_community.html deleted file mode 100644 index c8da6afd8..000000000 --- a/testing/firefox-ui/resources/layout/mozilla_community.html +++ /dev/null @@ -1,57 +0,0 @@ -<!DOCTYPE html> -<html lang="en" dir="ltr"> -<head> - <title>Mozilla Community</title> - <link rel="shortcut icon" type="image/ico" href="../images/seamonkey_favicon.ico" /> -</head> - -<body> - <a href="mozilla.html"> - <img id="mozilla_logo" src="../images/mozilla_logo.jpg" /> - </a> - - <a href="#history">History</a> | - <a href="#communicate">Communicate</a> | - <a href="#more">More</a> - - <div id="content"> - <h1 id="page-title" name="page-title">Our Community</h1> - - <h2><a name="history">History</a></h2> - <p id="history"> - When www.mozilla.org was launched in 1998 all community activity - occurred right here on this site. Since then the community has - grown much bigger and there are now many different sites, - forums, blogs and newsgroups in different places that track - different parts of the project. These pages aim to be a - comprehensive list to all of the different community resources - available. If you know of something that's not on these lists - that should be, please contact us and we'll update these - pages. - </p> - - <h2><a name="communicate">Communicate</a></h2> - <p id="communicate"> - There are a number of different ways community members - communicate and coordinate (people use mailing lists and - newsgroups, blogs, forums, wikis and they even meet in real - life sometimes too) and all of these options might be - overwhelming at first. Hopefully this set of links will provide - some useful pointers to help you figure out where to go to find - what you're looking for. If you do get lost though and need - some help, feel free to ask for more information. - </p> - - <h2><a name="more">More</a></h2> - <p id="more"> - Please note that this is intended to be an entry point that - provides a high-level overview of the different community areas. - If you're looking for more detailed information about a specific - topic, please look at our Developer, - <a href="mozilla_contribute.html">Contribute</a> and Support - pages or take a look at the other information referenced - throughout this site. - </p> - </div> -</body> -</html> diff --git a/testing/firefox-ui/resources/layout/mozilla_contribute.html b/testing/firefox-ui/resources/layout/mozilla_contribute.html deleted file mode 100644 index cf5e54b85..000000000 --- a/testing/firefox-ui/resources/layout/mozilla_contribute.html +++ /dev/null @@ -1,70 +0,0 @@ -<!DOCTYPE html> -<html lang="en" dir="ltr"> -<head> - <title>Mozilla Contribute</title> - <link rel="shortcut icon" type="image/ico" href="../images/thunderbird_favicon.ico" /> -</head> - -<body> - <a href="mozilla.html"> - <img id="mozilla_logo" src="../images/mozilla_logo.jpg" /> - </a> - - <a href="#summary">Summary</a> | - <a href="#contribute">Contribute</a> - - <div id="content"> - <h1 id="page-title">Get Involved</h1> - - <h2><a name="summary">Summary</a></h2> - <p id="summary"> - You can <a href="mozilla_mission.html">build a better Internet</a> - by getting involved with Mozilla. You don't have to be a C++ - guru (or even know what that means!) and you don't need to spend - lots of time. Take a look at the opportunities below and feel - free to ask if you have any questions. - </p> - - <h2><a name="contribute">Contribute</a></h2> - <p id="contribute"> - <h3>Area of Interest</h3> - <i>Browse contribution opportunities by area of interest.</i> - - <ul id="areas_of_interest"> - <li id="browser_choice"> - <h4>Web Browser Choice</h4> - <p> - Mozilla has always believed that the freedom to - make informed choices should be central to making - the Web, and the world, a better place. Tell us - why having a choice of browser is important to you - and help us spread the word about how others can - take control of their online lives. - </p> - </li> - <li id="helping_users"> - <h4>Helping Users</h4> - <p> - Interested in helping others get the most out of - using Firefox and other Mozilla projects? Our - support process relies on enthusiastic - contributors like you. Find out more about - supporting Firefox, Thunderbird and other Mozilla - projects. - </p> - </li> - <li id="localization"> - <h4>Localization</h4> - <p> - Get involved with Mozilla by making Firefox, - Thunderbird and other projects available in your - language. Also help us tell the world about how - Mozilla is building a better Internet by - translating content on our web sites. - </p> - </li> - </ul> - </p> - </div> -</body> -</html> diff --git a/testing/firefox-ui/resources/layout/mozilla_governance.html b/testing/firefox-ui/resources/layout/mozilla_governance.html deleted file mode 100644 index 8e25aaabd..000000000 --- a/testing/firefox-ui/resources/layout/mozilla_governance.html +++ /dev/null @@ -1,38 +0,0 @@ -<!DOCTYPE html> -<html lang="en" dir="ltr"> -<head> - <title>Mozilla Governance</title> - <link rel="shortcut icon" type="image/ico" href="../images/firefox_favicon.ico" /> -</head> - -<body> - <a href="mozilla.html"> - <img id="mozilla_logo" src="../images/mozilla_logo.jpg" /> - </a> - - <a href="#summary">Summary</a> | - <a href="#more">More</a> - - <div id="content"> - <h1 id="page-title">Governance</h1> - - <h2><a name="summary">Summary</a></h2> - <p id="summary"> - Mozilla is an open source project governed as a meritocracy. Our - community is structured as a virtual organization where - authority is distributed to both volunteer and employed - community members as they show their abilities through - contributions to the project. - </p> - - <h2><a name="more">More</a></h2> - <p id="more"> - <ul id="list"> - <li id="roles">Roles and Responsibilities</li> - <li id="policies">Policies</li> - <li id="discussion">Discussion</li> - </ul> - </p> - </div> -</body> -</html> diff --git a/testing/firefox-ui/resources/layout/mozilla_grants.html b/testing/firefox-ui/resources/layout/mozilla_grants.html deleted file mode 100644 index c8935c4fb..000000000 --- a/testing/firefox-ui/resources/layout/mozilla_grants.html +++ /dev/null @@ -1,72 +0,0 @@ -<!DOCTYPE html> -<html lang="en" dir="ltr"> -<head> - <title>Mozilla Grants</title> - <link rel="shortcut icon" type="image/ico" href="../images/mozilla_favicon.ico" /> -</head> - -<body> - <a href="mozilla.html"> - <img id="mozilla_logo" src="../images/mozilla_logo.jpg" /> - </a> - - <a href="#summary">Summary</a> | - <a href="#goals">Goals</a> - - <div id="content"> - <h1 id="page-title">Mozilla Grants</h1> - - <h2><a name="summary">Summary</a></h2> - <p id="summary"> - Since 2006, Mozilla has awarded over two million dollars to fund - projects that contribute to the health of the Open Web. The - Mozilla Grants program is jointly funded by the Mozilla - Corporation and the Mozilla Foundation, and awards financial - support to individuals and organizations whose work supports and - enhances the mission and values of the Mozilla Project. - </p> - - <h2><a name="goals">Goals</a></h2> - <p id="goals"> - Mozilla makes grants to individuals and organizations all over - the world. We mainly fund activity that supports the Mozilla - Grants program's four target areas: - - <ul id="goal_list"> - <li id="accessibility"> - <strong>Accessibility:</strong> Mozilla believes that - the Internet truly is for everyone, and that those with - disabilities should be able to participate on the Web - along with their sighted and hearing peers. As part of - our accessibility strategy, we are funding the - development of free, open source options for those with - visual and auditory impairments. - </li> - - <li id="community"> - <strong>Community:</strong> Mozilla offers suppport to - the broader free culture and open source community, as - part of Mozilla's general effort to 'give back', aiding - in the creation of technologies and projects that - increase the health of the open Web ecosystem. - </li> - - <li id="education"> - <strong>Education:</strong> As part of Mozilla's broader - education initiative, we support educational - institutions that are producing the next generation of - innovative creators of software. - </li> - - <li id="open_source"> - <strong>Open Source:</strong> These grants support the - creation and adoption of Web standards, open source - principles, and the overall principles of transparency, - collaboration, and openness that free and open source - software projects adhere to. - </li> - </ul> - </p> - </div> -</body> -</html> diff --git a/testing/firefox-ui/resources/layout/mozilla_mission.html b/testing/firefox-ui/resources/layout/mozilla_mission.html deleted file mode 100644 index c9ed2bd85..000000000 --- a/testing/firefox-ui/resources/layout/mozilla_mission.html +++ /dev/null @@ -1,51 +0,0 @@ -<!DOCTYPE html> -<html lang="en" dir="ltr"> -<head> - <title>Mozilla Mission</title> - <link rel="shortcut icon" type="image/ico" href="../images/seamonkey_favicon.ico" /> -</head> - -<body> - <a href="mozilla.html"> - <img id="mozilla_logo" src="../images/mozilla_logo.jpg" /> - </a> - - <a href="#mission">Mission</a> | - <a href="#organization">Organization</a> | - <a href="#goal">Goal</a> - - <div id="content" name="content"> - <h1 id="page-title" name="page-title">Mission</h1> - - <h2><a name="mission">Mission</a></h2> - <p id="mission_statement"> - Mozilla's mission is to <strong>promote openness, innovation, - and opportunity on the web</strong>. We do this by creating - great software, like the Firefox browser, and building - movements, like Drumbeat, that give people tools to take control - of their online lives. - </p> - - <h2><a name="organization">Organization</a></h2> - <p id="organization"> - As a non-profit organization, we define success in terms of - building communities and enriching people's lives instead of - benefiting our shareholders (guess what: we don't even have - shareholders). We believe in the power and potential of the - Internet and want to see it thrive for everyone, everywhere. - </p> - - <h2><a name="goal">Goal</a></h2> - <p id="goal"> - <strong> - Building a better Internet is an ambitious goal, but we - believe that it is possible - </strong> - when people who share our passion get involved. Coders, artists, - writers, testers, surfers, students, grandparents; anyone who - uses and cares about the web can help make it even better. - <a href="mozilla_contribute.html">Find out how you can help</a>. - </p> - </div> -</body> -</html> diff --git a/testing/firefox-ui/resources/layout/mozilla_organizations.html b/testing/firefox-ui/resources/layout/mozilla_organizations.html deleted file mode 100644 index 9d2ae9ff0..000000000 --- a/testing/firefox-ui/resources/layout/mozilla_organizations.html +++ /dev/null @@ -1,39 +0,0 @@ -<!DOCTYPE html> -<html lang="en" dir="ltr"> -<head> - <title>Mozilla Organizations</title> - <link rel="shortcut icon" type="image/ico" href="../images/thunderbird_favicon.ico" /> -</head> - -<body> - <a href="mozilla.html"> - <img id="mozilla_logo" src="../images/mozilla_logo.jpg" /> - </a> - - <a href="#summary">Summary</a> | - <a href="#organization">Organization</a> - - <div id="content"> - <h1 id="page-title">Mozilla Organizations</h1> - - <h2><a name="summary">Summary</a></h2> - <p id="summary"> - Mozilla is a global community of people creating a better - Internet. We build public benefit into the Internet by creating - free, open source products and technologies that improve the - online experience for people everywhere. - </p> - - <h2><a name="organization">Organization</a></h2> - <p id="organization"> - There are several organizations that support the Mozilla - community and Mozilla's principles. They include the non-profit - Mozilla Foundation as well as two wholly owned taxable - subsidiaries, the Mozilla Corporation and Mozilla Messaging. - Mozilla considers itself a hybrid organization, combining non- - profit and market strategies to ensure the Internet remains a - shared public resource. - </p> - </div> -</body> -</html> diff --git a/testing/firefox-ui/resources/layout/mozilla_projects.html b/testing/firefox-ui/resources/layout/mozilla_projects.html deleted file mode 100644 index a4ec7c840..000000000 --- a/testing/firefox-ui/resources/layout/mozilla_projects.html +++ /dev/null @@ -1,60 +0,0 @@ -<!DOCTYPE html> -<html lang="en" dir="ltr"> -<head> - <title>Mozilla Projects</title> - <link rel="shortcut icon" type="image/ico" href="../images/firefox_favicon.ico" /> -</head> - -<body> - <a href="mozilla.html"> - <img id="mozilla_logo" src="../images/mozilla_logo.jpg" /> - </a> - - <a href="#summary">Summary</a> | - <a href="#applications">Applications</a> - - <div id="content"> - <h1 id="page-title">Our Projects</h1> - - <h2><a name="summary">Summary</a></h2> - <p id="summary"> - The Mozilla community produces a lot of great software and acts - as an incubator for innovative ideas as a way to advance our - <a href="mozilla_mission.html">mission</a> of building a better - Internet. - </p> - - <h2><a name="applications">Applications</a></h2> - <p id="applications"> - <p> - These applications are developed by the Mozilla community - and their code is hosted on mozilla.org. - </p> - - <ul id="product_list"> - <li id="bugzilla"> - <h3><strong>Bugzilla</strong></h3> - Bugzilla is a bug tracking system designed to help teams - manage software development. Hundreds of organizations - across the globe are using this powerful tool to get - organized and communicate effectively. - </li> - - <li id="camino"> - <h3><strong>Camino</strong></h3> - Camino is a Web browser optimized for Mac OS X with a - Cocoa user interface, and powerful Gecko layout engine. - It's the simple, secure, and fast browser for Mac OS X. - </li> - - <li id="firefox"> - <h3><strong>Firefox for Desktop</strong></h3> - The award-winning Firefox Web browser has security, - speed and new features that will change the way you use - the Web. Don’t settle for anything less. - </li> - </ul> - </p> - </div> -</body> -</html> diff --git a/testing/firefox-ui/resources/private_browsing/about.html b/testing/firefox-ui/resources/private_browsing/about.html deleted file mode 100644 index 30b211be6..000000000 --- a/testing/firefox-ui/resources/private_browsing/about.html +++ /dev/null @@ -1,10 +0,0 @@ -<!DOCTYPE html> -<html lang="en" dir="ltr"> -<head> - <meta http-equiv="content-type" content="text/html; charset=UTF-8"> -</head> - -<body> - <div id="about_pb">About Private Browsing</div> -</body> -</html> diff --git a/testing/firefox-ui/resources/security/enable_privilege.html b/testing/firefox-ui/resources/security/enable_privilege.html deleted file mode 100644 index 9d18e4684..000000000 --- a/testing/firefox-ui/resources/security/enable_privilege.html +++ /dev/null @@ -1,21 +0,0 @@ -<!DOCTYPE html> -<html lang="en" dir="ltr"> - <head> - <title>Test page for enablePrivilege</title> - <script> - function init() { - var result = document.getElementById("result"); - try { - netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); - result.textContent = "FAIL"; - } - catch (ex) { - result.textContent = "PASS"; - } - } - </script> - </head> - <body onload="init();"> - <p id="result"></p> - </body> -</html> diff --git a/testing/firefox-ui/resources/support.html b/testing/firefox-ui/resources/support.html deleted file mode 100644 index b794e9ef9..000000000 --- a/testing/firefox-ui/resources/support.html +++ /dev/null @@ -1,19 +0,0 @@ -<!DOCTYPE html> -<html> -<head> - <meta http-equiv="content-type" content="text/html; charset=UTF-8"> - <script type="text/javascript"> - function show() { - var results = /\?topic=(.+)$/.exec(window.document.location); - var topic = decodeURIComponent(results[1].replace(/\+/g, " ")) - var node = document.getElementById("topic"); - - node.textContent = topic; - } - </script> -</head> - -<body onload="show()"> - <div id="topic"></div> -</body> -</html> diff --git a/testing/firefox-ui/tests/functional/keyboard_shortcuts/manifest.ini b/testing/firefox-ui/tests/functional/keyboard_shortcuts/manifest.ini deleted file mode 100644 index 97ec827f1..000000000 --- a/testing/firefox-ui/tests/functional/keyboard_shortcuts/manifest.ini +++ /dev/null @@ -1,4 +0,0 @@ -[DEFAULT] -tags = local - -[test_browser_window.py] diff --git a/testing/firefox-ui/tests/functional/keyboard_shortcuts/test_browser_window.py b/testing/firefox-ui/tests/functional/keyboard_shortcuts/test_browser_window.py deleted file mode 100644 index 5b656d0e5..000000000 --- a/testing/firefox-ui/tests/functional/keyboard_shortcuts/test_browser_window.py +++ /dev/null @@ -1,56 +0,0 @@ -# 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 firefox_puppeteer import PuppeteerMixin -from marionette_driver import Wait -from marionette_harness import MarionetteTestCase - - -class TestBrowserWindowShortcuts(PuppeteerMixin, MarionetteTestCase): - - def test_addons_manager(self): - # If an about:xyz page is visible, no new tab will be opened - with self.marionette.using_context('content'): - self.marionette.navigate('about:') - - # TODO: To be moved to the upcoming add-ons library - def opener(tab): - tab.window.send_shortcut(tab.window.localize_entity('addons.commandkey'), - accel=True, shift=True) - self.browser.tabbar.open_tab(opener) - - # TODO: Marionette currently fails to detect the correct tab - # with self.marionette.using_content('content'): - # self.wait_for_condition(lambda mn: mn.get_url() == "about:addons") - - # TODO: remove extra switch once it is done automatically - self.browser.tabbar.tabs[1].switch_to() - self.browser.tabbar.close_tab() - - def test_search_field(self): - current_name = self.marionette.execute_script(""" - return window.document.activeElement.localName; - """) - - # This doesn't test anything if we're already at input. - self.assertNotEqual(current_name, "input") - - # TODO: To be moved to the upcoming search library - if self.puppeteer.platform == 'linux': - key = 'searchFocusUnix.commandkey' - else: - key = 'searchFocus.commandkey' - self.browser.send_shortcut(self.browser.localize_entity(key), - accel=True) - - # TODO: Check that the right input box is focused - # Located below searchbar as class="autocomplete-textbox textbox-input" - # Anon locator has not been released yet (bug 1080764) - def has_input_selected(mn): - selection_name = mn.execute_script(""" - return window.document.activeElement.localName; - """) - return selection_name == "input" - - Wait(self.marionette).until(has_input_selected) diff --git a/testing/firefox-ui/tests/functional/locationbar/manifest.ini b/testing/firefox-ui/tests/functional/locationbar/manifest.ini deleted file mode 100644 index 72adfd767..000000000 --- a/testing/firefox-ui/tests/functional/locationbar/manifest.ini +++ /dev/null @@ -1,9 +0,0 @@ -[DEFAULT] -tags = local - -[test_access_locationbar.py] -disabled = Bug 1168727 - Timeout when opening auto-complete popup -[test_escape_autocomplete.py] -[test_favicon_in_autocomplete.py] -[test_suggest_bookmarks.py] - diff --git a/testing/firefox-ui/tests/functional/locationbar/test_access_locationbar.py b/testing/firefox-ui/tests/functional/locationbar/test_access_locationbar.py deleted file mode 100644 index 160a402c2..000000000 --- a/testing/firefox-ui/tests/functional/locationbar/test_access_locationbar.py +++ /dev/null @@ -1,60 +0,0 @@ -# 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 firefox_puppeteer import PuppeteerMixin -from marionette_driver import Wait -from marionette_harness import MarionetteTestCase - - -class TestAccessLocationBar(PuppeteerMixin, MarionetteTestCase): - - def setUp(self): - super(TestAccessLocationBar, self).setUp() - - # Clear complete history so there's no interference from previous entries. - self.puppeteer.places.remove_all_history() - - self.test_urls = [ - 'layout/mozilla_projects.html', - 'layout/mozilla.html', - 'layout/mozilla_mission.html' - ] - self.test_urls = [self.marionette.absolute_url(t) - for t in self.test_urls] - - self.locationbar = self.browser.navbar.locationbar - self.autocomplete_results = self.locationbar.autocomplete_results - self.urlbar = self.locationbar.urlbar - - def test_access_locationbar_history(self): - - # Open some local pages, then about:blank - def load_urls(): - with self.marionette.using_context('content'): - for url in self.test_urls: - self.marionette.navigate(url) - self.puppeteer.places.wait_for_visited(self.test_urls, load_urls) - with self.marionette.using_context('content'): - self.marionette.navigate('about:blank') - - # Need to blur url bar or autocomplete won't load - bug 1038614 - self.marionette.execute_script("""arguments[0].blur();""", script_args=[self.urlbar]) - - # Clear contents of url bar to focus, then arrow down for list of visited sites - # Verify that autocomplete is open and results are displayed - self.locationbar.clear() - self.urlbar.send_keys(self.puppeteer.keys.ARROW_DOWN) - Wait(self.marionette).until(lambda _: self.autocomplete_results.is_open) - Wait(self.marionette).until(lambda _: len(self.autocomplete_results.visible_results) > 1) - - # Arrow down again to select first item in list, appearing in reversed order, as loaded. - # Verify first item. - self.urlbar.send_keys(self.puppeteer.keys.ARROW_DOWN) - Wait(self.marionette).until(lambda _: self.autocomplete_results.selected_index == '0') - self.assertIn('mission', self.locationbar.value) - - # Navigate to the currently selected url - # Verify it loads by comparing the page url to the test url - self.urlbar.send_keys(self.puppeteer.keys.ENTER) - self.assertEqual(self.locationbar.value, self.test_urls[-1]) diff --git a/testing/firefox-ui/tests/functional/locationbar/test_escape_autocomplete.py b/testing/firefox-ui/tests/functional/locationbar/test_escape_autocomplete.py deleted file mode 100644 index 209d9b0f5..000000000 --- a/testing/firefox-ui/tests/functional/locationbar/test_escape_autocomplete.py +++ /dev/null @@ -1,56 +0,0 @@ -# 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 firefox_puppeteer import PuppeteerMixin -from marionette_driver import Wait -from marionette_harness import MarionetteTestCase - - -class TestEscapeAutocomplete(PuppeteerMixin, MarionetteTestCase): - - def setUp(self): - super(TestEscapeAutocomplete, self).setUp() - - # Clear complete history so there's no interference from previous entries. - self.puppeteer.places.remove_all_history() - - self.test_urls = [ - 'layout/mozilla.html', - 'layout/mozilla_community.html', - ] - self.test_urls = [self.marionette.absolute_url(t) - for t in self.test_urls] - - self.test_string = 'mozilla' - - self.locationbar = self.browser.navbar.locationbar - self.autocomplete_results = self.locationbar.autocomplete_results - - def tearDown(self): - self.autocomplete_results.close(force=True) - - super(TestEscapeAutocomplete, self).tearDown() - - def test_escape_autocomplete(self): - # Open some local pages - def load_urls(): - with self.marionette.using_context('content'): - for url in self.test_urls: - self.marionette.navigate(url) - self.puppeteer.places.wait_for_visited(self.test_urls, load_urls) - - # Clear the location bar, type the test string, check that autocomplete list opens - self.locationbar.clear() - self.locationbar.urlbar.send_keys(self.test_string) - self.assertEqual(self.locationbar.value, self.test_string) - Wait(self.marionette).until(lambda _: self.autocomplete_results.is_open) - - # Press escape, check location bar value, check autocomplete list closed - self.locationbar.urlbar.send_keys(self.puppeteer.keys.ESCAPE) - self.assertEqual(self.locationbar.value, self.test_string) - Wait(self.marionette).until(lambda _: not self.autocomplete_results.is_open) - - # Press escape again and check that locationbar returns to the page url - self.locationbar.urlbar.send_keys(self.puppeteer.keys.ESCAPE) - self.assertEqual(self.locationbar.value, self.test_urls[-1]) diff --git a/testing/firefox-ui/tests/functional/locationbar/test_favicon_in_autocomplete.py b/testing/firefox-ui/tests/functional/locationbar/test_favicon_in_autocomplete.py deleted file mode 100644 index 6e8a5f6b1..000000000 --- a/testing/firefox-ui/tests/functional/locationbar/test_favicon_in_autocomplete.py +++ /dev/null @@ -1,62 +0,0 @@ -# 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 firefox_puppeteer import PuppeteerMixin -from marionette_driver import Wait -from marionette_harness import MarionetteTestCase - - -class TestFaviconInAutocomplete(PuppeteerMixin, MarionetteTestCase): - - PREF_SUGGEST_SEARCHES = 'browser.urlbar.suggest.searches' - PREF_SUGGEST_BOOKMARK = 'browser.urlbar.suggest.bookmark' - - def setUp(self): - super(TestFaviconInAutocomplete, self).setUp() - - # Disable suggestions for searches and bookmarks to get results only for history - self.marionette.set_pref(self.PREF_SUGGEST_SEARCHES, False) - self.marionette.set_pref(self.PREF_SUGGEST_BOOKMARK, False) - - self.puppeteer.places.remove_all_history() - - self.test_urls = [self.marionette.absolute_url('layout/mozilla.html')] - - self.test_string = 'mozilla' - self.test_favicon = 'mozilla_favicon.ico' - - self.autocomplete_results = self.browser.navbar.locationbar.autocomplete_results - - def tearDown(self): - try: - self.autocomplete_results.close(force=True) - self.marionette.clear_pref(self.PREF_SUGGEST_SEARCHES) - self.marionette.clear_pref(self.PREF_SUGGEST_BOOKMARK) - finally: - super(TestFaviconInAutocomplete, self).tearDown() - - def test_favicon_in_autocomplete(self): - # Open the test page - def load_urls(): - with self.marionette.using_context('content'): - self.marionette.navigate(self.test_urls[0]) - self.puppeteer.places.wait_for_visited(self.test_urls, load_urls) - - locationbar = self.browser.navbar.locationbar - - # Clear the location bar, type the test string, check that autocomplete list opens - locationbar.clear() - locationbar.urlbar.send_keys(self.test_string) - self.assertEqual(locationbar.value, self.test_string) - Wait(self.marionette).until(lambda _: self.autocomplete_results.is_complete) - - result = self.autocomplete_results.visible_results[1] - - result_icon = self.marionette.execute_script(""" - return arguments[0].image; - """, script_args=[result]) - - self.assertIn(self.test_favicon, result_icon) - - self.autocomplete_results.close() diff --git a/testing/firefox-ui/tests/functional/locationbar/test_suggest_bookmarks.py b/testing/firefox-ui/tests/functional/locationbar/test_suggest_bookmarks.py deleted file mode 100644 index 9abc2d6cb..000000000 --- a/testing/firefox-ui/tests/functional/locationbar/test_suggest_bookmarks.py +++ /dev/null @@ -1,96 +0,0 @@ -# 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 firefox_puppeteer import PuppeteerMixin -from marionette_driver import By, Wait -from marionette_harness import MarionetteTestCase - - -class TestStarInAutocomplete(PuppeteerMixin, MarionetteTestCase): - """ This replaces - http://hg.mozilla.org/qa/mozmill-tests/file/default/firefox/tests/functional/testAwesomeBar/testSuggestBookmarks.js - Check a star appears in autocomplete list for a bookmarked page. - """ - - PREF_SUGGEST_SEARCHES = 'browser.urlbar.suggest.searches' - - def setUp(self): - super(TestStarInAutocomplete, self).setUp() - - self.bookmark_panel = None - self.test_urls = [self.marionette.absolute_url('layout/mozilla_grants.html')] - - # Disable search suggestions to only get results for history and bookmarks - self.marionette.set_pref(self.PREF_SUGGEST_SEARCHES, False) - - with self.marionette.using_context('content'): - self.marionette.navigate('about:blank') - - self.puppeteer.places.remove_all_history() - - def tearDown(self): - # Close the autocomplete results - try: - if self.bookmark_panel: - self.marionette.execute_script(""" - arguments[0].hidePopup(); - """, script_args=[self.bookmark_panel]) - - self.browser.navbar.locationbar.autocomplete_results.close() - self.puppeteer.places.restore_default_bookmarks() - self.marionette.clear_pref(self.PREF_SUGGEST_SEARCHES) - finally: - super(TestStarInAutocomplete, self).tearDown() - - def test_star_in_autocomplete(self): - search_string = 'grants' - - def visit_urls(): - with self.marionette.using_context('content'): - for url in self.test_urls: - self.marionette.navigate(url) - - # Navigate to all the urls specified in self.test_urls and wait for them to - # be registered as visited - self.puppeteer.places.wait_for_visited(self.test_urls, visit_urls) - - # Bookmark the current page using the bookmark menu - self.browser.menubar.select_by_id('bookmarksMenu', - 'menu_bookmarkThisPage') - - # TODO: Replace hard-coded selector with library method when one is available - self.bookmark_panel = self.marionette.find_element(By.ID, 'editBookmarkPanel') - done_button = self.marionette.find_element(By.ID, 'editBookmarkPanelDoneButton') - - Wait(self.marionette).until( - lambda mn: self.bookmark_panel.get_attribute('panelopen') == 'true') - done_button.click() - - # We must open the blank page so the autocomplete result isn't "Switch to tab" - with self.marionette.using_context('content'): - self.marionette.navigate('about:blank') - - self.puppeteer.places.remove_all_history() - - # Focus the locationbar, delete any contents there, and type the search string - locationbar = self.browser.navbar.locationbar - locationbar.clear() - locationbar.urlbar.send_keys(search_string) - autocomplete_results = locationbar.autocomplete_results - - # Wait for the search string to be present, for the autocomplete results to appear - # and for there to be exactly one autocomplete result - Wait(self.marionette).until(lambda mn: locationbar.value == search_string) - Wait(self.marionette).until(lambda mn: autocomplete_results.is_complete) - Wait(self.marionette).until(lambda mn: len(autocomplete_results.visible_results) == 2) - - # Compare the highlighted text in the autocomplete result to the search string - first_result = autocomplete_results.visible_results[1] - matching_titles = autocomplete_results.get_matching_text(first_result, 'title') - for title in matching_titles: - Wait(self.marionette).until(lambda mn: title.lower() == search_string) - - self.assertIn('bookmark', - first_result.get_attribute('type'), - 'The auto-complete result is a bookmark') diff --git a/testing/firefox-ui/tests/functional/manifest.ini b/testing/firefox-ui/tests/functional/manifest.ini deleted file mode 100644 index bc254e962..000000000 --- a/testing/firefox-ui/tests/functional/manifest.ini +++ /dev/null @@ -1,5 +0,0 @@ -[include:keyboard_shortcuts/manifest.ini] -[include:locationbar/manifest.ini] -[include:private_browsing/manifest.ini] -[include:security/manifest.ini] -[include:sessionstore/manifest.ini] diff --git a/testing/firefox-ui/tests/functional/private_browsing/manifest.ini b/testing/firefox-ui/tests/functional/private_browsing/manifest.ini deleted file mode 100644 index 405753082..000000000 --- a/testing/firefox-ui/tests/functional/private_browsing/manifest.ini +++ /dev/null @@ -1,4 +0,0 @@ -[DEFAULT] -tags = local - -[test_about_private_browsing.py]
\ No newline at end of file diff --git a/testing/firefox-ui/tests/functional/private_browsing/test_about_private_browsing.py b/testing/firefox-ui/tests/functional/private_browsing/test_about_private_browsing.py deleted file mode 100644 index 4b22d03ea..000000000 --- a/testing/firefox-ui/tests/functional/private_browsing/test_about_private_browsing.py +++ /dev/null @@ -1,60 +0,0 @@ -# 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 firefox_puppeteer import PuppeteerMixin -from firefox_puppeteer.ui.browser.window import BrowserWindow -from marionette_driver import By, Wait -from marionette_harness import MarionetteTestCase - - -class TestAboutPrivateBrowsing(PuppeteerMixin, MarionetteTestCase): - - def setUp(self): - super(TestAboutPrivateBrowsing, self).setUp() - - # Use a fake local support URL - support_url = 'about:blank?' - self.marionette.set_pref('app.support.baseURL', support_url) - - self.pb_url = support_url + 'private-browsing' - - def tearDown(self): - try: - self.marionette.clear_pref('app.support.baseURL') - finally: - super(TestAboutPrivateBrowsing, self).tearDown() - - def testCheckAboutPrivateBrowsing(self): - self.assertFalse(self.browser.is_private) - - with self.marionette.using_context('content'): - self.marionette.navigate('about:privatebrowsing') - - status_node = self.marionette.find_element(By.CSS_SELECTOR, 'p.showNormal') - self.assertEqual(status_node.text, - self.browser.localize_entity('aboutPrivateBrowsing.notPrivate'), - 'Status text indicates we are not in private browsing mode') - - def window_opener(win): - with win.marionette.using_context('content'): - button = self.marionette.find_element(By.ID, 'startPrivateBrowsing') - button.click() - - pb_window = self.browser.open_window(callback=window_opener, - expected_window_class=BrowserWindow) - - try: - self.assertTrue(pb_window.is_private) - - def tab_opener(tab): - with tab.marionette.using_context('content'): - link = tab.marionette.find_element(By.ID, 'learnMore') - link.click() - - tab = pb_window.tabbar.open_tab(trigger=tab_opener) - Wait(self.marionette, timeout=self.marionette.timeout.page_load).until( - lambda _: tab.location == self.pb_url) - - finally: - pb_window.close() diff --git a/testing/firefox-ui/tests/functional/security/manifest.ini b/testing/firefox-ui/tests/functional/security/manifest.ini deleted file mode 100644 index 46aeaf5c3..000000000 --- a/testing/firefox-ui/tests/functional/security/manifest.ini +++ /dev/null @@ -1,22 +0,0 @@ -[DEFAULT] -tags = remote - -[test_dv_certificate.py] -[test_enable_privilege.py] -tags = local -[test_ev_certificate.py] -skip-if = true # Bug 1407663 -[test_mixed_content_page.py] -[test_mixed_script_content_blocking.py] -[test_no_certificate.py] -tags = local -[test_safe_browsing_initial_download.py] -[test_safe_browsing_notification.py] -[test_safe_browsing_warning_pages.py] -[test_security_notification.py] -[test_ssl_disabled_error_page.py] -[test_ssl_status_after_restart.py] -skip-if = (os == "win" && os_version == "5.1") # Bug 1167179: Fails to open popups after restart -[test_submit_unencrypted_info_warning.py] -[test_unknown_issuer.py] -[test_untrusted_connection_error_page.py] diff --git a/testing/firefox-ui/tests/functional/security/test_dv_certificate.py b/testing/firefox-ui/tests/functional/security/test_dv_certificate.py deleted file mode 100644 index 565f64996..000000000 --- a/testing/firefox-ui/tests/functional/security/test_dv_certificate.py +++ /dev/null @@ -1,85 +0,0 @@ -# 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 firefox_puppeteer import PuppeteerMixin -from marionette_driver import Wait -from marionette_harness import MarionetteTestCase - - -class TestDVCertificate(PuppeteerMixin, MarionetteTestCase): - - def setUp(self): - super(TestDVCertificate, self).setUp() - - self.locationbar = self.browser.navbar.locationbar - self.identity_popup = self.browser.navbar.locationbar.identity_popup - - self.url = 'https://ssl-dv.mozqa.com' - - def tearDown(self): - try: - self.browser.switch_to() - self.identity_popup.close(force=True) - self.puppeteer.windows.close_all([self.browser]) - finally: - super(TestDVCertificate, self).tearDown() - - def test_dv_cert(self): - with self.marionette.using_context('content'): - self.marionette.navigate(self.url) - - self.assertEqual(self.locationbar.identity_box.get_property('className'), - 'verifiedDomain') - - # Open the identity popup - self.locationbar.open_identity_popup() - - # Check the identity popup doorhanger - self.assertEqual(self.identity_popup.element.get_attribute('connection'), 'secure') - - cert = self.browser.tabbar.selected_tab.certificate - - # The shown host equals to the certificate - self.assertEqual(self.identity_popup.view.main.host.get_property('textContent'), - cert['commonName']) - - # Only the secure label is visible in the main view - secure_label = self.identity_popup.view.main.secure_connection_label - self.assertNotEqual(secure_label.value_of_css_property('display'), 'none') - - insecure_label = self.identity_popup.view.main.insecure_connection_label - self.assertEqual(insecure_label.value_of_css_property('display'), 'none') - - self.identity_popup.view.main.expander.click() - Wait(self.marionette).until( - lambda _: self.identity_popup.view.security.selected, - message='Security view of identity popup has not been selected.') - - # Only the secure label is visible in the security view - secure_label = self.identity_popup.view.security.secure_connection_label - self.assertNotEqual(secure_label.value_of_css_property('display'), 'none') - - insecure_label = self.identity_popup.view.security.insecure_connection_label - self.assertEqual(insecure_label.value_of_css_property('display'), 'none') - - verifier_label = self.browser.localize_property('identity.identified.verifier') - self.assertEqual(self.identity_popup.view.security.verifier.get_property('textContent'), - verifier_label.replace("%S", cert['issuerOrganization'])) - - def opener(mn): - self.identity_popup.view.security.more_info_button.click() - - page_info_window = self.browser.open_page_info_window(opener) - deck = page_info_window.deck - - self.assertEqual(deck.selected_panel, deck.security) - - self.assertEqual(deck.security.domain.get_property('value'), - cert['commonName']) - - self.assertEqual(deck.security.owner.get_property('value'), - page_info_window.localize_property('securityNoOwner')) - - self.assertEqual(deck.security.verifier.get_property('value'), - cert['issuerOrganization']) diff --git a/testing/firefox-ui/tests/functional/security/test_enable_privilege.py b/testing/firefox-ui/tests/functional/security/test_enable_privilege.py deleted file mode 100644 index 17e883cc5..000000000 --- a/testing/firefox-ui/tests/functional/security/test_enable_privilege.py +++ /dev/null @@ -1,17 +0,0 @@ -# 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 -from marionette_harness import MarionetteTestCase - - -class TestEnablePrivilege(MarionetteTestCase): - - def test_enable_privilege(self): - with self.marionette.using_context('content'): - url = self.marionette.absolute_url('security/enable_privilege.html') - self.marionette.navigate(url) - - result = self.marionette.find_element(By.ID, 'result') - self.assertEqual(result.get_property('textContent'), 'PASS') diff --git a/testing/firefox-ui/tests/functional/security/test_ev_certificate.py b/testing/firefox-ui/tests/functional/security/test_ev_certificate.py deleted file mode 100644 index f5acf5795..000000000 --- a/testing/firefox-ui/tests/functional/security/test_ev_certificate.py +++ /dev/null @@ -1,112 +0,0 @@ -# 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 firefox_puppeteer import PuppeteerMixin -from marionette_driver import Wait -from marionette_harness import MarionetteTestCase - - -class TestEVCertificate(PuppeteerMixin, MarionetteTestCase): - - def setUp(self): - super(TestEVCertificate, self).setUp() - - self.locationbar = self.browser.navbar.locationbar - self.identity_popup = self.locationbar.identity_popup - - self.url = 'https://ssl-ev.mozqa.com/' - - def tearDown(self): - try: - self.browser.switch_to() - self.identity_popup.close(force=True) - self.puppeteer.windows.close_all([self.browser]) - finally: - super(TestEVCertificate, self).tearDown() - - def test_ev_certificate(self): - with self.marionette.using_context('content'): - self.marionette.navigate(self.url) - - # Check the identity box - self.assertEqual(self.locationbar.identity_box.get_property('className'), - 'verifiedIdentity') - - # Get the information from the certificate - cert = self.browser.tabbar.selected_tab.certificate - address = self.puppeteer.security.get_address_from_certificate(cert) - - # Check the identity popup label displays - self.assertEqual(self.locationbar.identity_organization_label.get_property('value'), - cert['organization']) - self.assertEqual(self.locationbar.identity_country_label.get_property('value'), - '(' + address['country'] + ')') - - # Open the identity popup - self.locationbar.open_identity_popup() - - # Check the idenity popup doorhanger - self.assertEqual(self.identity_popup.element.get_attribute('connection'), 'secure-ev') - - # For EV certificates no hostname but the organization name is shown - self.assertEqual(self.identity_popup.view.main.host.get_property('textContent'), - cert['organization']) - - # Only the secure label is visible in the main view - secure_label = self.identity_popup.view.main.secure_connection_label - self.assertNotEqual(secure_label.value_of_css_property('display'), 'none') - - insecure_label = self.identity_popup.view.main.insecure_connection_label - self.assertEqual(insecure_label.value_of_css_property('display'), 'none') - - self.identity_popup.view.main.expander.click() - Wait(self.marionette).until(lambda _: self.identity_popup.view.security.selected) - - security_view = self.identity_popup.view.security - - # Only the secure label is visible in the security view - secure_label = security_view.secure_connection_label - self.assertNotEqual(secure_label.value_of_css_property('display'), 'none') - - insecure_label = security_view.insecure_connection_label - self.assertEqual(insecure_label.value_of_css_property('display'), 'none') - - # Check the organization name - self.assertEqual(security_view.owner.get_property('textContent'), cert['organization']) - - # Check the owner location string - # More information: - # hg.mozilla.org/mozilla-central/file/eab4a81e4457/browser/base/content/browser.js#l7012 - location = self.browser.localize_property('identity.identified.state_and_country') - location = location.replace('%S', address['state'], 1).replace('%S', address['country']) - location = address['city'] + '\n' + location - self.assertEqual(security_view.owner_location.get_property('textContent'), location) - - # Check the verifier - l10n_verifier = self.browser.localize_property('identity.identified.verifier') - l10n_verifier = l10n_verifier.replace('%S', cert['issuerOrganization']) - self.assertEqual(security_view.verifier.get_property('textContent'), l10n_verifier) - - # Open the Page Info window by clicking the More Information button - page_info = self.browser.open_page_info_window( - lambda _: self.identity_popup.view.security.more_info_button.click()) - - try: - # Verify that the current panel is the security panel - self.assertEqual(page_info.deck.selected_panel, page_info.deck.security) - - # Verify the domain listed on the security panel - self.assertIn(cert['commonName'], - page_info.deck.security.domain.get_property('value')) - - # Verify the owner listed on the security panel - self.assertEqual(page_info.deck.security.owner.get_property('value'), - cert['organization']) - - # Verify the verifier listed on the security panel - self.assertEqual(page_info.deck.security.verifier.get_property('value'), - cert['issuerOrganization']) - finally: - page_info.close() - self.browser.focus() diff --git a/testing/firefox-ui/tests/functional/security/test_mixed_content_page.py b/testing/firefox-ui/tests/functional/security/test_mixed_content_page.py deleted file mode 100644 index c146b46f4..000000000 --- a/testing/firefox-ui/tests/functional/security/test_mixed_content_page.py +++ /dev/null @@ -1,55 +0,0 @@ -# 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 firefox_puppeteer import PuppeteerMixin -from marionette_harness import MarionetteTestCase - - -class TestMixedContentPage(PuppeteerMixin, MarionetteTestCase): - def setUp(self): - super(TestMixedContentPage, self).setUp() - - self.locationbar = self.browser.navbar.locationbar - self.identity_popup = self.locationbar.identity_popup - - self.url = 'https://mozqa.com/data/firefox/security/mixedcontent.html' - - def tearDown(self): - try: - self.identity_popup.close(force=True) - finally: - super(TestMixedContentPage, self).tearDown() - - def test_mixed_content(self): - with self.marionette.using_context('content'): - self.marionette.navigate(self.url) - - self.assertEqual(self.locationbar.identity_box.get_property('className'), - 'unknownIdentity mixedDisplayContent') - - # Open the identity popup - self.locationbar.open_identity_popup() - - # Only the insecure label is visible in the main view - secure_label = self.identity_popup.view.main.secure_connection_label - self.assertEqual(secure_label.value_of_css_property('display'), 'none') - - insecure_label = self.identity_popup.view.main.insecure_connection_label - self.assertNotEqual(insecure_label.value_of_css_property('display'), 'none') - - # TODO: Bug 1177417 - Needs to open and close the security view, but a second - # click on the expander doesn't hide the security view - # self.identity_popup.view.main.expander.click() - # Wait(self.marionette).until(lambda _: self.identity_popup.view.security.selected) - - # Only the insecure label is visible in the security view - secure_label = self.identity_popup.view.security.secure_connection_label - self.assertEqual(secure_label.value_of_css_property('display'), 'none') - - insecure_label = self.identity_popup.view.security.insecure_connection_label - self.assertNotEqual(insecure_label.value_of_css_property('display'), 'none') - - # owner is not visible - owner = self.identity_popup.view.security.owner - self.assertEqual(owner.value_of_css_property('display'), 'none') diff --git a/testing/firefox-ui/tests/functional/security/test_mixed_script_content_blocking.py b/testing/firefox-ui/tests/functional/security/test_mixed_script_content_blocking.py deleted file mode 100644 index 796b1dc29..000000000 --- a/testing/firefox-ui/tests/functional/security/test_mixed_script_content_blocking.py +++ /dev/null @@ -1,87 +0,0 @@ -# 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 firefox_puppeteer import PuppeteerMixin -from marionette_driver import By, Wait -from marionette_harness import MarionetteTestCase - - -class TestMixedScriptContentBlocking(PuppeteerMixin, MarionetteTestCase): - - def setUp(self): - super(TestMixedScriptContentBlocking, self).setUp() - - self.url = 'https://mozqa.com/data/firefox/security/mixed_content_blocked/index.html' - - self.test_elements = [ - ('result1', 'Insecure script one'), - ('result2', 'Insecure script from iFrame'), - ('result3', 'Insecure plugin'), - ('result4', 'Insecure stylesheet'), - ] - - self.locationbar = self.browser.navbar.locationbar - self.identity_popup = self.locationbar.identity_popup - - def tearDown(self): - try: - self.identity_popup.close(force=True) - finally: - super(TestMixedScriptContentBlocking, self).tearDown() - - def _expect_protection_status(self, enabled): - if enabled: - color, identity, state = ( - 'rgb(0, 136, 0)', - 'verifiedDomain mixedActiveBlocked', - 'blocked' - ) - else: - color, identity, state = ( - 'rgb(255, 0, 0)', - 'unknownIdentity mixedActiveContent', - 'unblocked' - ) - - # First call to Wait() needs a longer timeout due to the reload of the web page. - Wait(self.marionette, timeout=self.marionette.timeout.page_load).until( - lambda _: self.locationbar.identity_box.get_property('className') == identity, - message='Expected identity "{}" not found'.format(identity) - ) - - with self.marionette.using_context('content'): - for identifier, description in self.test_elements: - el = self.marionette.find_element(By.ID, identifier) - Wait(self.marionette).until( - lambda mn: el.value_of_css_property('color') == color, - message=("%s has been %s" % (description, state)) - ) - - def expect_protection_enabled(self): - self._expect_protection_status(True) - - def expect_protection_disabled(self): - self._expect_protection_status(False) - - def test_mixed_content_page(self): - with self.marionette.using_context('content'): - self.marionette.navigate(self.url) - - self.expect_protection_enabled() - - # Disable mixed content blocking via identity popup - self.locationbar.open_identity_popup() - self.identity_popup.view.main.expander.click() - Wait(self.marionette).until(lambda _: self.identity_popup.view.security.selected) - - disable_button = self.identity_popup.view.security.disable_mixed_content_blocking_button - disable_button.click() - - self.expect_protection_disabled() - - # A reload keeps blocking disabled - with self.marionette.using_context('content'): - self.marionette.navigate(self.url) - - self.expect_protection_disabled() diff --git a/testing/firefox-ui/tests/functional/security/test_no_certificate.py b/testing/firefox-ui/tests/functional/security/test_no_certificate.py deleted file mode 100644 index a3b7bf98a..000000000 --- a/testing/firefox-ui/tests/functional/security/test_no_certificate.py +++ /dev/null @@ -1,81 +0,0 @@ -# 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 urlparse import urlparse - -from firefox_puppeteer import PuppeteerMixin -from marionette_driver import expected, Wait -from marionette_harness import MarionetteTestCase - - -class TestNoCertificate(PuppeteerMixin, MarionetteTestCase): - - def setUp(self): - super(TestNoCertificate, self).setUp() - - self.locationbar = self.browser.navbar.locationbar - self.identity_popup = self.locationbar.identity_popup - - self.url = self.marionette.absolute_url('layout/mozilla.html') - - def tearDown(self): - try: - self.browser.switch_to() - self.identity_popup.close(force=True) - self.puppeteer.windows.close_all([self.browser]) - finally: - super(TestNoCertificate, self).tearDown() - - def test_no_certificate(self): - with self.marionette.using_context('content'): - self.marionette.navigate(self.url) - - # Check the favicon - # TODO: find a better way to check, e.g., mozmill's isDisplayed - favicon_hidden = self.marionette.execute_script(""" - return arguments[0].hasAttribute("hidden"); - """, script_args=[self.browser.navbar.locationbar.identity_icon]) - self.assertFalse(favicon_hidden, 'The identity icon is visible') - - # Check that the identity box organization label is blank - self.assertEqual(self.locationbar.identity_organization_label.get_property('value'), '', - 'The organization has no label') - - # Open the identity popup - self.locationbar.open_identity_popup() - - # Check the idenity popup doorhanger - self.assertEqual(self.identity_popup.element.get_attribute('connection'), 'not-secure') - - # The expander for the security view does not exist - expected.element_not_present(lambda m: self.identity_popup.main.expander) - - # Only the insecure label is visible - secure_label = self.identity_popup.view.main.secure_connection_label - self.assertEqual(secure_label.value_of_css_property('display'), 'none') - - insecure_label = self.identity_popup.view.main.insecure_connection_label - self.assertNotEqual(insecure_label.value_of_css_property('display'), 'none') - - self.identity_popup.view.main.expander.click() - Wait(self.marionette).until(lambda _: self.identity_popup.view.security.selected) - - # Open the Page Info window by clicking the "More Information" button - page_info = self.browser.open_page_info_window( - lambda _: self.identity_popup.view.security.more_info_button.click()) - - # Verify that the current panel is the security panel - self.assertEqual(page_info.deck.selected_panel, page_info.deck.security) - - # Check the domain listed on the security panel contains the url's host name - self.assertIn(urlparse(self.url).hostname, - page_info.deck.security.domain.get_property('value')) - - # Check the owner label equals localized 'securityNoOwner' - self.assertEqual(page_info.deck.security.owner.get_property('value'), - page_info.localize_property('securityNoOwner')) - - # Check the verifier label equals localized 'notset' - self.assertEqual(page_info.deck.security.verifier.get_property('value'), - page_info.localize_property('notset')) diff --git a/testing/firefox-ui/tests/functional/security/test_safe_browsing_initial_download.py b/testing/firefox-ui/tests/functional/security/test_safe_browsing_initial_download.py deleted file mode 100644 index 6f9c50ffb..000000000 --- a/testing/firefox-ui/tests/functional/security/test_safe_browsing_initial_download.py +++ /dev/null @@ -1,84 +0,0 @@ -# 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/. - -import os - -from firefox_puppeteer import PuppeteerMixin -from marionette_driver import Wait -from marionette_harness import MarionetteTestCase - - -class TestSafeBrowsingInitialDownload(PuppeteerMixin, MarionetteTestCase): - - file_extensions = [ - 'pset', - 'sbstore', - ] - - prefs_download_lists = [ - 'urlclassifier.blockedTable', - 'urlclassifier.downloadAllowTable', - 'urlclassifier.downloadBlockTable', - 'urlclassifier.malwareTable', - 'urlclassifier.phishTable', - 'urlclassifier.trackingTable', - 'urlclassifier.trackingWhitelistTable', - ] - - prefs_provider_update_time = { - # Force an immediate download of the safebrowsing files - 'browser.safebrowsing.provider.google.nextupdatetime': 1, - 'browser.safebrowsing.provider.mozilla.nextupdatetime': 1, - } - - prefs_safebrowsing = { - 'browser.safebrowsing.debug': True, - 'browser.safebrowsing.blockedURIs.enabled': True, - 'browser.safebrowsing.downloads.enabled': True, - 'browser.safebrowsing.phishing.enabled': True, - 'browser.safebrowsing.malware.enabled': True, - 'privacy.trackingprotection.enabled': True, - 'privacy.trackingprotection.pbmode.enabled': True, - } - - def get_safebrowsing_files(self): - files = [] - for pref_name in self.prefs_download_lists: - base_names = self.marionette.get_pref(pref_name).split(',') - for ext in self.file_extensions: - files.extend(['{file}.{ext}'.format(file=f, ext=ext) for f in base_names if f]) - - return set(sorted(files)) - - def setUp(self): - super(TestSafeBrowsingInitialDownload, self).setUp() - - # Force the preferences for the new profile - enforce_prefs = self.prefs_safebrowsing - enforce_prefs.update(self.prefs_provider_update_time) - self.marionette.enforce_gecko_prefs(enforce_prefs) - - self.safebrowsing_path = os.path.join(self.marionette.instance.profile.profile, - 'safebrowsing') - self.safebrowsing_files = self.get_safebrowsing_files() - - def tearDown(self): - try: - # Restart with a fresh profile - self.restart(clean=True) - finally: - super(TestSafeBrowsingInitialDownload, self).tearDown() - - def test_safe_browsing_initial_download(self): - def check_downloaded(_): - return reduce(lambda state, pref: state and int(self.marionette.get_pref(pref)) != 1, - self.prefs_provider_update_time.keys(), True) - - try: - Wait(self.marionette, timeout=60).until( - check_downloaded, message='Not all safebrowsing files have been downloaded') - finally: - files_on_disk_toplevel = os.listdir(self.safebrowsing_path) - for f in self.safebrowsing_files: - self.assertIn(f, files_on_disk_toplevel) diff --git a/testing/firefox-ui/tests/functional/security/test_safe_browsing_notification.py b/testing/firefox-ui/tests/functional/security/test_safe_browsing_notification.py deleted file mode 100644 index 5fb3d0389..000000000 --- a/testing/firefox-ui/tests/functional/security/test_safe_browsing_notification.py +++ /dev/null @@ -1,149 +0,0 @@ -# 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/. - -import time - -from firefox_puppeteer import PuppeteerMixin -from marionette_driver import By, expected, Wait -from marionette_harness import MarionetteTestCase - - -class TestSafeBrowsingNotificationBar(PuppeteerMixin, MarionetteTestCase): - - def setUp(self): - super(TestSafeBrowsingNotificationBar, self).setUp() - - self.test_data = [ - # Unwanted software URL - { - # First two properties are not needed, - # since these errors are not reported - 'button_property': None, - 'report_page': None, - 'unsafe_page': 'https://www.itisatrap.org/firefox/unwanted.html' - }, - # Phishing URL info - { - 'button_property': 'safebrowsing.notADeceptiveSiteButton.label', - 'report_page': 'google.com/safebrowsing/report_error', - 'unsafe_page': 'https://www.itisatrap.org/firefox/its-a-trap.html' - }, - # Malware URL object - { - 'button_property': 'safebrowsing.notAnAttackButton.label', - 'report_page': 'stopbadware.org', - 'unsafe_page': 'https://www.itisatrap.org/firefox/its-an-attack.html' - } - ] - - self.marionette.set_pref('browser.safebrowsing.phishing.enabled', True) - self.marionette.set_pref('browser.safebrowsing.malware.enabled', True) - - # Give the browser a little time, because SafeBrowsing.jsm takes a while - # between start up and adding the example urls to the db. - # hg.mozilla.org/mozilla-central/file/46aebcd9481e/browser/base/content/browser.js#l1194 - time.sleep(3) - - # TODO: Bug 1139544: While we don't have a reliable way to close the safe browsing - # notification bar when a test fails, run this test in a new tab. - self.browser.tabbar.open_tab() - - def tearDown(self): - try: - self.puppeteer.utils.permissions.remove('https://www.itisatrap.org', 'safe-browsing') - self.browser.tabbar.close_all_tabs([self.browser.tabbar.tabs[0]]) - self.marionette.clear_pref('browser.safebrowsing.phishing.enabled') - self.marionette.clear_pref('browser.safebrowsing.malware.enabled') - finally: - super(TestSafeBrowsingNotificationBar, self).tearDown() - - def test_notification_bar(self): - with self.marionette.using_context('content'): - for item in self.test_data: - button_property = item['button_property'] - report_page, unsafe_page = item['report_page'], item['unsafe_page'] - - # Navigate to the unsafe page - # Check "ignore warning" link then notification bar's "not badware" button - # Only do this if feature supports it - if button_property is not None: - self.marionette.navigate(unsafe_page) - # Wait for the DOM to receive events for about:blocked - time.sleep(1) - self.check_ignore_warning_button(unsafe_page) - self.check_not_badware_button(button_property, report_page) - - # Return to the unsafe page - # Check "ignore warning" link then notification bar's "get me out" button - self.marionette.navigate(unsafe_page) - # Wait for the DOM to receive events for about:blocked - time.sleep(1) - self.check_ignore_warning_button(unsafe_page) - self.check_get_me_out_of_here_button() - - # Return to the unsafe page - # Check "ignore warning" link then notification bar's "X" button - self.marionette.navigate(unsafe_page) - # Wait for the DOM to receive events for about:blocked - time.sleep(1) - self.check_ignore_warning_button(unsafe_page) - self.check_x_button() - - def check_ignore_warning_button(self, unsafe_page): - button = self.marionette.find_element(By.ID, 'ignoreWarningButton') - button.click() - - Wait(self.marionette, timeout=self.marionette.timeout.page_load).until( - expected.element_present(By.ID, 'main-feature'), - message='Expected target element "#main-feature" has not been found', - ) - self.assertEquals(self.marionette.get_url(), self.browser.get_final_url(unsafe_page)) - - # Clean up here since the permission gets set in this function - self.puppeteer.utils.permissions.remove('https://www.itisatrap.org', 'safe-browsing') - - # Check the not a forgery or attack button in the notification bar - def check_not_badware_button(self, button_property, report_page): - with self.marionette.using_context('chrome'): - # TODO: update to use safe browsing notification bar class when bug 1139544 lands - label = self.browser.localize_property(button_property) - button = (self.marionette.find_element(By.ID, 'content') - .find_element('anon attribute', {'label': label})) - - self.browser.tabbar.open_tab(lambda _: button.click()) - - Wait(self.marionette, timeout=self.marionette.timeout.page_load).until( - lambda mn: report_page in mn.get_url(), - message='The expected safe-browsing report page has not been opened', - ) - - with self.marionette.using_context('chrome'): - self.browser.tabbar.close_tab() - - def check_get_me_out_of_here_button(self): - with self.marionette.using_context('chrome'): - # TODO: update to use safe browsing notification bar class when bug 1139544 lands - label = self.browser.localize_property('safebrowsing.getMeOutOfHereButton.label') - button = (self.marionette.find_element(By.ID, 'content') - .find_element('anon attribute', {'label': label})) - button.click() - - Wait(self.marionette, timeout=self.marionette.timeout.page_load).until( - lambda mn: self.browser.default_homepage in mn.get_url(), - message='The default home page has not been loaded', - ) - - def check_x_button(self): - with self.marionette.using_context('chrome'): - # TODO: update to use safe browsing notification bar class when bug 1139544 lands - button = (self.marionette.find_element(By.ID, 'content') - .find_element('anon attribute', {'value': 'blocked-badware-page'}) - .find_element('anon attribute', - {'class': 'messageCloseButton close-icon tabbable'})) - button.click() - - Wait(self.marionette, timeout=self.marionette.timeout.page_load).until( - expected.element_stale(button), - message='The notification bar has not been closed', - ) diff --git a/testing/firefox-ui/tests/functional/security/test_safe_browsing_warning_pages.py b/testing/firefox-ui/tests/functional/security/test_safe_browsing_warning_pages.py deleted file mode 100644 index 968a9464b..000000000 --- a/testing/firefox-ui/tests/functional/security/test_safe_browsing_warning_pages.py +++ /dev/null @@ -1,115 +0,0 @@ -# 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/. - -import time - -from firefox_puppeteer import PuppeteerMixin -from marionette_driver import By, expected, Wait -from marionette_harness import MarionetteTestCase - - -class TestSafeBrowsingWarningPages(PuppeteerMixin, MarionetteTestCase): - - def setUp(self): - super(TestSafeBrowsingWarningPages, self).setUp() - - self.urls = [ - # Unwanted software URL - 'https://www.itisatrap.org/firefox/unwanted.html', - # Phishing URL - 'https://www.itisatrap.org/firefox/its-a-trap.html', - # Malware URL - 'https://www.itisatrap.org/firefox/its-an-attack.html' - ] - - self.marionette.set_pref('app.support.baseURL', - self.marionette.absolute_url("support.html?topic=")) - self.marionette.set_pref('browser.safebrowsing.phishing.enabled', True) - self.marionette.set_pref('browser.safebrowsing.malware.enabled', True) - - # Give the browser a little time, because SafeBrowsing.jsm takes a - # while between start up and adding the example urls to the db. - # hg.mozilla.org/mozilla-central/file/46aebcd9481e/browser/base/content/browser.js#l1194 - time.sleep(3) - - # TODO: Bug 1139544: While we don't have a reliable way to close the safe browsing - # notification bar when a test fails, run this test in a new tab. - self.browser.tabbar.open_tab() - - def tearDown(self): - try: - self.puppeteer.utils.permissions.remove('https://www.itisatrap.org', 'safe-browsing') - self.browser.tabbar.close_all_tabs([self.browser.tabbar.tabs[0]]) - self.marionette.clear_pref('app.support.baseURL') - self.marionette.clear_pref('browser.safebrowsing.malware.enabled') - self.marionette.clear_pref('browser.safebrowsing.phishing.enabled') - finally: - super(TestSafeBrowsingWarningPages, self).tearDown() - - def test_warning_pages(self): - with self.marionette.using_context("content"): - for unsafe_page in self.urls: - # Load a test page, then test the get me out button - self.marionette.navigate(unsafe_page) - # Wait for the DOM to receive events for about:blocked - time.sleep(1) - self.check_get_me_out_of_here_button(unsafe_page) - - # Load the test page again, then test the report button - self.marionette.navigate(unsafe_page) - # Wait for the DOM to receive events for about:blocked - time.sleep(1) - self.check_report_button(unsafe_page) - - # Load the test page again, then test the ignore warning button - self.marionette.navigate(unsafe_page) - # Wait for the DOM to receive events for about:blocked - time.sleep(1) - self.check_ignore_warning_button(unsafe_page) - - def check_get_me_out_of_here_button(self, unsafe_page): - button = self.marionette.find_element(By.ID, "getMeOutButton") - button.click() - - Wait(self.marionette, timeout=self.marionette.timeout.page_load).until( - lambda mn: self.browser.default_homepage in mn.get_url()) - - def check_report_button(self, unsafe_page): - # Get the URL of the support site for phishing and malware. This may result in a redirect. - with self.marionette.using_context('chrome'): - url = self.marionette.execute_script(""" - Components.utils.import("resource://gre/modules/Services.jsm"); - return Services.urlFormatter.formatURLPref("app.support.baseURL") - + "phishing-malware"; - """) - - button = self.marionette.find_element(By.ID, "reportButton") - button.click() - - # Wait for the button to become stale, whereby a longer timeout is needed - # here to not fail in case of slow connections. - Wait(self.marionette, timeout=self.marionette.timeout.page_load).until( - expected.element_stale(button)) - - # Wait for page load to be completed, so we can verify the URL even if a redirect happens. - # TODO: Bug 1140470: use replacement for mozmill's waitforPageLoad - expected_url = self.browser.get_final_url(url) - Wait(self.marionette, timeout=self.marionette.timeout.page_load).until( - lambda mn: expected_url == mn.get_url(), - message="The expected URL '{}' has not been loaded".format(expected_url) - ) - - topic = self.marionette.find_element(By.ID, "topic") - self.assertEquals(topic.text, "phishing-malware") - - def check_ignore_warning_button(self, unsafe_page): - button = self.marionette.find_element(By.ID, 'ignoreWarningButton') - button.click() - - Wait(self.marionette, timeout=self.marionette.timeout.page_load).until( - expected.element_present(By.ID, 'main-feature')) - self.assertEquals(self.marionette.get_url(), self.browser.get_final_url(unsafe_page)) - - # Clean up by removing safe browsing permission for unsafe page - self.puppeteer.utils.permissions.remove('https://www.itisatrap.org', 'safe-browsing') diff --git a/testing/firefox-ui/tests/functional/security/test_security_notification.py b/testing/firefox-ui/tests/functional/security/test_security_notification.py deleted file mode 100644 index 5825d0364..000000000 --- a/testing/firefox-ui/tests/functional/security/test_security_notification.py +++ /dev/null @@ -1,62 +0,0 @@ -# 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/. - -import time - -from firefox_puppeteer import PuppeteerMixin -from marionette_driver import By, Wait -from marionette_driver.errors import MarionetteException -from marionette_harness import MarionetteTestCase - - -class TestSecurityNotification(PuppeteerMixin, MarionetteTestCase): - - def setUp(self): - super(TestSecurityNotification, self).setUp() - - self.urls = [ - # Invalid cert page - 'https://ssl-expired.mozqa.com', - # Secure page - 'https://ssl-ev.mozqa.com/', - # Insecure page - 'http://no-ssl.mozqa.com' - ] - - self.identity_box = self.browser.navbar.locationbar.identity_box - - def test_invalid_cert(self): - with self.marionette.using_context('content'): - # Go to a site that has an invalid (expired) cert - self.assertRaises(MarionetteException, self.marionette.navigate, self.urls[0]) - - # Wait for the DOM to receive events - time.sleep(1) - - # Verify the text in Technical Content contains the page with invalid cert - text = self.marionette.find_element(By.ID, 'badCertTechnicalInfo') - self.assertIn(self.urls[0][8:], text.get_property('textContent')) - - # Verify the "Go Back" and "Advanced" buttons appear - self.assertIsNotNone(self.marionette.find_element(By.ID, 'returnButton')) - self.assertIsNotNone(self.marionette.find_element(By.ID, 'advancedButton')) - - # Verify the error code is correct - self.assertIn('SEC_ERROR_EXPIRED_CERTIFICATE', text.get_property('textContent')) - - def test_secure_website(self): - with self.marionette.using_context('content'): - self.marionette.navigate(self.urls[1]) - - Wait(self.marionette).until(lambda _: ( - self.identity_box.get_property('className') == 'verifiedIdentity') - ) - - def test_insecure_website(self): - with self.marionette.using_context('content'): - self.marionette.navigate(self.urls[2]) - - Wait(self.marionette).until(lambda _: ( - self.identity_box.get_property('className') == 'unknownIdentity') - ) diff --git a/testing/firefox-ui/tests/functional/security/test_ssl_disabled_error_page.py b/testing/firefox-ui/tests/functional/security/test_ssl_disabled_error_page.py deleted file mode 100644 index d1d9c531f..000000000 --- a/testing/firefox-ui/tests/functional/security/test_ssl_disabled_error_page.py +++ /dev/null @@ -1,60 +0,0 @@ -# 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/. - -import time - -from firefox_puppeteer import PuppeteerMixin -from marionette_driver import By, expected, Wait -from marionette_driver.errors import MarionetteException -from marionette_harness import MarionetteTestCase - - -class TestSSLDisabledErrorPage(PuppeteerMixin, MarionetteTestCase): - - def setUp(self): - super(TestSSLDisabledErrorPage, self).setUp() - - self.url = 'https://tlsv1-0.mozqa.com' - - self.puppeteer.utils.sanitize({"sessions": True}) - - # Disable SSL 3.0, TLS 1.0 and TLS 1.1 for secure connections - # by forcing the use of TLS 1.2 - # see: http://kb.mozillazine.org/Security.tls.version.*#Possible_values_and_their_effects - self.marionette.set_pref('security.tls.version.min', 3) - self.marionette.set_pref('security.tls.version.max', 3) - - def tearDown(self): - try: - self.marionette.clear_pref('security.tls.version.min') - self.marionette.clear_pref('security.tls.version.max') - finally: - super(TestSSLDisabledErrorPage, self).tearDown() - - def test_ssl_disabled_error_page(self): - with self.marionette.using_context('content'): - # Open the test page - self.assertRaises(MarionetteException, self.marionette.navigate, self.url) - - # Wait for the DOM to receive events - time.sleep(1) - - # Verify "Secure Connection Failed" error page title - title = self.marionette.find_element(By.CLASS_NAME, 'title-text') - nss_failure2title = self.browser.localize_entity('nssFailure2.title') - self.assertEquals(title.get_property('textContent'), nss_failure2title) - - # Verify the error message is correct - short_description = self.marionette.find_element(By.ID, 'errorShortDescText') - self.assertIn('SSL_ERROR_UNSUPPORTED_VERSION', - short_description.get_property('textContent')) - self.assertIn('mozqa.com', short_description.get_property('textContent')) - - # Verify that the "Restore" button appears and works - reset_button = self.marionette.find_element(By.ID, 'prefResetButton') - reset_button.click() - - # With the preferences reset, the page has to load correctly - Wait(self.marionette, timeout=self.marionette.timeout.page_load).until( - expected.element_present(By.LINK_TEXT, 'http://quality.mozilla.org')) diff --git a/testing/firefox-ui/tests/functional/security/test_ssl_status_after_restart.py b/testing/firefox-ui/tests/functional/security/test_ssl_status_after_restart.py deleted file mode 100644 index f274d8f2f..000000000 --- a/testing/firefox-ui/tests/functional/security/test_ssl_status_after_restart.py +++ /dev/null @@ -1,124 +0,0 @@ -# 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 firefox_puppeteer import PuppeteerMixin -from marionette_driver import Wait -from marionette_harness import MarionetteTestCase, skip_if_e10s - - -class TestSSLStatusAfterRestart(PuppeteerMixin, MarionetteTestCase): - - def setUp(self): - super(TestSSLStatusAfterRestart, self).setUp() - - self.test_data = ( - { - 'url': 'https://ssl-dv.mozqa.com', - 'identity': '', - 'type': 'secure' - }, - { - 'url': 'https://ssl-ev.mozqa.com/', - 'identity': 'Mozilla Corporation', - 'type': 'secure-ev' - }, - { - 'url': 'https://ssl-ov.mozqa.com/', - 'identity': '', - 'type': 'secure' - } - ) - - # Set browser to restore previous session - self.marionette.set_pref('browser.startup.page', 3) - - self.locationbar = self.browser.navbar.locationbar - self.identity_popup = self.locationbar.identity_popup - - def tearDown(self): - try: - self.puppeteer.windows.close_all([self.browser]) - self.browser.tabbar.close_all_tabs([self.browser.tabbar.tabs[0]]) - self.browser.switch_to() - self.identity_popup.close(force=True) - self.marionette.clear_pref('browser.startup.page') - finally: - super(TestSSLStatusAfterRestart, self).tearDown() - - @skip_if_e10s("Bug 1325047") - def test_ssl_status_after_restart(self): - for item in self.test_data: - with self.marionette.using_context('content'): - self.marionette.navigate(item['url']) - self.verify_certificate_status(item) - self.browser.tabbar.open_tab() - - self.restart() - - # Refresh references to elements - self.locationbar = self.browser.navbar.locationbar - self.identity_popup = self.locationbar.identity_popup - - for index, item in enumerate(self.test_data): - self.browser.tabbar.tabs[index].select() - self.verify_certificate_status(item) - - def verify_certificate_status(self, item): - url, identity, cert_type = item['url'], item['identity'], item['type'] - - # Check the favicon - # TODO: find a better way to check, e.g., mozmill's isDisplayed - favicon_hidden = self.marionette.execute_script(""" - return arguments[0].hasAttribute("hidden"); - """, script_args=[self.browser.navbar.locationbar.identity_icon]) - self.assertFalse(favicon_hidden) - - self.locationbar.open_identity_popup() - - # Check the type shown on the identity popup doorhanger - self.assertEqual(self.identity_popup.element.get_attribute('connection'), - cert_type) - - self.identity_popup.view.main.expander.click() - Wait(self.marionette).until(lambda _: self.identity_popup.view.security.selected) - - # Check the identity label - self.assertEqual(self.locationbar.identity_organization_label.get_property('value'), - identity) - - # Get the information from the certificate - cert = self.browser.tabbar.selected_tab.certificate - - # Open the Page Info window by clicking the More Information button - page_info = self.browser.open_page_info_window( - lambda _: self.identity_popup.view.security.more_info_button.click()) - - # Verify that the current panel is the security panel - self.assertEqual(page_info.deck.selected_panel, page_info.deck.security) - - # Verify the domain listed on the security panel - # If this is a wildcard cert, check only the domain - if cert['commonName'].startswith('*'): - self.assertIn(self.puppeteer.security.get_domain_from_common_name(cert['commonName']), - page_info.deck.security.domain.get_property('value'), - 'Expected domain found in certificate for ' + url) - else: - self.assertEqual(page_info.deck.security.domain.get_property('value'), - cert['commonName'], - 'Domain value matches certificate common name.') - - # Verify the owner listed on the security panel - if identity != '': - owner = cert['organization'] - else: - owner = page_info.localize_property('securityNoOwner') - - self.assertEqual(page_info.deck.security.owner.get_property('value'), owner, - 'Expected owner label found for ' + url) - - # Verify the verifier listed on the security panel - self.assertEqual(page_info.deck.security.verifier.get_property('value'), - cert['issuerOrganization'], - 'Verifier matches issuer of certificate for ' + url) - page_info.close() diff --git a/testing/firefox-ui/tests/functional/security/test_submit_unencrypted_info_warning.py b/testing/firefox-ui/tests/functional/security/test_submit_unencrypted_info_warning.py deleted file mode 100644 index a2f431fb5..000000000 --- a/testing/firefox-ui/tests/functional/security/test_submit_unencrypted_info_warning.py +++ /dev/null @@ -1,65 +0,0 @@ -# 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 firefox_puppeteer import PuppeteerMixin -from marionette_driver import By, expected, Wait -from marionette_driver.errors import NoAlertPresentException -from marionette_driver.marionette import Alert -from marionette_harness import MarionetteTestCase - - -class TestSubmitUnencryptedInfoWarning(PuppeteerMixin, MarionetteTestCase): - - def setUp(self): - super(TestSubmitUnencryptedInfoWarning, self).setUp() - - self.url = 'https://ssl-dv.mozqa.com/data/firefox/security/unencryptedsearch.html' - self.test_string = 'mozilla' - - self.marionette.set_pref('security.warn_submit_insecure', True) - - def tearDown(self): - try: - self.marionette.clear_pref('security.warn_submit_insecure') - finally: - super(TestSubmitUnencryptedInfoWarning, self).tearDown() - - def test_submit_unencrypted_info_warning(self): - with self.marionette.using_context('content'): - self.marionette.navigate(self.url) - - # Get the page's search box and submit button. - searchbox = self.marionette.find_element(By.ID, 'q') - button = self.marionette.find_element(By.ID, 'submit') - - # Use the page's search box to submit information. - searchbox.send_keys(self.test_string) - button.click() - - # Get the expected warning text and replace its two instances of "##" with "\n\n". - message = self.browser.localize_property('formPostSecureToInsecureWarning.message') - message = message.replace('##', '\n\n') - - # Wait for the warning, verify the expected text matches warning, accept the warning - warning = Alert(self.marionette) - try: - Wait(self.marionette, - ignored_exceptions=NoAlertPresentException, - timeout=self.marionette.timeout.page_load).until( - lambda _: warning.text == message) - finally: - warning.accept() - - # Wait for the search box to become stale, then wait for the page to be reloaded. - Wait(self.marionette).until(expected.element_stale(searchbox)) - - # TODO: Bug 1140470: use replacement for mozmill's waitforPageLoad - Wait(self.marionette, timeout=self.marionette.timeout.page_load).until( - lambda mn: mn.execute_script('return document.readyState == "DOMContentLoaded" ||' - ' document.readyState == "complete";') - ) - - # Check that search_term contains the test string. - search_term = self.marionette.find_element(By.ID, 'search-term') - self.assertEqual(search_term.get_property('textContent'), self.test_string) diff --git a/testing/firefox-ui/tests/functional/security/test_unknown_issuer.py b/testing/firefox-ui/tests/functional/security/test_unknown_issuer.py deleted file mode 100644 index b329f2500..000000000 --- a/testing/firefox-ui/tests/functional/security/test_unknown_issuer.py +++ /dev/null @@ -1,34 +0,0 @@ -# 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/. - -import time - -from marionette_driver import By -from marionette_driver.errors import MarionetteException -from marionette_harness import MarionetteTestCase - - -class TestUnknownIssuer(MarionetteTestCase): - - def setUp(self): - super(TestUnknownIssuer, self).setUp() - - self.url = 'https://ssl-unknownissuer.mozqa.com' - - def test_unknown_issuer(self): - with self.marionette.using_context('content'): - # Go to a site that has a cert with an unknown issuer - self.assertRaises(MarionetteException, self.marionette.navigate, self.url) - - # Wait for the DOM to receive events - time.sleep(1) - - # Check for the correct error code - error = self.marionette.find_element(By.ID, 'errorCode') - self.assertEquals(error.get_property('textContent'), - 'SEC_ERROR_UNKNOWN_ISSUER') - - # Verify the "Go Back" and "Advanced" buttons appear - self.assertIsNotNone(self.marionette.find_element(By.ID, 'returnButton')) - self.assertIsNotNone(self.marionette.find_element(By.ID, 'advancedButton')) diff --git a/testing/firefox-ui/tests/functional/security/test_untrusted_connection_error_page.py b/testing/firefox-ui/tests/functional/security/test_untrusted_connection_error_page.py deleted file mode 100644 index 0dbce1c8f..000000000 --- a/testing/firefox-ui/tests/functional/security/test_untrusted_connection_error_page.py +++ /dev/null @@ -1,35 +0,0 @@ -# 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/. - -import time - -from firefox_puppeteer import PuppeteerMixin -from marionette_driver import By, Wait -from marionette_driver.errors import MarionetteException -from marionette_harness import MarionetteTestCase - - -class TestUntrustedConnectionErrorPage(PuppeteerMixin, MarionetteTestCase): - - def setUp(self): - super(TestUntrustedConnectionErrorPage, self).setUp() - - self.url = 'https://ssl-selfsigned.mozqa.com' - - def test_untrusted_connection_error_page(self): - self.marionette.set_context('content') - - # In some localized builds, the default page redirects - target_url = self.browser.get_final_url(self.browser.default_homepage) - - self.assertRaises(MarionetteException, self.marionette.navigate, self.url) - - # Wait for the DOM to receive events - time.sleep(1) - - button = self.marionette.find_element(By.ID, "returnButton") - button.click() - - Wait(self.marionette, timeout=self.marionette.timeout.page_load).until( - lambda mn: target_url == self.marionette.get_url()) diff --git a/testing/firefox-ui/tests/functional/sessionstore/manifest.ini b/testing/firefox-ui/tests/functional/sessionstore/manifest.ini deleted file mode 100644 index c2d0a02b8..000000000 --- a/testing/firefox-ui/tests/functional/sessionstore/manifest.ini +++ /dev/null @@ -1,5 +0,0 @@ -[DEFAULT] -tags = local - -[test_restore_windows_after_restart.py] -skip-if = (os == "win" || e10s) # Bug 1291844 and Bug 1228446 diff --git a/testing/firefox-ui/tests/functional/sessionstore/test_restore_windows_after_restart.py b/testing/firefox-ui/tests/functional/sessionstore/test_restore_windows_after_restart.py deleted file mode 100644 index cc7de728b..000000000 --- a/testing/firefox-ui/tests/functional/sessionstore/test_restore_windows_after_restart.py +++ /dev/null @@ -1,150 +0,0 @@ -# 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 firefox_puppeteer import PuppeteerMixin -from marionette_harness import MarionetteTestCase - - -class TestRestoreWindowsAfterRestart(PuppeteerMixin, MarionetteTestCase): - - def setUp(self): - super(TestRestoreWindowsAfterRestart, self).setUp() - - # Each list element represents a window of tabs loaded at - # some testing URL - self.test_windows = set([ - # Window 1. Note the comma after the absolute_url call - - # this is Python's way of declaring a 1 item tuple. - (self.marionette.absolute_url('layout/mozilla.html'), ), - - # Window 2 - (self.marionette.absolute_url('layout/mozilla_organizations.html'), - self.marionette.absolute_url('layout/mozilla_community.html')), - - # Window 3 - (self.marionette.absolute_url('layout/mozilla_governance.html'), - self.marionette.absolute_url('layout/mozilla_grants.html')), - ]) - - self.private_windows = set([ - (self.marionette.absolute_url('layout/mozilla_mission.html'), - self.marionette.absolute_url('layout/mozilla_organizations.html')), - - (self.marionette.absolute_url('layout/mozilla_projects.html'), - self.marionette.absolute_url('layout/mozilla_mission.html')), - ]) - - self.marionette.enforce_gecko_prefs({ - # Set browser to restore previous session - 'browser.startup.page': 3, - # Make the content load right away instead of waiting for - # the user to click on the background tabs - 'browser.sessionstore.restore_on_demand': False, - # Avoid race conditions by having the content process never - # send us session updates unless the parent has explicitly asked - # for them via the TabStateFlusher. - 'browser.sessionstore.debug.no_auto_updates': True, - }) - - def tearDown(self): - try: - # Create a fresh profile for subsequent tests. - self.restart(clean=True) - finally: - super(TestRestoreWindowsAfterRestart, self).tearDown() - - def test_with_variety(self): - """ Opens a set of windows, both standard and private, with - some number of tabs in them. Once the tabs have loaded, restarts - the browser, and then ensures that the standard tabs have been - restored, and that the private ones have not. - """ - self.open_windows(self.test_windows) - self.open_windows(self.private_windows, is_private=True) - - self.restart() - - windows = self.puppeteer.windows.all - - # There's no guarantee that Marionette will return us an - # iterator for the opened windows that will match the - # order within our window list. Instead, we'll convert - # the list of URLs within each open window to a set of - # tuples that will allow us to do a direct comparison - # while allowing the windows to be in any order. - opened_windows = set() - for win in windows: - urls = tuple() - for tab in win.tabbar.tabs: - urls = urls + tuple([tab.location]) - opened_windows.add(urls) - - self.assertEqual(opened_windows, self.test_windows) - - def open_windows(self, window_sets, is_private=False): - """ Opens a set of windows with tabs pointing at some - URLs. - - @param window_sets (list) - A set of URL tuples. Each tuple within window_sets - represents a window, and each URL in the URL - tuples represents what will be loaded in a tab. - - Note that if is_private is False, then the first - URL tuple will be opened in the current window, and - subequent tuples will be opened in new windows. - - Example: - - set( - (self.marionette.absolute_url('layout/mozilla_1.html'), - self.marionette.absolute_url('layout/mozilla_2.html')), - - (self.marionette.absolute_url('layout/mozilla_3.html'), - self.marionette.absolute_url('layout/mozilla_4.html')), - ) - - This would take the currently open window, and load - mozilla_1.html and mozilla_2.html in new tabs. It would - then open a new, second window, and load tabs at - mozilla_3.html and mozilla_4.html. - @param is_private (boolean, optional) - Whether or not any new windows should be a private browsing - windows. - """ - - if (is_private): - win = self.browser.open_browser(is_private=True) - win.switch_to() - else: - win = self.browser - - for index, urls in enumerate(window_sets): - if index > 0: - win = self.browser.open_browser(is_private=is_private) - win.switch_to() - self.open_tabs(win, urls) - - def open_tabs(self, win, urls): - """ Opens a set of URLs inside a window in new tabs. - - @param win (browser window) - The browser window to load the tabs in. - @param urls (tuple) - A tuple of URLs to load in this window. The - first URL will be loaded in the currently selected - browser tab. Subsequent URLs will be loaded in - new tabs. - """ - # If there are any remaining URLs for this window, - # open some new tabs and navigate to them. - with self.marionette.using_context('content'): - if isinstance(urls, str): - self.marionette.navigate(urls) - else: - for index, url in enumerate(urls): - if index > 0: - with self.marionette.using_context('chrome'): - win.tabbar.open_tab() - self.marionette.navigate(url) diff --git a/testing/firefox-ui/tests/puppeteer/manifest.ini b/testing/firefox-ui/tests/puppeteer/manifest.ini deleted file mode 100644 index 2eb24b6dc..000000000 --- a/testing/firefox-ui/tests/puppeteer/manifest.ini +++ /dev/null @@ -1,24 +0,0 @@ -[DEFAULT] -tags = local - -# API tests -[test_appinfo.py] -[test_l10n.py] -[test_places.py] -[test_security.py] -tags = remote -[test_software_update.py] -tags = remote -[test_utils.py] - -# UI tests -[test_about_window.py] -[test_menubar.py] -[test_notifications.py] -[test_page_info_window.py] -[test_tabbar.py] -[test_toolbars.py] -tags = remote -[test_update_wizard.py] -tags = remote -[test_windows.py] diff --git a/testing/firefox-ui/tests/puppeteer/test_about_window.py b/testing/firefox-ui/tests/puppeteer/test_about_window.py deleted file mode 100644 index c957211bb..000000000 --- a/testing/firefox-ui/tests/puppeteer/test_about_window.py +++ /dev/null @@ -1,74 +0,0 @@ -# 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 firefox_puppeteer import PuppeteerMixin -from firefox_puppeteer.ui.deck import Panel -from marionette_harness import MarionetteTestCase - - -class TestAboutWindow(PuppeteerMixin, MarionetteTestCase): - - def setUp(self): - super(TestAboutWindow, self).setUp() - - self.about_window = self.browser.open_about_window() - self.deck = self.about_window.deck - - def tearDown(self): - try: - self.puppeteer.windows.close_all([self.browser]) - finally: - super(TestAboutWindow, self).tearDown() - - def test_basic(self): - self.assertEqual(self.about_window.window_type, 'Browser:About') - - def test_elements(self): - """Test correct retrieval of elements.""" - self.assertNotEqual(self.about_window.dtds, []) - - self.assertEqual(self.deck.element.get_property('localName'), 'deck') - - # apply panel - panel = self.deck.apply - self.assertEqual(panel.element.get_property('localName'), 'hbox') - self.assertEqual(panel.button.get_property('localName'), 'button') - - # check_for_updates panel - panel = self.deck.check_for_updates - self.assertEqual(panel.element.get_property('localName'), 'hbox') - self.assertEqual(panel.button.get_property('localName'), 'button') - - # checking_for_updates panel - self.assertEqual(self.deck.checking_for_updates.element.get_property('localName'), 'hbox') - - # download_and_install panel - panel = self.deck.download_and_install - self.assertEqual(panel.element.get_property('localName'), 'hbox') - self.assertEqual(panel.button.get_property('localName'), 'button') - - # download_failed panel - self.assertEqual(self.deck.download_failed.element.get_property('localName'), 'hbox') - - # downloading panel - self.assertEqual(self.deck.downloading.element.get_property('localName'), 'hbox') - - # check deck attributes - self.assertIsInstance(self.deck.selected_index, int) - self.assertIsInstance(self.deck.selected_panel, Panel) - - def test_open_window(self): - """Test various opening strategies.""" - def opener(win): - self.browser.menubar.select_by_id('helpMenu', 'aboutName') - - open_strategies = ('menu', - opener, - ) - - self.about_window.close() - for trigger in open_strategies: - about_window = self.browser.open_about_window(trigger=trigger) - self.assertEquals(about_window, self.puppeteer.windows.current) - about_window.close() diff --git a/testing/firefox-ui/tests/puppeteer/test_appinfo.py b/testing/firefox-ui/tests/puppeteer/test_appinfo.py deleted file mode 100644 index f0be0f616..000000000 --- a/testing/firefox-ui/tests/puppeteer/test_appinfo.py +++ /dev/null @@ -1,31 +0,0 @@ -# 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/. - -import mozversion - -from firefox_puppeteer import PuppeteerMixin -from marionette_harness import MarionetteTestCase - - -class TestAppInfo(PuppeteerMixin, MarionetteTestCase): - - def test_valid_properties(self): - binary = self.marionette.bin - version_info = mozversion.get_version(binary=binary) - - self.assertEqual(self.puppeteer.appinfo.ID, version_info['application_id']) - self.assertEqual(self.puppeteer.appinfo.name, version_info['application_name']) - self.assertEqual(self.puppeteer.appinfo.vendor, version_info['application_vendor']) - self.assertEqual(self.puppeteer.appinfo.version, version_info['application_version']) - # Bug 1298328 - Platform buildid mismatch due to incremental builds - # self.assertEqual(self.puppeteer.appinfo.platformBuildID, - # version_info['platform_buildid']) - self.assertEqual(self.puppeteer.appinfo.platformVersion, version_info['platform_version']) - self.assertIsNotNone(self.puppeteer.appinfo.locale) - self.assertIsNotNone(self.puppeteer.appinfo.user_agent) - self.assertIsNotNone(self.puppeteer.appinfo.XPCOMABI) - - def test_invalid_properties(self): - with self.assertRaises(AttributeError): - self.puppeteer.appinfo.unknown diff --git a/testing/firefox-ui/tests/puppeteer/test_l10n.py b/testing/firefox-ui/tests/puppeteer/test_l10n.py deleted file mode 100644 index 08f41f9d7..000000000 --- a/testing/firefox-ui/tests/puppeteer/test_l10n.py +++ /dev/null @@ -1,51 +0,0 @@ -# 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 firefox_puppeteer import PuppeteerMixin -from firefox_puppeteer.api.l10n import L10n -from marionette_driver import By -from marionette_driver.errors import NoSuchElementException -from marionette_harness import MarionetteTestCase - - -class TestL10n(PuppeteerMixin, MarionetteTestCase): - - def setUp(self): - super(TestL10n, self).setUp() - - self.l10n = L10n(self.marionette) - - def test_dtd_entity_chrome(self): - dtds = ['chrome://global/locale/about.dtd', - 'chrome://browser/locale/baseMenuOverlay.dtd'] - - value = self.l10n.localize_entity(dtds, 'helpSafeMode.label') - elm = self.marionette.find_element(By.ID, 'helpSafeMode') - self.assertEqual(value, elm.get_attribute('label')) - - self.assertRaises(NoSuchElementException, - self.l10n.localize_entity, dtds, 'notExistent') - - def test_dtd_entity_content(self): - dtds = ['chrome://global/locale/about.dtd', - 'chrome://global/locale/aboutSupport.dtd'] - - value = self.l10n.localize_entity(dtds, 'aboutSupport.pageTitle') - - self.marionette.set_context(self.marionette.CONTEXT_CONTENT) - self.marionette.navigate('about:support') - - elm = self.marionette.find_element(By.TAG_NAME, 'title') - self.assertEqual(value, elm.text) - - def test_properties(self): - properties = ['chrome://global/locale/filepicker.properties', - 'chrome://global/locale/findbar.properties'] - - # TODO: Find a way to verify the retrieved translated string - value = self.l10n.localize_property(properties, 'NotFound') - self.assertNotEqual(value, '') - - self.assertRaises(NoSuchElementException, - self.l10n.localize_property, properties, 'notExistent') diff --git a/testing/firefox-ui/tests/puppeteer/test_menubar.py b/testing/firefox-ui/tests/puppeteer/test_menubar.py deleted file mode 100644 index ddc6117bf..000000000 --- a/testing/firefox-ui/tests/puppeteer/test_menubar.py +++ /dev/null @@ -1,30 +0,0 @@ -# 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 firefox_puppeteer import PuppeteerMixin -from marionette_driver.errors import NoSuchElementException -from marionette_harness import MarionetteTestCase - - -class TestMenuBar(PuppeteerMixin, MarionetteTestCase): - - def setUp(self): - super(TestMenuBar, self).setUp() - - def test_click_item_in_menubar(self): - def opener(_): - self.browser.menubar.select_by_id('file-menu', - 'menu_newNavigatorTab') - - self.browser.tabbar.open_tab(trigger=opener) - - self.browser.tabbar.tabs[-1].close() - - def test_click_non_existent_menu_and_item(self): - with self.assertRaises(NoSuchElementException): - self.browser.menubar.select_by_id('foobar-menu', - 'menu_newNavigatorTab') - - with self.assertRaises(NoSuchElementException): - self.browser.menubar.select_by_id('file-menu', 'menu_foobar') diff --git a/testing/firefox-ui/tests/puppeteer/test_notifications.py b/testing/firefox-ui/tests/puppeteer/test_notifications.py deleted file mode 100644 index de44c7434..000000000 --- a/testing/firefox-ui/tests/puppeteer/test_notifications.py +++ /dev/null @@ -1,82 +0,0 @@ -# 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 firefox_puppeteer import PuppeteerMixin -from firefox_puppeteer.ui.browser.notifications import ( - AddOnInstallFailedNotification, - AddOnInstallConfirmationNotification, -) -from marionette_driver import By -from marionette_driver.errors import TimeoutException -from marionette_harness import MarionetteTestCase - - -class TestNotifications(PuppeteerMixin, MarionetteTestCase): - - def setUp(self): - super(TestNotifications, self).setUp() - - self.marionette.set_pref('extensions.install.requireSecureOrigin', False) - - self.addons_url = self.marionette.absolute_url('addons/extensions/') - self.puppeteer.utils.permissions.add(self.marionette.baseurl, 'install') - - def tearDown(self): - try: - self.marionette.clear_pref('extensions.install.requireSecureOrigin') - self.marionette.clear_pref('xpinstall.signatures.required') - - self.puppeteer.utils.permissions.remove(self.addons_url, 'install') - - if self.browser.notification: - self.browser.notification.close(force=True) - finally: - super(TestNotifications, self).tearDown() - - def test_open_close_notification(self): - """Trigger and dismiss a notification""" - self.assertIsNone(self.browser.notification) - self.trigger_addon_notification('restartless_addon_signed.xpi') - self.browser.notification.close() - self.assertIsNone(self.browser.notification) - - def test_wait_for_notification_timeout(self): - """Wait for a notification when one is not shown""" - message = 'No notification was shown' - with self.assertRaisesRegexp(TimeoutException, message): - self.browser.wait_for_notification() - - def test_wait_for_specific_notification_timeout(self): - """Wait for a notification when one is not shown""" - message = 'AddOnInstallFailedNotification was not shown' - with self.assertRaisesRegexp(TimeoutException, message): - self.browser.wait_for_notification(AddOnInstallFailedNotification) - - def test_wait_for_no_notification_timeout(self): - """Wait for no notification when one is shown""" - message = 'Unexpected notification shown' - self.trigger_addon_notification('restartless_addon_signed.xpi') - with self.assertRaisesRegexp(TimeoutException, message): - self.browser.wait_for_notification(None) - - def test_notification_with_origin(self): - """Trigger a notification with an origin""" - self.trigger_addon_notification('restartless_addon_signed.xpi') - self.assertIn(self.browser.notification.origin, self.marionette.baseurl) - self.assertIsNotNone(self.browser.notification.label) - - def test_addon_install_failed_notification(self): - """Trigger add-on blocked notification using an unsigned add-on""" - # Ensure that installing unsigned extensions will fail - self.marionette.set_pref('xpinstall.signatures.required', True) - - self.trigger_addon_notification( - 'restartless_addon_unsigned.xpi', - notification=AddOnInstallFailedNotification) - - def trigger_addon_notification(self, addon, notification=AddOnInstallConfirmationNotification): - with self.marionette.using_context('content'): - self.marionette.navigate(self.addons_url) - self.marionette.find_element(By.LINK_TEXT, addon).click() - self.browser.wait_for_notification(notification) diff --git a/testing/firefox-ui/tests/puppeteer/test_page_info_window.py b/testing/firefox-ui/tests/puppeteer/test_page_info_window.py deleted file mode 100644 index d86cbee3c..000000000 --- a/testing/firefox-ui/tests/puppeteer/test_page_info_window.py +++ /dev/null @@ -1,100 +0,0 @@ -# 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 firefox_puppeteer import PuppeteerMixin -from marionette_harness import MarionetteTestCase - - -class TestPageInfoWindow(PuppeteerMixin, MarionetteTestCase): - - def tearDown(self): - try: - self.puppeteer.windows.close_all([self.browser]) - finally: - super(TestPageInfoWindow, self).tearDown() - - def test_elements(self): - """Test correct retrieval of elements.""" - page_info = self.browser.open_page_info_window() - - self.assertNotEqual(page_info.dtds, []) - self.assertNotEqual(page_info.properties, []) - - self.assertEqual(page_info.deck.element.get_property('localName'), 'deck') - - # feed panel - self.assertEqual(page_info.deck.feed.element.get_property('localName'), 'vbox') - - # general panel - self.assertEqual(page_info.deck.general.element.get_property('localName'), 'vbox') - - # media panel - self.assertEqual(page_info.deck.media.element.get_property('localName'), 'vbox') - - # permissions panel - self.assertEqual(page_info.deck.permissions.element.get_property('localName'), 'vbox') - - # security panel - panel = page_info.deck.select(page_info.deck.security) - - self.assertEqual(panel.element.get_property('localName'), 'vbox') - - self.assertEqual(panel.domain.get_property('localName'), 'textbox') - self.assertEqual(panel.owner.get_property('localName'), 'textbox') - self.assertEqual(panel.verifier.get_property('localName'), 'textbox') - - self.assertEqual(panel.view_certificate.get_property('localName'), 'button') - self.assertEqual(panel.view_cookies.get_property('localName'), 'button') - self.assertEqual(panel.view_passwords.get_property('localName'), 'button') - - def test_select(self): - """Test properties and methods for switching between panels.""" - page_info = self.browser.open_page_info_window() - deck = page_info.deck - - self.assertEqual(deck.selected_panel, deck.general) - - self.assertEqual(deck.select(deck.security), deck.security) - self.assertEqual(deck.selected_panel, deck.security) - - def test_open_window(self): - """Test various opening strategies.""" - def opener(win): - self.browser.menubar.select_by_id('tools-menu', 'menu_pageInfo') - - open_strategies = ('menu', - 'shortcut', - opener, - ) - - for trigger in open_strategies: - if trigger == 'shortcut' and self.puppeteer.platform == 'windows_nt': - # The shortcut for page info window does not exist on windows. - self.assertRaises(ValueError, self.browser.open_page_info_window, - trigger=trigger) - continue - - page_info = self.browser.open_page_info_window(trigger=trigger) - self.assertEquals(page_info, self.puppeteer.windows.current) - page_info.close() - - def test_close_window(self): - """Test various closing strategies.""" - def closer(win): - win.send_shortcut(win.localize_entity('closeWindow.key'), - accel=True) - - # Close a tab by each trigger method - close_strategies = ('menu', - 'shortcut', - closer, - ) - for trigger in close_strategies: - # menu only works on OS X - if trigger == 'menu' and self.puppeteer.platform != 'darwin': - continue - - page_info = self.browser.open_page_info_window() - page_info.close(trigger=trigger) - self.assertTrue(page_info.closed) diff --git a/testing/firefox-ui/tests/puppeteer/test_places.py b/testing/firefox-ui/tests/puppeteer/test_places.py deleted file mode 100644 index 95d0f23a4..000000000 --- a/testing/firefox-ui/tests/puppeteer/test_places.py +++ /dev/null @@ -1,85 +0,0 @@ -# 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 firefox_puppeteer import PuppeteerMixin -from marionette_driver import By, Wait -from marionette_harness import MarionetteTestCase - - -class TestPlaces(PuppeteerMixin, MarionetteTestCase): - - def setUp(self): - super(TestPlaces, self).setUp() - - self.urls = [self.marionette.absolute_url('layout/mozilla_governance.html'), - self.marionette.absolute_url('layout/mozilla_grants.html'), - ] - - def tearDown(self): - try: - self.puppeteer.places.restore_default_bookmarks() - self.puppeteer.places.remove_all_history() - finally: - super(TestPlaces, self).tearDown() - - def get_all_urls_in_history(self): - return self.marionette.execute_script(""" - let hs = Components.classes["@mozilla.org/browser/nav-history-service;1"] - .getService(Components.interfaces.nsINavHistoryService); - let urls = []; - - let options = hs.getNewQueryOptions(); - options.resultType = options.RESULTS_AS_URI; - - let root = hs.executeQuery(hs.getNewQuery(), options).root - root.containerOpen = true; - for (let i = 0; i < root.childCount; i++) { - urls.push(root.getChild(i).uri) - } - root.containerOpen = false; - - return urls; - """) - - def test_plugins(self): - # TODO: Once we use a plugin, add a test case to verify that the data will be removed - self.puppeteer.places.clear_plugin_data() - - def test_bookmarks(self): - star_button = self.marionette.find_element(By.ID, 'bookmarks-menu-button') - - # Visit URLs and bookmark them all - for url in self.urls: - with self.marionette.using_context('content'): - self.marionette.navigate(url) - - Wait(self.marionette).until( - lambda _: self.puppeteer.places.is_bookmark_star_button_ready()) - star_button.click() - Wait(self.marionette).until(lambda _: self.puppeteer.places.is_bookmarked(url)) - - ids = self.puppeteer.places.get_folder_ids_for_url(url) - self.assertEqual(len(ids), 1) - self.assertEqual(ids[0], self.puppeteer.places.bookmark_folders.unfiled) - - # Restore default bookmarks, so the added URLs are gone - self.puppeteer.places.restore_default_bookmarks() - for url in self.urls: - self.assertFalse(self.puppeteer.places.is_bookmarked(url)) - - def test_history(self): - self.assertEqual(len(self.get_all_urls_in_history()), 0) - - # Visit pages and check that they are all present - def load_urls(): - with self.marionette.using_context('content'): - for url in self.urls: - self.marionette.navigate(url) - self.puppeteer.places.wait_for_visited(self.urls, load_urls) - - self.assertEqual(self.get_all_urls_in_history(), self.urls) - - # Check that both pages are no longer in the remove_all_history - self.puppeteer.places.remove_all_history() - self.assertEqual(len(self.get_all_urls_in_history()), 0) diff --git a/testing/firefox-ui/tests/puppeteer/test_security.py b/testing/firefox-ui/tests/puppeteer/test_security.py deleted file mode 100644 index 879053e5a..000000000 --- a/testing/firefox-ui/tests/puppeteer/test_security.py +++ /dev/null @@ -1,45 +0,0 @@ -# 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 firefox_puppeteer import PuppeteerMixin -from firefox_puppeteer.errors import NoCertificateError -from marionette_harness import MarionetteTestCase - - -class TestSecurity(PuppeteerMixin, MarionetteTestCase): - - def test_get_address_from_certificate(self): - url = 'https://ssl-ev.mozqa.com' - - with self.marionette.using_context(self.marionette.CONTEXT_CONTENT): - self.marionette.navigate(url) - - cert = self.browser.tabbar.tabs[0].certificate - self.assertIn(cert['commonName'], url) - self.assertEqual(cert['organization'], 'Mozilla Corporation') - self.assertEqual(cert['issuerOrganization'], 'DigiCert Inc') - - address = self.puppeteer.security.get_address_from_certificate(cert) - self.assertIsNotNone(address) - self.assertIsNotNone(address['city']) - self.assertIsNotNone(address['country']) - self.assertIsNotNone(address['postal_code']) - self.assertIsNotNone(address['state']) - self.assertIsNotNone(address['street']) - - def test_get_certificate(self): - url_http = self.marionette.absolute_url('layout/mozilla.html') - url_https = 'https://ssl-ev.mozqa.com' - - # Test EV certificate - with self.marionette.using_context(self.marionette.CONTEXT_CONTENT): - self.marionette.navigate(url_https) - cert = self.browser.tabbar.tabs[0].certificate - self.assertIn(cert['commonName'], url_https) - - # HTTP connections do not have a SSL certificate - with self.marionette.using_context(self.marionette.CONTEXT_CONTENT): - self.marionette.navigate(url_http) - with self.assertRaises(NoCertificateError): - self.browser.tabbar.tabs[0].certificate diff --git a/testing/firefox-ui/tests/puppeteer/test_software_update.py b/testing/firefox-ui/tests/puppeteer/test_software_update.py deleted file mode 100644 index 4bad47d94..000000000 --- a/testing/firefox-ui/tests/puppeteer/test_software_update.py +++ /dev/null @@ -1,134 +0,0 @@ -# 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/. - -import os - -from firefox_puppeteer import PuppeteerMixin -from firefox_puppeteer.api.software_update import SoftwareUpdate -from marionette_harness import MarionetteTestCase - - -class TestSoftwareUpdate(PuppeteerMixin, MarionetteTestCase): - - def setUp(self): - super(TestSoftwareUpdate, self).setUp() - - self.software_update = SoftwareUpdate(self.marionette) - - self.saved_mar_channels = self.software_update.mar_channels.channels - self.software_update.mar_channels.channels = set(['expected', 'channels']) - - def tearDown(self): - try: - self.software_update.mar_channels.channels = self.saved_mar_channels - finally: - super(TestSoftwareUpdate, self).tearDown() - - def test_abi(self): - self.assertTrue(self.software_update.ABI) - - def test_allowed(self): - self.assertTrue(self.software_update.allowed) - - def test_build_info(self): - build_info = self.software_update.build_info - self.assertEqual(build_info['disabled_addons'], None) - self.assertIn('Mozilla/', build_info['user_agent']) - self.assertEqual(build_info['mar_channels'], set(['expected', 'channels'])) - self.assertTrue(build_info['version']) - self.assertTrue(build_info['buildid'].isdigit()) - self.assertTrue(build_info['locale']) - self.assertIn('force=1', build_info['update_url']) - self.assertIn('xml', build_info['update_snippet']) - self.assertEqual(build_info['channel'], self.software_update.update_channel) - - def test_force_fallback(self): - status_file = os.path.join(self.software_update.staging_directory, 'update.status') - - try: - self.software_update.force_fallback() - with open(status_file, 'r') as f: - content = f.read() - self.assertEqual(content, 'failed: 6\n') - finally: - os.remove(status_file) - - def test_get_update_url(self): - update_url = self.software_update.get_update_url() - self.assertIn('Firefox', update_url) - self.assertNotIn('force=1', update_url) - update_url = self.software_update.get_update_url(True) - self.assertIn('Firefox', update_url) - self.assertIn('force=1', update_url) - - def test_os_version(self): - self.assertTrue(self.software_update.os_version) - - def test_staging_directory(self): - self.assertTrue(self.software_update.staging_directory) - - -class TestUpdateChannel(PuppeteerMixin, MarionetteTestCase): - - def setUp(self): - super(TestUpdateChannel, self).setUp() - - self.software_update = SoftwareUpdate(self.marionette) - - self.saved_channel = self.software_update.update_channel - self.software_update.update_channel = 'expected_channel' - - def tearDown(self): - try: - self.software_update.update_channel = self.saved_channel - finally: - super(TestUpdateChannel, self).tearDown() - - def test_update_channel_default_channel(self): - # Without a restart the update channel will not change. - self.assertEqual(self.software_update.update_channel, self.saved_channel) - - def test_update_channel_set_channel(self): - try: - # Use the clean option to force a non in_app restart, which would allow - # Firefox to dump the logs to the console. - self.restart(clean=True) - self.assertEqual(self.software_update.update_channel, 'expected_channel') - finally: - self.software_update.update_channel = self.saved_channel - self.restart(clean=True) - - -class TestMARChannels(PuppeteerMixin, MarionetteTestCase): - - def setUp(self): - super(TestMARChannels, self).setUp() - - self.software_update = SoftwareUpdate(self.marionette) - - self.saved_mar_channels = self.software_update.mar_channels.channels - self.software_update.mar_channels.channels = set(['expected', 'channels']) - - def tearDown(self): - try: - self.software_update.mar_channels.channels = self.saved_mar_channels - finally: - super(TestMARChannels, self).tearDown() - - def test_mar_channels_channels(self): - self.assertEqual(self.software_update.mar_channels.channels, set(['expected', 'channels'])) - - def test_mar_channels_set_channels(self): - self.software_update.mar_channels.channels = set(['a', 'b', 'c']) - self.assertEqual(self.software_update.mar_channels.channels, set(['a', 'b', 'c'])) - - def test_mar_channels_add_channels(self): - self.software_update.mar_channels.add_channels(set(['some', 'new', 'channels'])) - self.assertEqual( - self.software_update.mar_channels.channels, - set(['expected', 'channels', 'some', 'new'])) - - def test_mar_channels_remove_channels(self): - self.software_update.mar_channels.remove_channels(set(['expected'])) - self.assertEqual(self.software_update.mar_channels.channels, set(['channels'])) diff --git a/testing/firefox-ui/tests/puppeteer/test_tabbar.py b/testing/firefox-ui/tests/puppeteer/test_tabbar.py deleted file mode 100644 index 7da3f7ee7..000000000 --- a/testing/firefox-ui/tests/puppeteer/test_tabbar.py +++ /dev/null @@ -1,191 +0,0 @@ -# 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 firefox_puppeteer import PuppeteerMixin -from firefox_puppeteer.errors import NoCertificateError -from marionette_harness import MarionetteTestCase - - -class TestTabBar(PuppeteerMixin, MarionetteTestCase): - - def tearDown(self): - try: - self.browser.tabbar.close_all_tabs([self.browser.tabbar.tabs[0]]) - finally: - super(TestTabBar, self).tearDown() - - def test_basics(self): - tabbar = self.browser.tabbar - - self.assertEqual(tabbar.window, self.browser) - - self.assertEqual(len(tabbar.tabs), 1) - self.assertEqual(tabbar.tabs[0].handle, self.marionette.current_window_handle) - - self.assertEqual(tabbar.newtab_button.get_property('localName'), 'toolbarbutton') - self.assertEqual(tabbar.toolbar.get_property('localName'), 'tabs') - - def test_open_close(self): - tabbar = self.browser.tabbar - - self.assertEqual(len(tabbar.tabs), 1) - self.assertEqual(tabbar.selected_index, 0) - - # Open with default trigger, and force closing the tab - tabbar.open_tab() - tabbar.close_tab(force=True) - - # Open a new tab by each trigger method - open_strategies = ('button', - 'menu', - 'shortcut', - lambda tab: tabbar.newtab_button.click() - ) - for trigger in open_strategies: - new_tab = tabbar.open_tab(trigger=trigger) - self.assertEqual(len(tabbar.tabs), 2) - self.assertEqual(new_tab.handle, self.marionette.current_window_handle) - self.assertEqual(new_tab.handle, tabbar.tabs[1].handle) - - tabbar.close_tab() - self.assertEqual(len(tabbar.tabs), 1) - self.assertEqual(tabbar.tabs[0].handle, self.marionette.current_window_handle) - self.assertNotEqual(new_tab.handle, tabbar.tabs[0].handle) - - # Close a tab by each trigger method - close_strategies = ('button', - 'menu', - 'shortcut', - lambda tab: tab.close_button.click()) - for trigger in close_strategies: - new_tab = tabbar.open_tab() - self.assertEqual(len(tabbar.tabs), 2) - self.assertEqual(new_tab.handle, self.marionette.current_window_handle) - self.assertEqual(new_tab.handle, tabbar.tabs[1].handle) - - tabbar.close_tab(trigger=trigger) - self.assertEqual(len(tabbar.tabs), 1) - self.assertEqual(tabbar.tabs[0].handle, self.marionette.current_window_handle) - self.assertNotEqual(new_tab.handle, tabbar.tabs[0].handle) - - def test_close_not_selected_tab(self): - tabbar = self.browser.tabbar - - new_tab = tabbar.open_tab() - tabbar.close_tab(tabbar.tabs[0], trigger="button") - - self.assertEqual(len(tabbar.tabs), 1) - self.assertEqual(new_tab, tabbar.tabs[0]) - - def test_close_all_tabs_except_first(self): - tabbar = self.browser.tabbar - - orig_tab = tabbar.tabs[0] - - for i in range(0, 3): - tabbar.open_tab() - - tabbar.close_all_tabs([orig_tab]) - self.assertEqual(len(tabbar.tabs), 1) - self.assertEqual(orig_tab.handle, self.marionette.current_window_handle) - - def test_switch_to(self): - tabbar = self.browser.tabbar - - # Open a new tab in the foreground (will be auto-selected) - new_tab = tabbar.open_tab() - self.assertEqual(new_tab.handle, self.marionette.current_window_handle) - self.assertEqual(tabbar.selected_index, 1) - self.assertEqual(tabbar.selected_tab, new_tab) - - # Switch by index - tabbar.switch_to(0) - self.assertEqual(tabbar.tabs[0].handle, self.marionette.current_window_handle) - - # Switch by tab - tabbar.switch_to(new_tab) - self.assertEqual(new_tab.handle, self.marionette.current_window_handle) - - # Switch by callback - tabbar.switch_to(lambda tab: tab.window.tabbar.selected_tab != tab) - self.assertEqual(tabbar.tabs[0].handle, self.marionette.current_window_handle) - - tabbar.close_tab(tabbar.tabs[1]) - - -class TestTab(PuppeteerMixin, MarionetteTestCase): - - def tearDown(self): - try: - self.browser.tabbar.close_all_tabs([self.browser.tabbar.tabs[0]]) - finally: - super(TestTab, self).tearDown() - - def test_basic(self): - tab = self.browser.tabbar.tabs[0] - - self.assertEqual(tab.window, self.browser) - - self.assertEqual(tab.tab_element.get_property('localName'), 'tab') - self.assertEqual(tab.close_button.get_property('localName'), 'toolbarbutton') - - def test_certificate(self): - url = self.marionette.absolute_url('layout/mozilla.html') - - with self.marionette.using_context(self.marionette.CONTEXT_CONTENT): - self.marionette.navigate(url) - with self.assertRaises(NoCertificateError): - self.browser.tabbar.tabs[0].certificate - - def test_close(self): - tabbar = self.browser.tabbar - - self.assertEqual(len(tabbar.tabs), 1) - self.assertEqual(tabbar.selected_index, 0) - - # Force closing the tab - new_tab = tabbar.open_tab() - new_tab.close(force=True) - - # Close a tab by each trigger method - close_strategies = ('button', - 'menu', - 'shortcut', - lambda tab: tab.close_button.click()) - for trigger in close_strategies: - new_tab = tabbar.open_tab() - self.assertEqual(len(tabbar.tabs), 2) - self.assertEqual(new_tab.handle, self.marionette.current_window_handle) - self.assertEqual(new_tab.handle, tabbar.tabs[1].handle) - - new_tab.close(trigger=trigger) - self.assertEqual(len(tabbar.tabs), 1) - self.assertEqual(tabbar.tabs[0].handle, self.marionette.current_window_handle) - self.assertNotEqual(new_tab.handle, tabbar.tabs[0].handle) - - def test_location(self): - url = self.marionette.absolute_url('layout/mozilla.html') - with self.marionette.using_context(self.marionette.CONTEXT_CONTENT): - self.marionette.navigate(url) - self.assertEqual(self.browser.tabbar.tabs[0].location, url) - - def test_switch_to(self): - tabbar = self.browser.tabbar - - new_tab = tabbar.open_tab() - - # Switch to the first tab, which will not select it - tabbar.tabs[0].switch_to() - self.assertEqual(tabbar.tabs[0].handle, self.marionette.current_window_handle) - # Bug 1128656: We cannot test as long as switch_to_window() auto-selects the tab - # self.assertEqual(tabbar.selected_index, 1) - # self.assertEqual(tabbar.selected_tab, new_tab) - - # Now select the first tab - tabbar.tabs[0].select() - self.assertEqual(tabbar.tabs[0].handle, self.marionette.current_window_handle) - self.assertTrue(tabbar.tabs[0].selected) - self.assertFalse(tabbar.tabs[1].selected) - - new_tab.close() diff --git a/testing/firefox-ui/tests/puppeteer/test_toolbars.py b/testing/firefox-ui/tests/puppeteer/test_toolbars.py deleted file mode 100644 index 8150be7e1..000000000 --- a/testing/firefox-ui/tests/puppeteer/test_toolbars.py +++ /dev/null @@ -1,283 +0,0 @@ -# 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 firefox_puppeteer import PuppeteerMixin -from marionette_driver import expected, By, Wait -from marionette_driver.errors import NoSuchElementException -from marionette_harness import MarionetteTestCase - - -class TestNavBar(PuppeteerMixin, MarionetteTestCase): - - def setUp(self): - super(TestNavBar, self).setUp() - - self.navbar = self.browser.navbar - self.url = self.marionette.absolute_url('layout/mozilla.html') - - with self.marionette.using_context('content'): - self.marionette.navigate('about:blank') - - # TODO: check why self.puppeteer.places.remove_all_history() does not work here - self.marionette.execute_script(""" - let count = gBrowser.sessionHistory.count; - gBrowser.sessionHistory.PurgeHistory(count); - """) - - def test_elements(self): - self.assertEqual(self.navbar.back_button.get_property('localName'), 'toolbarbutton') - self.assertEqual(self.navbar.forward_button.get_property('localName'), 'toolbarbutton') - self.assertEqual(self.navbar.home_button.get_property('localName'), 'toolbarbutton') - self.assertEqual(self.navbar.menu_button.get_property('localName'), 'toolbarbutton') - self.assertEqual(self.navbar.toolbar.get_property('localName'), 'toolbar') - - def test_buttons(self): - self.marionette.set_context('content') - - # Load initial web page - self.marionette.navigate(self.url) - Wait(self.marionette).until(expected.element_present(lambda m: - m.find_element(By.ID, 'mozilla_logo'))) - - with self.marionette.using_context('chrome'): - # Both buttons are disabled - self.assertFalse(self.navbar.back_button.is_enabled()) - self.assertFalse(self.navbar.forward_button.is_enabled()) - - # Go to the homepage - self.navbar.home_button.click() - - Wait(self.marionette).until(expected.element_not_present(lambda m: - m.find_element(By.ID, 'mozilla_logo'))) - self.assertEqual(self.marionette.get_url(), self.browser.default_homepage) - - with self.marionette.using_context('chrome'): - # Only back button is enabled - self.assertTrue(self.navbar.back_button.is_enabled()) - self.assertFalse(self.navbar.forward_button.is_enabled()) - - # Navigate back - self.navbar.back_button.click() - - Wait(self.marionette).until(expected.element_present(lambda m: - m.find_element(By.ID, 'mozilla_logo'))) - self.assertEqual(self.marionette.get_url(), self.url) - - with self.marionette.using_context('chrome'): - # Only forward button is enabled - self.assertFalse(self.navbar.back_button.is_enabled()) - self.assertTrue(self.navbar.forward_button.is_enabled()) - - # Navigate forward - self.navbar.forward_button.click() - - Wait(self.marionette).until(expected.element_not_present(lambda m: - m.find_element(By.ID, 'mozilla_logo'))) - self.assertEqual(self.marionette.get_url(), self.browser.default_homepage) - - -class TestLocationBar(PuppeteerMixin, MarionetteTestCase): - - def setUp(self): - super(TestLocationBar, self).setUp() - - self.locationbar = self.browser.navbar.locationbar - - def test_elements(self): - self.assertEqual(self.locationbar.urlbar.get_property('localName'), 'textbox') - self.assertIn('urlbar-input', self.locationbar.urlbar_input.get_property('className')) - - self.assertEqual(self.locationbar.connection_icon.get_property('localName'), 'image') - self.assertEqual(self.locationbar.identity_box.get_property('localName'), 'box') - self.assertEqual(self.locationbar.identity_country_label.get_property('localName'), - 'label') - self.assertEqual(self.locationbar.identity_organization_label.get_property('localName'), - 'label') - self.assertEqual(self.locationbar.identity_icon.get_property('localName'), 'image') - self.assertEqual(self.locationbar.history_drop_marker.get_property('localName'), - 'dropmarker') - self.assertEqual(self.locationbar.reload_button.get_property('localName'), - 'toolbarbutton') - self.assertEqual(self.locationbar.stop_button.get_property('localName'), - 'toolbarbutton') - - self.assertEqual(self.locationbar.contextmenu.get_property('localName'), 'menupopup') - self.assertEqual(self.locationbar.get_contextmenu_entry('paste').get_attribute('cmd'), - 'cmd_paste') - - def test_reload(self): - event_types = ["shortcut", "shortcut2", "button"] - for event in event_types: - # TODO: Until we have waitForPageLoad, this only tests API - # compatibility. - self.locationbar.reload_url(event, force=True) - self.locationbar.reload_url(event, force=False) - - def test_focus_and_clear(self): - self.locationbar.urlbar.send_keys("zyx") - self.locationbar.clear() - self.assertEqual(self.locationbar.value, '') - - self.locationbar.urlbar.send_keys("zyx") - self.assertEqual(self.locationbar.value, 'zyx') - - self.locationbar.clear() - self.assertEqual(self.locationbar.value, '') - - def test_load_url(self): - data_uri = 'data:text/html,<title>Title</title>' - self.locationbar.load_url(data_uri) - - with self.marionette.using_context('content'): - Wait(self.marionette).until(lambda mn: mn.get_url() == data_uri) - - -class TestAutoCompleteResults(PuppeteerMixin, MarionetteTestCase): - - def setUp(self): - super(TestAutoCompleteResults, self).setUp() - - self.browser.navbar.locationbar.clear() - - self.autocomplete_results = self.browser.navbar.locationbar.autocomplete_results - - def tearDown(self): - try: - self.autocomplete_results.close(force=True) - except NoSuchElementException: - # TODO: A NoSuchElementException is thrown here when tests accessing the - # autocomplete_results element are skipped. - pass - finally: - super(TestAutoCompleteResults, self).tearDown() - - def test_popup_elements(self): - # TODO: This test is not very robust because it relies on the history - # in the default profile. - self.assertFalse(self.autocomplete_results.is_open) - self.browser.navbar.locationbar.urlbar.send_keys('a') - results = self.autocomplete_results.results - Wait(self.marionette).until(lambda _: self.autocomplete_results.is_complete) - visible_result_count = len(self.autocomplete_results.visible_results) - self.assertTrue(visible_result_count > 0) - self.assertEqual(visible_result_count, - int(results.get_property('itemCount'))) - - def test_close(self): - self.browser.navbar.locationbar.urlbar.send_keys('a') - Wait(self.marionette).until(lambda _: self.autocomplete_results.is_open) - # The Wait in the library implementation will fail this if this doesn't - # end up closing. - self.autocomplete_results.close() - - def test_force_close(self): - self.browser.navbar.locationbar.urlbar.send_keys('a') - Wait(self.marionette).until(lambda _: self.autocomplete_results.is_open) - # The Wait in the library implementation will fail this if this doesn't - # end up closing. - self.autocomplete_results.close(force=True) - - def test_matching_text(self): - # The default profile always has existing bookmarks. So no sites have to - # be visited and bookmarked. - for input_text in ('about', 'zilla'): - self.browser.navbar.locationbar.urlbar.clear() - self.browser.navbar.locationbar.urlbar.send_keys(input_text) - Wait(self.marionette).until(lambda _: self.autocomplete_results.is_open) - Wait(self.marionette).until(lambda _: self.autocomplete_results.is_complete) - visible_results = self.autocomplete_results.visible_results - self.assertTrue(len(visible_results) > 0) - - for result in visible_results: - # check matching text only for results of type bookmark - if result.get_attribute('type') != 'bookmark': - continue - title_matches = self.autocomplete_results.get_matching_text(result, "title") - url_matches = self.autocomplete_results.get_matching_text(result, "url") - all_matches = title_matches + url_matches - self.assertTrue(len(all_matches) > 0) - for match_fragment in all_matches: - self.assertIn(match_fragment.lower(), input_text) - - self.autocomplete_results.close() - - -class TestIdentityPopup(PuppeteerMixin, MarionetteTestCase): - def setUp(self): - super(TestIdentityPopup, self).setUp() - - self.locationbar = self.browser.navbar.locationbar - self.identity_popup = self.locationbar.identity_popup - - self.url = 'https://ssl-ev.mozqa.com' - - with self.marionette.using_context('content'): - self.marionette.navigate(self.url) - - def tearDown(self): - try: - self.identity_popup.close(force=True) - finally: - super(TestIdentityPopup, self).tearDown() - - def test_elements(self): - self.locationbar.open_identity_popup() - - # Test main view elements - main = self.identity_popup.view.main - self.assertEqual(main.element.get_property('localName'), 'panelview') - - self.assertEqual(main.expander.get_property('localName'), 'button') - self.assertEqual(main.host.get_property('localName'), 'label') - self.assertEqual(main.insecure_connection_label.get_property('localName'), - 'description') - self.assertEqual(main.internal_connection_label.get_property('localName'), - 'description') - self.assertEqual(main.secure_connection_label.get_property('localName'), - 'description') - - self.assertEqual(main.permissions.get_property('localName'), 'vbox') - - # Test security view elements - security = self.identity_popup.view.security - self.assertEqual(security.element.get_property('localName'), 'panelview') - - self.assertEqual(security.host.get_property('localName'), 'label') - self.assertEqual(security.insecure_connection_label.get_property('localName'), - 'description') - self.assertEqual(security.secure_connection_label.get_property('localName'), - 'description') - - self.assertEqual(security.owner.get_property('localName'), 'description') - self.assertEqual(security.owner_location.get_property('localName'), 'description') - self.assertEqual(security.verifier.get_property('localName'), 'description') - - self.assertEqual(security.disable_mixed_content_blocking_button.get_property('localName'), - 'button') - self.assertEqual(security.enable_mixed_content_blocking_button.get_property('localName'), - 'button') - - self.assertEqual(security.more_info_button.get_property('localName'), 'button') - - def test_open_close(self): - with self.marionette.using_context('content'): - self.marionette.navigate(self.url) - - self.assertFalse(self.identity_popup.is_open) - - self.locationbar.open_identity_popup() - - self.identity_popup.close() - self.assertFalse(self.identity_popup.is_open) - - def test_force_close(self): - with self.marionette.using_context('content'): - self.marionette.navigate(self.url) - - self.assertFalse(self.identity_popup.is_open) - - self.locationbar.open_identity_popup() - - self.identity_popup.close(force=True) - self.assertFalse(self.identity_popup.is_open) diff --git a/testing/firefox-ui/tests/puppeteer/test_update_wizard.py b/testing/firefox-ui/tests/puppeteer/test_update_wizard.py deleted file mode 100644 index 7170ea8e2..000000000 --- a/testing/firefox-ui/tests/puppeteer/test_update_wizard.py +++ /dev/null @@ -1,67 +0,0 @@ -# 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 firefox_puppeteer import PuppeteerMixin -from firefox_puppeteer.ui.deck import Panel -from firefox_puppeteer.ui.update_wizard import UpdateWizardDialog -from marionette_harness import MarionetteTestCase - - -class TestUpdateWizard(PuppeteerMixin, MarionetteTestCase): - - def setUp(self): - super(TestUpdateWizard, self).setUp() - - def opener(win): - self.marionette.execute_script(""" - let updatePrompt = Components.classes["@mozilla.org/updates/update-prompt;1"] - .createInstance(Components.interfaces.nsIUpdatePrompt); - updatePrompt.checkForUpdates(); - """) - - self.dialog = self.browser.open_window(callback=opener, - expected_window_class=UpdateWizardDialog) - self.wizard = self.dialog.wizard - - def tearDown(self): - try: - self.puppeteer.windows.close_all([self.browser]) - finally: - super(TestUpdateWizard, self).tearDown() - - def test_basic(self): - self.assertEqual(self.dialog.window_type, 'Update:Wizard') - self.assertNotEqual(self.dialog.dtds, []) - self.assertNotEqual(self.dialog.properties, []) - - def test_elements(self): - """Test correct retrieval of elements.""" - self.assertEqual(self.wizard.element.get_property('localName'), 'wizard') - - buttons = ('cancel_button', 'extra1_button', 'extra2_button', - 'finish_button', 'next_button', 'previous_button', - ) - for button in buttons: - self.assertEqual(getattr(self.wizard, button).get_property('localName'), - 'button') - - panels = ('checking', 'downloading', 'dummy', 'error_patching', 'error', - 'error_extra', 'finished', 'finished_background', - 'manual_update', 'no_updates_found', 'updates_found_basic', - ) - for panel in panels: - self.assertEqual(getattr(self.wizard, panel).element.get_property('localName'), - 'wizardpage') - - # elements of the checking panel - self.assertEqual(self.wizard.checking.progress.get_property('localName'), - 'progressmeter') - - # elements of the downloading panel - self.assertEqual(self.wizard.downloading.progress.get_property('localName'), - 'progressmeter') - - # check wizard attributes - self.assertIsInstance(self.wizard.selected_index, int) - self.assertIsInstance(self.wizard.selected_panel, Panel) diff --git a/testing/firefox-ui/tests/puppeteer/test_utils.py b/testing/firefox-ui/tests/puppeteer/test_utils.py deleted file mode 100644 index 664722cce..000000000 --- a/testing/firefox-ui/tests/puppeteer/test_utils.py +++ /dev/null @@ -1,48 +0,0 @@ -# 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 firefox_puppeteer import PuppeteerMixin -from marionette_harness import MarionetteTestCase - - -class TestSanitize(PuppeteerMixin, MarionetteTestCase): - - def setUp(self): - super(TestSanitize, self).setUp() - - # Clear all previous history and cookies. - self.puppeteer.places.remove_all_history() - with self.marionette.using_context('content'): - self.marionette.delete_all_cookies() - - self.urls = [ - 'layout/mozilla_projects.html', - 'layout/mozilla.html', - 'layout/mozilla_mission.html', - 'cookies/cookie_single.html' - ] - self.urls = [self.marionette.absolute_url(url) for url in self.urls] - - # Open the test urls, including the single cookie setting page. - def load_urls(): - with self.marionette.using_context('content'): - for url in self.urls: - self.marionette.navigate(url) - self.puppeteer.places.wait_for_visited(self.urls, load_urls) - - def test_sanitize_history(self): - """ Clears history. """ - self.assertEqual(self.puppeteer.places.get_all_urls_in_history(), self.urls) - self.puppeteer.utils.sanitize(data_type={"history": True}) - self.assertEqual(self.puppeteer.places.get_all_urls_in_history(), []) - - def test_sanitize_cookies(self): - """ Clears cookies. """ - with self.marionette.using_context('content'): - self.assertIsNotNone(self.marionette.get_cookie('litmus_1')) - - self.puppeteer.utils.sanitize(data_type={"cookies": True}) - - with self.marionette.using_context('content'): - self.assertIsNone(self.marionette.get_cookie('litmus_1')) diff --git a/testing/firefox-ui/tests/puppeteer/test_windows.py b/testing/firefox-ui/tests/puppeteer/test_windows.py deleted file mode 100644 index 1ba13fa37..000000000 --- a/testing/firefox-ui/tests/puppeteer/test_windows.py +++ /dev/null @@ -1,259 +0,0 @@ -# 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/. - -import firefox_puppeteer.errors as errors - -from firefox_puppeteer import PuppeteerMixin -from firefox_puppeteer.ui.windows import BaseWindow -from marionette_driver import By, Wait -from marionette_driver.errors import NoSuchWindowException -from marionette_harness import MarionetteTestCase - - -class BaseWindowTestCase(PuppeteerMixin, MarionetteTestCase): - - def setUp(self): - """ - These tests open and close windows pretty rapidly, which - (since bug 1261842) can cause content processes to be - spawned and discarded in large numbers. By default, Firefox - has a 5 second timeout for shutting down content processes, - but we can get into cases where the content process just - doesn't have enough time to get itself all sorted before - the timeout gets hit, which results in the parent killing - the content process manually, which generates a crash report, - which causes these tests to orange. We side-step this by - setting dom.ipc.tabs.shutdownTimeoutSecs to 0, which disables - the shutdown timer. - """ - super(BaseWindowTestCase, self).setUp() - - self.marionette.set_pref('dom.ipc.tabs.shutdownTimeoutSecs', 0) - - def tearDown(self): - try: - self.marionette.clear_pref('dom.ipc.tabs.shutdownTimeoutSecs') - finally: - super(BaseWindowTestCase, self).tearDown() - - -class TestWindows(BaseWindowTestCase): - - def tearDown(self): - try: - self.puppeteer.windows.close_all([self.browser]) - finally: - super(TestWindows, self).tearDown() - - def test_switch_to(self): - url = self.marionette.absolute_url('layout/mozilla.html') - - # Open two more windows - for index in range(0, 2): - self.marionette.execute_script(""" window.open(); """) - - windows = self.puppeteer.windows.all - self.assertEquals(len(windows), 3) - - # Switch to the 2nd window - self.puppeteer.windows.switch_to(windows[1].handle) - self.assertEquals(windows[1].handle, self.marionette.current_chrome_window_handle) - - # TODO: Needs updated tabs module for improved navigation - with self.marionette.using_context('content'): - self.marionette.navigate(url) - - # Switch to the last window and find 2nd window by URL - self.puppeteer.windows.switch_to(windows[2].handle) - - # TODO: A window can have multiple tabs, so this may need an update - # when the tabs module gets implemented - def find_by_url(win): - with win.marionette.using_context('content'): - return win.marionette.get_url() == url - - self.puppeteer.windows.switch_to(find_by_url) - self.assertEquals(windows[1].handle, self.marionette.current_chrome_window_handle) - - self.puppeteer.windows.switch_to(find_by_url) - - # Switching to an unknown handles has to fail - self.assertRaises(NoSuchWindowException, - self.puppeteer.windows.switch_to, "humbug") - self.assertRaises(NoSuchWindowException, - self.puppeteer.windows.switch_to, lambda win: False) - - self.puppeteer.windows.close_all([self.browser]) - self.browser.switch_to() - - self.assertEqual(len(self.puppeteer.windows.all), 1) - - def test_switch_to_unknown_window_type(self): - def open_by_js(_): - with self.marionette.using_context('chrome'): - self.marionette.execute_script(""" - window.open('chrome://browser/content/safeMode.xul', '_blank', - 'chrome,centerscreen,resizable=no'); - """) - - win = self.browser.open_window(callback=open_by_js, expected_window_class=BaseWindow) - win.close() - self.browser.switch_to() - - -class TestBaseWindow(BaseWindowTestCase): - - def tearDown(self): - try: - self.puppeteer.windows.close_all([self.browser]) - finally: - BaseWindowTestCase.tearDown(self) - - def test_basics(self): - # force BaseWindow instance - win1 = BaseWindow(self.marionette, self.browser.handle) - - self.assertEquals(win1.handle, self.marionette.current_chrome_window_handle) - self.assertEquals(win1.window_element, - self.marionette.find_element(By.CSS_SELECTOR, ':root')) - self.assertEquals(win1.window_element.get_attribute('windowtype'), - self.marionette.get_window_type()) - self.assertFalse(win1.closed) - - # Test invalid parameters for BaseWindow constructor - self.assertRaises(errors.UnknownWindowError, - BaseWindow, self.marionette, 10) - - # Test invalid shortcuts - self.assertRaises(KeyError, - win1.send_shortcut, 'l', acel=True) - - def test_open_close(self): - # force BaseWindow instance - win1 = BaseWindow(self.marionette, self.browser.handle) - - # Open a new window (will be focused), and check states - win2 = win1.open_window() - - # force BaseWindow instance - win2 = BaseWindow(self.marionette, win2.handle) - - self.assertEquals(len(self.marionette.chrome_window_handles), 2) - self.assertNotEquals(win1.handle, win2.handle) - self.assertEquals(win2.handle, self.marionette.current_chrome_window_handle) - - win2.close() - - self.assertTrue(win2.closed) - self.assertEquals(len(self.marionette.chrome_window_handles), 1) - self.assertEquals(win2.handle, self.marionette.current_chrome_window_handle) - Wait(self.marionette).until(lambda _: win1.focused) # catch the no focused window - - win1.focus() - - # Open and close a new window by a custom callback - def opener(window): - window.marionette.execute_script(""" window.open(); """) - - def closer(window): - window.marionette.execute_script(""" window.close(); """) - - win2 = win1.open_window(callback=opener) - - # force BaseWindow instance - win2 = BaseWindow(self.marionette, win2.handle) - - self.assertEquals(len(self.marionette.chrome_window_handles), 2) - win2.close(callback=closer) - - win1.focus() - - # Check for an unexpected window class - self.assertRaises(errors.UnexpectedWindowTypeError, - win1.open_window, expected_window_class=BaseWindow) - self.puppeteer.windows.close_all([win1]) - - def test_switch_to_and_focus(self): - # force BaseWindow instance - win1 = BaseWindow(self.marionette, self.browser.handle) - - # Open a new window (will be focused), and check states - win2 = win1.open_window() - - # force BaseWindow instance - win2 = BaseWindow(self.marionette, win2.handle) - - self.assertEquals(win2.handle, self.marionette.current_chrome_window_handle) - self.assertEquals(win2.handle, self.puppeteer.windows.focused_chrome_window_handle) - self.assertFalse(win1.focused) - self.assertTrue(win2.focused) - - # Switch back to win1 without moving the focus, but focus separately - win1.switch_to() - self.assertEquals(win1.handle, self.marionette.current_chrome_window_handle) - self.assertTrue(win2.focused) - - win1.focus() - self.assertTrue(win1.focused) - - # Switch back to win2 by focusing it directly - win2.focus() - self.assertEquals(win2.handle, self.marionette.current_chrome_window_handle) - self.assertEquals(win2.handle, self.puppeteer.windows.focused_chrome_window_handle) - self.assertTrue(win2.focused) - - # Close win2, and check that it keeps active but looses focus - win2.switch_to() - win2.close() - - win1.switch_to() - - -class TestBrowserWindow(BaseWindowTestCase): - - def tearDown(self): - try: - self.puppeteer.windows.close_all([self.browser]) - finally: - BaseWindowTestCase.tearDown(self) - - def test_basic(self): - self.assertNotEqual(self.browser.dtds, []) - self.assertNotEqual(self.browser.properties, []) - - self.assertFalse(self.browser.is_private) - - self.assertIsNotNone(self.browser.menubar) - self.assertIsNotNone(self.browser.navbar) - self.assertIsNotNone(self.browser.tabbar) - - def test_open_close(self): - # open and close a new browser windows by menu - win2 = self.browser.open_browser(trigger='menu') - self.assertEquals(win2, self.puppeteer.windows.current) - self.assertFalse(self.browser.is_private) - win2.close(trigger='menu') - - # open and close a new browser window by shortcut - win2 = self.browser.open_browser(trigger='shortcut') - self.assertEquals(win2, self.puppeteer.windows.current) - self.assertFalse(self.browser.is_private) - win2.close(trigger='shortcut') - - # open and close a new private browsing window - win2 = self.browser.open_browser(is_private=True) - self.assertEquals(win2, self.puppeteer.windows.current) - self.assertTrue(win2.is_private) - win2.close() - - # open and close a new private browsing window - win2 = self.browser.open_browser(trigger='shortcut', is_private=True) - self.assertEquals(win2, self.puppeteer.windows.current) - self.assertTrue(win2.is_private) - win2.close() - - # force closing a window - win2 = self.browser.open_browser() - self.assertEquals(win2, self.puppeteer.windows.current) - win2.close(force=True) diff --git a/testing/firefox-ui/tests/update/direct/manifest.ini b/testing/firefox-ui/tests/update/direct/manifest.ini deleted file mode 100644 index f5edb3c10..000000000 --- a/testing/firefox-ui/tests/update/direct/manifest.ini +++ /dev/null @@ -1,4 +0,0 @@ -[DEFAULT] -tags = direct - -[test_direct_update.py] diff --git a/testing/firefox-ui/tests/update/direct/test_direct_update.py b/testing/firefox-ui/tests/update/direct/test_direct_update.py deleted file mode 100644 index 41842349e..000000000 --- a/testing/firefox-ui/tests/update/direct/test_direct_update.py +++ /dev/null @@ -1,21 +0,0 @@ -# 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 firefox_ui_harness.testcases import UpdateTestCase - - -class TestDirectUpdate(UpdateTestCase): - - def setUp(self): - UpdateTestCase.setUp(self, is_fallback=False) - - def tearDown(self): - try: - self.puppeteer.windows.close_all([self.browser]) - finally: - UpdateTestCase.tearDown(self) - - def test_update(self): - self.download_and_apply_available_update(force_fallback=False) - self.check_update_applied() diff --git a/testing/firefox-ui/tests/update/fallback/manifest.ini b/testing/firefox-ui/tests/update/fallback/manifest.ini deleted file mode 100644 index 704686db7..000000000 --- a/testing/firefox-ui/tests/update/fallback/manifest.ini +++ /dev/null @@ -1,4 +0,0 @@ -[DEFAULT] -tags = fallback - -[test_fallback_update.py] diff --git a/testing/firefox-ui/tests/update/fallback/test_fallback_update.py b/testing/firefox-ui/tests/update/fallback/test_fallback_update.py deleted file mode 100644 index 264142bd8..000000000 --- a/testing/firefox-ui/tests/update/fallback/test_fallback_update.py +++ /dev/null @@ -1,22 +0,0 @@ -# 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 firefox_ui_harness.testcases import UpdateTestCase - - -class TestFallbackUpdate(UpdateTestCase): - - def setUp(self): - UpdateTestCase.setUp(self, is_fallback=True) - - def tearDown(self): - try: - self.puppeteer.windows.close_all([self.browser]) - finally: - UpdateTestCase.tearDown(self) - - def test_update(self): - self.download_and_apply_available_update(force_fallback=True) - self.download_and_apply_forced_update() - self.check_update_applied() diff --git a/testing/firefox-ui/tests/update/manifest.ini b/testing/firefox-ui/tests/update/manifest.ini deleted file mode 100644 index 2a126e331..000000000 --- a/testing/firefox-ui/tests/update/manifest.ini +++ /dev/null @@ -1,2 +0,0 @@ -[include:direct/manifest.ini] -[include:fallback/manifest.ini] diff --git a/testing/mach_commands.py b/testing/mach_commands.py index 866bc530c..a744b0c44 100644 --- a/testing/mach_commands.py +++ b/testing/mach_commands.py @@ -56,16 +56,6 @@ TEST_SUITES = { 'mach_command': 'crashtest', 'kwargs': {'test_file': None}, }, - 'firefox-ui-functional': { - 'aliases': ('Fxfn',), - 'mach_command': 'firefox-ui-functional', - 'kwargs': {}, - }, - 'firefox-ui-update': { - 'aliases': ('Fxup',), - 'mach_command': 'firefox-ui-update', - 'kwargs': {}, - }, 'jetpack': { 'aliases': ('J',), 'mach_command': 'jetpack-test', @@ -139,18 +129,6 @@ TEST_FLAVORS = { 'mach_command': 'mochitest', 'kwargs': {'flavor': 'chrome', 'test_paths': []}, }, - 'firefox-ui-functional': { - 'mach_command': 'firefox-ui-functional', - 'kwargs': {'tests': []}, - }, - 'firefox-ui-update': { - 'mach_command': 'firefox-ui-update', - 'kwargs': {'tests': []}, - }, - 'marionette': { - 'mach_command': 'marionette-test', - 'kwargs': {'tests': []}, - }, 'mochitest': { 'mach_command': 'mochitest', 'kwargs': {'flavor': 'mochitest', 'test_paths': []}, diff --git a/testing/marionette/accessibility.js b/testing/marionette/accessibility.js deleted file mode 100644 index 4ada0b88d..000000000 --- a/testing/marionette/accessibility.js +++ /dev/null @@ -1,441 +0,0 @@ -/* 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/. */ - -"use strict"; - -const {classes: Cc, interfaces: Ci, utils: Cu} = Components; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Log.jsm"); - -const logger = Log.repository.getLogger("Marionette"); - -Cu.import("chrome://marionette/content/error.js"); - -XPCOMUtils.defineLazyModuleGetter( - this, "setInterval", "resource://gre/modules/Timer.jsm"); -XPCOMUtils.defineLazyModuleGetter( - this, "clearInterval", "resource://gre/modules/Timer.jsm"); - -XPCOMUtils.defineLazyGetter(this, "service", () => { - let service; - try { - service = Cc["@mozilla.org/accessibilityService;1"].getService( - Ci.nsIAccessibilityService); - } catch (e) { - logger.warn("Accessibility module is not present"); - } finally { - return service; - } -}); - -this.EXPORTED_SYMBOLS = ["accessibility"]; - -/** - * Number of attempts to get an accessible object for an element. - * We attempt more than once because accessible tree can be out of sync - * with the DOM tree for a short period of time. - */ -const GET_ACCESSIBLE_ATTEMPTS = 100; - -/** - * An interval between attempts to retrieve an accessible object for an - * element. - */ -const GET_ACCESSIBLE_ATTEMPT_INTERVAL = 10; - -this.accessibility = { - get service() { - return service; - } -}; - -/** - * Accessible states used to check element"s state from the accessiblity API - * perspective. - * Note: if gecko is built with --disable-accessibility, the interfaces are not - * defined. This is why we use getters instead to be able to use these - * statically. - */ -accessibility.State = { - get Unavailable() { - return Ci.nsIAccessibleStates.STATE_UNAVAILABLE; - }, - get Focusable() { - return Ci.nsIAccessibleStates.STATE_FOCUSABLE; - }, - get Selectable() { - return Ci.nsIAccessibleStates.STATE_SELECTABLE; - }, - get Selected() { - return Ci.nsIAccessibleStates.STATE_SELECTED; - } -}; - -/** - * Accessible object roles that support some action. - */ -accessibility.ActionableRoles = new Set([ - "checkbutton", - "check menu item", - "check rich option", - "combobox", - "combobox option", - "entry", - "key", - "link", - "listbox option", - "listbox rich option", - "menuitem", - "option", - "outlineitem", - "pagetab", - "pushbutton", - "radiobutton", - "radio menu item", - "rowheader", - "slider", - "spinbutton", - "switch", -]); - - -/** - * Factory function that constructs a new {@code accessibility.Checks} - * object with enforced strictness or not. - */ -accessibility.get = function (strict = false) { - return new accessibility.Checks(!!strict); -}; - -/** - * Component responsible for interacting with platform accessibility - * API. - * - * Its methods serve as wrappers for testing content and chrome - * accessibility as well as accessibility of user interactions. - */ -accessibility.Checks = class { - - /** - * @param {boolean} strict - * Flag indicating whether the accessibility issue should be logged - * or cause an error to be thrown. Default is to log to stdout. - */ - constructor(strict) { - this.strict = strict; - } - - /** - * Get an accessible object for an element. - * - * @param {DOMElement|XULElement} element - * Element to get the accessible object for. - * @param {boolean=} mustHaveAccessible - * Flag indicating that the element must have an accessible object. - * Defaults to not require this. - * - * @return {Promise: nsIAccessible} - * Promise with an accessibility object for the given element. - */ - getAccessible(element, mustHaveAccessible = false) { - if (!this.strict) { - return Promise.resolve(); - } - - return new Promise((resolve, reject) => { - if (!accessibility.service) { - reject(); - return; - } - - let acc = accessibility.service.getAccessibleFor(element); - if (acc || !mustHaveAccessible) { - // if accessible object is found, return it; - // if it is not required, also resolve - resolve(acc); - } else { - // if we require an accessible object, we need to poll for it - // because accessible tree might be - // out of sync with DOM tree for a short time - let attempts = GET_ACCESSIBLE_ATTEMPTS; - let intervalId = setInterval(() => { - let acc = accessibility.service.getAccessibleFor(element); - if (acc || --attempts <= 0) { - clearInterval(intervalId); - if (acc) { - resolve(acc); - } else { - reject(); - } - } - }, GET_ACCESSIBLE_ATTEMPT_INTERVAL); - } - }).catch(() => this.error( - "Element does not have an accessible object", element)); - }; - - /** - * Test if the accessible has a role that supports some arbitrary - * action. - * - * @param {nsIAccessible} accessible - * Accessible object. - * - * @return {boolean} - * True if an actionable role is found on the accessible, false - * otherwise. - */ - isActionableRole(accessible) { - return accessibility.ActionableRoles.has( - accessibility.service.getStringRole(accessible.role)); - } - - /** - * Test if an accessible has at least one action that it supports. - * - * @param {nsIAccessible} accessible - * Accessible object. - * - * @return {boolean} - * True if the accessible has at least one supported action, - * false otherwise. - */ - hasActionCount(accessible) { - return accessible.actionCount > 0; - } - - /** - * Test if an accessible has a valid name. - * - * @param {nsIAccessible} accessible - * Accessible object. - * - * @return {boolean} - * True if the accessible has a non-empty valid name, or false if - * this is not the case. - */ - hasValidName(accessible) { - return accessible.name && accessible.name.trim(); - } - - /** - * Test if an accessible has a {@code hidden} attribute. - * - * @param {nsIAccessible} accessible - * Accessible object. - * - * @return {boolean} - * True if the accesible object has a {@code hidden} attribute, - * false otherwise. - */ - hasHiddenAttribute(accessible) { - let hidden = false; - try { - hidden = accessible.attributes.getStringProperty("hidden"); - } finally { - // if the property is missing, error will be thrown - return hidden && hidden === "true"; - } - } - - /** - * Verify if an accessible has a given state. - * Test if an accessible has a given state. - * - * @param {nsIAccessible} accessible - * Accessible object to test. - * @param {number} stateToMatch - * State to match. - * - * @return {boolean} - * True if |accessible| has |stateToMatch|, false otherwise. - */ - matchState(accessible, stateToMatch) { - let state = {}; - accessible.getState(state, {}); - return !!(state.value & stateToMatch); - } - - /** - * Test if an accessible is hidden from the user. - * - * @param {nsIAccessible} accessible - * Accessible object. - * - * @return {boolean} - * True if element is hidden from user, false otherwise. - */ - isHidden(accessible) { - while (accessible) { - if (this.hasHiddenAttribute(accessible)) { - return true; - } - accessible = accessible.parent; - } - return false; - } - - /** - * Test if the element's visible state corresponds to its accessibility - * API visibility. - * - * @param {nsIAccessible} accessible - * Accessible object. - * @param {DOMElement|XULElement} element - * Element associated with |accessible|. - * @param {boolean} visible - * Visibility state of |element|. - * - * @throws ElementNotAccessibleError - * If |element|'s visibility state does not correspond to - * |accessible|'s. - */ - assertVisible(accessible, element, visible) { - if (!accessible) { - return; - } - - let hiddenAccessibility = this.isHidden(accessible); - - let message; - if (visible && hiddenAccessibility) { - message = "Element is not currently visible via the accessibility API " + - "and may not be manipulated by it"; - } else if (!visible && !hiddenAccessibility) { - message = "Element is currently only visible via the accessibility API " + - "and can be manipulated by it"; - } - this.error(message, element); - } - - /** - * Test if the element's unavailable accessibility state matches the - * enabled state. - * - * @param {nsIAccessible} accessible - * Accessible object. - * @param {DOMElement|XULElement} element - * Element associated with |accessible|. - * @param {boolean} enabled - * Enabled state of |element|. - * - * @throws ElementNotAccessibleError - * If |element|'s enabled state does not match |accessible|'s. - */ - assertEnabled(accessible, element, enabled) { - if (!accessible) { - return; - } - - let win = element.ownerDocument.defaultView; - let disabledAccessibility = this.matchState( - accessible, accessibility.State.Unavailable); - let explorable = win.getComputedStyle(element) - .getPropertyValue("pointer-events") !== "none"; - - let message; - if (!explorable && !disabledAccessibility) { - message = "Element is enabled but is not explorable via the " + - "accessibility API"; - } else if (enabled && disabledAccessibility) { - message = "Element is enabled but disabled via the accessibility API"; - } else if (!enabled && !disabledAccessibility) { - message = "Element is disabled but enabled via the accessibility API"; - } - this.error(message, element); - } - - /** - * Test if it is possible to activate an element with the accessibility - * API. - * - * @param {nsIAccessible} accessible - * Accessible object. - * @param {DOMElement|XULElement} element - * Element associated with |accessible|. - * - * @throws ElementNotAccessibleError - * If it is impossible to activate |element| with |accessible|. - */ - assertActionable(accessible, element) { - if (!accessible) { - return; - } - - let message; - if (!this.hasActionCount(accessible)) { - message = "Element does not support any accessible actions"; - } else if (!this.isActionableRole(accessible)) { - message = "Element does not have a correct accessibility role " + - "and may not be manipulated via the accessibility API"; - } else if (!this.hasValidName(accessible)) { - message = "Element is missing an accessible name"; - } else if (!this.matchState(accessible, accessibility.State.Focusable)) { - message = "Element is not focusable via the accessibility API"; - } - - this.error(message, element); - } - - /** - * Test that an element's selected state corresponds to its - * accessibility API selected state. - * - * @param {nsIAccessible} accessible - * Accessible object. - * @param {DOMElement|XULElement} - * Element associated with |accessible|. - * @param {boolean} selected - * The |element|s selected state. - * - * @throws ElementNotAccessibleError - * If |element|'s selected state does not correspond to - * |accessible|'s. - */ - assertSelected(accessible, element, selected) { - if (!accessible) { - return; - } - - // element is not selectable via the accessibility API - if (!this.matchState(accessible, accessibility.State.Selectable)) { - return; - } - - let selectedAccessibility = this.matchState(accessible, accessibility.State.Selected); - - let message; - if (selected && !selectedAccessibility) { - message = "Element is selected but not selected via the accessibility API"; - } else if (!selected && selectedAccessibility) { - message = "Element is not selected but selected via the accessibility API"; - } - this.error(message, element); - } - - /** - * Throw an error if strict accessibility checks are enforced and log - * the error to the log. - * - * @param {string} message - * @param {DOMElement|XULElement} element - * Element that caused an error. - * - * @throws ElementNotAccessibleError - * If |strict| is true. - */ - error(message, element) { - if (!message || !this.strict) { - return; - } - if (element) { - let {id, tagName, className} = element; - message += `: id: ${id}, tagName: ${tagName}, className: ${className}`; - } - - throw new ElementNotAccessibleError(message); - } - -}; diff --git a/testing/marionette/action.js b/testing/marionette/action.js deleted file mode 100644 index 2eb39810b..000000000 --- a/testing/marionette/action.js +++ /dev/null @@ -1,1348 +0,0 @@ -/* 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/. */ - -"use strict"; - -const {classes: Cc, interfaces: Ci, utils: Cu} = Components; - -Cu.import("resource://gre/modules/Task.jsm"); - -Cu.import("chrome://marionette/content/assert.js"); -Cu.import("chrome://marionette/content/element.js"); -Cu.import("chrome://marionette/content/error.js"); -Cu.import("chrome://marionette/content/event.js"); - -this.EXPORTED_SYMBOLS = ["action"]; - -// TODO? With ES 2016 and Symbol you can make a safer approximation -// to an enum e.g. https://gist.github.com/xmlking/e86e4f15ec32b12c4689 -/** - * Implements WebDriver Actions API: a low-level interface for providing - * virtualised device input to the web browser. - */ -this.action = { - Pause: "pause", - KeyDown: "keyDown", - KeyUp: "keyUp", - PointerDown: "pointerDown", - PointerUp: "pointerUp", - PointerMove: "pointerMove", - PointerCancel: "pointerCancel", -}; - -const ACTIONS = { - none: new Set([action.Pause]), - key: new Set([action.Pause, action.KeyDown, action.KeyUp]), - pointer: new Set([ - action.Pause, - action.PointerDown, - action.PointerUp, - action.PointerMove, - action.PointerCancel, - ]), -}; - -/** Map from normalized key value to UI Events modifier key name */ -const MODIFIER_NAME_LOOKUP = { - "Alt": "alt", - "Shift": "shift", - "Control": "ctrl", - "Meta": "meta", -}; - -/** Map from raw key (codepoint) to normalized key value */ -const NORMALIZED_KEY_LOOKUP = { - "\uE000": "Unidentified", - "\uE001": "Cancel", - "\uE002": "Help", - "\uE003": "Backspace", - "\uE004": "Tab", - "\uE005": "Clear", - "\uE006": "Return", - "\uE007": "Enter", - "\uE008": "Shift", - "\uE009": "Control", - "\uE00A": "Alt", - "\uE00B": "Pause", - "\uE00C": "Escape", - "\uE00D": " ", - "\uE00E": "PageUp", - "\uE00F": "PageDown", - "\uE010": "End", - "\uE011": "Home", - "\uE012": "ArrowLeft", - "\uE013": "ArrowUp", - "\uE014": "ArrowRight", - "\uE015": "ArrowDown", - "\uE016": "Insert", - "\uE017": "Delete", - "\uE018": ";", - "\uE019": "=", - "\uE01A": "0", - "\uE01B": "1", - "\uE01C": "2", - "\uE01D": "3", - "\uE01E": "4", - "\uE01F": "5", - "\uE020": "6", - "\uE021": "7", - "\uE022": "8", - "\uE023": "9", - "\uE024": "*", - "\uE025": "+", - "\uE026": ",", - "\uE027": "-", - "\uE028": ".", - "\uE029": "/", - "\uE031": "F1", - "\uE032": "F2", - "\uE033": "F3", - "\uE034": "F4", - "\uE035": "F5", - "\uE036": "F6", - "\uE037": "F7", - "\uE038": "F8", - "\uE039": "F9", - "\uE03A": "F10", - "\uE03B": "F11", - "\uE03C": "F12", - "\uE03D": "Meta", - "\uE040": "ZenkakuHankaku", - "\uE050": "Shift", - "\uE051": "Control", - "\uE052": "Alt", - "\uE053": "Meta", - "\uE054": "PageUp", - "\uE055": "PageDown", - "\uE056": "End", - "\uE057": "Home", - "\uE058": "ArrowLeft", - "\uE059": "ArrowUp", - "\uE05A": "ArrowRight", - "\uE05B": "ArrowDown", - "\uE05C": "Insert", - "\uE05D": "Delete", -}; - -/** Map from raw key (codepoint) to key location */ -const KEY_LOCATION_LOOKUP = { - "\uE007": 1, - "\uE008": 1, - "\uE009": 1, - "\uE00A": 1, - "\uE01A": 3, - "\uE01B": 3, - "\uE01C": 3, - "\uE01D": 3, - "\uE01E": 3, - "\uE01F": 3, - "\uE020": 3, - "\uE021": 3, - "\uE022": 3, - "\uE023": 3, - "\uE024": 3, - "\uE025": 3, - "\uE026": 3, - "\uE027": 3, - "\uE028": 3, - "\uE029": 3, - "\uE03D": 1, - "\uE050": 2, - "\uE051": 2, - "\uE052": 2, - "\uE053": 2, - "\uE054": 3, - "\uE055": 3, - "\uE056": 3, - "\uE057": 3, - "\uE058": 3, - "\uE059": 3, - "\uE05A": 3, - "\uE05B": 3, - "\uE05C": 3, - "\uE05D": 3, -}; - -const KEY_CODE_LOOKUP = { - "\uE00A": "AltLeft", - "\uE052": "AltRight", - "\uE015": "ArrowDown", - "\uE012": "ArrowLeft", - "\uE014": "ArrowRight", - "\uE013": "ArrowUp", - "`": "Backquote", - "~": "Backquote", - "\\": "Backslash", - "|": "Backslash", - "\uE003": "Backspace", - "[": "BracketLeft", - "{": "BracketLeft", - "]": "BracketRight", - "}": "BracketRight", - ",": "Comma", - "<": "Comma", - "\uE009": "ControlLeft", - "\uE051": "ControlRight", - "\uE017": "Delete", - ")": "Digit0", - "0": "Digit0", - "!": "Digit1", - "1": "Digit1", - "2": "Digit2", - "@": "Digit2", - "#": "Digit3", - "3": "Digit3", - "$": "Digit4", - "4": "Digit4", - "%": "Digit5", - "5": "Digit5", - "6": "Digit6", - "^": "Digit6", - "&": "Digit7", - "7": "Digit7", - "*": "Digit8", - "8": "Digit8", - "(": "Digit9", - "9": "Digit9", - "\uE010": "End", - "\uE006": "Enter", - "+": "Equal", - "=": "Equal", - "\uE00C": "Escape", - "\uE031": "F1", - "\uE03A": "F10", - "\uE03B": "F11", - "\uE03C": "F12", - "\uE032": "F2", - "\uE033": "F3", - "\uE034": "F4", - "\uE035": "F5", - "\uE036": "F6", - "\uE037": "F7", - "\uE038": "F8", - "\uE039": "F9", - "\uE002": "Help", - "\uE011": "Home", - "\uE016": "Insert", - "<": "IntlBackslash", - ">": "IntlBackslash", - "A": "KeyA", - "a": "KeyA", - "B": "KeyB", - "b": "KeyB", - "C": "KeyC", - "c": "KeyC", - "D": "KeyD", - "d": "KeyD", - "E": "KeyE", - "e": "KeyE", - "F": "KeyF", - "f": "KeyF", - "G": "KeyG", - "g": "KeyG", - "H": "KeyH", - "h": "KeyH", - "I": "KeyI", - "i": "KeyI", - "J": "KeyJ", - "j": "KeyJ", - "K": "KeyK", - "k": "KeyK", - "L": "KeyL", - "l": "KeyL", - "M": "KeyM", - "m": "KeyM", - "N": "KeyN", - "n": "KeyN", - "O": "KeyO", - "o": "KeyO", - "P": "KeyP", - "p": "KeyP", - "Q": "KeyQ", - "q": "KeyQ", - "R": "KeyR", - "r": "KeyR", - "S": "KeyS", - "s": "KeyS", - "T": "KeyT", - "t": "KeyT", - "U": "KeyU", - "u": "KeyU", - "V": "KeyV", - "v": "KeyV", - "W": "KeyW", - "w": "KeyW", - "X": "KeyX", - "x": "KeyX", - "Y": "KeyY", - "y": "KeyY", - "Z": "KeyZ", - "z": "KeyZ", - "-": "Minus", - "_": "Minus", - "\uE01A": "Numpad0", - "\uE05C": "Numpad0", - "\uE01B": "Numpad1", - "\uE056": "Numpad1", - "\uE01C": "Numpad2", - "\uE05B": "Numpad2", - "\uE01D": "Numpad3", - "\uE055": "Numpad3", - "\uE01E": "Numpad4", - "\uE058": "Numpad4", - "\uE01F": "Numpad5", - "\uE020": "Numpad6", - "\uE05A": "Numpad6", - "\uE021": "Numpad7", - "\uE057": "Numpad7", - "\uE022": "Numpad8", - "\uE059": "Numpad8", - "\uE023": "Numpad9", - "\uE054": "Numpad9", - "\uE024": "NumpadAdd", - "\uE026": "NumpadComma", - "\uE028": "NumpadDecimal", - "\uE05D": "NumpadDecimal", - "\uE029": "NumpadDivide", - "\uE007": "NumpadEnter", - "\uE024": "NumpadMultiply", - "\uE026": "NumpadSubtract", - "\uE03D": "OSLeft", - "\uE053": "OSRight", - "\uE01E": "PageDown", - "\uE01F": "PageUp", - ".": "Period", - ">": "Period", - "\"": "Quote", - "'": "Quote", - ":": "Semicolon", - ";": "Semicolon", - "\uE008": "ShiftLeft", - "\uE050": "ShiftRight", - "/": "Slash", - "?": "Slash", - "\uE00D": "Space", - " ": "Space", - "\uE004": "Tab", -}; - -/** Represents possible values for a pointer-move origin. */ -action.PointerOrigin = { - Viewport: "viewport", - Pointer: "pointer", -}; - -/** - * Look up a PointerOrigin. - * - * @param {?} obj - * Origin for a pointerMove action. - * - * @return {?} - * A pointer origin that is either "viewport" (default), "pointer", or a - * web-element reference. - * - * @throws {InvalidArgumentError} - * If |obj| is not a valid origin. - */ -action.PointerOrigin.get = function(obj) { - let origin = obj; - if (typeof obj == "undefined") { - origin = this.Viewport; - } else if (typeof obj == "string") { - let name = capitalize(obj); - assert.in(name, this, error.pprint`Unknown pointer-move origin: ${obj}`); - origin = this[name]; - } else if (!element.isWebElementReference(obj)) { - throw new InvalidArgumentError("Expected 'origin' to be a string or a " + - `web element reference, got: ${obj}`); - } - return origin; -}; - -/** Represents possible subtypes for a pointer input source. */ -action.PointerType = { - Mouse: "mouse", - // TODO For now, only mouse is supported - //Pen: "pen", - //Touch: "touch", -}; - -/** - * Look up a PointerType. - * - * @param {string} str - * Name of pointer type. - * - * @return {string} - * A pointer type for processing pointer parameters. - * - * @throws {InvalidArgumentError} - * If |str| is not a valid pointer type. - */ -action.PointerType.get = function (str) { - let name = capitalize(str); - assert.in(name, this, error.pprint`Unknown pointerType: ${str}`); - return this[name]; -}; - -/** - * Input state associated with current session. This is a map between input ID and - * the device state for that input source, with one entry for each active input source. - * - * Initialized in listener.js - */ -action.inputStateMap = undefined; - -/** - * List of |action.Action| associated with current session. Used to manage dispatching - * events when resetting the state of the input sources. Reset operations are assumed - * to be idempotent. - * - * Initialized in listener.js - */ -action.inputsToCancel = undefined; - -/** - * Represents device state for an input source. - */ -class InputState { - constructor() { - this.type = this.constructor.name.toLowerCase(); - } - - /** - * Check equality of this InputState object with another. - * - * @para{?} other - * Object representing an input state. - * @return {boolean} - * True if |this| has the same |type| as |other|. - */ - is(other) { - if (typeof other == "undefined") { - return false; - } - return this.type === other.type; - } - - toString() { - return `[object ${this.constructor.name}InputState]`; - } - - /** - * @param {?} obj - * Object with property |type| and optionally |parameters| or |pointerType|, - * representing an action sequence or an action item. - * - * @return {action.InputState} - * An |action.InputState| object for the type of the |actionSequence|. - * - * @throws {InvalidArgumentError} - * If |actionSequence.type| is not valid. - */ - static fromJson(obj) { - let type = obj.type; - assert.in(type, ACTIONS, error.pprint`Unknown action type: ${type}`); - let name = type == "none" ? "Null" : capitalize(type); - if (name == "Pointer") { - if (!obj.pointerType && (!obj.parameters || !obj.parameters.pointerType)) { - throw new InvalidArgumentError( - error.pprint`Expected obj to have pointerType, got: ${obj}`); - } - let pointerType = obj.pointerType || obj.parameters.pointerType; - return new action.InputState[name](pointerType); - } else { - return new action.InputState[name](); - } - } -} - -/** Possible kinds of |InputState| for supported input sources. */ -action.InputState = {}; - -/** - * Input state associated with a keyboard-type device. - */ -action.InputState.Key = class Key extends InputState { - constructor() { - super(); - this.pressed = new Set(); - this.alt = false; - this.shift = false; - this.ctrl = false; - this.meta = false; - } - - /** - * Update modifier state according to |key|. - * - * @param {string} key - * Normalized key value of a modifier key. - * @param {boolean} value - * Value to set the modifier attribute to. - * - * @throws {InvalidArgumentError} - * If |key| is not a modifier. - */ - setModState(key, value) { - if (key in MODIFIER_NAME_LOOKUP) { - this[MODIFIER_NAME_LOOKUP[key]] = value; - } else { - throw new InvalidArgumentError("Expected 'key' to be one of " + - `${Object.keys(MODIFIER_NAME_LOOKUP)}; got: ${key}`); - } - } - - /** - * Check whether |key| is pressed. - * - * @param {string} key - * Normalized key value. - * - * @return {boolean} - * True if |key| is in set of pressed keys. - */ - isPressed(key) { - return this.pressed.has(key); - } - - /** - * Add |key| to the set of pressed keys. - * - * @param {string} key - * Normalized key value. - * - * @return {boolean} - * True if |key| is in list of pressed keys. - */ - press(key) { - return this.pressed.add(key); - } - - /** - * Remove |key| from the set of pressed keys. - * - * @param {string} key - * Normalized key value. - * - * @return {boolean} - * True if |key| was present before removal, false otherwise. - */ - release(key) { - return this.pressed.delete(key); - } -}; - -/** - * Input state not associated with a specific physical device. - */ -action.InputState.Null = class Null extends InputState { - constructor() { - super(); - this.type = "none"; - } -}; - -/** - * Input state associated with a pointer-type input device. - * - * @param {string} subtype - * Kind of pointing device: mouse, pen, touch. - * - * @throws {InvalidArgumentError} - * If subtype is undefined or an invalid pointer type. - */ -action.InputState.Pointer = class Pointer extends InputState { - constructor(subtype) { - super(); - this.pressed = new Set(); - assert.defined(subtype, error.pprint`Expected subtype to be defined, got: ${subtype}`); - this.subtype = action.PointerType.get(subtype); - this.x = 0; - this.y = 0; - } - - /** - * Check whether |button| is pressed. - * - * @param {number} button - * Positive integer that refers to a mouse button. - * - * @return {boolean} - * True if |button| is in set of pressed buttons. - */ - isPressed(button) { - assert.positiveInteger(button); - return this.pressed.has(button); - } - - /** - * Add |button| to the set of pressed keys. - * - * @param {number} button - * Positive integer that refers to a mouse button. - * - * @return {Set} - * Set of pressed buttons. - */ - press(button) { - assert.positiveInteger(button); - return this.pressed.add(button); - } - - /** - * Remove |button| from the set of pressed buttons. - * - * @param {number} button - * A positive integer that refers to a mouse button. - * - * @return {boolean} - * True if |button| was present before removals, false otherwise. - */ - release(button) { - assert.positiveInteger(button); - return this.pressed.delete(button); - } -}; - -/** - * Repesents an action for dispatch. Used in |action.Chain| and |action.Sequence|. - * - * @param {string} id - * Input source ID. - * @param {string} type - * Action type: none, key, pointer. - * @param {string} subtype - * Action subtype: pause, keyUp, keyDown, pointerUp, pointerDown, pointerMove, pointerCancel. - * - * @throws {InvalidArgumentError} - * If any parameters are undefined. - */ -action.Action = class { - constructor(id, type, subtype) { - if ([id, type, subtype].includes(undefined)) { - throw new InvalidArgumentError("Missing id, type or subtype"); - } - for (let attr of [id, type, subtype]) { - assert.string(attr, error.pprint`Expected string, got: ${attr}`); - } - this.id = id; - this.type = type; - this.subtype = subtype; - }; - - toString() { - return `[action ${this.type}]`; - } - - /** - * @param {?} actionSequence - * Object representing sequence of actions from one input source. - * @param {?} actionItem - * Object representing a single action from |actionSequence|. - * - * @return {action.Action} - * An action that can be dispatched; corresponds to |actionItem|. - * - * @throws {InvalidArgumentError} - * If any |actionSequence| or |actionItem| attributes are invalid. - * @throws {UnsupportedOperationError} - * If |actionItem.type| is |pointerCancel|. - */ - static fromJson(actionSequence, actionItem) { - let type = actionSequence.type; - let id = actionSequence.id; - let subtypes = ACTIONS[type]; - if (!subtypes) { - throw new InvalidArgumentError("Unknown type: " + type); - } - let subtype = actionItem.type; - if (!subtypes.has(subtype)) { - throw new InvalidArgumentError(`Unknown subtype for ${type} action: ${subtype}`); - } - - let item = new action.Action(id, type, subtype); - if (type === "pointer") { - action.processPointerAction(id, - action.PointerParameters.fromJson(actionSequence.parameters), item); - } - - switch (item.subtype) { - case action.KeyUp: - case action.KeyDown: - let key = actionItem.value; - // TODO countGraphemes - // TODO key.value could be a single code point like "\uE012" (see rawKey) - // or "grapheme cluster" - assert.string(key, - error.pprint("Expected 'value' to be a string that represents single code point " + - `or grapheme cluster, got: ${key}`)); - item.value = key; - break; - - case action.PointerDown: - case action.PointerUp: - assert.positiveInteger(actionItem.button, - error.pprint`Expected 'button' (${actionItem.button}) to be >= 0`); - item.button = actionItem.button; - break; - - case action.PointerMove: - item.duration = actionItem.duration; - if (typeof item.duration != "undefined"){ - assert.positiveInteger(item.duration, - error.pprint`Expected 'duration' (${item.duration}) to be >= 0`); - } - item.origin = action.PointerOrigin.get(actionItem.origin); - item.x = actionItem.x; - if (typeof item.x != "undefined") { - assert.integer(item.x, error.pprint`Expected 'x' (${item.x}) to be an Integer`); - } - item.y = actionItem.y; - if (typeof item.y != "undefined") { - assert.integer(item.y, error.pprint`Expected 'y' (${item.y}) to be an Integer`); - } - break; - - case action.PointerCancel: - throw new UnsupportedOperationError(); - break; - - case action.Pause: - item.duration = actionItem.duration; - if (typeof item.duration != "undefined") { - assert.positiveInteger(item.duration, - error.pprint`Expected 'duration' (${item.duration}) to be >= 0`); - } - break; - } - - return item; - } -}; - -/** - * Represents a series of ticks, specifying which actions to perform at each tick. - */ -action.Chain = class extends Array { - toString() { - return `[chain ${super.toString()}]`; - } - - /** - * @param {Array.<?>} actions - * Array of objects that each represent an action sequence. - * - * @return {action.Chain} - * Transpose of |actions| such that actions to be performed in a single tick - * are grouped together. - * - * @throws {InvalidArgumentError} - * If |actions| is not an Array. - */ - static fromJson(actions) { - assert.array(actions, - error.pprint`Expected 'actions' to be an Array, got: ${actions}`); - let actionsByTick = new action.Chain(); - // TODO check that each actionSequence in actions refers to a different input ID - for (let actionSequence of actions) { - let inputSourceActions = action.Sequence.fromJson(actionSequence); - for (let i = 0; i < inputSourceActions.length; i++) { - // new tick - if (actionsByTick.length < (i + 1)) { - actionsByTick.push([]); - } - actionsByTick[i].push(inputSourceActions[i]); - } - } - return actionsByTick; - } -}; - -/** - * Represents one input source action sequence; this is essentially an |Array.<action.Action>|. - */ -action.Sequence = class extends Array { - toString() { - return `[sequence ${super.toString()}]`; - } - - /** - * @param {?} actionSequence - * Object that represents a sequence action items for one input source. - * - * @return {action.Sequence} - * Sequence of actions that can be dispatched. - * - * @throws {InvalidArgumentError} - * If |actionSequence.id| is not a string or it's aleady mapped - * to an |action.InputState} incompatible with |actionSequence.type|. - * If |actionSequence.actions| is not an Array. - */ - static fromJson(actionSequence) { - // used here to validate 'type' in addition to InputState type below - let inputSourceState = InputState.fromJson(actionSequence); - let id = actionSequence.id; - assert.defined(id, "Expected 'id' to be defined"); - assert.string(id, error.pprint`Expected 'id' to be a string, got: ${id}`); - let actionItems = actionSequence.actions; - assert.array(actionItems, - error.pprint("Expected 'actionSequence.actions' to be an Array, " + - `got: ${actionSequence.actions}`)); - if (!action.inputStateMap.has(id)) { - action.inputStateMap.set(id, inputSourceState); - } else if (!action.inputStateMap.get(id).is(inputSourceState)) { - throw new InvalidArgumentError( - `Expected ${id} to be mapped to ${inputSourceState}, ` + - `got: ${action.inputStateMap.get(id)}`); - } - let actions = new action.Sequence(); - for (let actionItem of actionItems) { - actions.push(action.Action.fromJson(actionSequence, actionItem)); - } - return actions; - } -}; - -/** - * Represents parameters in an action for a pointer input source. - * - * @param {string=} pointerType - * Type of pointing device. If the parameter is undefined, "mouse" is used. - */ -action.PointerParameters = class { - constructor(pointerType = "mouse") { - this.pointerType = action.PointerType.get(pointerType); - } - - toString() { - return `[pointerParameters ${this.pointerType}]`; - } - - /** - * @param {?} parametersData - * Object that represents pointer parameters. - * - * @return {action.PointerParameters} - * Validated pointer paramters. - */ - static fromJson(parametersData) { - if (typeof parametersData == "undefined") { - return new action.PointerParameters(); - } else { - return new action.PointerParameters(parametersData.pointerType); - } - } -}; - -/** - * Adds |pointerType| attribute to Action |act|. Helper function - * for |action.Action.fromJson|. - * - * @param {string} id - * Input source ID. - * @param {action.PointerParams} pointerParams - * Input source pointer parameters. - * @param {action.Action} act - * Action to be updated. - * - * @throws {InvalidArgumentError} - * If |id| is already mapped to an |action.InputState| that is - * not compatible with |act.type| or |pointerParams.pointerType|. - */ -action.processPointerAction = function processPointerAction(id, pointerParams, act) { - if (action.inputStateMap.has(id) && action.inputStateMap.get(id).type !== act.type) { - throw new InvalidArgumentError( - `Expected 'id' ${id} to be mapped to InputState whose type is ` + - `${action.inputStateMap.get(id).type}, got: ${act.type}`); - } - let pointerType = pointerParams.pointerType; - if (action.inputStateMap.has(id) && action.inputStateMap.get(id).subtype !== pointerType) { - throw new InvalidArgumentError( - `Expected 'id' ${id} to be mapped to InputState whose subtype is ` + - `${action.inputStateMap.get(id).subtype}, got: ${pointerType}`); - } - act.pointerType = pointerParams.pointerType; -}; - -/** Collect properties associated with KeyboardEvent */ -action.Key = class { - constructor(rawKey) { - this.key = NORMALIZED_KEY_LOOKUP[rawKey] || rawKey; - this.code = KEY_CODE_LOOKUP[rawKey]; - this.location = KEY_LOCATION_LOOKUP[rawKey] || 0; - this.altKey = false; - this.shiftKey = false; - this.ctrlKey = false; - this.metaKey = false; - this.repeat = false; - this.isComposing = false; - // Prevent keyCode from being guessed in event.js; we don't want to use it anyway. - this.keyCode = 0; - } - - update(inputState) { - this.altKey = inputState.alt; - this.shiftKey = inputState.shift; - this.ctrlKey = inputState.ctrl; - this.metaKey = inputState.meta; - } -}; - -/** Collect properties associated with MouseEvent */ -action.Mouse = class { - constructor(type, button = 0) { - this.type = type; - assert.positiveInteger(button); - this.button = button; - this.buttons = 0; - } - - update(inputState) { - let allButtons = Array.from(inputState.pressed); - this.buttons = allButtons.reduce((a, i) => a + Math.pow(2, i), 0); - } -}; - -/** - * Dispatch a chain of actions over |chain.length| ticks. - * - * This is done by creating a Promise for each tick that resolves once all the - * Promises for individual tick-actions are resolved. The next tick's actions are - * not dispatched until the Promise for the current tick is resolved. - * - * @param {action.Chain} chain - * Actions grouped by tick; each element in |chain| is a sequence of - * actions for one tick. - * @param {element.Store} seenEls - * Element store. - * @param {?} container - * Object with |frame| attribute of type |nsIDOMWindow|. - * - * @return {Promise} - * Promise for dispatching all actions in |chain|. - */ -action.dispatch = function(chain, seenEls, container) { - let chainEvents = Task.spawn(function*() { - for (let tickActions of chain) { - yield action.dispatchTickActions( - tickActions, action.computeTickDuration(tickActions), seenEls, container); - } - }); - return chainEvents; -}; - -/** - * Dispatch sequence of actions for one tick. - * - * This creates a Promise for one tick that resolves once the Promise for each - * tick-action is resolved, which takes at least |tickDuration| milliseconds. - * The resolved set of events for each tick is followed by firing of pending DOM events. - * - * Note that the tick-actions are dispatched in order, but they may have different - * durations and therefore may not end in the same order. - * - * @param {Array.<action.Action>} tickActions - * List of actions for one tick. - * @param {number} tickDuration - * Duration in milliseconds of this tick. - * @param {element.Store} seenEls - * Element store. - * @param {?} container - * Object with |frame| attribute of type |nsIDOMWindow|. - * - * @return {Promise} - * Promise for dispatching all tick-actions and pending DOM events. - */ -action.dispatchTickActions = function(tickActions, tickDuration, seenEls, container) { - let pendingEvents = tickActions.map(toEvents(tickDuration, seenEls, container)); - return Promise.all(pendingEvents).then(() => flushEvents(container)); -}; - -/** - * Compute tick duration in milliseconds for a collection of actions. - * - * @param {Array.<action.Action>} tickActions - * List of actions for one tick. - * - * @return {number} - * Longest action duration in |tickActions| if any, or 0. - */ -action.computeTickDuration = function(tickActions) { - let max = 0; - for (let a of tickActions) { - let affectsWallClockTime = a.subtype == action.Pause || - (a.type == "pointer" && a.subtype == action.PointerMove); - if (affectsWallClockTime && a.duration) { - max = Math.max(a.duration, max); - } - } - return max; -}; - -/** - * Compute viewport coordinates of pointer target based on given origin. - * - * @param {action.Action} a - * Action that specifies pointer origin and x and y coordinates of target. - * @param {action.InputState} inputState - * Input state that specifies current x and y coordinates of pointer. - * @param {Map.<string, number>=} center - * Object representing x and y coordinates of an element center-point. - * This is only used if |a.origin| is a web element reference. - * - * @return {Map.<string, number>} - * x and y coordinates of pointer destination. - */ -action.computePointerDestination = function(a, inputState, center = undefined) { - let {x, y} = a; - switch (a.origin) { - case action.PointerOrigin.Viewport: - break; - case action.PointerOrigin.Pointer: - x += inputState.x; - y += inputState.y; - break; - default: - // origin represents web element - assert.defined(center); - assert.in("x", center); - assert.in("y", center); - x += center.x; - y += center.y; - } - return {"x": x, "y": y}; -}; - -/** - * Create a closure to use as a map from action definitions to Promise events. - * - * @param {number} tickDuration - * Duration in milliseconds of this tick. - * @param {element.Store} seenEls - * Element store. - * @param {?} container - * Object with |frame| attribute of type |nsIDOMWindow|. - * - * @return {function(action.Action): Promise} - * Function that takes an action and returns a Promise for dispatching - * the event that corresponds to that action. - */ -function toEvents(tickDuration, seenEls, container) { - return function (a) { - let inputState = action.inputStateMap.get(a.id); - switch (a.subtype) { - case action.KeyUp: - return dispatchKeyUp(a, inputState, container.frame); - - case action.KeyDown: - return dispatchKeyDown(a, inputState, container.frame); - - case action.PointerDown: - return dispatchPointerDown(a, inputState, container.frame); - - case action.PointerUp: - return dispatchPointerUp(a, inputState, container.frame); - - case action.PointerMove: - return dispatchPointerMove(a, inputState, tickDuration, seenEls, container); - - case action.PointerCancel: - throw new UnsupportedOperationError(); - - case action.Pause: - return dispatchPause(a, tickDuration); - } - }; -} - -/** - * Dispatch a keyDown action equivalent to pressing a key on a keyboard. - * - * @param {action.Action} a - * Action to dispatch. - * @param {action.InputState} inputState - * Input state for this action's input source. - * @param {nsIDOMWindow} win - * Current window. - * - * @return {Promise} - * Promise to dispatch at least a keydown event, and keypress if appropriate. - */ -function dispatchKeyDown(a, inputState, win) { - return new Promise(resolve => { - let keyEvent = new action.Key(a.value); - keyEvent.repeat = inputState.isPressed(keyEvent.key); - inputState.press(keyEvent.key); - if (keyEvent.key in MODIFIER_NAME_LOOKUP) { - inputState.setModState(keyEvent.key, true); - } - // Append a copy of |a| with keyUp subtype - action.inputsToCancel.push(Object.assign({}, a, {subtype: action.KeyUp})); - keyEvent.update(inputState); - event.sendKeyDown(keyEvent.key, keyEvent, win); - - resolve(); - }); -} - -/** - * Dispatch a keyUp action equivalent to releasing a key on a keyboard. - * - * @param {action.Action} a - * Action to dispatch. - * @param {action.InputState} inputState - * Input state for this action's input source. - * @param {nsIDOMWindow} win - * Current window. - * - * @return {Promise} - * Promise to dispatch a keyup event. - */ -function dispatchKeyUp(a, inputState, win) { - return new Promise(resolve => { - let keyEvent = new action.Key(a.value); - if (!inputState.isPressed(keyEvent.key)) { - resolve(); - return; - } - if (keyEvent.key in MODIFIER_NAME_LOOKUP) { - inputState.setModState(keyEvent.key, false); - } - inputState.release(keyEvent.key); - keyEvent.update(inputState); - event.sendKeyUp(keyEvent.key, keyEvent, win); - - resolve(); - }); -} - -/** - * Dispatch a pointerDown action equivalent to pressing a pointer-device - * button. - * - * @param {action.Action} a - * Action to dispatch. - * @param {action.InputState} inputState - * Input state for this action's input source. - * @param {nsIDOMWindow} win - * Current window. - * - * @return {Promise} - * Promise to dispatch at least a pointerdown event. - */ -function dispatchPointerDown(a, inputState, win) { - return new Promise(resolve => { - if (inputState.isPressed(a.button)) { - resolve(); - return; - } - inputState.press(a.button); - // Append a copy of |a| with pointerUp subtype - action.inputsToCancel.push(Object.assign({}, a, {subtype: action.PointerUp})); - switch (inputState.subtype) { - case action.PointerType.Mouse: - let mouseEvent = new action.Mouse("mousedown", a.button); - mouseEvent.update(inputState); - event.synthesizeMouseAtPoint(inputState.x, inputState.y, mouseEvent, win); - break; - case action.PointerType.Pen: - case action.PointerType.Touch: - throw new UnsupportedOperationError("Only 'mouse' pointer type is supported"); - break; - default: - throw new TypeError(`Unknown pointer type: ${inputState.subtype}`); - } - resolve(); - }); -} - -/** - * Dispatch a pointerUp action equivalent to releasing a pointer-device - * button. - * - * @param {action.Action} a - * Action to dispatch. - * @param {action.InputState} inputState - * Input state for this action's input source. - * @param {nsIDOMWindow} win - * Current window. - * - * @return {Promise} - * Promise to dispatch at least a pointerup event. - */ -function dispatchPointerUp(a, inputState, win) { - return new Promise(resolve => { - if (!inputState.isPressed(a.button)) { - resolve(); - return; - } - inputState.release(a.button); - switch (inputState.subtype) { - case action.PointerType.Mouse: - let mouseEvent = new action.Mouse("mouseup", a.button); - mouseEvent.update(inputState); - event.synthesizeMouseAtPoint(inputState.x, inputState.y, - mouseEvent, win); - break; - case action.PointerType.Pen: - case action.PointerType.Touch: - throw new UnsupportedOperationError("Only 'mouse' pointer type is supported"); - default: - throw new TypeError(`Unknown pointer type: ${inputState.subtype}`); - } - resolve(); - }); -} - -/** - * Dispatch a pointerMove action equivalent to moving pointer device in a line. - * - * If the action duration is 0, the pointer jumps immediately to the target coordinates. - * Otherwise, events are synthesized to mimic a pointer travelling in a discontinuous, - * approximately straight line, with the pointer coordinates being updated around 60 - * times per second. - * - * @param {action.Action} a - * Action to dispatch. - * @param {action.InputState} inputState - * Input state for this action's input source. - * @param {element.Store} seenEls - * Element store. - * @param {?} container - * Object with |frame| attribute of type |nsIDOMWindow|. - * - * @return {Promise} - * Promise to dispatch at least one pointermove event, as well as mousemove events - * as appropriate. - */ -function dispatchPointerMove(a, inputState, tickDuration, seenEls, container) { - const timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - // interval between pointermove increments in ms, based on common vsync - const fps60 = 17; - return new Promise(resolve => { - const start = Date.now(); - const [startX, startY] = [inputState.x, inputState.y]; - let target = action.computePointerDestination(a, inputState, - getElementCenter(a.origin, seenEls, container)); - const [targetX, targetY] = [target.x, target.y]; - if (!inViewPort(targetX, targetY, container.frame)) { - throw new MoveTargetOutOfBoundsError( - `(${targetX}, ${targetY}) is out of bounds of viewport ` + - `width (${container.frame.innerWidth}) and height (${container.frame.innerHeight})`); - } - - const duration = typeof a.duration == "undefined" ? tickDuration : a.duration; - if (duration === 0) { - // move pointer to destination in one step - performOnePointerMove(inputState, targetX, targetY, container.frame); - resolve(); - return; - } - - const distanceX = targetX - startX; - const distanceY = targetY - startY; - const ONE_SHOT = Ci.nsITimer.TYPE_ONE_SHOT; - let intermediatePointerEvents = Task.spawn(function* () { - // wait |fps60| ms before performing first incremental pointer move - yield new Promise(resolveTimer => - timer.initWithCallback(resolveTimer, fps60, ONE_SHOT) - ); - let durationRatio = Math.floor(Date.now() - start) / duration; - const epsilon = fps60 / duration / 10; - while ((1 - durationRatio) > epsilon) { - let x = Math.floor(durationRatio * distanceX + startX); - let y = Math.floor(durationRatio * distanceY + startY); - performOnePointerMove(inputState, x, y, container.frame); - // wait |fps60| ms before performing next pointer move - yield new Promise(resolveTimer => - timer.initWithCallback(resolveTimer, fps60, ONE_SHOT)); - durationRatio = Math.floor(Date.now() - start) / duration; - } - }); - // perform last pointer move after all incremental moves are resolved and - // durationRatio is close enough to 1 - intermediatePointerEvents.then(() => { - performOnePointerMove(inputState, targetX, targetY, container.frame); - resolve(); - }); - - }); -} - -function performOnePointerMove(inputState, targetX, targetY, win) { - if (targetX == inputState.x && targetY == inputState.y) { - return; - } - switch (inputState.subtype) { - case action.PointerType.Mouse: - let mouseEvent = new action.Mouse("mousemove"); - mouseEvent.update(inputState); - //TODO both pointermove (if available) and mousemove - event.synthesizeMouseAtPoint(targetX, targetY, mouseEvent, win); - break; - case action.PointerType.Pen: - case action.PointerType.Touch: - throw new UnsupportedOperationError("Only 'mouse' pointer type is supported"); - default: - throw new TypeError(`Unknown pointer type: ${inputState.subtype}`); - } - inputState.x = targetX; - inputState.y = targetY; -} - -/** - * Dispatch a pause action equivalent waiting for |a.duration| milliseconds, or a - * default time interval of |tickDuration|. - * - * @param {action.Action} a - * Action to dispatch. - * @param {number} tickDuration - * Duration in milliseconds of this tick. - * - * @return {Promise} - * Promise that is resolved after the specified time interval. - */ -function dispatchPause(a, tickDuration) { - const timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - let duration = typeof a.duration == "undefined" ? tickDuration : a.duration; - return new Promise(resolve => - timer.initWithCallback(resolve, duration, Ci.nsITimer.TYPE_ONE_SHOT) - ); -} - -// helpers -/** - * Force any pending DOM events to fire. - * - * @param {?} container - * Object with |frame| attribute of type |nsIDOMWindow|. - * - * @return {Promise} - * Promise to flush DOM events. - */ -function flushEvents(container) { - return new Promise(resolve => container.frame.requestAnimationFrame(resolve)); -} - -function capitalize(str) { - assert.string(str); - return str.charAt(0).toUpperCase() + str.slice(1); -} - -function inViewPort(x, y, win) { - assert.number(x, `Expected x to be finite number`); - assert.number(y, `Expected y to be finite number`); - // Viewport includes scrollbars if rendered. - return !(x < 0 || y < 0 || x > win.innerWidth || y > win.innerHeight); -} - -function getElementCenter(elementReference, seenEls, container) { - if (element.isWebElementReference(elementReference)) { - let uuid = elementReference[element.Key] || elementReference[element.LegacyKey]; - let el = seenEls.get(uuid, container); - return element.coordinates(el); - } -} diff --git a/testing/marionette/addon.js b/testing/marionette/addon.js deleted file mode 100644 index d2ead6fb2..000000000 --- a/testing/marionette/addon.js +++ /dev/null @@ -1,104 +0,0 @@ -/* 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/. */ - -"use strict"; - -const {interfaces: Ci, utils: Cu} = Components; - -Cu.import("resource://gre/modules/AddonManager.jsm"); -Cu.import("resource://gre/modules/FileUtils.jsm"); - -Cu.import("chrome://marionette/content/error.js"); - -this.EXPORTED_SYMBOLS = ["addon"]; - -this.addon = {}; - -// from https://developer.mozilla.org/en-US/Add-ons/Add-on_Manager/AddonManager#AddonInstall_errors -addon.Errors = { - [-1]: "ERROR_NETWORK_FAILURE: A network error occured.", - [-2]: "ERROR_INCORECT_HASH: The downloaded file did not match the expected hash.", - [-3]: "ERROR_CORRUPT_FILE: The file appears to be corrupt.", - [-4]: "ERROR_FILE_ACCESS: There was an error accessing the filesystem.", - [-5]: "ERROR_SIGNEDSTATE_REQUIRED: The addon must be signed and isn't.", -}; - -function lookupError(code) { - let msg = addon.Errors[code]; - return new UnknownError(msg); -} - -/** - * Install a Firefox addon. - * - * If the addon is restartless, it can be used right away. Otherwise a - * restart is required. - * - * Temporary addons will automatically be uninstalled on shutdown and - * do not need to be signed, though they must be restartless. - * - * @param {string} path - * Full path to the extension package archive. - * @param {boolean=} temporary - * True to install the addon temporarily, false (default) otherwise. - * - * @return {Promise: string} - * Addon ID. - * - * @throws {UnknownError} - * If there is a problem installing the addon. - */ -addon.install = function (path, temporary = false) { - return new Promise((resolve, reject) => { - let file = new FileUtils.File(path); - - let listener = { - onInstallEnded: function (install, addon) { - resolve(addon.id); - }, - - onInstallFailed: function (install) { - reject(lookupError(install.error)); - }, - - onInstalled: function (addon) { - AddonManager.removeAddonListener(listener); - resolve(addon.id); - } - }; - - if (!temporary) { - AddonManager.getInstallForFile(file, function (aInstall) { - if (aInstall.error !== 0) { - reject(lookupError(aInstall.error)); - } - aInstall.addListener(listener); - aInstall.install(); - }); - } else { - AddonManager.addAddonListener(listener); - AddonManager.installTemporaryAddon(file); - } - }); -}; - -/** - * Uninstall a Firefox addon. - * - * If the addon is restartless it will be uninstalled right away. - * Otherwise, Firefox must be restarted for the change to take effect. - * - * @param {string} id - * ID of the addon to uninstall. - * - * @return {Promise} - */ -addon.uninstall = function (id) { - return new Promise(resolve => { - AddonManager.getAddonByID(id, function (addon) { - addon.uninstall(); - resolve(); - }); - }); -}; diff --git a/testing/marionette/assert.js b/testing/marionette/assert.js deleted file mode 100644 index b2d228d0e..000000000 --- a/testing/marionette/assert.js +++ /dev/null @@ -1,322 +0,0 @@ -/* 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/. */ - -"use strict"; - -const {classes: Cc, interfaces: Ci, utils: Cu} = Components; - -Cu.import("resource://gre/modules/AppConstants.jsm"); -Cu.import("resource://gre/modules/Preferences.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -Cu.import("chrome://marionette/content/error.js"); - -this.EXPORTED_SYMBOLS = ["assert"]; - -const isFennec = () => AppConstants.platform == "android"; -const isB2G = () => false; -const isFirefox = () => Services.appinfo.name == "Firefox"; - -/** Shorthands for common assertions made in Marionette. */ -this.assert = {}; - -/** - * Asserts that Marionette has a session. - * - * @param {GeckoDriver} driver - * Marionette driver instance. - * @param {string=} msg - * Custom error message. - * - * @return {string} - * Session ID. - * - * @throws {InvalidSessionIDError} - * If |driver| does not have a session ID. - */ -assert.session = function (driver, msg = "") { - assert.that(sessionID => sessionID, - msg, InvalidSessionIDError)(driver.sessionId); - return driver.sessionId; -}; - -/** - * Asserts that the current browser is Firefox Desktop. - * - * @param {string=} msg - * Custom error message. - * - * @throws {UnsupportedOperationError} - * If current browser is not Firefox. - */ -assert.firefox = function (msg = "") { - msg = msg || "Only supported in Firefox"; - assert.that(isFirefox, msg, UnsupportedOperationError)(); -}; - -/** - * Asserts that the current browser is Fennec, or Firefox for Android. - * - * @param {string=} msg - * Custom error message. - * - * @throws {UnsupportedOperationError} - * If current browser is not Fennec. - */ -assert.fennec = function (msg = "") { - msg = msg || "Only supported in Fennec"; - assert.that(isFennec, msg, UnsupportedOperationError)(); -}; - -/** - * Asserts that the current browser is B2G. - * - * @param {string=} msg - * Custom error message. - * - * @throws {UnsupportedOperationError} - * If the current browser is not B2G. - */ -assert.b2g = function (msg = "") { - msg = msg || "Only supported in B2G"; - assert.that(isB2G, msg, UnsupportedOperationError)(); -}; - -/** - * Asserts that the current |context| is content. - * - * @param {string} context - * Context to test. - * @param {string=} msg - * Custom error message. - * - * @return {string} - * |context| is returned unaltered. - * - * @throws {UnsupportedOperationError} - * If |context| is not content. - */ -assert.content = function (context, msg = "") { - msg = msg || "Only supported in content context"; - assert.that(c => c.toString() == "content", msg, UnsupportedOperationError)(context); -}; - -/** - * Asserts that the current browser is a mobile browser, that is either - * B2G or Fennec. - * - * @param {string=} msg - * Custom error message. - * - * @throws {UnsupportedOperationError} - * If the current browser is not B2G or Fennec. - */ -assert.mobile = function (msg = "") { - msg = msg || "Only supported in Fennec or B2G"; - assert.that(() => isFennec() || isB2G(), msg, UnsupportedOperationError)(); -}; - -/** - * Asserts that |obj| is defined. - * - * @param {?} obj - * Value to test. - * @param {string=} msg - * Custom error message. - * - * @return {?} - * |obj| is returned unaltered. - * - * @throws {InvalidArgumentError} - * If |obj| is not defined. - */ -assert.defined = function (obj, msg = "") { - msg = msg || error.pprint`Expected ${obj} to be defined`; - return assert.that(o => typeof o != "undefined", msg)(obj); -}; - -/** - * Asserts that |obj| is a finite number. - * - * @param {?} obj - * Value to test. - * @param {string=} msg - * Custom error message. - * - * @return {number} - * |obj| is returned unaltered. - * - * @throws {InvalidArgumentError} - * If |obj| is not a number. - */ -assert.number = function (obj, msg = "") { - msg = msg || error.pprint`Expected ${obj} to be finite number`; - return assert.that(Number.isFinite, msg)(obj); -}; - -/** - * Asserts that |obj| is an integer. - * - * @param {?} obj - * Value to test. - * @param {string=} msg - * Custom error message. - * - * @return {number} - * |obj| is returned unaltered. - * - * @throws {InvalidArgumentError} - * If |obj| is not an integer. - */ -assert.integer = function (obj, msg = "") { - msg = msg || error.pprint`Expected ${obj} to be an integer`; - return assert.that(Number.isInteger, msg)(obj); -}; - -/** - * Asserts that |obj| is a positive integer. - * - * @param {?} obj - * Value to test. - * @param {string=} msg - * Custom error message. - * - * @return {number} - * |obj| is returned unaltered. - * - * @throws {InvalidArgumentError} - * If |obj| is not a positive integer. - */ -assert.positiveInteger = function (obj, msg = "") { - assert.integer(obj, msg); - msg = msg || error.pprint`Expected ${obj} to be >= 0`; - return assert.that(n => n >= 0, msg)(obj); -}; - -/** - * Asserts that |obj| is a boolean. - * - * @param {?} obj - * Value to test. - * @param {string=} msg - * Custom error message. - * - * @return {boolean} - * |obj| is returned unaltered. - * - * @throws {InvalidArgumentError} - * If |obj| is not a boolean. - */ -assert.boolean = function (obj, msg = "") { - msg = msg || error.pprint`Expected ${obj} to be boolean`; - return assert.that(b => typeof b == "boolean", msg)(obj); -}; - -/** - * Asserts that |obj| is a string. - * - * @param {?} obj - * Value to test. - * @param {string=} msg - * Custom error message. - * - * @return {string} - * |obj| is returned unaltered. - * - * @throws {InvalidArgumentError} - * If |obj| is not a string. - */ -assert.string = function (obj, msg = "") { - msg = msg || error.pprint`Expected ${obj} to be a string`; - return assert.that(s => typeof s == "string", msg)(obj); -}; - -/** - * Asserts that |obj| is an object. - * - * @param {?} obj - * Value to test. - * @param {string=} msg - * Custom error message. - * - * @return {Object} - * |obj| is returned unaltered. - * - * @throws {InvalidArgumentError} - * If |obj| is not an object. - */ -assert.object = function (obj, msg = "") { - msg = msg || error.pprint`Expected ${obj} to be an object`; - return assert.that(o => - Object.prototype.toString.call(o) == "[object Object]", msg)(obj); -}; - -/** - * Asserts that |prop| is in |obj|. - * - * @param {?} prop - * Own property to test if is in |obj|. - * @param {?} obj - * Object. - * @param {string=} msg - * Custom error message. - * - * @return {?} - * Value of |obj|'s own property |prop|. - * - * @throws {InvalidArgumentError} - * If |prop| is not in |obj|, or |obj| is not an object. - */ -assert.in = function (prop, obj, msg = "") { - assert.object(obj, msg); - msg = msg || error.pprint`Expected ${prop} in ${obj}`; - assert.that(p => obj.hasOwnProperty(p), msg)(prop); - return obj[prop]; -}; - -/** - * Asserts that |obj| is an Array. - * - * @param {?} obj - * Value to test. - * @param {string=} msg - * Custom error message. - * - * @return {Object} - * |obj| is returned unaltered. - * - * @throws {InvalidArgumentError} - * If |obj| is not an Array. - */ -assert.array = function (obj, msg = "") { - msg = msg || error.pprint`Expected ${obj} to be an Array`; - return assert.that(Array.isArray, msg)(obj); -}; - -/** - * Returns a function that is used to assert the |predicate|. - * - * @param {function(?): boolean} predicate - * Evaluated on calling the return value of this function. If its - * return value of the inner function is false, |error| is thrown - * with |message|. - * @param {string=} message - * Custom error message. - * @param {Error=} error - * Custom error type by its class. - * - * @return {function(?): ?} - * Function that takes and returns the passed in value unaltered, and - * which may throw |error| with |message| if |predicate| evaluates - * to false. - */ -assert.that = function ( - predicate, message = "", error = InvalidArgumentError) { - return obj => { - if (!predicate(obj)) { - throw new error(message); - } - return obj; - }; -}; diff --git a/testing/marionette/atom.js b/testing/marionette/atom.js deleted file mode 100644 index 369e5c44c..000000000 --- a/testing/marionette/atom.js +++ /dev/null @@ -1,192 +0,0 @@ -// Copyright 2011-2014 Software Freedom Conservancy -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -this.EXPORTED_SYMBOLS = ["atom"]; - -this.atom = {}; - -atom.clearElement = function (element, window){return function(){function g(a){throw a;}var h=void 0,i=!0,k=null,l=!1;function n(a){return function(){return this[a]}}function o(a){return function(){return a}}var p,q=this; -function aa(a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!=typeof a.call&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("call"))return"function"}else return"null"; -else if("function"==b&&"undefined"==typeof a.call)return"object";return b}function r(a){return a!==h}function ba(a){var b=aa(a);return"array"==b||"object"==b&&"number"==typeof a.length}function t(a){return"string"==typeof a}function w(a){return"function"==aa(a)}function ca(a){a=aa(a);return"object"==a||"array"==a||"function"==a}var da="closure_uid_"+Math.floor(2147483648*Math.random()).toString(36),ea=0,fa=Date.now||function(){return+new Date}; -function x(a,b){function c(){}c.prototype=b.prototype;a.$=b.prototype;a.prototype=new c};function ga(a,b){for(var c=1;c<arguments.length;c++)var d=(""+arguments[c]).replace(/\$/g,"$$$$"),a=a.replace(/\%s/,d);return a}function ha(a){return a.replace(/^[\s\xa0]+|[\s\xa0]+$/g,"")}function ia(a){if(!ja.test(a))return a;-1!=a.indexOf("&")&&(a=a.replace(ka,"&"));-1!=a.indexOf("<")&&(a=a.replace(la,"<"));-1!=a.indexOf(">")&&(a=a.replace(ma,">"));-1!=a.indexOf('"')&&(a=a.replace(na,"""));return a}var ka=/&/g,la=/</g,ma=/>/g,na=/\"/g,ja=/[&<>\"]/; -function oa(a,b){for(var c=0,d=ha(""+a).split("."),e=ha(""+b).split("."),f=Math.max(d.length,e.length),j=0;0==c&&j<f;j++){var m=d[j]||"",s=e[j]||"",O=RegExp("(\\d*)(\\D*)","g"),E=RegExp("(\\d*)(\\D*)","g");do{var u=O.exec(m)||["","",""],v=E.exec(s)||["","",""];if(0==u[0].length&&0==v[0].length)break;c=((0==u[1].length?0:parseInt(u[1],10))<(0==v[1].length?0:parseInt(v[1],10))?-1:(0==u[1].length?0:parseInt(u[1],10))>(0==v[1].length?0:parseInt(v[1],10))?1:0)||((0==u[2].length)<(0==v[2].length)?-1:(0== -u[2].length)>(0==v[2].length)?1:0)||(u[2]<v[2]?-1:u[2]>v[2]?1:0)}while(0==c)}return c}var pa=2147483648*Math.random()|0,qa={};function ra(a){return qa[a]||(qa[a]=(""+a).replace(/\-([a-z])/g,function (a,c){return c.toUpperCase()}))};var sa,ta;function ua(){return q.navigator?q.navigator.userAgent:k}var va,wa=q.navigator;va=wa&&wa.platform||"";sa=-1!=va.indexOf("Mac");ta=-1!=va.indexOf("Win");var xa=-1!=va.indexOf("Linux"),ya,za="",Aa=/rv\:([^\);]+)(\)|;)/.exec(ua());ya=za=Aa?Aa[1]:"";var Ba={};var Ca=window;function Da(a,b){for(var c in a)b.call(h,a[c],c,a)}function Ea(a){var b=[],c=0,d;for(d in a)b[c++]=a[d];return b};function y(a,b){this.code=a;this.message=b||"";this.name=Fa[a]||Fa[13];var c=Error(this.message);c.name=this.name;this.stack=c.stack||""}x(y,Error); -var Fa={7:"NoSuchElementError",8:"NoSuchFrameError",9:"UnknownCommandError",10:"StaleElementReferenceError",11:"ElementNotVisibleError",12:"InvalidElementStateError",13:"UnknownError",15:"ElementNotSelectableError",19:"XPathLookupError",23:"NoSuchWindowError",24:"InvalidCookieDomainError",25:"UnableToSetCookieError",26:"ModalDialogOpenedError",27:"NoModalDialogOpenError",28:"ScriptTimeoutError",32:"InvalidSelectorError",33:"SqlDatabaseError",34:"MoveTargetOutOfBoundsError"}; -y.prototype.toString=function(){return"["+this.name+"] "+this.message};function Ga(a){this.stack=Error().stack||"";a&&(this.message=""+a)}x(Ga,Error);Ga.prototype.name="CustomError";function Ha(a,b){b.unshift(a);Ga.call(this,ga.apply(k,b));b.shift()}x(Ha,Ga);Ha.prototype.name="AssertionError";function Ia(a,b,c){if(!a){var d=Array.prototype.slice.call(arguments,2),e="Assertion failed";if(b)var e=e+(": "+b),f=d;g(new Ha(""+e,f||[]))}}function Ja(a,b){g(new Ha("Failure"+(a?": "+a:""),Array.prototype.slice.call(arguments,1)))};function z(a){return a[a.length-1]}var Ka=Array.prototype;function A(a,b){if(t(a))return!t(b)||1!=b.length?-1:a.indexOf(b,0);for(var c=0;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1}function La(a,b){for(var c=a.length,d=t(a)?a.split(""):a,e=0;e<c;e++)e in d&&b.call(h,d[e],e,a)}function Ma(a,b){for(var c=a.length,d=Array(c),e=t(a)?a.split(""):a,f=0;f<c;f++)f in e&&(d[f]=b.call(h,e[f],f,a));return d} -function Na(a,b,c){for(var d=a.length,e=t(a)?a.split(""):a,f=0;f<d;f++)if(f in e&&b.call(c,e[f],f,a))return i;return l}function Oa(a,b,c){for(var d=a.length,e=t(a)?a.split(""):a,f=0;f<d;f++)if(f in e&&!b.call(c,e[f],f,a))return l;return i}function Pa(a,b){var c;a:{c=a.length;for(var d=t(a)?a.split(""):a,e=0;e<c;e++)if(e in d&&b.call(h,d[e],e,a)){c=e;break a}c=-1}return 0>c?k:t(a)?a.charAt(c):a[c]}function Qa(a){return Ka.concat.apply(Ka,arguments)} -function Ra(a){if("array"==aa(a))return Qa(a);for(var b=[],c=0,d=a.length;c<d;c++)b[c]=a[c];return b}function Sa(a,b,c){Ia(a.length!=k);return 2>=arguments.length?Ka.slice.call(a,b):Ka.slice.call(a,b,c)};var Ta;Ba["1.9.1"]||(Ba["1.9.1"]=0<=oa(ya,"1.9.1"));function Ua(a,b){var c;c=(c=a.className)&&"function"==typeof c.split?c.split(/\s+/):[];var d=Sa(arguments,1),e;e=c;for(var f=0,j=0;j<d.length;j++)0<=A(e,d[j])||(e.push(d[j]),f++);e=f==d.length;a.className=c.join(" ");return e};function B(a,b){this.x=r(a)?a:0;this.y=r(b)?b:0}B.prototype.toString=function(){return"("+this.x+", "+this.y+")"};function Va(a,b){this.width=a;this.height=b}Va.prototype.toString=function(){return"("+this.width+" x "+this.height+")"};Va.prototype.floor=function(){this.width=Math.floor(this.width);this.height=Math.floor(this.height);return this};Va.prototype.scale=function (a){this.width*=a;this.height*=a;return this};var C=3;function Wa(a){return a?new Xa(D(a)):Ta||(Ta=new Xa)}function Ya(a,b){Da(b,function (b,d){"style"==d?a.style.cssText=b:"class"==d?a.className=b:"for"==d?a.htmlFor=b:d in Za?a.setAttribute(Za[d],b):0==d.lastIndexOf("aria-",0)?a.setAttribute(d,b):a[d]=b})}var Za={cellpadding:"cellPadding",cellspacing:"cellSpacing",colspan:"colSpan",rowspan:"rowSpan",valign:"vAlign",height:"height",width:"width",usemap:"useMap",frameborder:"frameBorder",maxlength:"maxLength",type:"type"}; -function F(a){return a?a.parentWindow||a.defaultView:window}function $a(a,b,c){function d(c){c&&b.appendChild(t(c)?a.createTextNode(c):c)}for(var e=2;e<c.length;e++){var f=c[e];ba(f)&&!(ca(f)&&0<f.nodeType)?La(ab(f)?Ra(f):f,d):d(f)}}function bb(a){return a&&a.parentNode?a.parentNode.removeChild(a):k} -function G(a,b){if(a.contains&&1==b.nodeType)return a==b||a.contains(b);if("undefined"!=typeof a.compareDocumentPosition)return a==b||Boolean(a.compareDocumentPosition(b)&16);for(;b&&a!=b;)b=b.parentNode;return b==a} -function cb(a,b){if(a==b)return 0;if(a.compareDocumentPosition)return a.compareDocumentPosition(b)&2?1:-1;if("sourceIndex"in a||a.parentNode&&"sourceIndex"in a.parentNode){var c=1==a.nodeType,d=1==b.nodeType;if(c&&d)return a.sourceIndex-b.sourceIndex;var e=a.parentNode,f=b.parentNode;return e==f?db(a,b):!c&&G(e,b)?-1*eb(a,b):!d&&G(f,a)?eb(b,a):(c?a.sourceIndex:e.sourceIndex)-(d?b.sourceIndex:f.sourceIndex)}d=D(a);c=d.createRange();c.selectNode(a);c.collapse(i);d=d.createRange();d.selectNode(b);d.collapse(i); -return c.compareBoundaryPoints(q.Range.START_TO_END,d)}function eb(a,b){var c=a.parentNode;if(c==b)return-1;for(var d=b;d.parentNode!=c;)d=d.parentNode;return db(d,a)}function db(a,b){for(var c=b;c=c.previousSibling;)if(c==a)return-1;return 1} -function fb(a){var b,c=arguments.length;if(c){if(1==c)return arguments[0]}else return k;var d=[],e=Infinity;for(b=0;b<c;b++){for(var f=[],j=arguments[b];j;)f.unshift(j),j=j.parentNode;d.push(f);e=Math.min(e,f.length)}f=k;for(b=0;b<e;b++){for(var j=d[0][b],m=1;m<c;m++)if(j!=d[m][b])return f;f=j}return f}function D(a){return 9==a.nodeType?a:a.ownerDocument||a.document}function gb(a,b){var c=[];return hb(a,b,c,i)?c[0]:h} -function hb(a,b,c,d){if(a!=k)for(a=a.firstChild;a;){if(b(a)&&(c.push(a),d)||hb(a,b,c,d))return i;a=a.nextSibling}return l}var ib={SCRIPT:1,STYLE:1,HEAD:1,IFRAME:1,OBJECT:1},jb={IMG:" ",BR:"\n"};function kb(a,b,c){if(!(a.nodeName in ib))if(a.nodeType==C)c?b.push((""+a.nodeValue).replace(/(\r\n|\r|\n)/g,"")):b.push(a.nodeValue);else if(a.nodeName in jb)b.push(jb[a.nodeName]);else for(a=a.firstChild;a;)kb(a,b,c),a=a.nextSibling} -function ab(a){if(a&&"number"==typeof a.length){if(ca(a))return"function"==typeof a.item||"string"==typeof a.item;if(w(a))return"function"==typeof a.item}return l}function lb(a,b){for(var a=a.parentNode,c=0;a;){if(b(a))return a;a=a.parentNode;c++}return k}function Xa(a){this.v=a||q.document||document}p=Xa.prototype;p.ea=n("v");p.z=function (a){return t(a)?this.v.getElementById(a):a}; -p.da=function (a,b,c){var d=this.v,e=arguments,f=e[1],j=d.createElement(e[0]);f&&(t(f)?j.className=f:"array"==aa(f)?Ua.apply(k,[j].concat(f)):Ya(j,f));2<e.length&&$a(d,j,e);return j};p.createElement=function (a){return this.v.createElement(a)};p.createTextNode=function (a){return this.v.createTextNode(a)};p.qa=function(){return this.v.parentWindow||this.v.defaultView}; -function mb(a){var b=a.v,a="CSS1Compat"==b.compatMode?b.documentElement:b.body,b=b.parentWindow||b.defaultView;return new B(b.pageXOffset||a.scrollLeft,b.pageYOffset||a.scrollTop)}p.appendChild=function (a,b){a.appendChild(b)};p.removeNode=bb;p.contains=G;var H={};H.ya=function(){var a={Oa:"http://www.w3.org/2000/svg"};return function (b){return a[b]||k}}();H.ma=function (a,b,c){var d=D(a);if(!d.implementation.hasFeature("XPath","3.0"))return k;try{var e=d.createNSResolver?d.createNSResolver(d.documentElement):H.ya;return d.evaluate(b,a,e,c,k)}catch(f){"NS_ERROR_ILLEGAL_VALUE"!=f.name&&g(new y(32,"Unable to locate an element with the xpath expression "+b+" because of the following error:\n"+f))}}; -H.ka=function (a,b){(!a||1!=a.nodeType)&&g(new y(32,'The result of the xpath expression "'+b+'" is: '+a+". It should be an element."))};H.Ia=function (a,b){var c=function(){var c=H.ma(b,a,9);return c?c.singleNodeValue||k:b.selectSingleNode?(c=D(b),c.setProperty&&c.setProperty("SelectionLanguage","XPath"),b.selectSingleNode(a)):k}();c===k||H.ka(c,a);return c}; -H.Na=function (a,b){var c=function(){var c=H.ma(b,a,7);if(c){for(var e=c.snapshotLength,f=[],j=0;j<e;++j)f.push(c.snapshotItem(j));return f}return b.selectNodes?(c=D(b),c.setProperty&&c.setProperty("SelectionLanguage","XPath"),b.selectNodes(a)):[]}();La(c,function (b){H.ka(b,a)});return c};var nb,ob="",pb=/Firefox\/([0-9.]+)/.exec(ua());nb=ob=pb?pb[2]||pb[1]:"";var qb=k,rb=function(){var a=q.Components;if(!a)return l;try{if(!a.classes)return l}catch(b){return l}var c=a.classes,a=a.interfaces,d=c["@mozilla.org/xpcom/version-comparator;1"].getService(a.nsIVersionComparator),e=c["@mozilla.org/xre/app-info;1"].getService(a.nsIXULAppInfo).version;qb=function (a){return 0<=d.Ka(e,""+a)};return i}();var I="StopIteration"in q?q.StopIteration:Error("StopIteration");function J(){}J.prototype.next=function(){g(I)};J.prototype.r=function(){return this};function sb(a){if(a instanceof J)return a;if("function"==typeof a.r)return a.r(l);if(ba(a)){var b=0,c=new J;c.next=function(){for(;;){b>=a.length&&g(I);if(b in a)return a[b++];b++}};return c}g(Error("Not implemented"))};function K(a,b,c,d,e){this.o=!!b;a&&L(this,a,d);this.depth=e!=h?e:this.q||0;this.o&&(this.depth*=-1);this.za=!c}x(K,J);p=K.prototype;p.p=k;p.q=0;p.ha=l;function L(a,b,c,d){if(a.p=b)a.q="number"==typeof c?c:1!=a.p.nodeType?0:a.o?-1:1;"number"==typeof d&&(a.depth=d)} -p.next=function(){var a;if(this.ha){(!this.p||this.za&&0==this.depth)&&g(I);a=this.p;var b=this.o?-1:1;if(this.q==b){var c=this.o?a.lastChild:a.firstChild;c?L(this,c):L(this,a,-1*b)}else(c=this.o?a.previousSibling:a.nextSibling)?L(this,c):L(this,a.parentNode,-1*b);this.depth+=this.q*(this.o?-1:1)}else this.ha=i;(a=this.p)||g(I);return a}; -p.splice=function (a){var b=this.p,c=this.o?1:-1;this.q==c&&(this.q=-1*c,this.depth+=this.q*(this.o?-1:1));this.o=!this.o;K.prototype.next.call(this);this.o=!this.o;for(var c=ba(arguments[0])?arguments[0]:arguments,d=c.length-1;0<=d;d--)b.parentNode&&b.parentNode.insertBefore(c[d],b.nextSibling);bb(b)};function tb(a,b,c,d){K.call(this,a,b,c,k,d)}x(tb,K);tb.prototype.next=function(){do tb.$.next.call(this);while(-1==this.q);return this.p};function ub(a,b){var c=D(a);return c.defaultView&&c.defaultView.getComputedStyle&&(c=c.defaultView.getComputedStyle(a,k))?c[b]||c.getPropertyValue(b):""}function vb(a,b){return ub(a,b)||(a.currentStyle?a.currentStyle[b]:k)||a.style&&a.style[b]} -function wb(a){for(var b=D(a),c=vb(a,"position"),d="fixed"==c||"absolute"==c,a=a.parentNode;a&&a!=b;a=a.parentNode)if(c=vb(a,"position"),d=d&&"static"==c&&a!=b.documentElement&&a!=b.body,!d&&(a.scrollWidth>a.clientWidth||a.scrollHeight>a.clientHeight||"fixed"==c||"absolute"==c||"relative"==c))return a;return k} -function xb(a){var b=new B;if(1==a.nodeType)if(a.getBoundingClientRect)a=a.getBoundingClientRect(),b.x=a.left,b.y=a.top;else{var c=mb(Wa(a));var d,e=D(a),f=vb(a,"position"),j=e.getBoxObjectFor&&!a.getBoundingClientRect&&"absolute"==f&&(d=e.getBoxObjectFor(a))&&(0>d.screenX||0>d.screenY),f=new B(0,0),m=(e?9==e.nodeType?e:D(e):document).documentElement;if(a!=m)if(a.getBoundingClientRect)d=a.getBoundingClientRect(),a=mb(Wa(e)),f.x=d.left+a.x,f.y=d.top+a.y;else if(e.getBoxObjectFor&&!j)d=e.getBoxObjectFor(a), -a=e.getBoxObjectFor(m),f.x=d.screenX-a.screenX,f.y=d.screenY-a.screenY;else{d=a;do f.x+=d.offsetLeft,f.y+=d.offsetTop,d!=a&&(f.x+=d.clientLeft||0,f.y+=d.clientTop||0),d=d.offsetParent;while(d&&d!=a);for(d=a;(d=wb(d))&&d!=e.body&&d!=m;)f.x-=d.scrollLeft,f.y-=d.scrollTop}b.x=f.x-c.x;b.y=f.y-c.y}else c=w(a.pa),d=a,a.targetTouches?d=a.targetTouches[0]:c&&a.pa().targetTouches&&(d=a.pa().targetTouches[0]),b.x=d.clientX,b.y=d.clientY;return b} -function yb(a){var b=a.offsetWidth,c=a.offsetHeight;return!r(b)&&a.getBoundingClientRect?(a=a.getBoundingClientRect(),new Va(a.right-a.left,a.bottom-a.top)):new Va(b,c)};function M(a,b){return!!a&&1==a.nodeType&&(!b||a.tagName.toUpperCase()==b)}var zb={"class":"className",readonly:"readOnly"},Ab=["checked","disabled","draggable","hidden"];function Bb(a,b){var c=zb[b]||b,d=a[c];if(!r(d)&&0<=A(Ab,c))return l;if(c="value"==b)if(c=M(a,"OPTION")){var e;c=b.toLowerCase();if(a.hasAttribute)e=a.hasAttribute(c);else try{e=a.attributes[c].specified}catch(f){e=l}c=!e}c&&(d=[],kb(a,d,l),d=d.join(""));return d} -var Cb="async,autofocus,autoplay,checked,compact,complete,controls,declare,defaultchecked,defaultselected,defer,disabled,draggable,ended,formnovalidate,hidden,indeterminate,iscontenteditable,ismap,itemscope,loop,multiple,muted,nohref,noresize,noshade,novalidate,nowrap,open,paused,pubdate,readonly,required,reversed,scoped,seamless,seeking,selected,spellcheck,truespeed,willvalidate".split(","),Db="BUTTON,INPUT,OPTGROUP,OPTION,SELECT,TEXTAREA".split(","); -function Eb(a){var b=a.tagName.toUpperCase();return!(0<=A(Db,b))?i:Bb(a,"disabled")?l:a.parentNode&&1==a.parentNode.nodeType&&"OPTGROUP"==b||"OPTION"==b?Eb(a.parentNode):i}var Fb="text,search,tel,url,email,password,number".split(",");function Gb(a){return M(a,"TEXTAREA")?i:M(a,"INPUT")?0<=A(Fb,a.type.toLowerCase()):Hb(a)?i:l} -function Hb(a){function b(a){return"inherit"==a.contentEditable?(a=Ib(a))?b(a):l:"true"==a.contentEditable}return!r(a.contentEditable)?l:r(a.isContentEditable)?a.isContentEditable:b(a)}function Ib(a){for(a=a.parentNode;a&&1!=a.nodeType&&9!=a.nodeType&&11!=a.nodeType;)a=a.parentNode;return M(a)?a:k}function Jb(a,b){b=ra(b);return ub(a,b)||Kb(a,b)} -function Kb(a,b){var c=a.currentStyle||a.style,d=c[b];!r(d)&&w(c.getPropertyValue)&&(d=c.getPropertyValue(b));return"inherit"!=d?r(d)?d:k:(c=Ib(a))?Kb(c,b):k}function Lb(a){if(w(a.getBBox))try{var b=a.getBBox();if(b)return b}catch(c){}if("none"!=vb(a,"display"))a=yb(a);else{var b=a.style,d=b.display,e=b.visibility,f=b.position;b.visibility="hidden";b.position="absolute";b.display="inline";a=yb(a);b.display=d;b.position=f;b.visibility=e}return a} -function Mb(a,b){function c(a){if("none"==Jb(a,"display"))return l;a=Ib(a);return!a||c(a)}function d(a){var b=Lb(a);return 0<b.height&&0<b.width?i:Na(a.childNodes,function (a){return a.nodeType==C||M(a)&&d(a)})}function e(a){var b=Ib(a);if(b&&"hidden"==Jb(b,"overflow")){var c=Lb(b),d=xb(b),a=xb(a);return d.x+c.width<a.x||d.y+c.height<a.y?l:e(b)}return i}M(a)||g(Error("Argument to isShown must be of type Element"));if(M(a,"OPTION")||M(a,"OPTGROUP")){var f=lb(a,function (a){return M(a,"SELECT")});return!!f&& -Mb(f,i)}if(M(a,"MAP")){if(!a.name)return l;f=D(a);f=f.evaluate?H.Ia('/descendant::*[@usemap = "#'+a.name+'"]',f):gb(f,function (b){var c;if(c=M(b))8==b.nodeType?b=k:(c="usemap","style"==c?(b=ha(b.style.cssText).toLowerCase(),b=";"==b.charAt(b.length-1)?b:b+";"):(b=b.getAttributeNode(c),b=!b?k:0<=A(Cb,c)?"true":b.specified?b.value:k)),c=b=="#"+a.name;return c});return!!f&&Mb(f,b)}return M(a,"AREA")?(f=lb(a,function (a){return M(a,"MAP")}),!!f&&Mb(f,b)):M(a,"INPUT")&&"hidden"==a.type.toLowerCase()||M(a, -"NOSCRIPT")||"hidden"==Jb(a,"visibility")||!c(a)||!b&&0==Nb(a)||!d(a)||!e(a)?l:i}function Nb(a){var b=1,c=Jb(a,"opacity");c&&(b=Number(c));(a=Ib(a))&&(b*=Nb(a));return b};function N(){this.w=Ca.document.documentElement;this.ua=k;var a=D(this.w).activeElement;a&&Ob(this,a)}N.prototype.z=n("w");function Ob(a,b){a.w=b;a.ua=M(b,"OPTION")?lb(b,function (a){return M(a,"SELECT")}):k} -function Pb(a,b,c,d,e,f){function j(a,c){var d={identifier:a,screenX:c.x,screenY:c.y,clientX:c.x,clientY:c.y,pageX:c.x,pageY:c.y};m.changedTouches.push(d);if(b==Qb||b==Rb)m.touches.push(d),m.targetTouches.push(d)}var m={touches:[],targetTouches:[],changedTouches:[],altKey:l,ctrlKey:l,shiftKey:l,metaKey:l,relatedTarget:k,scale:0,rotation:0};j(c,d);r(e)&&j(e,f);Sb(a.w,b,m)}rb&&rb&&(rb?qb(4):oa(nb,4));rb&&(rb?qb(4):oa(nb,4));function P(a,b,c){this.J=a;this.S=b;this.T=c}P.prototype.create=function (a){a=D(a).createEvent("HTMLEvents");a.initEvent(this.J,this.S,this.T);return a};P.prototype.toString=n("J");function Q(a,b,c){P.call(this,a,b,c)}x(Q,P); -Q.prototype.create=function (a,b){var c=D(a),d=F(c),c=c.createEvent("MouseEvents"),e=1;this==Tb&&(e=b.wheelDelta/-40);this==Ub&&(e=b.wheelDelta);c.initMouseEvent(this.J,this.S,this.T,d,e,0,0,b.clientX,b.clientY,b.ctrlKey,b.altKey,b.shiftKey,b.metaKey,b.button,b.relatedTarget);return c};function Vb(a,b,c){P.call(this,a,b,c)}x(Vb,P); -Vb.prototype.create=function (a,b){var c=D(a),d=F(c),e=b.charCode?0:b.keyCode,c=c.createEvent("KeyboardEvent");c.initKeyEvent(this.J,this.S,this.T,d,b.ctrlKey,b.altKey,b.shiftKey,b.metaKey,e,b.charCode);this.J==Wb&&b.preventDefault&&c.preventDefault();return c};function Xb(a,b,c){P.call(this,a,b,c)}x(Xb,P); -Xb.prototype.create=function (a,b){function c(b){var c=Ma(b,function (b){return{identifier:b.identifier,screenX:b.screenX,screenY:b.screenY,clientX:b.clientX,clientY:b.clientY,pageX:b.pageX,pageY:b.pageY,target:a}});c.item=function (a){return c[a]};return c}var d=D(a),e=F(d),f=c(b.changedTouches),j=b.touches==b.changedTouches?f:c(b.touches),m=b.targetTouches==b.changedTouches?f:c(b.targetTouches),d=d.createEvent("MouseEvents");d.initMouseEvent(this.J,this.S,this.T,e,1,0,0,b.clientX,b.clientY,b.ctrlKey, -b.altKey,b.shiftKey,b.metaKey,0,b.relatedTarget);d.touches=j;d.targetTouches=m;d.changedTouches=f;d.scale=b.scale;d.rotation=b.rotation;return d}; -var Yb=new P("change",i,l),Zb=new Q("click",i,i),$b=new Q("contextmenu",i,i),ac=new Q("dblclick",i,i),bc=new Q("mousedown",i,i),cc=new Q("mousemove",i,l),dc=new Q("mouseout",i,i),ec=new Q("mouseover",i,i),fc=new Q("mouseup",i,i),Tb=new Q("DOMMouseScroll",i,i),Ub=new Q("MozMousePixelScroll",i,i),Wb=new Vb("keypress",i,i),Rb=new Xb("touchmove",i,i),Qb=new Xb("touchstart",i,i);function Sb(a,b,c){b=b.create(a,c);"isTrusted"in b||(b.La=l);a.dispatchEvent(b)};function gc(a){if("function"==typeof a.L)return a.L();if(t(a))return a.split("");if(ba(a)){for(var b=[],c=a.length,d=0;d<c;d++)b.push(a[d]);return b}return Ea(a)};function hc(a,b){this.n={};this.ta={};var c=arguments.length;if(1<c){c%2&&g(Error("Uneven number of arguments"));for(var d=0;d<c;d+=2)this.set(arguments[d],arguments[d+1])}else a&&this.aa(a)}p=hc.prototype;p.ia=0;p.L=function(){var a=[],b;for(b in this.n)":"==b.charAt(0)&&a.push(this.n[b]);return a};function ic(a){var b=[],c;for(c in a.n)if(":"==c.charAt(0)){var d=c.substring(1);b.push(a.ta[c]?Number(d):d)}return b} -p.set=function (a,b){var c=":"+a;c in this.n||(this.ia++,"number"==typeof a&&(this.ta[c]=i));this.n[c]=b};p.aa=function (a){var b;if(a instanceof hc)b=ic(a),a=a.L();else{b=[];var c=0,d;for(d in a)b[c++]=d;a=Ea(a)}for(c=0;c<b.length;c++)this.set(b[c],a[c])};p.r=function (a){var b=0,c=ic(this),d=this.n,e=this.ia,f=this,j=new J;j.next=function(){for(;;){e!=f.ia&&g(Error("The map has changed since the iterator was created"));b>=c.length&&g(I);var j=c[b++];return a?j:d[":"+j]}};return j};function jc(a){this.n=new hc;a&&this.aa(a)}function kc(a){var b=typeof a;return"object"==b&&a||"function"==b?"o"+(a[da]||(a[da]=++ea)):b.substr(0,1)+a}p=jc.prototype;p.add=function (a){this.n.set(kc(a),a)};p.aa=function (a){for(var a=gc(a),b=a.length,c=0;c<b;c++)this.add(a[c])};p.contains=function (a){return":"+kc(a)in this.n.n};p.L=function(){return this.n.L()};p.r=function(){return this.n.r(l)};function lc(){N.call(this);Gb(this.z())&&Bb(this.z(),"readOnly");this.Ha=new jc}x(lc,N);var mc={};function R(a,b,c){ca(a)&&(a=a.c);a=new nc(a);if(b&&(!(b in mc)||c))mc[b]={key:a,shift:l},c&&(mc[c]={key:a,shift:i})}function nc(a){this.code=a}R(8);R(9);R(13);R(16);R(17);R(18);R(19);R(20);R(27);R(32," ");R(33);R(34);R(35);R(36);R(37);R(38);R(39);R(40);R(44);R(45);R(46);R(48,"0",")");R(49,"1","!");R(50,"2","@");R(51,"3","#");R(52,"4","$");R(53,"5","%");R(54,"6","^");R(55,"7","&");R(56,"8","*"); -R(57,"9","(");R(65,"a","A");R(66,"b","B");R(67,"c","C");R(68,"d","D");R(69,"e","E");R(70,"f","F");R(71,"g","G");R(72,"h","H");R(73,"i","I");R(74,"j","J");R(75,"k","K");R(76,"l","L");R(77,"m","M");R(78,"n","N");R(79,"o","O");R(80,"p","P");R(81,"q","Q");R(82,"r","R");R(83,"s","S");R(84,"t","T");R(85,"u","U");R(86,"v","V");R(87,"w","W");R(88,"x","X");R(89,"y","Y");R(90,"z","Z");R(ta?{c:91,e:91,opera:219}:sa?{c:224,e:91,opera:17}:{c:0,e:91,opera:k}); -R(ta?{c:92,e:92,opera:220}:sa?{c:224,e:93,opera:17}:{c:0,e:92,opera:k});R(ta?{c:93,e:93,opera:0}:sa?{c:0,e:0,opera:16}:{c:93,e:k,opera:0});R({c:96,e:96,opera:48},"0");R({c:97,e:97,opera:49},"1");R({c:98,e:98,opera:50},"2");R({c:99,e:99,opera:51},"3");R({c:100,e:100,opera:52},"4");R({c:101,e:101,opera:53},"5");R({c:102,e:102,opera:54},"6");R({c:103,e:103,opera:55},"7");R({c:104,e:104,opera:56},"8");R({c:105,e:105,opera:57},"9");R({c:106,e:106,opera:xa?56:42},"*");R({c:107,e:107,opera:xa?61:43},"+"); -R({c:109,e:109,opera:xa?109:45},"-");R({c:110,e:110,opera:xa?190:78},".");R({c:111,e:111,opera:xa?191:47},"/");R(144);R(112);R(113);R(114);R(115);R(116);R(117);R(118);R(119);R(120);R(121);R(122);R(123);R({c:107,e:187,opera:61},"=","+");R({c:109,e:189,opera:109},"-","_");R(188,",","<");R(190,".",">");R(191,"/","?");R(192,"`","~");R(219,"[","{");R(220,"\\","|");R(221,"]","}");R({c:59,e:186,opera:59},";",":");R(222,"'",'"');lc.prototype.X=function (a){return this.Ha.contains(a)};function oc(a){return pc(a||arguments.callee.caller,[])} -function pc(a,b){var c=[];if(0<=A(b,a))c.push("[...circular reference...]");else if(a&&50>b.length){c.push(qc(a)+"(");for(var d=a.arguments,e=0;e<d.length;e++){0<e&&c.push(", ");var f;f=d[e];switch(typeof f){case "object":f=f?"object":"null";break;case "string":break;case "number":f=""+f;break;case "boolean":f=f?"true":"false";break;case "function":f=(f=qc(f))?f:"[fn]";break;default:f=typeof f}40<f.length&&(f=f.substr(0,40)+"...");c.push(f)}b.push(a);c.push(")\n");try{c.push(pc(a.caller,b))}catch(j){c.push("[exception trying to get caller]\n")}}else a? -c.push("[...long stack...]"):c.push("[end]");return c.join("")}function qc(a){if(rc[a])return rc[a];a=""+a;if(!rc[a]){var b=/function ([^\(]+)/.exec(a);rc[a]=b?b[1]:"[Anonymous]"}return rc[a]}var rc={};function sc(a,b,c,d,e){this.reset(a,b,c,d,e)}sc.prototype.oa=k;sc.prototype.na=k;var tc=0;sc.prototype.reset=function (a,b,c,d,e){"number"==typeof e||tc++;d||fa();this.N=a;this.Fa=b;delete this.oa;delete this.na};sc.prototype.va=function (a){this.N=a};function S(a){this.Ga=a}S.prototype.Y=k;S.prototype.N=k;S.prototype.ba=k;S.prototype.ra=k;function uc(a,b){this.name=a;this.value=b}uc.prototype.toString=n("name");var vc=new uc("WARNING",900),wc=new uc("CONFIG",700);S.prototype.getParent=n("Y");S.prototype.va=function (a){this.N=a};function xc(a){if(a.N)return a.N;if(a.Y)return xc(a.Y);Ja("Root logger has no level set.");return k} -S.prototype.log=function (a,b,c){if(a.value>=xc(this).value){a=this.Ca(a,b,c);b="log:"+a.Fa;q.console&&(q.console.timeStamp?q.console.timeStamp(b):q.console.markTimeline&&q.console.markTimeline(b));q.msWriteProfilerMark&&q.msWriteProfilerMark(b);for(b=this;b;){var c=b,d=a;if(c.ra)for(var e=0,f=h;f=c.ra[e];e++)f(d);b=b.getParent()}}}; -S.prototype.Ca=function (a,b,c){var d=new sc(a,""+b,this.Ga);if(c){d.oa=c;var e;var f=arguments.callee.caller;try{var j;var m;c:{for(var s=["window","location","href"],O=q,E;E=s.shift();)if(O[E]!=k)O=O[E];else{m=k;break c}m=O}if(t(c))j={message:c,name:"Unknown error",lineNumber:"Not available",fileName:m,stack:"Not available"};else{var u,v,s=l;try{u=c.lineNumber||c.Ma||"Not available"}catch(md){u="Not available",s=i}try{v=c.fileName||c.filename||c.sourceURL||m}catch(nd){v="Not available",s=i}j=s|| -!c.lineNumber||!c.fileName||!c.stack?{message:c.message,name:c.name,lineNumber:u,fileName:v,stack:c.stack||"Not available"}:c}e="Message: "+ia(j.message)+'\nUrl: <a href="view-source:'+j.fileName+'" target="_new">'+j.fileName+"</a>\nLine: "+j.lineNumber+"\n\nBrowser stack:\n"+ia(j.stack+"-> ")+"[end]\n\nJS stack traversal:\n"+ia(oc(f)+"-> ")}catch(kd){e="Exception trying to expose exception! You win, we lose. "+kd}d.na=e}return d};var yc={},zc=k; -function Ac(a){zc||(zc=new S(""),yc[""]=zc,zc.va(wc));var b;if(!(b=yc[a])){b=new S(a);var c=a.lastIndexOf("."),d=a.substr(c+1),c=Ac(a.substr(0,c));c.ba||(c.ba={});c.ba[d]=b;b.Y=c;yc[a]=b}return b};function Bc(){}x(Bc,function(){});Ac("goog.dom.SavedRange");x(function (a){this.Ja="goog_"+pa++;this.Aa="goog_"+pa++;this.la=Wa(a.ea());a.R(this.la.da("SPAN",{id:this.Ja}),this.la.da("SPAN",{id:this.Aa}))},Bc);function T(){}function Cc(a){if(a.getSelection)return a.getSelection();var a=a.document,b=a.selection;if(b){try{var c=b.createRange();if(c.parentElement){if(c.parentElement().document!=a)return k}else if(!c.length||c.item(0).document!=a)return k}catch(d){return k}return b}return k}function Dc(a){for(var b=[],c=0,d=a.D();c<d;c++)b.push(a.A(c));return b}T.prototype.F=o(l);T.prototype.ea=function(){return D(this.b())};T.prototype.qa=function(){return F(this.ea())}; -T.prototype.containsNode=function (a,b){return this.u(Ec(Fc(a),h),b)};function U(a,b){K.call(this,a,b,i)}x(U,K);function V(){}x(V,T);V.prototype.u=function (a,b){var c=Dc(this),d=Dc(a);return(b?Na:Oa)(d,function (a){return Na(c,function (c){return c.u(a,b)})})};V.prototype.insertNode=function (a,b){if(b){var c=this.b();c.parentNode&&c.parentNode.insertBefore(a,c)}else c=this.g(),c.parentNode&&c.parentNode.insertBefore(a,c.nextSibling);return a};V.prototype.R=function (a,b){this.insertNode(a,i);this.insertNode(b,l)};function Gc(a,b,c,d,e){var f;if(a&&(this.f=a,this.i=b,this.d=c,this.h=d,1==a.nodeType&&"BR"!=a.tagName&&(a=a.childNodes,(b=a[b])?(this.f=b,this.i=0):(a.length&&(this.f=z(a)),f=i)),1==c.nodeType))(this.d=c.childNodes[d])?this.h=0:this.d=c;U.call(this,e?this.d:this.f,e);if(f)try{this.next()}catch(j){j!=I&&g(j)}}x(Gc,U);p=Gc.prototype;p.f=k;p.d=k;p.i=0;p.h=0;p.b=n("f");p.g=n("d");p.M=function(){return this.ha&&this.p==this.d&&(!this.h||1!=this.q)};p.next=function(){this.M()&&g(I);return Gc.$.next.call(this)};"ScriptEngine"in q&&"JScript"==q.ScriptEngine()&&(q.ScriptEngineMajorVersion(),q.ScriptEngineMinorVersion(),q.ScriptEngineBuildVersion());function Hc(){}Hc.prototype.u=function (a,b){var c=b&&!a.isCollapsed(),d=a.a;try{return c?0<=this.l(d,0,1)&&0>=this.l(d,1,0):0<=this.l(d,0,0)&&0>=this.l(d,1,1)}catch(e){g(e)}};Hc.prototype.containsNode=function (a,b){return this.u(Fc(a),b)};Hc.prototype.r=function(){return new Gc(this.b(),this.j(),this.g(),this.k())};function Ic(a){this.a=a}x(Ic,Hc);p=Ic.prototype;p.C=function(){return this.a.commonAncestorContainer};p.b=function(){return this.a.startContainer};p.j=function(){return this.a.startOffset};p.g=function(){return this.a.endContainer};p.k=function(){return this.a.endOffset};p.l=function (a,b,c){return this.a.compareBoundaryPoints(1==c?1==b?q.Range.START_TO_START:q.Range.START_TO_END:1==b?q.Range.END_TO_START:q.Range.END_TO_END,a)};p.isCollapsed=function(){return this.a.collapsed}; -p.select=function (a){this.Z(F(D(this.b())).getSelection(),a)};p.Z=function (a){a.removeAllRanges();a.addRange(this.a)};p.insertNode=function (a,b){var c=this.a.cloneRange();c.collapse(b);c.insertNode(a);c.detach();return a}; -p.R=function (a,b){var c=F(D(this.b()));if(c=(c=Cc(c||window))&&Jc(c))var d=c.b(),e=c.g(),f=c.j(),j=c.k();var m=this.a.cloneRange(),s=this.a.cloneRange();m.collapse(l);s.collapse(i);m.insertNode(b);s.insertNode(a);m.detach();s.detach();if(c){if(d.nodeType==C)for(;f>d.length;){f-=d.length;do d=d.nextSibling;while(d==a||d==b)}if(e.nodeType==C)for(;j>e.length;){j-=e.length;do e=e.nextSibling;while(e==a||e==b)}c=new Kc;c.G=Lc(d,f,e,j);"BR"==d.tagName&&(m=d.parentNode,f=A(m.childNodes,d),d=m);"BR"==e.tagName&& -(m=e.parentNode,j=A(m.childNodes,e),e=m);c.G?(c.f=e,c.i=j,c.d=d,c.h=f):(c.f=d,c.i=f,c.d=e,c.h=j);c.select()}};p.collapse=function (a){this.a.collapse(a)};function W(a){this.a=a}x(W,Ic);function Fc(a){var b=D(a).createRange();if(a.nodeType==C)b.setStart(a,0),b.setEnd(a,a.length);else if(X(a)){for(var c,d=a;(c=d.firstChild)&&X(c);)d=c;b.setStart(d,0);for(d=a;(c=d.lastChild)&&X(c);)d=c;b.setEnd(d,1==d.nodeType?d.childNodes.length:d.length)}else c=a.parentNode,a=A(c.childNodes,a),b.setStart(c,a),b.setEnd(c,a+1);return new W(b)} -W.prototype.Z=function (a,b){var c=b?this.g():this.b(),d=b?this.k():this.j(),e=b?this.b():this.g(),f=b?this.j():this.k();a.collapse(c,d);(c!=e||d!=f)&&a.extend(e,f)};function Mc(a){this.a=a}x(Mc,Hc);Ac("goog.dom.browserrange.IeRange");function Nc(a){var b=D(a).body.createTextRange();if(1==a.nodeType)b.moveToElementText(a),X(a)&&!a.childNodes.length&&b.collapse(l);else{for(var c=0,d=a;d=d.previousSibling;){var e=d.nodeType;if(e==C)c+=d.length;else if(1==e){b.moveToElementText(d);break}}d||b.moveToElementText(a.parentNode);b.collapse(!d);c&&b.move("character",c);b.moveEnd("character",a.length)}return b}p=Mc.prototype;p.O=k;p.f=k;p.d=k;p.i=-1;p.h=-1; -p.s=function(){this.O=this.f=this.d=k;this.i=this.h=-1}; -p.C=function(){if(!this.O){var a=this.a.text,b=this.a.duplicate(),c=a.replace(/ +$/,"");(c=a.length-c.length)&&b.moveEnd("character",-c);c=b.parentElement();b=b.htmlText.replace(/(\r\n|\r|\n)+/g," ").length;if(this.isCollapsed()&&0<b)return this.O=c;for(;b>c.outerHTML.replace(/(\r\n|\r|\n)+/g," ").length;)c=c.parentNode;for(;1==c.childNodes.length&&c.innerText==(c.firstChild.nodeType==C?c.firstChild.nodeValue:c.firstChild.innerText)&&X(c.firstChild);)c=c.firstChild;0==a.length&&(c=Oc(this,c));this.O= -c}return this.O};function Oc(a,b){for(var c=b.childNodes,d=0,e=c.length;d<e;d++){var f=c[d];if(X(f)){var j=Nc(f),m=j.htmlText!=f.outerHTML;if(a.isCollapsed()&&m?0<=a.l(j,1,1)&&0>=a.l(j,1,0):a.a.inRange(j))return Oc(a,f)}}return b}p.b=function(){this.f||(this.f=Pc(this,1),this.isCollapsed()&&(this.d=this.f));return this.f};p.j=function(){0>this.i&&(this.i=Qc(this,1),this.isCollapsed()&&(this.h=this.i));return this.i}; -p.g=function(){if(this.isCollapsed())return this.b();this.d||(this.d=Pc(this,0));return this.d};p.k=function(){if(this.isCollapsed())return this.j();0>this.h&&(this.h=Qc(this,0),this.isCollapsed()&&(this.i=this.h));return this.h};p.l=function (a,b,c){return this.a.compareEndPoints((1==b?"Start":"End")+"To"+(1==c?"Start":"End"),a)}; -function Pc(a,b,c){c=c||a.C();if(!c||!c.firstChild)return c;for(var d=1==b,e=0,f=c.childNodes.length;e<f;e++){var j=d?e:f-e-1,m=c.childNodes[j],s;try{s=Fc(m)}catch(O){continue}var E=s.a;if(a.isCollapsed())if(X(m)){if(s.u(a))return Pc(a,b,m)}else{if(0==a.l(E,1,1)){a.i=a.h=j;break}}else{if(a.u(s)){if(!X(m)){d?a.i=j:a.h=j+1;break}return Pc(a,b,m)}if(0>a.l(E,1,0)&&0<a.l(E,0,1))return Pc(a,b,m)}}return c} -function Qc(a,b){var c=1==b,d=c?a.b():a.g();if(1==d.nodeType){for(var d=d.childNodes,e=d.length,f=c?1:-1,j=c?0:e-1;0<=j&&j<e;j+=f){var m=d[j];if(!X(m)&&0==a.a.compareEndPoints((1==b?"Start":"End")+"To"+(1==b?"Start":"End"),Fc(m).a))return c?j:j+1}return-1==j?0:j}e=a.a.duplicate();f=Nc(d);e.setEndPoint(c?"EndToEnd":"StartToStart",f);e=e.text.length;return c?d.length-e:e}p.isCollapsed=function(){return 0==this.a.compareEndPoints("StartToEnd",this.a)};p.select=function(){this.a.select()}; -function Rc(a,b,c){var d;d=d||Wa(a.parentElement());var e;1!=b.nodeType&&(e=i,b=d.da("DIV",k,b));a.collapse(c);d=d||Wa(a.parentElement());var f=c=b.id;c||(c=b.id="goog_"+pa++);a.pasteHTML(b.outerHTML);(b=d.z(c))&&(f||b.removeAttribute("id"));if(e){a=b.firstChild;e=b;if((d=e.parentNode)&&11!=d.nodeType)if(e.removeNode)e.removeNode(l);else{for(;b=e.firstChild;)d.insertBefore(b,e);bb(e)}b=a}return b}p.insertNode=function (a,b){var c=Rc(this.a.duplicate(),a,b);this.s();return c}; -p.R=function (a,b){var c=this.a.duplicate(),d=this.a.duplicate();Rc(c,a,i);Rc(d,b,l);this.s()};p.collapse=function (a){this.a.collapse(a);a?(this.d=this.f,this.h=this.i):(this.f=this.d,this.i=this.h)};function Sc(a){this.a=a}x(Sc,Ic);Sc.prototype.Z=function (a){a.collapse(this.b(),this.j());(this.g()!=this.b()||this.k()!=this.j())&&a.extend(this.g(),this.k());0==a.rangeCount&&a.addRange(this.a)};function Tc(a){this.a=a}x(Tc,Ic);Tc.prototype.l=function (a,b,c){return Ba["528"]||(Ba["528"]=0<=oa(ya,"528"))?Tc.$.l.call(this,a,b,c):this.a.compareBoundaryPoints(1==c?1==b?q.Range.START_TO_START:q.Range.END_TO_START:1==b?q.Range.START_TO_END:q.Range.END_TO_END,a)};Tc.prototype.Z=function (a,b){a.removeAllRanges();b?a.setBaseAndExtent(this.g(),this.k(),this.b(),this.j()):a.setBaseAndExtent(this.b(),this.j(),this.g(),this.k())};function X(a){var b;a:if(1!=a.nodeType)b=l;else{switch(a.tagName){case "APPLET":case "AREA":case "BASE":case "BR":case "COL":case "FRAME":case "HR":case "IMG":case "INPUT":case "IFRAME":case "ISINDEX":case "LINK":case "NOFRAMES":case "NOSCRIPT":case "META":case "OBJECT":case "PARAM":case "SCRIPT":case "STYLE":b=l;break a}b=i}return b||a.nodeType==C};function Kc(){}x(Kc,T);function Ec(a,b){var c=new Kc;c.K=a;c.G=!!b;return c}p=Kc.prototype;p.K=k;p.f=k;p.i=k;p.d=k;p.h=k;p.G=l;p.fa=o("text");p.W=function(){return Y(this).a};p.s=function(){this.f=this.i=this.d=this.h=k};p.D=o(1);p.A=function(){return this};function Y(a){var b;if(!(b=a.K)){b=a.b();var c=a.j(),d=a.g(),e=a.k(),f=D(b).createRange();f.setStart(b,c);f.setEnd(d,e);b=a.K=new W(f)}return b}p.C=function(){return Y(this).C()};p.b=function(){return this.f||(this.f=Y(this).b())}; -p.j=function(){return this.i!=k?this.i:this.i=Y(this).j()};p.g=function(){return this.d||(this.d=Y(this).g())};p.k=function(){return this.h!=k?this.h:this.h=Y(this).k()};p.F=n("G");p.u=function (a,b){var c=a.fa();return"text"==c?Y(this).u(Y(a),b):"control"==c?(c=Uc(a),(b?Na:Oa)(c,function (a){return this.containsNode(a,b)},this)):l};p.isCollapsed=function(){return Y(this).isCollapsed()};p.r=function(){return new Gc(this.b(),this.j(),this.g(),this.k())};p.select=function(){Y(this).select(this.G)}; -p.insertNode=function (a,b){var c=Y(this).insertNode(a,b);this.s();return c};p.R=function (a,b){Y(this).R(a,b);this.s()};p.ga=function(){return new Vc(this)};p.collapse=function (a){a=this.F()?!a:a;this.K&&this.K.collapse(a);a?(this.d=this.f,this.h=this.i):(this.f=this.d,this.i=this.h);this.G=l};function Vc(a){a.F()?a.g():a.b();a.F()?a.k():a.j();a.F()?a.b():a.g();a.F()?a.j():a.k()}x(Vc,Bc);function Wc(){}x(Wc,V);p=Wc.prototype;p.a=k;p.m=k;p.Q=k;p.s=function(){this.Q=this.m=k};p.fa=o("control");p.W=function(){return this.a||document.body.createControlRange()};p.D=function(){return this.a?this.a.length:0};p.A=function (a){a=this.a.item(a);return Ec(Fc(a),h)};p.C=function(){return fb.apply(k,Uc(this))};p.b=function(){return Xc(this)[0]};p.j=o(0);p.g=function(){var a=Xc(this),b=z(a);return Pa(a,function (a){return G(a,b)})};p.k=function(){return this.g().childNodes.length}; -function Uc(a){if(!a.m&&(a.m=[],a.a))for(var b=0;b<a.a.length;b++)a.m.push(a.a.item(b));return a.m}function Xc(a){a.Q||(a.Q=Uc(a).concat(),a.Q.sort(function (a,c){return a.sourceIndex-c.sourceIndex}));return a.Q}p.isCollapsed=function(){return!this.a||!this.a.length};p.r=function(){return new Yc(this)};p.select=function(){this.a&&this.a.select()};p.ga=function(){return new Zc(this)};p.collapse=function(){this.a=k;this.s()};function Zc(a){this.m=Uc(a)}x(Zc,Bc); -function Yc(a){a&&(this.m=Xc(a),this.f=this.m.shift(),this.d=z(this.m)||this.f);U.call(this,this.f,l)}x(Yc,U);p=Yc.prototype;p.f=k;p.d=k;p.m=k;p.b=n("f");p.g=n("d");p.M=function(){return!this.depth&&!this.m.length};p.next=function(){this.M()&&g(I);if(!this.depth){var a=this.m.shift();L(this,a,1,1);return a}return Yc.$.next.call(this)};function $c(){this.t=[];this.P=[];this.U=this.I=k}x($c,V);p=$c.prototype;p.Ea=Ac("goog.dom.MultiRange");p.s=function(){this.P=[];this.U=this.I=k};p.fa=o("mutli");p.W=function(){1<this.t.length&&this.Ea.log(vc,"getBrowserRangeObject called on MultiRange with more than 1 range",h);return this.t[0]};p.D=function(){return this.t.length};p.A=function (a){this.P[a]||(this.P[a]=Ec(new W(this.t[a]),h));return this.P[a]}; -p.C=function(){if(!this.U){for(var a=[],b=0,c=this.D();b<c;b++)a.push(this.A(b).C());this.U=fb.apply(k,a)}return this.U};function ad(a){a.I||(a.I=Dc(a),a.I.sort(function (a,c){var d=a.b(),e=a.j(),f=c.b(),j=c.j();return d==f&&e==j?0:Lc(d,e,f,j)?1:-1}));return a.I}p.b=function(){return ad(this)[0].b()};p.j=function(){return ad(this)[0].j()};p.g=function(){return z(ad(this)).g()};p.k=function(){return z(ad(this)).k()};p.isCollapsed=function(){return 0==this.t.length||1==this.t.length&&this.A(0).isCollapsed()}; -p.r=function(){return new bd(this)};p.select=function(){var a=Cc(this.qa());a.removeAllRanges();for(var b=0,c=this.D();b<c;b++)a.addRange(this.A(b).W())};p.ga=function(){return new cd(this)};p.collapse=function (a){if(!this.isCollapsed()){var b=a?this.A(0):this.A(this.D()-1);this.s();b.collapse(a);this.P=[b];this.I=[b];this.t=[b.W()]}};function cd(a){Ma(Dc(a),function (a){return a.ga()})}x(cd,Bc);function bd(a){a&&(this.H=Ma(ad(a),function (a){return sb(a)}));U.call(this,a?this.b():k,l)}x(bd,U);p=bd.prototype; -p.H=k;p.V=0;p.b=function(){return this.H[0].b()};p.g=function(){return z(this.H).g()};p.M=function(){return this.H[this.V].M()};p.next=function(){try{var a=this.H[this.V],b=a.next();L(this,a.p,a.q,a.depth);return b}catch(c){return(c!==I||this.H.length-1==this.V)&&g(c),this.V++,this.next()}};function Jc(a){var b,c=l;if(a.createRange)try{b=a.createRange()}catch(d){return k}else if(a.rangeCount){if(1<a.rangeCount){b=new $c;for(var c=0,e=a.rangeCount;c<e;c++)b.t.push(a.getRangeAt(c));return b}b=a.getRangeAt(0);c=Lc(a.anchorNode,a.anchorOffset,a.focusNode,a.focusOffset)}else return k;b&&b.addElement?(a=new Wc,a.a=b):a=Ec(new W(b),c);return a} -function Lc(a,b,c,d){if(a==c)return d<b;var e;if(1==a.nodeType&&b)if(e=a.childNodes[b])a=e,b=0;else if(G(a,c))return i;if(1==c.nodeType&&d)if(e=c.childNodes[d])c=e,d=0;else if(G(c,a))return l;return 0<(cb(a,c)||b-d)};function dd(){N.call(this);this.ja=k;this.B=new B(0,0);this.sa=l}x(dd,N);var Z={};Z[Zb]=[0,1,2,k];Z[$b]=[k,k,2,k];Z[fc]=[0,1,2,k];Z[dc]=[0,0,0,0];Z[cc]=[0,0,0,0];Z[ac]=Z[Zb];Z[bc]=Z[fc];Z[ec]=Z[dc];dd.prototype.move=function (a,b){var c=xb(a);this.B.x=b.x+c.x;this.B.y=b.y+c.y;a!=this.z()&&(c=this.z()===Ca.document.documentElement||this.z()===Ca.document.body,c=!this.sa&&c?k:this.z(),ed(this,dc,a),Ob(this,a),ed(this,ec,c));ed(this,cc)}; -function ed(a,b,c){a.sa=i;var d=a.B,e;b in Z?(e=Z[b][a.ja===k?3:a.ja],e===k&&g(new y(13,"Event does not permit the specified mouse button."))):e=0;Mb(a.w,i)&&Eb(a.w)&&(c&&!(ec==b||dc==b)&&g(new y(12,"Event type does not allow related target: "+b)),c={clientX:d.x,clientY:d.y,button:e,altKey:l,ctrlKey:l,shiftKey:l,metaKey:l,wheelDelta:0,relatedTarget:c||k},(a=a.w)&&Sb(a,b,c))};function fd(){N.call(this);this.B=new B(0,0);this.ca=new B(0,0)}x(fd,N);fd.prototype.xa=0;fd.prototype.wa=0;fd.prototype.move=function (a,b,c){this.X()||Ob(this,a);a=xb(a);this.B.x=b.x+a.x;this.B.y=b.y+a.y;r(c)&&(this.ca.x=c.x+a.x,this.ca.y=c.y+a.y);if(this.X()){b=Rb;this.X()||g(new y(13,"Should never fire event when touchscreen is not pressed."));var d,e;this.wa&&(d=this.wa,e=this.ca);Pb(this,b,this.xa,this.B,d,e)}};fd.prototype.X=function(){return!!this.xa};function gd(a,b){this.x=a;this.y=b}x(gd,B);gd.prototype.scale=function (a){this.x*=a;this.y*=a;return this};gd.prototype.add=function (a){this.x+=a.x;this.y+=a.y;return this};function hd(){N.call(this)}x(hd,N);(function (a){a.Ba=function(){return a.Da||(a.Da=new a)}})(hd);function id(a){(!Mb(a,i)||!Eb(a))&&g(new y(12,"Element is not currently interactable and may not be manipulated"));(!Gb(a)||Bb(a,"readOnly"))&&g(new y(12,"Element must be user-editable in order to clear it."));var b=hd.Ba();Ob(b,a);var b=b.ua||b.w,c=D(b).activeElement;if(b!=c){if(c&&w(c.blur))try{c.blur()}catch(d){g(d)}w(b.focus)&&b.focus()}a.value&&(a.value="",Sb(a,Yb));Hb(a)&&(a.innerHTML=" ")}var jd=["_"],$=q;!(jd[0]in $)&&$.execScript&&$.execScript("var "+jd[0]); -for(var ld;jd.length&&(ld=jd.shift());)!jd.length&&r(id)?$[ld]=id:$=$[ld]?$[ld]:$[ld]={};; return this._.apply(null,arguments);}.apply({navigator:typeof window!='undefined'?window.navigator:null}, arguments);} - -atom.getElementAttribute = function (element, name, window){return function(){var f=null,g=!1,h=this; -function i(a){var c=typeof a;if("object"==c)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return c;var b=Object.prototype.toString.call(a);if("[object Window]"==b)return"object";if("[object Array]"==b||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==b||"undefined"!=typeof a.call&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("call"))return"function"}else return"null";else if("function"== -c&&"undefined"==typeof a.call)return"object";return c}function j(a,c){function b(){}b.prototype=c.prototype;a.f=c.prototype;a.prototype=new b};function k(a,c){for(var b=1;b<arguments.length;b++)var d=(""+arguments[b]).replace(/\$/g,"$$$$"),a=a.replace(/\%s/,d);return a}function l(a){return a.replace(/^[\s\xa0]+|[\s\xa0]+$/g,"")};var m,n="",o=/rv\:([^\);]+)(\)|;)/.exec(h.navigator?h.navigator.userAgent:f);m=n=o?o[1]:"";var p={};function q(a,c){this.code=a;this.message=c||"";this.name=r[a]||r[13];var b=Error(this.message);b.name=this.name;this.stack=b.stack||""}j(q,Error); -var r={7:"NoSuchElementError",8:"NoSuchFrameError",9:"UnknownCommandError",10:"StaleElementReferenceError",11:"ElementNotVisibleError",12:"InvalidElementStateError",13:"UnknownError",15:"ElementNotSelectableError",19:"XPathLookupError",23:"NoSuchWindowError",24:"InvalidCookieDomainError",25:"UnableToSetCookieError",26:"ModalDialogOpenedError",27:"NoModalDialogOpenError",28:"ScriptTimeoutError",32:"InvalidSelectorError",33:"SqlDatabaseError",34:"MoveTargetOutOfBoundsError"}; -q.prototype.toString=function(){return"["+this.name+"] "+this.message};function s(a){this.stack=Error().stack||"";a&&(this.message=""+a)}j(s,Error);s.prototype.name="CustomError";function t(a,c){c.unshift(a);s.call(this,k.apply(f,c));c.shift()}j(t,s);t.prototype.name="AssertionError";function u(a,c){if("string"==typeof a)return"string"!=typeof c||1!=c.length?-1:a.indexOf(c,0);for(var b=0;b<a.length;b++)if(b in a&&a[b]===c)return b;return-1};if(!p["1.9.1"]){for(var v=0,w=l(""+m).split("."),x=l("1.9.1").split("."),y=Math.max(w.length,x.length),z=0;0==v&&z<y;z++){var A=w[z]||"",B=x[z]||"",C=RegExp("(\\d*)(\\D*)","g"),D=RegExp("(\\d*)(\\D*)","g");do{var E=C.exec(A)||["","",""],F=D.exec(B)||["","",""];if(0==E[0].length&&0==F[0].length)break;v=((0==E[1].length?0:parseInt(E[1],10))<(0==F[1].length?0:parseInt(F[1],10))?-1:(0==E[1].length?0:parseInt(E[1],10))>(0==F[1].length?0:parseInt(F[1],10))?1:0)||((0==E[2].length)<(0==F[2].length)?-1:(0== -E[2].length)>(0==F[2].length)?1:0)||(E[2]<F[2]?-1:E[2]>F[2]?1:0)}while(0==v)}p["1.9.1"]=0<=v};var G={SCRIPT:1,STYLE:1,HEAD:1,IFRAME:1,OBJECT:1},H={IMG:" ",BR:"\n"};function I(a,c,b){if(!(a.nodeName in G))if(3==a.nodeType)b?c.push((""+a.nodeValue).replace(/(\r\n|\r|\n)/g,"")):c.push(a.nodeValue);else if(a.nodeName in H)c.push(H[a.nodeName]);else for(a=a.firstChild;a;)I(a,c,b),a=a.nextSibling};(function(){var a=h.Components;if(!a)return g;try{if(!a.classes)return g}catch(c){return g}var b=a.classes,a=a.interfaces;b["@mozilla.org/xpcom/version-comparator;1"].getService(a.nsIVersionComparator);b["@mozilla.org/xre/app-info;1"].getService(a.nsIXULAppInfo);return!0})();var J="StopIteration"in h?h.StopIteration:Error("StopIteration");function K(){}K.prototype.next=function(){throw J;};function L(a,c,b,d,e){this.a=!!c;a&&M(this,a,d);this.depth=void 0!=e?e:this.c||0;this.a&&(this.depth*=-1);this.e=!b}j(L,K);L.prototype.b=f;L.prototype.c=0;L.prototype.d=g;function M(a,c,b){if(a.b=c)a.c="number"==typeof b?b:1!=a.b.nodeType?0:a.a?-1:1} -L.prototype.next=function(){var a;if(this.d){if(!this.b||this.e&&0==this.depth)throw J;a=this.b;var c=this.a?-1:1;if(this.c==c){var b=this.a?a.lastChild:a.firstChild;b?M(this,b):M(this,a,-1*c)}else(b=this.a?a.previousSibling:a.nextSibling)?M(this,b):M(this,a.parentNode,-1*c);this.depth+=this.c*(this.a?-1:1)}else this.d=!0;a=this.b;if(!this.b)throw J;return a}; -L.prototype.splice=function (a){var c=this.b,b=this.a?1:-1;this.c==b&&(this.c=-1*b,this.depth+=this.c*(this.a?-1:1));this.a=!this.a;L.prototype.next.call(this);this.a=!this.a;for(var b=arguments[0],d=i(b),b="array"==d||"object"==d&&"number"==typeof b.length?arguments[0]:arguments,d=b.length-1;0<=d;d--)c.parentNode&&c.parentNode.insertBefore(b[d],c.nextSibling);c&&c.parentNode&&c.parentNode.removeChild(c)};function N(a,c,b,d){L.call(this,a,c,b,f,d)}j(N,L);N.prototype.next=function(){do N.f.next.call(this);while(-1==this.c);return this.b};function O(a,c){return!!a&&1==a.nodeType&&(!c||a.tagName.toUpperCase()==c)}function P(a){return O(a,"OPTION")?!0:O(a,"INPUT")?(a=a.type.toLowerCase(),"checkbox"==a||"radio"==a):g}var Q={"class":"className",readonly:"readOnly"},R=["checked","disabled","draggable","hidden"]; -function S(a,c){var b=Q[c]||c,d=a[b];if(void 0===d&&0<=u(R,b))return g;if(b="value"==c)if(b=O(a,"OPTION")){var e;b=c.toLowerCase();if(a.hasAttribute)e=a.hasAttribute(b);else try{e=a.attributes[b].specified}catch(Y){e=g}b=!e}b&&(d=[],I(a,d,g),d=d.join(""));return d}var T="async,autofocus,autoplay,checked,compact,complete,controls,declare,defaultchecked,defaultselected,defer,disabled,draggable,ended,formnovalidate,hidden,indeterminate,iscontenteditable,ismap,itemscope,loop,multiple,muted,nohref,noresize,noshade,novalidate,nowrap,open,paused,pubdate,readonly,required,reversed,scoped,seamless,seeking,selected,spellcheck,truespeed,willvalidate".split(","); -function U(a,c){if(8==a.nodeType)return f;c=c.toLowerCase();if("style"==c){var b=l(a.style.cssText).toLowerCase();return b=";"==b.charAt(b.length-1)?b:b+";"}b=a.getAttributeNode(c);return!b?f:0<=u(T,c)?"true":b.specified?b.value:f};function V(a,c){var b=f,d=c.toLowerCase();if("style"==c.toLowerCase()){if((b=a.style)&&"string"!=typeof b)b=b.cssText;return b}if("selected"==d||"checked"==d&&P(a)){if(!P(a))throw new q(15,"Element is not selectable");var e="selected",d=a.type&&a.type.toLowerCase();if("checkbox"==d||"radio"==d)e="checked";return S(a,e)?"true":f}b=O(a,"A");if(O(a,"IMG")&&"src"==d||b&&"href"==d)return(b=U(a,d))&&(b=S(a,d)),b;try{e=S(a,c)}catch(Y){}if(!(d=e==f))d=i(e),d="object"==d||"array"==d||"function"==d;b=d?U(a, -c):e;return b!=f?b.toString():f}var W=["_"],X=h;!(W[0]in X)&&X.execScript&&X.execScript("var "+W[0]);for(var Z;W.length&&(Z=W.shift());)!W.length&&void 0!==V?X[Z]=V:X=X[Z]?X[Z]:X[Z]={};; return this._.apply(null,arguments);}.apply({navigator:typeof window!='undefined'?window.navigator:null}, arguments);} - -atom.getElementText = function (element, window){return function(){var g=void 0,h=!0,i=null,j=!1,k=this; -function l(a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!=typeof a.call&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("call"))return"function"}else return"null"; -else if("function"==b&&"undefined"==typeof a.call)return"object";return b}function m(a){return"string"==typeof a}function n(a,b){function c(){}c.prototype=b.prototype;a.h=b.prototype;a.prototype=new c};function o(a){var b=a.length-1;return 0<=b&&a.indexOf(" ",b)==b}function aa(a,b){for(var c=1;c<arguments.length;c++)var d=(""+arguments[c]).replace(/\$/g,"$$$$"),a=a.replace(/\%s/,d);return a}function p(a){return a.replace(/^[\s\xa0]+|[\s\xa0]+$/g,"")}var q={};function ba(a){return q[a]||(q[a]=(""+a).replace(/\-([a-z])/g,function (a,c){return c.toUpperCase()}))};var s,ca="",u=/rv\:([^\);]+)(\)|;)/.exec(k.navigator?k.navigator.userAgent:i);s=ca=u?u[1]:"";var v={};var da=window;function w(a,b){this.code=a;this.message=b||"";this.name=x[a]||x[13];var c=Error(this.message);c.name=this.name;this.stack=c.stack||""}n(w,Error); -var x={7:"NoSuchElementError",8:"NoSuchFrameError",9:"UnknownCommandError",10:"StaleElementReferenceError",11:"ElementNotVisibleError",12:"InvalidElementStateError",13:"UnknownError",15:"ElementNotSelectableError",19:"XPathLookupError",23:"NoSuchWindowError",24:"InvalidCookieDomainError",25:"UnableToSetCookieError",26:"ModalDialogOpenedError",27:"NoModalDialogOpenError",28:"ScriptTimeoutError",32:"InvalidSelectorError",33:"SqlDatabaseError",34:"MoveTargetOutOfBoundsError"}; -w.prototype.toString=function(){return"["+this.name+"] "+this.message};function y(a){this.stack=Error().stack||"";a&&(this.message=""+a)}n(y,Error);y.prototype.name="CustomError";function z(a,b){b.unshift(a);y.call(this,aa.apply(i,b));b.shift()}n(z,y);z.prototype.name="AssertionError";function ea(a,b){for(var c=a.length,d=m(a)?a.split(""):a,e=0;e<c;e++)e in d&&b.call(g,d[e],e,a)}function fa(a,b){for(var c=a.length,d=m(a)?a.split(""):a,e=0;e<c;e++)if(e in d&&b.call(g,d[e],e,a))return h;return j}function A(a,b){var c;a:if(m(a))c=!m(b)||1!=b.length?-1:a.indexOf(b,0);else{for(c=0;c<a.length;c++)if(c in a&&a[c]===b)break a;c=-1}return 0<=c};var B; -if(!v["1.9.1"]){for(var C=0,D=p(""+s).split("."),E=p("1.9.1").split("."),ga=Math.max(D.length,E.length),F=0;0==C&&F<ga;F++){var ha=D[F]||"",ia=E[F]||"",ja=RegExp("(\\d*)(\\D*)","g"),ka=RegExp("(\\d*)(\\D*)","g");do{var G=ja.exec(ha)||["","",""],H=ka.exec(ia)||["","",""];if(0==G[0].length&&0==H[0].length)break;C=((0==G[1].length?0:parseInt(G[1],10))<(0==H[1].length?0:parseInt(H[1],10))?-1:(0==G[1].length?0:parseInt(G[1],10))>(0==H[1].length?0:parseInt(H[1],10))?1:0)||((0==G[2].length)<(0==H[2].length)?-1: -(0==G[2].length)>(0==H[2].length)?1:0)||(G[2]<H[2]?-1:G[2]>H[2]?1:0)}while(0==C)}v["1.9.1"]=0<=C};function I(a,b){this.x=a!==g?a:0;this.y=b!==g?b:0}I.prototype.toString=function(){return"("+this.x+", "+this.y+")"};function J(a,b){this.width=a;this.height=b}J.prototype.toString=function(){return"("+this.width+" x "+this.height+")"};var K=3;function L(a){return 9==a.nodeType?a:a.ownerDocument||a.document}function la(a,b){var c=[];return M(a,b,c,h)?c[0]:g}function M(a,b,c,d){if(a!=i)for(a=a.firstChild;a;){if(b(a)&&(c.push(a),d)||M(a,b,c,d))return h;a=a.nextSibling}return j}function ma(a,b){for(var a=a.parentNode,c=0;a;){if(b(a))return a;a=a.parentNode;c++}return i}function N(a){this.g=a||k.document||document} -function na(a){var b=a.g,a="CSS1Compat"==b.compatMode?b.documentElement:b.body,b=b.parentWindow||b.defaultView;return new I(b.pageXOffset||a.scrollLeft,b.pageYOffset||a.scrollTop)};var oa=function(){var a={i:"http://www.w3.org/2000/svg"};return function (b){return a[b]||i}}(); -function pa(a,b){var c=function(){var c;a:{var e=L(b);if(e.implementation.hasFeature("XPath","3.0")){try{var f=e.createNSResolver?e.createNSResolver(e.documentElement):oa;c=e.evaluate(a,b,f,9,i);break a}catch(r){if("NS_ERROR_ILLEGAL_VALUE"!=r.name)throw new w(32,"Unable to locate an element with the xpath expression "+a+" because of the following error:\n"+r);}c=g}else c=i}return c?c.singleNodeValue||i:b.selectSingleNode?(c=L(b),c.setProperty&&c.setProperty("SelectionLanguage","XPath"),b.selectSingleNode(a)): -i}();if(c!==i&&(!c||1!=c.nodeType))throw new w(32,'The result of the xpath expression "'+a+'" is: '+c+". It should be an element.");return c};(function(){var a=k.Components;if(!a)return j;try{if(!a.classes)return j}catch(b){return j}var c=a.classes,a=a.interfaces;c["@mozilla.org/xpcom/version-comparator;1"].getService(a.nsIVersionComparator);c["@mozilla.org/xre/app-info;1"].getService(a.nsIXULAppInfo);return h})();var P="StopIteration"in k?k.StopIteration:Error("StopIteration");function qa(){}qa.prototype.next=function(){throw P;};function Q(a,b,c,d,e){this.a=!!b;a&&R(this,a,d);this.depth=e!=g?e:this.c||0;this.a&&(this.depth*=-1);this.f=!c}n(Q,qa);Q.prototype.b=i;Q.prototype.c=0;Q.prototype.e=j;function R(a,b,c){if(a.b=b)a.c="number"==typeof c?c:1!=a.b.nodeType?0:a.a?-1:1} -Q.prototype.next=function(){var a;if(this.e){if(!this.b||this.f&&0==this.depth)throw P;a=this.b;var b=this.a?-1:1;if(this.c==b){var c=this.a?a.lastChild:a.firstChild;c?R(this,c):R(this,a,-1*b)}else(c=this.a?a.previousSibling:a.nextSibling)?R(this,c):R(this,a.parentNode,-1*b);this.depth+=this.c*(this.a?-1:1)}else this.e=h;a=this.b;if(!this.b)throw P;return a}; -Q.prototype.splice=function (a){var b=this.b,c=this.a?1:-1;this.c==c&&(this.c=-1*c,this.depth+=this.c*(this.a?-1:1));this.a=!this.a;Q.prototype.next.call(this);this.a=!this.a;for(var c=arguments[0],d=l(c),c="array"==d||"object"==d&&"number"==typeof c.length?arguments[0]:arguments,d=c.length-1;0<=d;d--)b.parentNode&&b.parentNode.insertBefore(c[d],b.nextSibling);b&&b.parentNode&&b.parentNode.removeChild(b)};function S(a,b,c,d){Q.call(this,a,b,c,i,d)}n(S,Q);S.prototype.next=function(){do S.h.next.call(this);while(-1==this.c);return this.b};function ra(a,b){var c=L(a);return c.defaultView&&c.defaultView.getComputedStyle&&(c=c.defaultView.getComputedStyle(a,i))?c[b]||c.getPropertyValue(b):""}function T(a,b){return ra(a,b)||(a.currentStyle?a.currentStyle[b]:i)||a.style&&a.style[b]} -function sa(a){for(var b=L(a),c=T(a,"position"),d="fixed"==c||"absolute"==c,a=a.parentNode;a&&a!=b;a=a.parentNode)if(c=T(a,"position"),d=d&&"static"==c&&a!=b.documentElement&&a!=b.body,!d&&(a.scrollWidth>a.clientWidth||a.scrollHeight>a.clientHeight||"fixed"==c||"absolute"==c||"relative"==c))return a;return i} -function ta(a){var b=new I;if(1==a.nodeType)if(a.getBoundingClientRect)a=a.getBoundingClientRect(),b.x=a.left,b.y=a.top;else{var c=na(a?new N(L(a)):B||(B=new N));var d,e=L(a),f=T(a,"position"),r=e.getBoxObjectFor&&!a.getBoundingClientRect&&"absolute"==f&&(d=e.getBoxObjectFor(a))&&(0>d.screenX||0>d.screenY),f=new I(0,0),t=(e?9==e.nodeType?e:L(e):document).documentElement;if(a!=t)if(a.getBoundingClientRect)d=a.getBoundingClientRect(),a=na(e?new N(L(e)):B||(B=new N)),f.x=d.left+a.x,f.y=d.top+a.y;else if(e.getBoxObjectFor&& -!r)d=e.getBoxObjectFor(a),a=e.getBoxObjectFor(t),f.x=d.screenX-a.screenX,f.y=d.screenY-a.screenY;else{d=a;do f.x+=d.offsetLeft,f.y+=d.offsetTop,d!=a&&(f.x+=d.clientLeft||0,f.y+=d.clientTop||0),d=d.offsetParent;while(d&&d!=a);for(d=a;(d=sa(d))&&d!=e.body&&d!=t;)f.x-=d.scrollLeft,f.y-=d.scrollTop}b.x=f.x-c.x;b.y=f.y-c.y}else c="function"==l(a.d),d=a,a.targetTouches?d=a.targetTouches[0]:c&&a.d().targetTouches&&(d=a.d().targetTouches[0]),b.x=d.clientX,b.y=d.clientY;return b} -function ua(a){var b=a.offsetWidth,c=a.offsetHeight;return b===g&&a.getBoundingClientRect?(a=a.getBoundingClientRect(),new J(a.right-a.left,a.bottom-a.top)):new J(b,c)};function U(a,b){return!!a&&1==a.nodeType&&(!b||a.tagName.toUpperCase()==b)}var va="async,autofocus,autoplay,checked,compact,complete,controls,declare,defaultchecked,defaultselected,defer,disabled,draggable,ended,formnovalidate,hidden,indeterminate,iscontenteditable,ismap,itemscope,loop,multiple,muted,nohref,noresize,noshade,novalidate,nowrap,open,paused,pubdate,readonly,required,reversed,scoped,seamless,seeking,selected,spellcheck,truespeed,willvalidate".split(","); -function V(a){for(a=a.parentNode;a&&1!=a.nodeType&&9!=a.nodeType&&11!=a.nodeType;)a=a.parentNode;return U(a)?a:i}function W(a,b){b=ba(b);return ra(a,b)||wa(a,b)}function wa(a,b){var c=a.currentStyle||a.style,d=c[b];d===g&&"function"==l(c.getPropertyValue)&&(d=c.getPropertyValue(b));return"inherit"!=d?d!==g?d:i:(c=V(a))?wa(c,b):i} -function xa(a){if("function"==l(a.getBBox))try{var b=a.getBBox();if(b)return b}catch(c){}if("none"!=T(a,"display"))a=ua(a);else{var b=a.style,d=b.display,e=b.visibility,f=b.position;b.visibility="hidden";b.position="absolute";b.display="inline";a=ua(a);b.display=d;b.position=f;b.visibility=e}return a} -function X(a,b){function c(a){if("none"==W(a,"display"))return j;a=V(a);return!a||c(a)}function d(a){var b=xa(a);return 0<b.height&&0<b.width?h:fa(a.childNodes,function (a){return a.nodeType==K||U(a)&&d(a)})}function e(a){var b=V(a);if(b&&"hidden"==W(b,"overflow")){var c=xa(b),d=ta(b),a=ta(a);return d.x+c.width<a.x||d.y+c.height<a.y?j:e(b)}return h}if(!U(a))throw Error("Argument to isShown must be of type Element");if(U(a,"OPTION")||U(a,"OPTGROUP")){var f=ma(a,function (a){return U(a,"SELECT")});return!!f&& -X(f,h)}if(U(a,"MAP")){if(!a.name)return j;f=L(a);f=f.evaluate?pa('/descendant::*[@usemap = "#'+a.name+'"]',f):la(f,function (b){var c;if(c=U(b))8==b.nodeType?b=i:(c="usemap","style"==c?(b=p(b.style.cssText).toLowerCase(),b=";"==b.charAt(b.length-1)?b:b+";"):(b=b.getAttributeNode(c),b=!b?i:A(va,c)?"true":b.specified?b.value:i)),c=b=="#"+a.name;return c});return!!f&&X(f,b)}return U(a,"AREA")?(f=ma(a,function (a){return U(a,"MAP")}),!!f&&X(f,b)):U(a,"INPUT")&&"hidden"==a.type.toLowerCase()||U(a,"NOSCRIPT")|| -"hidden"==W(a,"visibility")||!c(a)||!b&&0==ya(a)||!d(a)||!e(a)?j:h}function za(a){return a.replace(/^[^\S\xa0]+|[^\S\xa0]+$/g,"")} -function Aa(a,b){if(U(a,"BR"))b.push("");else{var c=U(a,"TD"),d=W(a,"display"),e=!c&&!A(Ba,d);e&&!/^[\s\xa0]*$/.test(b[b.length-1]||"")&&b.push("");var f=X(a),r=i,t=i;f&&(r=W(a,"white-space"),t=W(a,"text-transform"));ea(a.childNodes,function (a){a.nodeType==K&&f?Ca(a,b,r,t):U(a)&&Aa(a,b)});var O=b[b.length-1]||"";if((c||"table-cell"==d)&&O&&!o(O))b[b.length-1]+=" ";e&&!/^[\s\xa0]*$/.test(O)&&b.push("")}}var Ba="inline,inline-block,inline-table,none,table-cell,table-column,table-column-group".split(","); -function Ca(a,b,c,d){a=a.nodeValue.replace(/\u200b/g,"");a=a.replace(/(\r\n|\r|\n)/g,"\n");if("normal"==c||"nowrap"==c)a=a.replace(/\n/g," ");a="pre"==c||"pre-wrap"==c?a.replace(/[ \f\t\v\u2028\u2029]/g,"\u00a0"):a.replace(/[\ \f\t\v\u2028\u2029]+/g," ");"capitalize"==d?a=a.replace(/(^|\s)(\S)/g,function (a,b,c){return b+c.toUpperCase()}):"uppercase"==d?a=a.toUpperCase():"lowercase"==d&&(a=a.toLowerCase());c=b.pop()||"";o(c)&&0==a.lastIndexOf(" ",0)&&(a=a.substr(1));b.push(c+a)} -function ya(a){var b=1,c=W(a,"opacity");c&&(b=Number(c));(a=V(a))&&(b*=ya(a));return b};function Da(a){var b;a:{for(b=a;b;){if(b.tagName&&"head"==b.tagName.toLowerCase()){b=h;break a}try{b=b.parentNode}catch(c){break}}b=j}if(b)return b=L(a),"TITLE"==a.tagName.toUpperCase()&&(b?b.parentWindow||b.defaultView:window)==da.top?p(b.title):"";b=[];Aa(a,b);var d=b,a=d.length;b=Array(a);for(var d=m(d)?d.split(""):d,e=0;e<a;e++)e in d&&(b[e]=za.call(g,d[e]));return za(b.join("\n")).replace(/\xa0/g," ")}var Y=["_"],Z=k;!(Y[0]in Z)&&Z.execScript&&Z.execScript("var "+Y[0]); -for(var $;Y.length&&($=Y.shift());)!Y.length&&Da!==g?Z[$]=Da:Z=Z[$]?Z[$]:Z[$]={};; return this._.apply(null,arguments);}.apply({navigator:typeof window!='undefined'?window.navigator:null}, arguments);} - -atom.isElementEnabled = function (element, window){return function(){var e=this;function f(a,c){function b(){}b.prototype=c.prototype;a.d=c.prototype;a.prototype=new b};function g(a,c){for(var b=1;b<arguments.length;b++)var d=(""+arguments[b]).replace(/\$/g,"$$$$"),a=a.replace(/\%s/,d);return a};var h,i="",j=/rv\:([^\);]+)(\)|;)/.exec(e.navigator?e.navigator.userAgent:null);h=i=j?j[1]:"";var k={};function l(a,c){this.code=a;this.message=c||"";this.name=m[a]||m[13];var b=Error(this.message);b.name=this.name;this.stack=b.stack||""}f(l,Error); -var m={7:"NoSuchElementError",8:"NoSuchFrameError",9:"UnknownCommandError",10:"StaleElementReferenceError",11:"ElementNotVisibleError",12:"InvalidElementStateError",13:"UnknownError",15:"ElementNotSelectableError",19:"XPathLookupError",23:"NoSuchWindowError",24:"InvalidCookieDomainError",25:"UnableToSetCookieError",26:"ModalDialogOpenedError",27:"NoModalDialogOpenError",28:"ScriptTimeoutError",32:"InvalidSelectorError",33:"SqlDatabaseError",34:"MoveTargetOutOfBoundsError"}; -l.prototype.toString=function(){return"["+this.name+"] "+this.message};function n(a){this.stack=Error().stack||"";a&&(this.message=""+a)}f(n,Error);n.prototype.name="CustomError";function o(a,c){c.unshift(a);n.call(this,g.apply(null,c));c.shift()}f(o,n);o.prototype.name="AssertionError";function p(a,c){var b;a:if("string"==typeof a)b="string"!=typeof c||1!=c.length?-1:a.indexOf(c,0);else{for(b=0;b<a.length;b++)if(b in a&&a[b]===c)break a;b=-1}return 0<=b};if(!k["1.9.1"]){for(var q=0,r=(""+h).replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").split("."),s="1.9.1".replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").split("."),t=Math.max(r.length,s.length),u=0;0==q&&u<t;u++){var v=r[u]||"",w=s[u]||"",x=RegExp("(\\d*)(\\D*)","g"),z=RegExp("(\\d*)(\\D*)","g");do{var A=x.exec(v)||["","",""],B=z.exec(w)||["","",""];if(0==A[0].length&&0==B[0].length)break;q=((0==A[1].length?0:parseInt(A[1],10))<(0==B[1].length?0:parseInt(B[1],10))?-1:(0==A[1].length?0:parseInt(A[1],10))>(0==B[1].length? -0:parseInt(B[1],10))?1:0)||((0==A[2].length)<(0==B[2].length)?-1:(0==A[2].length)>(0==B[2].length)?1:0)||(A[2]<B[2]?-1:A[2]>B[2]?1:0)}while(0==q)}k["1.9.1"]=0<=q};(function(){var a=e.Components;if(!a)return!1;try{if(!a.classes)return!1}catch(c){return!1}var b=a.classes,a=a.interfaces;b["@mozilla.org/xpcom/version-comparator;1"].getService(a.nsIVersionComparator);b["@mozilla.org/xre/app-info;1"].getService(a.nsIXULAppInfo);return!0})();function C(a,c,b,d,y){this.b=!!c;if(a&&(this.a=a))this.c="number"==typeof d?d:1!=this.a.nodeType?0:this.b?-1:1;this.depth=void 0!=y?y:this.c||0;this.b&&(this.depth*=-1)}f(C,function(){});C.prototype.a=null;C.prototype.c=0;f(function (a,c,b,d){C.call(this,a,c,0,null,d)},C);var D={"class":"className",readonly:"readOnly"},E=["checked","disabled","draggable","hidden"],F="BUTTON,INPUT,OPTGROUP,OPTION,SELECT,TEXTAREA".split(",");function G(a){var c=a.tagName.toUpperCase();if(p(F,c)){var b;b=D.disabled||"disabled";var d=a[b];b=void 0===d&&p(E,b)?!1:d;a=b?!1:a.parentNode&&1==a.parentNode.nodeType&&"OPTGROUP"==c||"OPTION"==c?G(a.parentNode):!0}else a=!0;return a};var H=G,I=["_"],J=e;!(I[0]in J)&&J.execScript&&J.execScript("var "+I[0]);for(var K;I.length&&(K=I.shift());)!I.length&&void 0!==H?J[K]=H:J=J[K]?J[K]:J[K]={};; return this._.apply(null,arguments);}.apply({navigator:typeof window!='undefined'?window.navigator:null}, arguments);} - -atom.isElementSelected = function (element, window){return function(){var f=!1,g=this;function h(a,b){function c(){}c.prototype=b.prototype;a.d=b.prototype;a.prototype=new c};function i(a,b){for(var c=1;c<arguments.length;c++)var d=(""+arguments[c]).replace(/\$/g,"$$$$"),a=a.replace(/\%s/,d);return a};var k,l="",m=/rv\:([^\);]+)(\)|;)/.exec(g.navigator?g.navigator.userAgent:null);k=l=m?m[1]:"";var n={};function o(a,b){this.code=a;this.message=b||"";this.name=p[a]||p[13];var c=Error(this.message);c.name=this.name;this.stack=c.stack||""}h(o,Error); -var p={7:"NoSuchElementError",8:"NoSuchFrameError",9:"UnknownCommandError",10:"StaleElementReferenceError",11:"ElementNotVisibleError",12:"InvalidElementStateError",13:"UnknownError",15:"ElementNotSelectableError",19:"XPathLookupError",23:"NoSuchWindowError",24:"InvalidCookieDomainError",25:"UnableToSetCookieError",26:"ModalDialogOpenedError",27:"NoModalDialogOpenError",28:"ScriptTimeoutError",32:"InvalidSelectorError",33:"SqlDatabaseError",34:"MoveTargetOutOfBoundsError"}; -o.prototype.toString=function(){return"["+this.name+"] "+this.message};function q(a){this.stack=Error().stack||"";a&&(this.message=""+a)}h(q,Error);q.prototype.name="CustomError";function r(a,b){b.unshift(a);q.call(this,i.apply(null,b));b.shift()}h(r,q);r.prototype.name="AssertionError";if(!n["1.9.1"]){for(var s=0,t=(""+k).replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").split("."),u="1.9.1".replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").split("."),v=Math.max(t.length,u.length),w=0;0==s&&w<v;w++){var x=t[w]||"",y=u[w]||"",z=RegExp("(\\d*)(\\D*)","g"),A=RegExp("(\\d*)(\\D*)","g");do{var B=z.exec(x)||["","",""],C=A.exec(y)||["","",""];if(0==B[0].length&&0==C[0].length)break;s=((0==B[1].length?0:parseInt(B[1],10))<(0==C[1].length?0:parseInt(C[1],10))?-1:(0==B[1].length?0:parseInt(B[1],10))>(0==C[1].length? -0:parseInt(C[1],10))?1:0)||((0==B[2].length)<(0==C[2].length)?-1:(0==B[2].length)>(0==C[2].length)?1:0)||(B[2]<C[2]?-1:B[2]>C[2]?1:0)}while(0==s)}n["1.9.1"]=0<=s};var D={SCRIPT:1,STYLE:1,HEAD:1,IFRAME:1,OBJECT:1},E={IMG:" ",BR:"\n"};function F(a,b,c){if(!(a.nodeName in D))if(3==a.nodeType)c?b.push((""+a.nodeValue).replace(/(\r\n|\r|\n)/g,"")):b.push(a.nodeValue);else if(a.nodeName in E)b.push(E[a.nodeName]);else for(a=a.firstChild;a;)F(a,b,c),a=a.nextSibling};(function(){var a=g.Components;if(!a)return f;try{if(!a.classes)return f}catch(b){return f}var c=a.classes,a=a.interfaces;c["@mozilla.org/xpcom/version-comparator;1"].getService(a.nsIVersionComparator);c["@mozilla.org/xre/app-info;1"].getService(a.nsIXULAppInfo);return!0})();function G(a,b,c,d,e){this.b=!!b;if(a&&(this.a=a))this.c="number"==typeof d?d:1!=this.a.nodeType?0:this.b?-1:1;this.depth=void 0!=e?e:this.c||0;this.b&&(this.depth*=-1)}h(G,function(){});G.prototype.a=null;G.prototype.c=0;h(function (a,b,c,d){G.call(this,a,b,0,null,d)},G);function H(a,b){return!!a&&1==a.nodeType&&(!b||a.tagName.toUpperCase()==b)}function I(a){return H(a,"OPTION")?!0:H(a,"INPUT")?(a=a.type.toLowerCase(),"checkbox"==a||"radio"==a):f}var J={"class":"className",readonly:"readOnly"},K=["checked","disabled","draggable","hidden"];function L(a){if(I(a)){if(!I(a))throw new o(15,"Element is not selectable");var b="selected",c=a.type&&a.type.toLowerCase();if("checkbox"==c||"radio"==c)b="checked";var c=b,d=J[c]||c,b=a[d],e;if(e=void 0===b){b:if("string"==typeof K)d="string"!=typeof d||1!=d.length?-1:K.indexOf(d,0);else{for(e=0;e<K.length;e++)if(e in K&&K[e]===d){d=e;break b}d=-1}e=0<=d}if(e)a=f;else{if(d="value"==c)if(d=H(a,"OPTION")){var j;c=c.toLowerCase();if(a.hasAttribute)j=a.hasAttribute(c);else try{j=a.attributes[c].specified}catch(P){j= -f}d=!j}d&&(j=[],F(a,j,f),b=j.join(""));a=b}a=!!a}else a=f;return a}var M=["_"],N=g;!(M[0]in N)&&N.execScript&&N.execScript("var "+M[0]);for(var O;M.length&&(O=M.shift());)!M.length&&void 0!==L?N[O]=L:N=N[O]?N[O]:N[O]={};; return this._.apply(null,arguments);}.apply({navigator:typeof window!='undefined'?window.navigator:null}, arguments);} - -atom.isElementDisplayed = function (element, window){return function(){function h(a){return function(){return a}}var k=this; -function m(a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!=typeof a.call&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("call"))return"function"}else return"null"; -else if("function"==b&&"undefined"==typeof a.call)return"object";return b}function n(a){return"string"==typeof a};function q(a){var b=0,c=String(r).replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").split(".");a=String(a).replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").split(".");for(var d=Math.max(c.length,a.length),e=0;0==b&&e<d;e++){var f=c[e]||"",g=a[e]||"",w=RegExp("(\\d*)(\\D*)","g"),p=RegExp("(\\d*)(\\D*)","g");do{var l=w.exec(f)||["","",""],v=p.exec(g)||["","",""];if(0==l[0].length&&0==v[0].length)break;b=((0==l[1].length?0:parseInt(l[1],10))<(0==v[1].length?0:parseInt(v[1],10))?-1:(0==l[1].length?0:parseInt(l[1],10))>(0==v[1].length? -0:parseInt(v[1],10))?1:0)||((0==l[2].length)<(0==v[2].length)?-1:(0==l[2].length)>(0==v[2].length)?1:0)||(l[2]<v[2]?-1:l[2]>v[2]?1:0)}while(0==b)}return b}function aa(a){return String(a).replace(/\-([a-z])/g,function (a,c){return c.toUpperCase()})};var s=Array.prototype;function t(a,b){for(var c=a.length,d=n(a)?a.split(""):a,e=0;e<c;e++)e in d&&b.call(void 0,d[e],e,a)}function ba(a,b){if(a.reduce)return a.reduce(b,"");var c="";t(a,function (d,e){c=b.call(void 0,c,d,e,a)});return c}function ca(a,b){for(var c=a.length,d=n(a)?a.split(""):a,e=0;e<c;e++)if(e in d&&b.call(void 0,d[e],e,a))return!0;return!1} -function da(a,b){var c;a:if(n(a))c=n(b)&&1==b.length?a.indexOf(b,0):-1;else{for(c=0;c<a.length;c++)if(c in a&&a[c]===b)break a;c=-1}return 0<=c}function ea(a,b,c){return 2>=arguments.length?s.slice.call(a,b):s.slice.call(a,b,c)};var u={aliceblue:"#f0f8ff",antiquewhite:"#faebd7",aqua:"#00ffff",aquamarine:"#7fffd4",azure:"#f0ffff",beige:"#f5f5dc",bisque:"#ffe4c4",black:"#000000",blanchedalmond:"#ffebcd",blue:"#0000ff",blueviolet:"#8a2be2",brown:"#a52a2a",burlywood:"#deb887",cadetblue:"#5f9ea0",chartreuse:"#7fff00",chocolate:"#d2691e",coral:"#ff7f50",cornflowerblue:"#6495ed",cornsilk:"#fff8dc",crimson:"#dc143c",cyan:"#00ffff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkgray:"#a9a9a9",darkgreen:"#006400", -darkgrey:"#a9a9a9",darkkhaki:"#bdb76b",darkmagenta:"#8b008b",darkolivegreen:"#556b2f",darkorange:"#ff8c00",darkorchid:"#9932cc",darkred:"#8b0000",darksalmon:"#e9967a",darkseagreen:"#8fbc8f",darkslateblue:"#483d8b",darkslategray:"#2f4f4f",darkslategrey:"#2f4f4f",darkturquoise:"#00ced1",darkviolet:"#9400d3",deeppink:"#ff1493",deepskyblue:"#00bfff",dimgray:"#696969",dimgrey:"#696969",dodgerblue:"#1e90ff",firebrick:"#b22222",floralwhite:"#fffaf0",forestgreen:"#228b22",fuchsia:"#ff00ff",gainsboro:"#dcdcdc", -ghostwhite:"#f8f8ff",gold:"#ffd700",goldenrod:"#daa520",gray:"#808080",green:"#008000",greenyellow:"#adff2f",grey:"#808080",honeydew:"#f0fff0",hotpink:"#ff69b4",indianred:"#cd5c5c",indigo:"#4b0082",ivory:"#fffff0",khaki:"#f0e68c",lavender:"#e6e6fa",lavenderblush:"#fff0f5",lawngreen:"#7cfc00",lemonchiffon:"#fffacd",lightblue:"#add8e6",lightcoral:"#f08080",lightcyan:"#e0ffff",lightgoldenrodyellow:"#fafad2",lightgray:"#d3d3d3",lightgreen:"#90ee90",lightgrey:"#d3d3d3",lightpink:"#ffb6c1",lightsalmon:"#ffa07a", -lightseagreen:"#20b2aa",lightskyblue:"#87cefa",lightslategray:"#778899",lightslategrey:"#778899",lightsteelblue:"#b0c4de",lightyellow:"#ffffe0",lime:"#00ff00",limegreen:"#32cd32",linen:"#faf0e6",magenta:"#ff00ff",maroon:"#800000",mediumaquamarine:"#66cdaa",mediumblue:"#0000cd",mediumorchid:"#ba55d3",mediumpurple:"#9370db",mediumseagreen:"#3cb371",mediumslateblue:"#7b68ee",mediumspringgreen:"#00fa9a",mediumturquoise:"#48d1cc",mediumvioletred:"#c71585",midnightblue:"#191970",mintcream:"#f5fffa",mistyrose:"#ffe4e1", -moccasin:"#ffe4b5",navajowhite:"#ffdead",navy:"#000080",oldlace:"#fdf5e6",olive:"#808000",olivedrab:"#6b8e23",orange:"#ffa500",orangered:"#ff4500",orchid:"#da70d6",palegoldenrod:"#eee8aa",palegreen:"#98fb98",paleturquoise:"#afeeee",palevioletred:"#db7093",papayawhip:"#ffefd5",peachpuff:"#ffdab9",peru:"#cd853f",pink:"#ffc0cb",plum:"#dda0dd",powderblue:"#b0e0e6",purple:"#800080",red:"#ff0000",rosybrown:"#bc8f8f",royalblue:"#4169e1",saddlebrown:"#8b4513",salmon:"#fa8072",sandybrown:"#f4a460",seagreen:"#2e8b57", -seashell:"#fff5ee",sienna:"#a0522d",silver:"#c0c0c0",skyblue:"#87ceeb",slateblue:"#6a5acd",slategray:"#708090",slategrey:"#708090",snow:"#fffafa",springgreen:"#00ff7f",steelblue:"#4682b4",tan:"#d2b48c",teal:"#008080",thistle:"#d8bfd8",tomato:"#ff6347",turquoise:"#40e0d0",violet:"#ee82ee",wheat:"#f5deb3",white:"#ffffff",whitesmoke:"#f5f5f5",yellow:"#ffff00",yellowgreen:"#9acd32"};var fa="background-color border-top-color border-right-color border-bottom-color border-left-color color outline-color".split(" "),ga=/#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])/;function ha(a){if(!x.test(a))throw Error("'"+a+"' is not a valid hex color");4==a.length&&(a=a.replace(ga,"#$1$1$2$2$3$3"));return a.toLowerCase()}var x=/^#(?:[0-9a-f]{3}){1,2}$/i,ia=/^(?:rgba)?\((\d{1,3}),\s?(\d{1,3}),\s?(\d{1,3}),\s?(0|1|0\.\d*)\)$/i; -function ja(a){var b=a.match(ia);if(b){a=Number(b[1]);var c=Number(b[2]),d=Number(b[3]),b=Number(b[4]);if(0<=a&&255>=a&&0<=c&&255>=c&&0<=d&&255>=d&&0<=b&&1>=b)return[a,c,d,b]}return[]}var ka=/^(?:rgb)?\((0|[1-9]\d{0,2}),\s?(0|[1-9]\d{0,2}),\s?(0|[1-9]\d{0,2})\)$/i;function la(a){var b=a.match(ka);if(b){a=Number(b[1]);var c=Number(b[2]),b=Number(b[3]);if(0<=a&&255>=a&&0<=c&&255>=c&&0<=b&&255>=b)return[a,c,b]}return[]};function y(a,b){this.code=a;this.state=z[a]||ma;this.message=b||"";var c=this.state.replace(/((?:^|\s+)[a-z])/g,function (a){return a.toUpperCase().replace(/^[\s\xa0]+/g,"")}),d=c.length-5;if(0>d||c.indexOf("Error",d)!=d)c+="Error";this.name=c;c=Error(this.message);c.name=this.name;this.stack=c.stack||""}(function(){var a=Error;function b(){}b.prototype=a.prototype;y.I=a.prototype;y.prototype=new b})(); -var ma="unknown error",z={15:"element not selectable",11:"element not visible",31:"ime engine activation failed",30:"ime not available",24:"invalid cookie domain",29:"invalid element coordinates",12:"invalid element state",32:"invalid selector",51:"invalid selector",52:"invalid selector",17:"javascript error",405:"unsupported operation",34:"move target out of bounds",27:"no such alert",7:"no such element",8:"no such frame",23:"no such window",28:"script timeout",33:"session not created",10:"stale element reference", -0:"success",21:"timeout",25:"unable to set cookie",26:"unexpected alert open"};z[13]=ma;z[9]="unknown command";y.prototype.toString=function(){return this.name+": "+this.message};var r,na="",oa=/rv\:([^\);]+)(\)|;)/.exec(k.navigator?k.navigator.userAgent:null);r=na=oa?oa[1]:"";var A={};var B;A["1.9.1"]||(A["1.9.1"]=0<=q("1.9.1"));function C(a,b){this.x=void 0!==a?a:0;this.y=void 0!==b?b:0}C.prototype.toString=function(){return"("+this.x+", "+this.y+")"};C.prototype.ceil=function(){this.x=Math.ceil(this.x);this.y=Math.ceil(this.y);return this};C.prototype.floor=function(){this.x=Math.floor(this.x);this.y=Math.floor(this.y);return this};C.prototype.round=function(){this.x=Math.round(this.x);this.y=Math.round(this.y);return this};function D(a,b){this.width=a;this.height=b}D.prototype.toString=function(){return"("+this.width+" x "+this.height+")"};D.prototype.ceil=function(){this.width=Math.ceil(this.width);this.height=Math.ceil(this.height);return this};D.prototype.floor=function(){this.width=Math.floor(this.width);this.height=Math.floor(this.height);return this};D.prototype.round=function(){this.width=Math.round(this.width);this.height=Math.round(this.height);return this};var pa=3;function E(a,b){if(a.contains&&1==b.nodeType)return a==b||a.contains(b);if("undefined"!=typeof a.compareDocumentPosition)return a==b||Boolean(a.compareDocumentPosition(b)&16);for(;b&&a!=b;)b=b.parentNode;return b==a} -function qa(a,b){if(a==b)return 0;if(a.compareDocumentPosition)return a.compareDocumentPosition(b)&2?1:-1;if("sourceIndex"in a||a.parentNode&&"sourceIndex"in a.parentNode){var c=1==a.nodeType,d=1==b.nodeType;if(c&&d)return a.sourceIndex-b.sourceIndex;var e=a.parentNode,f=b.parentNode;return e==f?ra(a,b):!c&&E(e,b)?-1*sa(a,b):!d&&E(f,a)?sa(b,a):(c?a.sourceIndex:e.sourceIndex)-(d?b.sourceIndex:f.sourceIndex)}d=F(a);c=d.createRange();c.selectNode(a);c.collapse(!0);d=d.createRange();d.selectNode(b);d.collapse(!0); -return c.compareBoundaryPoints(k.Range.START_TO_END,d)}function sa(a,b){var c=a.parentNode;if(c==b)return-1;for(var d=b;d.parentNode!=c;)d=d.parentNode;return ra(d,a)}function ra(a,b){for(var c=b;c=c.previousSibling;)if(c==a)return-1;return 1}function F(a){return 9==a.nodeType?a:a.ownerDocument||a.document}function ta(a,b){a=a.parentNode;for(var c=0;a;){if(b(a))return a;a=a.parentNode;c++}return null}function G(a){this.p=a||k.document||document} -function ua(a){var b=a.p;a="CSS1Compat"==b.compatMode?b.documentElement:b.body;b=b.parentWindow||b.defaultView;return new C(b.pageXOffset||a.scrollLeft,b.pageYOffset||a.scrollTop)}G.prototype.contains=E;function H(a){var b=null,c=a.nodeType;1==c&&(b=a.textContent,b=void 0==b||null==b?a.innerText:b,b=void 0==b||null==b?"":b);if("string"!=typeof b)if(9==c||1==c){a=9==c?a.documentElement:a.firstChild;for(var c=0,d=[],b="";a;){do 1!=a.nodeType&&(b+=a.nodeValue),d[c++]=a;while(a=a.firstChild);for(;c&&!(a=d[--c].nextSibling););}}else b=a.nodeValue;return""+b} -function I(a,b,c){if(null===b)return!0;try{if(!a.getAttribute)return!1}catch(d){return!1}return null==c?!!a.getAttribute(b):a.getAttribute(b,2)==c}function J(a,b,c,d,e){return va.call(null,a,b,n(c)?c:null,n(d)?d:null,e||new K)} -function va(a,b,c,d,e){b.getElementsByName&&d&&"name"==c?(b=b.getElementsByName(d),t(b,function (b){a.matches(b)&&e.add(b)})):b.getElementsByClassName&&d&&"class"==c?(b=b.getElementsByClassName(d),t(b,function (b){b.className==d&&a.matches(b)&&e.add(b)})):b.getElementsByTagName&&(b=b.getElementsByTagName(a.getName()),t(b,function (a){I(a,c,d)&&e.add(a)}));return e}function wa(a,b,c,d,e){for(b=b.firstChild;b;b=b.nextSibling)I(b,c,d)&&a.matches(b)&&e.add(b);return e};function K(){this.d=this.c=null;this.g=0}function xa(a){this.m=a;this.next=this.i=null}K.prototype.unshift=function (a){a=new xa(a);a.next=this.c;this.d?this.c.i=a:this.c=this.d=a;this.c=a;this.g++};K.prototype.add=function (a){a=new xa(a);a.i=this.d;this.c?this.d.next=a:this.c=this.d=a;this.d=a;this.g++};function ya(a){return(a=a.c)?a.m:null}function L(a){return new za(a,!1)}function za(a,b){this.F=a;this.j=(this.n=b)?a.d:a.c;this.r=null} -za.prototype.next=function(){var a=this.j;if(null==a)return null;var b=this.r=a;this.j=this.n?a.i:a.next;return b.m};function M(a,b,c,d,e){b=b.evaluate(d);c=c.evaluate(d);var f;if(b instanceof K&&c instanceof K){e=L(b);for(d=e.next();d;d=e.next())for(b=L(c),f=b.next();f;f=b.next())if(a(H(d),H(f)))return!0;return!1}if(b instanceof K||c instanceof K){b instanceof K?e=b:(e=c,c=b);e=L(e);b=typeof c;for(d=e.next();d;d=e.next()){switch(b){case "number":d=+H(d);break;case "boolean":d=!!H(d);break;case "string":d=H(d);break;default:throw Error("Illegal primitive type for comparison.");}if(a(d,c))return!0}return!1}return e? -"boolean"==typeof b||"boolean"==typeof c?a(!!b,!!c):"number"==typeof b||"number"==typeof c?a(+b,+c):a(b,c):a(+b,+c)}function Aa(a,b,c,d){this.s=a;this.H=b;this.o=c;this.q=d}Aa.prototype.toString=function(){return this.s};var Ba={};function N(a,b,c,d){if(a in Ba)throw Error("Binary operator already created: "+a);a=new Aa(a,b,c,d);Ba[a.toString()]=a}N("div",6,1,function (a,b,c){return a.b(c)/b.b(c)});N("mod",6,1,function (a,b,c){return a.b(c)%b.b(c)});N("*",6,1,function (a,b,c){return a.b(c)*b.b(c)}); -N("+",5,1,function (a,b,c){return a.b(c)+b.b(c)});N("-",5,1,function (a,b,c){return a.b(c)-b.b(c)});N("<",4,2,function (a,b,c){return M(function (a,b){return a<b},a,b,c)});N(">",4,2,function (a,b,c){return M(function (a,b){return a>b},a,b,c)});N("<=",4,2,function (a,b,c){return M(function (a,b){return a<=b},a,b,c)});N(">=",4,2,function (a,b,c){return M(function (a,b){return a>=b},a,b,c)});N("=",3,2,function (a,b,c){return M(function (a,b){return a==b},a,b,c,!0)}); -N("!=",3,2,function (a,b,c){return M(function (a,b){return a!=b},a,b,c,!0)});N("and",2,2,function (a,b,c){return a.f(c)&&b.f(c)});N("or",1,2,function (a,b,c){return a.f(c)||b.f(c)});function Ca(a,b,c,d,e,f,g,w,p){this.h=a;this.o=b;this.D=c;this.C=d;this.B=e;this.q=f;this.A=g;this.w=void 0!==w?w:g;this.G=!!p}Ca.prototype.toString=function(){return this.h};var Da={};function O(a,b,c,d,e,f,g,w){if(a in Da)throw Error("Function already created: "+a+".");Da[a]=new Ca(a,b,c,d,!1,e,f,g,w)}O("boolean",2,!1,!1,function (a,b){return b.f(a)},1);O("ceiling",1,!1,!1,function (a,b){return Math.ceil(b.b(a))},1); -O("concat",3,!1,!1,function (a,b){var c=ea(arguments,1);return ba(c,function (b,c){return b+c.a(a)})},2,null);O("contains",2,!1,!1,function (a,b,c){b=b.a(a);a=c.a(a);return-1!=b.indexOf(a)},2);O("count",1,!1,!1,function (a,b){return b.evaluate(a).g},1,1,!0);O("false",2,!1,!1,h(!1),0);O("floor",1,!1,!1,function (a,b){return Math.floor(b.b(a))},1); -O("id",4,!1,!1,function (a,b){var c=a.e(),d=9==c.nodeType?c:c.ownerDocument,c=b.a(a).split(/\s+/),e=[];t(c,function (a){(a=d.getElementById(a))&&!da(e,a)&&e.push(a)});e.sort(qa);var f=new K;t(e,function (a){f.add(a)});return f},1);O("lang",2,!1,!1,h(!1),1);O("last",1,!0,!1,function (a){if(1!=arguments.length)throw Error("Function last expects ()");return a.u()},0);O("local-name",3,!1,!0,function (a,b){var c=b?ya(b.evaluate(a)):a.e();return c?c.nodeName.toLowerCase():""},0,1,!0); -O("name",3,!1,!0,function (a,b){var c=b?ya(b.evaluate(a)):a.e();return c?c.nodeName.toLowerCase():""},0,1,!0);O("namespace-uri",3,!0,!1,h(""),0,1,!0);O("normalize-space",3,!1,!0,function (a,b){return(b?b.a(a):H(a.e())).replace(/[\s\xa0]+/g," ").replace(/^\s+|\s+$/g,"")},0,1);O("not",2,!1,!1,function (a,b){return!b.f(a)},1);O("number",1,!1,!0,function (a,b){return b?b.b(a):+H(a.e())},0,1);O("position",1,!0,!1,function (a){return a.v()},0);O("round",1,!1,!1,function (a,b){return Math.round(b.b(a))},1); -O("starts-with",2,!1,!1,function (a,b,c){b=b.a(a);a=c.a(a);return 0==b.lastIndexOf(a,0)},2);O("string",3,!1,!0,function (a,b){return b?b.a(a):H(a.e())},0,1);O("string-length",1,!1,!0,function (a,b){return(b?b.a(a):H(a.e())).length},0,1); -O("substring",3,!1,!1,function (a,b,c,d){c=c.b(a);if(isNaN(c)||Infinity==c||-Infinity==c)return"";d=d?d.b(a):Infinity;if(isNaN(d)||-Infinity===d)return"";c=Math.round(c)-1;var e=Math.max(c,0);a=b.a(a);if(Infinity==d)return a.substring(e);b=Math.round(d);return a.substring(e,c+b)},2,3);O("substring-after",3,!1,!1,function (a,b,c){b=b.a(a);a=c.a(a);c=b.indexOf(a);return-1==c?"":b.substring(c+a.length)},2); -O("substring-before",3,!1,!1,function (a,b,c){b=b.a(a);a=c.a(a);a=b.indexOf(a);return-1==a?"":b.substring(0,a)},2);O("sum",1,!1,!1,function (a,b){for(var c=L(b.evaluate(a)),d=0,e=c.next();e;e=c.next())d+=+H(e);return d},1,1,!0);O("translate",3,!1,!1,function (a,b,c,d){b=b.a(a);c=c.a(a);var e=d.a(a);a=[];for(d=0;d<c.length;d++){var f=c.charAt(d);f in a||(a[f]=e.charAt(d))}c="";for(d=0;d<b.length;d++)f=b.charAt(d),c+=f in a?a[f]:f;return c},3);O("true",2,!1,!1,h(!0),0);function Ea(a,b,c,d){this.h=a;this.t=b;this.n=c;this.J=d}Ea.prototype.toString=function(){return this.h};var Fa={};function P(a,b,c,d){if(a in Fa)throw Error("Axis already created: "+a);Fa[a]=new Ea(a,b,c,!!d)}P("ancestor",function (a,b){for(var c=new K,d=b;d=d.parentNode;)a.matches(d)&&c.unshift(d);return c},!0);P("ancestor-or-self",function (a,b){var c=new K,d=b;do a.matches(d)&&c.unshift(d);while(d=d.parentNode);return c},!0); -P("attribute",function (a,b){var c=new K,d=a.getName(),e=b.attributes;if(e)if("*"==d)for(var d=0,f;f=e[d];d++)c.add(f);else(f=e.getNamedItem(d))&&c.add(f);return c},!1);P("child",function (a,b,c,d,e){return wa.call(null,a,b,n(c)?c:null,n(d)?d:null,e||new K)},!1,!0);P("descendant",J,!1,!0);P("descendant-or-self",function (a,b,c,d){var e=new K;I(b,c,d)&&a.matches(b)&&e.add(b);return J(a,b,c,d,e)},!1,!0); -P("following",function (a,b,c,d){var e=new K;do for(var f=b;f=f.nextSibling;)I(f,c,d)&&a.matches(f)&&e.add(f),e=J(a,f,c,d,e);while(b=b.parentNode);return e},!1,!0);P("following-sibling",function (a,b){for(var c=new K,d=b;d=d.nextSibling;)a.matches(d)&&c.add(d);return c},!1);P("namespace",function(){return new K},!1);P("parent",function (a,b){var c=new K;if(9==b.nodeType)return c;if(2==b.nodeType)return c.add(b.ownerElement),c;var d=b.parentNode;a.matches(d)&&c.add(d);return c},!1); -P("preceding",function (a,b,c,d){var e=new K,f=[];do f.unshift(b);while(b=b.parentNode);for(var g=1,w=f.length;g<w;g++){var p=[];for(b=f[g];b=b.previousSibling;)p.unshift(b);for(var l=0,v=p.length;l<v;l++)b=p[l],I(b,c,d)&&a.matches(b)&&e.add(b),e=J(a,b,c,d,e)}return e},!0,!0);P("preceding-sibling",function (a,b){for(var c=new K,d=b;d=d.previousSibling;)a.matches(d)&&c.unshift(d);return c},!0);P("self",function (a,b){var c=new K;a.matches(b)&&c.add(b);return c},!1);var Ga=function(){var a={K:"http://www.w3.org/2000/svg"};return function (b){return a[b]||null}}(); -function Ha(a,b){var c=function(){var c;a:{var e=F(b);try{var f=e.createNSResolver?e.createNSResolver(e.documentElement):Ga;c=e.evaluate(a,b,f,9,null);break a}catch(g){if("NS_ERROR_ILLEGAL_VALUE"!=g.name)throw new y(32,"Unable to locate an element with the xpath expression "+a+" because of the following error:\n"+g);}c=void 0}return c?c.singleNodeValue||null:b.selectSingleNode?(c=F(b),c.setProperty&&c.setProperty("SelectionLanguage","XPath"),b.selectSingleNode(a)):null}();if(null!==c&&(!c||1!=c.nodeType))throw new y(32, -'The result of the xpath expression "'+a+'" is: '+c+". It should be an element.");return c};(function(){var a=k.Components;if(!a)return!1;try{if(!a.classes)return!1}catch(b){return!1}var c=a.classes,a=a.interfaces;c["@mozilla.org/xpcom/version-comparator;1"].getService(a.nsIVersionComparator);c["@mozilla.org/xre/app-info;1"].getService(a.nsIXULAppInfo);return!0})();function Q(a,b,c,d){this.left=a;this.top=b;this.width=c;this.height=d}Q.prototype.toString=function(){return"("+this.left+", "+this.top+" - "+this.width+"w x "+this.height+"h)"};Q.prototype.contains=function (a){return a instanceof Q?this.left<=a.left&&this.left+this.width>=a.left+a.width&&this.top<=a.top&&this.top+this.height>=a.top+a.height:a.x>=this.left&&a.x<=this.left+this.width&&a.y>=this.top&&a.y<=this.top+this.height}; -Q.prototype.ceil=function(){this.left=Math.ceil(this.left);this.top=Math.ceil(this.top);this.width=Math.ceil(this.width);this.height=Math.ceil(this.height);return this};Q.prototype.floor=function(){this.left=Math.floor(this.left);this.top=Math.floor(this.top);this.width=Math.floor(this.width);this.height=Math.floor(this.height);return this}; -Q.prototype.round=function(){this.left=Math.round(this.left);this.top=Math.round(this.top);this.width=Math.round(this.width);this.height=Math.round(this.height);return this};function Ia(a,b){var c=F(a);return c.defaultView&&c.defaultView.getComputedStyle&&(c=c.defaultView.getComputedStyle(a,null))?c[b]||c.getPropertyValue(b)||"":""}function R(a,b){return Ia(a,b)||(a.currentStyle?a.currentStyle[b]:null)||a.style&&a.style[b]}function Ja(a){var b;try{b=a.getBoundingClientRect()}catch(c){return{left:0,top:0,right:0,bottom:0}}return b} -function Ka(a){var b=F(a),c=R(a,"position"),d="fixed"==c||"absolute"==c;for(a=a.parentNode;a&&a!=b;a=a.parentNode)if(c=R(a,"position"),d=d&&"static"==c&&a!=b.documentElement&&a!=b.body,!d&&(a.scrollWidth>a.clientWidth||a.scrollHeight>a.clientHeight||"fixed"==c||"absolute"==c||"relative"==c))return a;return null} -function La(a){if(1==a.nodeType){var b;if(a.getBoundingClientRect)b=Ja(a),b=new C(b.left,b.top);else{b=ua(a?new G(F(a)):B||(B=new G));var c,d=F(a),e=R(a,"position"),f=d.getBoxObjectFor&&!a.getBoundingClientRect&&"absolute"==e&&(c=d.getBoxObjectFor(a))&&(0>c.screenX||0>c.screenY),e=new C(0,0),g=(d?F(d):document).documentElement;if(a!=g)if(a.getBoundingClientRect)c=Ja(a),d=ua(d?new G(F(d)):B||(B=new G)),e.x=c.left+d.x,e.y=c.top+d.y;else if(d.getBoxObjectFor&&!f)c=d.getBoxObjectFor(a),d=d.getBoxObjectFor(g), -e.x=c.screenX-d.screenX,e.y=c.screenY-d.screenY;else{c=a;do e.x+=c.offsetLeft,e.y+=c.offsetTop,c!=a&&(e.x+=c.clientLeft||0,e.y+=c.clientTop||0),c=c.offsetParent;while(c&&c!=a);for(c=a;(c=Ka(c))&&c!=d.body&&c!=g;)e.x-=c.scrollLeft,e.y-=c.scrollTop}b=new C(e.x-b.x,e.y-b.y)}A[12]||(A[12]=0<=q(12))?a=b:((c=R(a,"-moz-transform"))||(c=R(a,"transform")),a=c?(a=c.match(Ma))?new C(parseFloat(a[1]),parseFloat(a[2])):new C(0,0):new C(0,0),a=new C(b.x+a.x,b.y+a.y));return a}b="function"==m(a.k);c=a;a.targetTouches? -c=a.targetTouches[0]:b&&a.k().targetTouches&&(c=a.k().targetTouches[0]);return new C(c.clientX,c.clientY)}var Ma=/matrix\([0-9\.\-]+, [0-9\.\-]+, [0-9\.\-]+, [0-9\.\-]+, ([0-9\.\-]+)p?x?, ([0-9\.\-]+)p?x?\)/;function S(a,b){return!!a&&1==a.nodeType&&(!b||a.tagName.toUpperCase()==b)}function T(a){for(a=a.parentNode;a&&1!=a.nodeType&&9!=a.nodeType&&11!=a.nodeType;)a=a.parentNode;return S(a)?a:null} -function U(a,b){var c=aa(b);if("float"==c||"cssFloat"==c||"styleFloat"==c)c="cssFloat";c=Ia(a,c)||Na(a,c);if(null===c)c=null;else if(da(fa,b)&&(x.test("#"==c.charAt(0)?c:"#"+c)||la(c).length||u&&u[c.toLowerCase()]||ja(c).length)){var d=ja(c);if(!d.length){a:if(d=la(c),!d.length){d=(d=u[c.toLowerCase()])?d:"#"==c.charAt(0)?c:"#"+c;if(x.test(d)&&(d=ha(d),d=ha(d),d=[parseInt(d.substr(1,2),16),parseInt(d.substr(3,2),16),parseInt(d.substr(5,2),16)],d.length))break a;d=[]}3==d.length&&d.push(1)}c=4!=d.length? -c:"rgba("+d.join(", ")+")"}return c}function Na(a,b){var c=a.currentStyle||a.style,d=c[b];void 0===d&&"function"==m(c.getPropertyValue)&&(d=c.getPropertyValue(b));return"inherit"!=d?void 0!==d?d:null:(c=T(a))?Na(c,b):null} -function V(a,b){function c(a){if("none"==U(a,"display"))return!1;a=T(a);return!a||c(a)}function d(a){if(a.hasAttribute){if(a.hasAttribute("hidden"))return!1}else return!0;a=T(a);return!a||d(a)}function e(a){var b=W(a);return 0<b.height&&0<b.width?!0:S(a,"PATH")&&(0<b.height||0<b.width)?(a=U(a,"stroke-width"),!!a&&0<parseInt(a,10)):"hidden"!=U(a,"overflow")&&ca(a.childNodes,function (a){return a.nodeType==pa||S(a)&&e(a)})}function f(a){var b=U(a,"-o-transform")||U(a,"-webkit-transform")||U(a,"-ms-transform")|| -U(a,"-moz-transform")||U(a,"transform");if(b&&"none"!==b)return b=La(a),a=W(a),0<=b.x+a.width&&0<=b.y+a.height?!0:!1;a=T(a);return!a||f(a)}if(!S(a))throw Error("Argument to isShown must be of type Element");if(S(a,"OPTION")||S(a,"OPTGROUP")){var g=ta(a,function (a){return S(a,"SELECT")});return!!g&&V(g,!0)}return(g=Oa(a))?!!g.l&&0<g.rect.width&&0<g.rect.height&&V(g.l,b):S(a,"INPUT")&&"hidden"==a.type.toLowerCase()||S(a,"NOSCRIPT")||"hidden"==U(a,"visibility")||!c(a)||!b&&0==Pa(a)||!d(a)||!e(a)||Qa(a)== -X?!1:f(a)}var X="hidden"; -function Qa(a){function b(a){var b=a;if("visible"==w)if(a==f)b=g;else if(a==g)return{x:"visible",y:"visible"};b={x:U(b,"overflow-x"),y:U(b,"overflow-y")};a==f&&(b.x="hidden"==b.x?"hidden":"auto",b.y="hidden"==b.y?"hidden":"auto");return b}function c(a){var b=U(a,"position");if("fixed"==b)return f;for(a=T(a);a&&a!=f&&(0==U(a,"display").lastIndexOf("inline",0)||"absolute"==b&&"static"==U(a,"position"));)a=T(a);return a}var d=W(a),e=F(a),f=e.documentElement,g=e.body||f,w=U(f,"overflow");for(a=c(a);a;a= -c(a)){var p=W(a),e=b(a),l=d.left>=p.left+p.width,p=d.top>=p.top+p.height;if(l&&"hidden"==e.x||p&&"hidden"==e.y)return X;if(l&&"visible"!=e.x||p&&"visible"!=e.y)return Qa(a)==X?X:"scroll"}return"none"} -function W(a){var b=Oa(a);if(b)return b.rect;if("function"==m(a.getBBox))try{var c=a.getBBox();return new Q(c.x,c.y,c.width,c.height)}catch(d){if("NS_ERROR_FAILURE"===d.name||-1!=d.message.indexOf("Component returned failure code: 0x80004005"))return new Q(0,0,0,0);throw d;}else{if(S(a,"HTML"))return a=((F(a)?F(a).parentWindow||F(a).defaultView:window)||window).document,a="CSS1Compat"==a.compatMode?a.documentElement:a.body,a=new D(a.clientWidth,a.clientHeight),new Q(0,0,a.width,a.height);b=La(a); -return new Q(b.x,b.y,a.offsetWidth,a.offsetHeight)}} -function Oa(a){var b=S(a,"MAP");if(!b&&!S(a,"AREA"))return null;var c=b?a:S(a.parentNode,"MAP")?a.parentNode:null,d=null,e=null;if(c&&c.name&&(d=Ha('/descendant::*[@usemap = "#'+c.name+'"]',F(c)))&&(e=W(d),!b&&"default"!=a.shape.toLowerCase())){var f=Ra(a);a=Math.min(Math.max(f.left,0),e.width);b=Math.min(Math.max(f.top,0),e.height);c=Math.min(f.width,e.width-a);f=Math.min(f.height,e.height-b);e=new Q(a+e.left,b+e.top,c,f)}return{l:d,rect:e||new Q(0,0,0,0)}} -function Ra(a){var b=a.shape.toLowerCase();a=a.coords.split(",");if("rect"==b&&4==a.length){var b=a[0],c=a[1];return new Q(b,c,a[2]-b,a[3]-c)}if("circle"==b&&3==a.length)return b=a[2],new Q(a[0]-b,a[1]-b,2*b,2*b);if("poly"==b&&2<a.length){for(var b=a[0],c=a[1],d=b,e=c,f=2;f+1<a.length;f+=2)b=Math.min(b,a[f]),d=Math.max(d,a[f]),c=Math.min(c,a[f+1]),e=Math.max(e,a[f+1]);return new Q(b,c,d-b,e-c)}return new Q(0,0,0,0)} -function Pa(a){var b=1,c=U(a,"opacity");c&&(b=Number(c));(a=T(a))&&(b*=Pa(a));return b};var Sa=V,Y=["_"],Z=k;Y[0]in Z||!Z.execScript||Z.execScript("var "+Y[0]);for(var $;Y.length&&($=Y.shift());)Y.length||void 0===Sa?Z=Z[$]?Z[$]:Z[$]={}:Z[$]=Sa;; return this._.apply(null,arguments);}.apply({navigator:typeof window!=undefined?window.navigator:null,document:typeof window!=undefined?window.document:null}, arguments);} diff --git a/testing/marionette/browser.js b/testing/marionette/browser.js deleted file mode 100644 index c6f9a2338..000000000 --- a/testing/marionette/browser.js +++ /dev/null @@ -1,436 +0,0 @@ -/* 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/. */ - -"use strict"; - -const {utils: Cu} = Components; - -Cu.import("chrome://marionette/content/element.js"); -Cu.import("chrome://marionette/content/error.js"); -Cu.import("chrome://marionette/content/frame.js"); - -this.EXPORTED_SYMBOLS = ["browser"]; - -this.browser = {}; - -const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; - - -/** - * Get the <xul:browser> for the specified tab. - * - * @param {<xul:tab>} tab - * The tab whose browser needs to be returned. - * - * @return {<xul:browser>} - * The linked browser for the tab or null if no browser can be found. - */ -browser.getBrowserForTab = function (tab) { - if ("browser" in tab) { - // Fennec - return tab.browser; - - } else if ("linkedBrowser" in tab) { - // Firefox - return tab.linkedBrowser; - - } else { - return null; - } -}; - -/** - * Return the tab browser for the specified chrome window. - * - * @param {nsIDOMWindow} win - * The window whose tabbrowser needs to be accessed. - * - * @return {<xul:tabbrowser>} - * Tab browser or null if it's not a browser window. - */ -browser.getTabBrowser = function (win) { - if ("BrowserApp" in win) { - // Fennec - return win.BrowserApp; - - } else if ("gBrowser" in win) { - // Firefox - return win.gBrowser; - - } else { - return null; - } -}; - -/** - * Creates a browsing context wrapper. - * - * Browsing contexts handle interactions with the browser, according to - * the current environment (desktop, B2G, Fennec, &c). - * - * @param {nsIDOMWindow} win - * The window whose browser needs to be accessed. - * @param {GeckoDriver} driver - * Reference to the driver the browser is attached to. - */ -browser.Context = class { - - /** - * @param {<xul:browser>} win - * Frame that is expected to contain the view of the web document. - * @param {GeckoDriver} driver - * Reference to driver instance. - */ - constructor(win, driver) { - this.window = win; - this.driver = driver; - - // In Firefox this is <xul:tabbrowser> (not <xul:browser>!) - // and BrowserApp in Fennec - this.tabBrowser = browser.getTabBrowser(win); - - this.knownFrames = []; - - // Used in B2G to identify the homescreen content page - this.mainContentId = null; - - // Used to set curFrameId upon new session - this.newSession = true; - - this.seenEls = new element.Store(); - - // A reference to the tab corresponding to the current window handle, if any. - // Specifically, this.tab refers to the last tab that Marionette switched - // to in this browser window. Note that this may not equal the currently - // selected tab. For example, if Marionette switches to tab A, and then - // clicks on a button that opens a new tab B in the same browser window, - // this.tab will still point to tab A, despite tab B being the currently - // selected tab. - this.tab = null; - this.pendingCommands = []; - - // We should have one frame.Manager per browser.Context so that we - // can handle modals in each <xul:browser>. - this.frameManager = new frame.Manager(driver); - this.frameRegsPending = 0; - - // register all message listeners - this.frameManager.addMessageManagerListeners(driver.mm); - this.getIdForBrowser = driver.getIdForBrowser.bind(driver); - this.updateIdForBrowser = driver.updateIdForBrowser.bind(driver); - this._curFrameId = null; - this._browserWasRemote = null; - this._hasRemotenessChange = false; - } - - /** - * The current frame ID is managed per browser element on desktop in - * case the ID needs to be refreshed. The currently selected window is - * identified by a tab. - */ - get curFrameId() { - let rv = null; - if (this.driver.appName == "B2G") { - rv = this._curFrameId; - } else if (this.tab) { - rv = this.getIdForBrowser(browser.getBrowserForTab(this.tab)); - } - return rv; - } - - set curFrameId(id) { - if (this.driver.appName != "Firefox") { - this._curFrameId = id; - } - } - - /** - * Retrieves the current tabmodal UI object. According to the browser - * associated with the currently selected tab. - */ - getTabModalUI() { - let br = browser.getBrowserForTab(this.tab); - if (!br.hasAttribute("tabmodalPromptShowing")) { - return null; - } - - // The modal is a direct sibling of the browser element. - // See tabbrowser.xml's getTabModalPromptBox. - let modals = br.parentNode.getElementsByTagNameNS( - XUL_NS, "tabmodalprompt"); - return modals[0].ui; - } - - /** - * Close the current window. - * - * @return {Promise} - * A promise which is resolved when the current window has been closed. - */ - closeWindow() { - return new Promise(resolve => { - this.window.addEventListener("unload", ev => { - resolve(); - }, {once: true}); - this.window.close(); - }); - } - - /** Called when we start a session with this browser. */ - startSession(newSession, win, callback) { - callback(win, newSession); - } - - /** - * Close the current tab. - * - * @return {Promise} - * A promise which is resolved when the current tab has been closed. - * - * @throws UnsupportedOperationError - * If tab handling for the current application isn't supported. - */ - closeTab() { - // If the current window is not a browser then close it directly. Do the - // same if only one remaining tab is open, or no tab selected at all. - if (!this.tabBrowser || this.tabBrowser.tabs.length === 1 || !this.tab) { - return this.closeWindow(); - } - - return new Promise((resolve, reject) => { - if (this.tabBrowser.closeTab) { - // Fennec - this.tabBrowser.deck.addEventListener("TabClose", ev => { - resolve(); - }, {once: true}); - this.tabBrowser.closeTab(this.tab); - - } else if (this.tabBrowser.removeTab) { - // Firefox - this.tab.addEventListener("TabClose", ev => { - resolve(); - }, {once: true}); - this.tabBrowser.removeTab(this.tab); - - } else { - reject(new UnsupportedOperationError( - `closeTab() not supported in ${this.driver.appName}`)); - } - }); - } - - /** - * Opens a tab with given URI. - * - * @param {string} uri - * URI to open. - */ - addTab(uri) { - return this.tabBrowser.addTab(uri, true); - } - - /** - * Set the current tab and update remoteness tracking if a tabbrowser is available. - * - * @param {number=} index - * Tab index to switch to. If the parameter is undefined, - * the currently selected tab will be used. - * @param {nsIDOMWindow=} win - * Switch to this window before selecting the tab. - * @param {boolean=} focus - * A boolean value which determins whether to focus - * the window. Defaults to true. - * - * @throws UnsupportedOperationError - * If tab handling for the current application isn't supported. - */ - switchToTab(index, win, focus = true) { - if (win) { - this.window = win; - this.tabBrowser = browser.getTabBrowser(win); - } - - if (!this.tabBrowser) { - return; - } - - if (typeof index == "undefined") { - this.tab = this.tabBrowser.selectedTab; - } else { - this.tab = this.tabBrowser.tabs[index]; - - if (focus) { - if (this.tabBrowser.selectTab) { - // Fennec - this.tabBrowser.selectTab(this.tab); - - } else if ("selectedTab" in this.tabBrowser) { - // Firefox - this.tabBrowser.selectedTab = this.tab; - - } else { - throw new UnsupportedOperationError("switchToTab() not supported"); - } - } - } - - if (this.driver.appName == "Firefox") { - this._browserWasRemote = browser.getBrowserForTab(this.tab).isRemoteBrowser; - this._hasRemotenessChange = false; - } - } - - /** - * Registers a new frame, and sets its current frame id to this frame - * if it is not already assigned, and if a) we already have a session - * or b) we're starting a new session and it is the right start frame. - * - * @param {string} uid - * Frame uid for use by Marionette. - * @param the XUL <browser> that was the target of the originating message. - */ - register(uid, target) { - let remotenessChange = this.hasRemotenessChange(); - if (this.curFrameId === null || remotenessChange) { - if (this.tabBrowser) { - // If we're setting up a new session on Firefox, we only process the - // registration for this frame if it belongs to the current tab. - if (!this.tab) { - this.switchToTab(); - } - - if (target == browser.getBrowserForTab(this.tab)) { - this.updateIdForBrowser(browser.getBrowserForTab(this.tab), uid); - this.mainContentId = uid; - } - } else { - this._curFrameId = uid; - this.mainContentId = uid; - } - } - - // used to delete sessions - this.knownFrames.push(uid); - return remotenessChange; - } - - /** - * When navigating between pages results in changing a browser's - * process, we need to take measures not to lose contact with a listener - * script. This function does the necessary bookkeeping. - */ - hasRemotenessChange() { - // None of these checks are relevant on b2g or if we don't have a tab yet, - // and may not apply on Fennec. - if (this.driver.appName != "Firefox" || - this.tab === null || - browser.getBrowserForTab(this.tab) === null) { - return false; - } - - if (this._hasRemotenessChange) { - return true; - } - - let currentIsRemote = browser.getBrowserForTab(this.tab).isRemoteBrowser; - this._hasRemotenessChange = this._browserWasRemote !== currentIsRemote; - this._browserWasRemote = currentIsRemote; - return this._hasRemotenessChange; - } - - /** - * Flushes any pending commands queued when a remoteness change is being - * processed and mark this remotenessUpdate as complete. - */ - flushPendingCommands() { - if (!this._hasRemotenessChange) { - return; - } - - this._hasRemotenessChange = false; - this.pendingCommands.forEach(cb => cb()); - this.pendingCommands = []; - } - - /** - * This function intercepts commands interacting with content and queues - * or executes them as needed. - * - * No commands interacting with content are safe to process until - * the new listener script is loaded and registers itself. - * This occurs when a command whose effect is asynchronous (such - * as goBack) results in a remoteness change and new commands - * are subsequently posted to the server. - */ - executeWhenReady(cb) { - if (this.hasRemotenessChange()) { - this.pendingCommands.push(cb); - } else { - cb(); - } - } - - /** - * Returns the position of the OS window. - */ - get position() { - return { - x: this.window.screenX, - y: this.window.screenY, - }; - } - -}; - -/** - * The window storage is used to save outer window IDs mapped to weak - * references of Window objects. - * - * Usage: - * - * let wins = new browser.Windows(); - * wins.set(browser.outerWindowID, window); - * - * ... - * - * let win = wins.get(browser.outerWindowID); - * - */ -browser.Windows = class extends Map { - - /** - * Save a weak reference to the Window object. - * - * @param {string} id - * Outer window ID. - * @param {Window} win - * Window object to save. - * - * @return {browser.Windows} - * Instance of self. - */ - set(id, win) { - let wref = Cu.getWeakReference(win); - super.set(id, wref); - return this; - } - - /** - * Get the window object stored by provided |id|. - * - * @param {string} id - * Outer window ID. - * - * @return {Window} - * Saved window object, or |undefined| if no window is stored by - * provided |id|. - */ - get(id) { - let wref = super.get(id); - if (wref) { - return wref.get(); - } - } - -}; diff --git a/testing/marionette/capture.js b/testing/marionette/capture.js deleted file mode 100644 index 274d20567..000000000 --- a/testing/marionette/capture.js +++ /dev/null @@ -1,193 +0,0 @@ -/* 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/. */ - -"use strict"; - -const {utils: Cu} = Components; -Cu.importGlobalProperties(["crypto"]); - -this.EXPORTED_SYMBOLS = ["capture"]; - -const CONTEXT_2D = "2d"; -const BG_COLOUR = "rgb(255,255,255)"; -const PNG_MIME = "image/png"; -const XHTML_NS = "http://www.w3.org/1999/xhtml"; - -/** Provides primitives to capture screenshots. */ -this.capture = {}; - -capture.Format = { - Base64: 0, - Hash: 1, -}; - -/** - * Take a screenshot of a single element. - * - * @param {Node} node - * The node to take a screenshot of. - * @param {Array.<Node>=} highlights - * Optional array of nodes, around which a border will be marked to - * highlight them in the screenshot. - * - * @return {HTMLCanvasElement} - * The canvas element where the element has been painted on. - */ -capture.element = function (node, highlights = []) { - let win = node.ownerDocument.defaultView; - let rect = node.getBoundingClientRect(); - - return capture.canvas( - win, - rect.left, - rect.top, - rect.width, - rect.height, - highlights); -}; - -/** - * Take a screenshot of the window's viewport by taking into account - * the current offsets. - * - * @param {DOMWindow} win - * The DOM window providing the document element to capture, - * and the offsets for the viewport. - * @param {Array.<Node>=} highlights - * Optional array of nodes, around which a border will be marked to - * highlight them in the screenshot. - * - * @return {HTMLCanvasElement} - * The canvas element where the viewport has been painted on. - */ -capture.viewport = function (win, highlights = []) { - let rootNode = win.document.documentElement; - - return capture.canvas( - win, - win.pageXOffset, - win.pageYOffset, - rootNode.clientWidth, - rootNode.clientHeight, - highlights); -}; - -/** - * Low-level interface to draw a rectangle off the framebuffer. - * - * @param {DOMWindow} win - * The DOM window used for the framebuffer, and providing the interfaces - * for creating an HTMLCanvasElement. - * @param {number} left - * The left, X axis offset of the rectangle. - * @param {number} top - * The top, Y axis offset of the rectangle. - * @param {number} width - * The width dimension of the rectangle to paint. - * @param {number} height - * The height dimension of the rectangle to paint. - * @param {Array.<Node>=} highlights - * Optional array of nodes, around which a border will be marked to - * highlight them in the screenshot. - * - * @return {HTMLCanvasElement} - * The canvas on which the selection from the window's framebuffer - * has been painted on. - */ -capture.canvas = function (win, left, top, width, height, highlights = []) { - let scale = win.devicePixelRatio; - - let canvas = win.document.createElementNS(XHTML_NS, "canvas"); - canvas.width = width * scale; - canvas.height = height * scale; - - let ctx = canvas.getContext(CONTEXT_2D); - let flags = ctx.DRAWWINDOW_DRAW_CARET; - // Disabled in bug 1243415 for webplatform-test failures due to out of view elements. - // Needs https://github.com/w3c/web-platform-tests/issues/4383 fixed. - // ctx.DRAWWINDOW_DRAW_VIEW; - // Bug 1009762 - Crash in [@ mozilla::gl::ReadPixelsIntoDataSurface] - // ctx.DRAWWINDOW_USE_WIDGET_LAYERS; - - ctx.scale(scale, scale); - ctx.drawWindow(win, left, top, width, height, BG_COLOUR, flags); - ctx = capture.highlight_(ctx, highlights, top, left); - - return canvas; -}; - -capture.highlight_ = function (context, highlights, top = 0, left = 0) { - if (!highlights) { - return; - } - - context.lineWidth = "2"; - context.strokeStyle = "red"; - context.save(); - - for (let el of highlights) { - let rect = el.getBoundingClientRect(); - let oy = -top; - let ox = -left; - - context.strokeRect( - rect.left + ox, - rect.top + oy, - rect.width, - rect.height); - } - - return context; -}; - -/** - * Encode the contents of an HTMLCanvasElement to a Base64 encoded string. - * - * @param {HTMLCanvasElement} canvas - * The canvas to encode. - * - * @return {string} - * A Base64 encoded string. - */ -capture.toBase64 = function (canvas) { - let u = canvas.toDataURL(PNG_MIME); - return u.substring(u.indexOf(",") + 1); -}; - -/** -* Hash the contents of an HTMLCanvasElement to a SHA-256 hex digest. -* -* @param {HTMLCanvasElement} canvas -* The canvas to encode. -* -* @return {string} -* A hex digest of the SHA-256 hash of the base64 encoded string. -*/ -capture.toHash = function (canvas) { - let u = capture.toBase64(canvas); - let buffer = new TextEncoder("utf-8").encode(u); - return crypto.subtle.digest("SHA-256", buffer).then(hash => hex(hash)); -}; - -/** -* Convert buffer into to hex. -* -* @param {ArrayBuffer} buffer -* The buffer containing the data to convert to hex. -* -* @return {string} -* A hex digest of the input buffer. -*/ -function hex(buffer) { - let hexCodes = []; - let view = new DataView(buffer); - for (let i = 0; i < view.byteLength; i += 4) { - let value = view.getUint32(i); - let stringValue = value.toString(16); - let padding = '00000000'; - let paddedValue = (padding + stringValue).slice(-padding.length); - hexCodes.push(paddedValue); - } - return hexCodes.join(""); -};
\ No newline at end of file diff --git a/testing/marionette/cert.js b/testing/marionette/cert.js deleted file mode 100644 index e54129c57..000000000 --- a/testing/marionette/cert.js +++ /dev/null @@ -1,139 +0,0 @@ -/* 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/. */ - -"use strict"; - -const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; - -Cu.import("resource://gre/modules/Preferences.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -this.EXPORTED_SYMBOLS = ["cert"]; - -const registrar = - Components.manager.QueryInterface(Ci.nsIComponentRegistrar); -const sss = Cc["@mozilla.org/ssservice;1"] - .getService(Ci.nsISiteSecurityService); - -const CONTRACT_ID = "@mozilla.org/security/certoverride;1"; -const CERT_PINNING_ENFORCEMENT_PREF = - "security.cert_pinning.enforcement_level"; -const HSTS_PRELOAD_LIST_PREF = - "network.stricttransportsecurity.preloadlist"; - -/** TLS certificate service override management for Marionette. */ -this.cert = { - Error: { - Untrusted: 1, - Mismatch: 2, - Time: 4, - }, - - currentOverride: null, -}; - -/** - * Installs a TLS certificate service override. - * - * The provided |service| must implement the |register| and |unregister| - * functions that causes a new |nsICertOverrideService| interface - * implementation to be registered with the |nsIComponentRegistrar|. - * - * After |service| is registered and made the |cert.currentOverride|, - * |nsICertOverrideService| is reinitialised to cause all Gecko components - * to pick up the new service. - * - * If an override is already installed, i.e. when |cert.currentOverride| - * is not null, this functions acts as a NOOP. - * - * @param {cert.Override} service - * Service generator that registers and unregisters the XPCOM service. - * - * @throws {Components.Exception} - * If unable to register or initialise |service|. - */ -cert.installOverride = function (service) { - if (this.currentOverride) { - return; - } - - service.register(); - cert.currentOverride = service; -}; - -/** - * Uninstall a TLS certificate service override. - * - * After the service has been unregistered, |cert.currentOverride| - * is reset to null. - * - * If there no current override installed, i.e. if |cert.currentOverride| - * is null, this function acts as a NOOP. - */ -cert.uninstallOverride = function() { - if (!cert.currentOverride) { - return; - } - cert.currentOverride.unregister(); - this.currentOverride = null; -}; - -/** - * Certificate override service that acts in an all-inclusive manner - * on TLS certificates. - * - * When an invalid certificate is encountered, it is overriden - * with the |matching| bit level, which is typically a combination of - * |cert.Error.Untrusted|, |cert.Error.Mismatch|, and |cert.Error.Time|. - * - * @type cert.Override - * - * @throws {Components.Exception} - * If there are any problems registering the service. - */ -cert.InsecureSweepingOverride = function() { - const CID = Components.ID("{4b67cce0-a51c-11e6-9598-0800200c9a66}"); - const DESC = "All-encompassing cert service that matches on a bitflag"; - - // This needs to be an old-style class with a function constructor - // and prototype assignment because... XPCOM. Any attempt at - // modernisation will be met with cryptic error messages which will - // make your life miserable. - let service = function() {}; - service.prototype = { - hasMatchingOverride: function ( - aHostName, aPort, aCert, aOverrideBits, aIsTemporary) { - aIsTemporary.value = false; - aOverrideBits.value = - cert.Error.Untrusted | cert.Error.Mismatch | cert.Error.Time; - - return true; - }, - - QueryInterface: XPCOMUtils.generateQI([Ci.nsICertOverrideService]), - }; - let factory = XPCOMUtils.generateSingletonFactory(service); - - return { - register: function() { - // make it possible to register certificate overrides for domains - // that use HSTS or HPKP - Preferences.set(HSTS_PRELOAD_LIST_PREF, false); - Preferences.set(CERT_PINNING_ENFORCEMENT_PREF, 0); - - registrar.registerFactory(CID, DESC, CONTRACT_ID, factory); - }, - - unregister: function() { - registrar.unregisterFactory(CID, factory); - - Preferences.reset(HSTS_PRELOAD_LIST_PREF); - Preferences.reset(CERT_PINNING_ENFORCEMENT_PREF); - - // clear collected HSTS and HPKP state - // through the site security service - sss.clearAll(); - }, - }; -}; diff --git a/testing/marionette/chrome/test.xul b/testing/marionette/chrome/test.xul deleted file mode 100644 index 2baa28e5f..000000000 --- a/testing/marionette/chrome/test.xul +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0"?> -<!-- 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/. --> - -<!DOCTYPE window [ -]> -<window id="winTest" title="Title Test" windowtype="Test Type" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> - <dialog id="dia" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> - - <vbox id="things"> - <checkbox id="testBox" label="box" /> - <textbox id="textInput" size="6" value="test" label="input" /> - <textbox id="textInput2" size="6" value="test" label="input" /> - <textbox id="textInput3" class="asdf" size="6" value="test" label="input" /> - </vbox> - - <iframe id="iframe" name="iframename" src="chrome://marionette/content/test2.xul"/> - <iframe id="iframe" name="iframename" src="chrome://marionette/content/test_nested_iframe.xul"/> - <hbox id="testXulBox"/> - <browser id='aBrowser' src="chrome://marionette/content/test2.xul"/> - </dialog> -</window> diff --git a/testing/marionette/chrome/test2.xul b/testing/marionette/chrome/test2.xul deleted file mode 100644 index d450e0305..000000000 --- a/testing/marionette/chrome/test2.xul +++ /dev/null @@ -1,19 +0,0 @@ -<?xml version="1.0"?> -<!-- 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/. --> - -<!DOCTYPE window [ -]> - -<dialog id="dia" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> - - <vbox id="things"> - <checkbox id="testBox" label="box" /> - <textbox id="textInput" size="6" value="test" label="input" /> - <textbox id="textInput2" size="6" value="test" label="input" /> - <textbox id="textInput3" class="asdf" size="6" value="test" label="input" /> - </vbox> - -</dialog> diff --git a/testing/marionette/chrome/test_anonymous_content.xul b/testing/marionette/chrome/test_anonymous_content.xul deleted file mode 100644 index 49f9eff29..000000000 --- a/testing/marionette/chrome/test_anonymous_content.xul +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0"?> -<!-- 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/. --> - -<!DOCTYPE dialog [ -]> - -<dialog id="testDialogAnonymousNode" - buttons="accept, cancel" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> - - <bindings id="testBindings" xmlns="http://www.mozilla.org/xbl" - xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> - <binding id="framebox"> - <content orient="vertical" mousethrough="never"> - <xul:browser anonid="content" id="browser" flex="1" - context="contentAreaContextMenu" - src="test.xul" - type="content"/> - </content> - </binding> - - <binding id="iframebox"> - <content> - <xul:box> - <xul:iframe anonid="iframe" src="chrome://marionette/content/test.xul"></xul:iframe> - </xul:box> - </content> - </binding> - </bindings> - - <hbox id="testAnonymousContentBox"/> - <hbox id="container" style="-moz-binding: url('#testBindings');"/> - <hbox id="container2" style="-moz-binding: url('#iframebox');"/> - -</dialog> diff --git a/testing/marionette/chrome/test_dialog.dtd b/testing/marionette/chrome/test_dialog.dtd deleted file mode 100644 index 414cb0ee8..000000000 --- a/testing/marionette/chrome/test_dialog.dtd +++ /dev/null @@ -1,7 +0,0 @@ -<!-- 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/. --> - -<!ENTITY testDialog.title "Test Dialog"> - -<!ENTITY settings.label "Settings"> diff --git a/testing/marionette/chrome/test_dialog.properties b/testing/marionette/chrome/test_dialog.properties deleted file mode 100644 index ade7b6bde..000000000 --- a/testing/marionette/chrome/test_dialog.properties +++ /dev/null @@ -1,7 +0,0 @@ -# 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/. - -testDialog.title=Test Dialog - -settings.label=Settings diff --git a/testing/marionette/chrome/test_dialog.xul b/testing/marionette/chrome/test_dialog.xul deleted file mode 100644 index 0d4816b97..000000000 --- a/testing/marionette/chrome/test_dialog.xul +++ /dev/null @@ -1,36 +0,0 @@ -<?xml version="1.0"?> -<!-- 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/. --> - -<!DOCTYPE testdialog [ -<!ENTITY % dialogDTD SYSTEM "chrome://marionette/content/test_dialog.dtd" > -%dialogDTD; -]> - -<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> - - -<dialog id="testDialog" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - title="&testDialog.title;" - buttons="accept,cancel"> - - <vbox flex="1" style="min-width: 300px; min-height: 500px;"> - <label>&settings.label;</label> - <separator class="thin"/> - <richlistbox id="test-list" flex="1"> - <richlistitem id="item-choose" orient="horizontal" selected="true"> - <label id="choose-label" value="First Entry" flex="1"/> - <button id="choose-button" oncommand="" label="Choose..."/> - </richlistitem> - </richlistbox> - <separator class="thin"/> - <checkbox id="check-box" label="Test Mode 2" /> - <hbox align="center"> - <label id="text-box-label" control="text-box">Name:</label> - <textbox id="text-box" flex="1" /> - </hbox> - </vbox> - -</dialog> diff --git a/testing/marionette/chrome/test_nested_iframe.xul b/testing/marionette/chrome/test_nested_iframe.xul deleted file mode 100644 index 0bec83cbe..000000000 --- a/testing/marionette/chrome/test_nested_iframe.xul +++ /dev/null @@ -1,9 +0,0 @@ -<?xml version="1.0"?> -<!-- 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/. --> - -<!DOCTYPE window [ -]> - - <iframe id="iframe" name="iframename" src="test2.xul"/> diff --git a/testing/marionette/client/.flake8 b/testing/marionette/client/.flake8 deleted file mode 100644 index 5f607bc63..000000000 --- a/testing/marionette/client/.flake8 +++ /dev/null @@ -1,3 +0,0 @@ -[flake8] -max-line-length = 99 -exclude = __init__.py,disti/*,build/*, diff --git a/testing/marionette/client/MANIFEST.in b/testing/marionette/client/MANIFEST.in deleted file mode 100644 index cf628b039..000000000 --- a/testing/marionette/client/MANIFEST.in +++ /dev/null @@ -1,2 +0,0 @@ -exclude MANIFEST.in -include requirements.txt diff --git a/testing/marionette/client/docs/Makefile b/testing/marionette/client/docs/Makefile deleted file mode 100644 index f3d89d6d4..000000000 --- a/testing/marionette/client/docs/Makefile +++ /dev/null @@ -1,153 +0,0 @@ -# Makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -PAPER = -BUILDDIR = _build - -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . -# the i18n builder cannot share the environment and doctrees with the others -I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . - -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext - -help: - @echo "Please use \`make <target>' where <target> is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " singlehtml to make a single large HTML file" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " devhelp to make HTML files and a Devhelp project" - @echo " epub to make an epub" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run them through pdflatex" - @echo " text to make text files" - @echo " man to make manual pages" - @echo " texinfo to make Texinfo files" - @echo " info to make Texinfo files and run them through makeinfo" - @echo " gettext to make PO message catalogs" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - -clean: - -rm -rf $(BUILDDIR)/* - -html: - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -singlehtml: - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml - @echo - @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/MarionettePythonClient.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/MarionettePythonClient.qhc" - -devhelp: - $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp - @echo - @echo "Build finished." - @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/MarionettePythonClient" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/MarionettePythonClient" - @echo "# devhelp" - -epub: - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub - @echo - @echo "Build finished. The epub file is in $(BUILDDIR)/epub." - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make' in that directory to run these through (pdf)latex" \ - "(use \`make latexpdf' here to do that automatically)." - -latexpdf: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through pdflatex..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -text: - $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text - @echo - @echo "Build finished. The text files are in $(BUILDDIR)/text." - -man: - $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man - @echo - @echo "Build finished. The manual pages are in $(BUILDDIR)/man." - -texinfo: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo - @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." - @echo "Run \`make' in that directory to run these through makeinfo" \ - "(use \`make info' here to do that automatically)." - -info: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo "Running Texinfo files through makeinfo..." - make -C $(BUILDDIR)/texinfo info - @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." - -gettext: - $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale - @echo - @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." - -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." - -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." diff --git a/testing/marionette/client/docs/advanced/actions.rst b/testing/marionette/client/docs/advanced/actions.rst deleted file mode 100644 index 294855a6f..000000000 --- a/testing/marionette/client/docs/advanced/actions.rst +++ /dev/null @@ -1,46 +0,0 @@ -Actions -======= - -.. py:currentmodule:: marionette - -Action Sequences ----------------- - -:class:`Actions` are designed as a way to simulate user input as closely as possible -on a touch device like a smart phone. A common operation is to tap the screen -and drag your finger to another part of the screen and lift it off. - -This can be simulated using an Action:: - - from marionette import Actions - - start_element = marionette.find_element('id', 'start') - end_element = marionette.find_element('id', 'end') - - action = Actions(marionette) - action.press(start_element).wait(1).move(end_element).release() - action.perform() - -This will simulate pressing an element, waiting for one second, moving the -finger over to another element and then lifting the finger off the screen. The -wait is optional in this case, but can be useful for simulating delays typical -to a users behaviour. - -Multi-Action Sequences ----------------------- - -Sometimes it may be necessary to simulate multiple actions at the same time. -For example a user may be dragging one finger while tapping another. This is -where :class:`MultiActions` come in. MultiActions are simply a way of combining -two or more actions together and performing them all at the same time:: - - action1 = Actions(marionette) - action1.press(start_element).move(end_element).release() - - action2 = Actions(marionette) - action2.press(another_element).wait(1).release() - - multi = MultiActions(marionette) - multi.add(action1) - multi.add(action2) - multi.perform() diff --git a/testing/marionette/client/docs/advanced/debug.rst b/testing/marionette/client/docs/advanced/debug.rst deleted file mode 100644 index e72d2549b..000000000 --- a/testing/marionette/client/docs/advanced/debug.rst +++ /dev/null @@ -1,54 +0,0 @@ -Debugging -========= - -.. py:currentmodule:: marionette - -Sometimes when working with Marionette you'll run into unexpected behaviour and -need to do some debugging. This page outlines some of the Marionette methods -that can be useful to you. - -Please note that the best tools for debugging are the `ones that ship with -Gecko`_. This page doesn't describe how to use those with Marionette. Also see -a related topic about `using the debugger with Marionette`_ on MDN. - -.. _ones that ship with Gecko: https://developer.mozilla.org/en-US/docs/Tools -.. _using the debugger with Marionette: https://developer.mozilla.org/en-US/docs/Marionette/Debugging - - -Storing Logs on the Server -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -By calling `~Marionette.log` it is possible to store a message on the server. -Logs can later be retrieved using `~Marionette.get_logs`. For example:: - - try: - marionette.log("Sending a click event") # logged at INFO level - elem.click() - except: - marionette.log("Something went wrong!", "ERROR") - - print(marionette.get_logs()) - -Disclaimer: Example for illustrative purposes only, don't actually hide -tracebacks like that! - - -Seeing What's on the Page -~~~~~~~~~~~~~~~~~~~~~~~~~ - -Sometimes it's difficult to tell what is actually on the page that is being -manipulated. Either because it happens too fast, the window isn't big enough or -you are manipulating a remote server! There are two methods that can help you -out. The first is `~Marionette.screenshot`:: - - marionette.screenshot() # takes screenshot of entire frame - elem = marionette.find_element(By.ID, 'some-div') - marionette.screenshot(elem) # takes a screenshot of only the given element - -Sometimes you just want to see the DOM layout. You can do this with the -`~Marionette.page_source` property. Note that the page source depends on the -context you are in:: - - print(marionette.page_source) - marionette.set_context('chrome') - print(marionette.page_source) diff --git a/testing/marionette/client/docs/advanced/findelement.rst b/testing/marionette/client/docs/advanced/findelement.rst deleted file mode 100644 index abcbc8e89..000000000 --- a/testing/marionette/client/docs/advanced/findelement.rst +++ /dev/null @@ -1,126 +0,0 @@ -Finding Elements -================ -.. py:currentmodule:: marionette - -One of the most common and yet often most difficult tasks in Marionette is -finding a DOM element on a webpage or in the chrome UI. Marionette provides -several different search strategies to use when finding elements. All search -strategies work with both :func:`~Marionette.find_element` and -:func:`~Marionette.find_elements`, though some strategies are not implemented -in chrome scope. - -In the event that more than one element is matched by the query, -:func:`~Marionette.find_element` will only return the first element found. In -the event that no elements are matched by the query, -:func:`~Marionette.find_element` will raise `NoSuchElementException` while -:func:`~Marionette.find_elements` will return an empty list. - -Search Strategies ------------------ - -Search strategies are defined in the :class:`By` class:: - - from marionette import By - print(By.ID) - -The strategies are: - -* `id` - The easiest way to find an element is to refer to its id directly:: - - container = client.find_element(By.ID, 'container') - -* `class name` - To find elements belonging to a certain class, use `class name`:: - - buttons = client.find_elements(By.CLASS_NAME, 'button') - -* `css selector` - It's also possible to find elements using a `css selector`_:: - - container_buttons = client.find_elements(By.CSS_SELECTOR, '#container .buttons') - -* `name` - Find elements by their name attribute (not implemented in chrome - scope):: - - form = client.find_element(By.NAME, 'signup') - -* `tag name` - To find all the elements with a given tag, use `tag name`:: - - paragraphs = client.find_elements(By.TAG_NAME, 'p') - -* `link text` - A convenience strategy for finding link elements by their - innerHTML (not implemented in chrome scope):: - - link = client.find_element(By.LINK_TEXT, 'Click me!') - -* `partial link text` - Same as `link text` except substrings of the innerHTML - are matched (not implemented in chrome scope):: - - link = client.find_element(By.PARTIAL_LINK_TEXT, 'Clic') - -* `xpath` - Find elements using an xpath_ query:: - - elem = client.find_element(By.XPATH, './/*[@id="foobar"') - -.. _css selector: https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_Started/Selectors -.. _xpath: https://developer.mozilla.org/en-US/docs/Web/XPath - - - -Chaining Searches ------------------ - -In addition to the methods on the Marionette object, HTMLElement objects also -provide :func:`~HTMLElement.find_element` and :func:`~HTMLElement.find_elements` -methods. The difference is that only child nodes of the element will be searched. -Consider the following html snippet:: - - <div id="content"> - <span id="main"></span> - </div> - <div id="footer"></div> - -Doing the following will work:: - - client.find_element(By.ID, 'container').find_element(By.ID, 'main') - -But this will raise a `NoSuchElementException`:: - - client.find_element(By.ID, 'container').find_element(By.ID, 'footer') - - -Finding Anonymous Nodes ------------------------ - -When working in chrome scope, for example manipulating the Firefox user -interface, you may run into something called an anonymous node. - -Firefox uses a markup language called XUL_ for its interface. XUL is similar -to HTML in that it has a DOM and tags that render controls on the display. One -ability of XUL is to create re-useable widgets that are made up out of several -smaller XUL elements. These widgets can be bound to the DOM using something -called the `XML binding language (XBL)`_. - -The end result is that the DOM sees the widget as a single entity. It doesn't -know anything about how that widget is made up. All of the smaller XUL elements -that make up the widget are called `anonymous content`_. It is not possible to -query such elements using traditional DOM methods like `getElementById`. - -Marionette provides two special strategies used for finding anonymous content. -Unlike normal elements, anonymous nodes can only be seen by their parent. So -it's necessary to first find the parent element and then search for the -anonymous children from there. - -* `anon` - Finds all anonymous children of the element, there is no search term - so `None` must be passed in:: - - anon_children = client.find_element('id', 'parent').find_elements('anon', None) - -* `anon attribute` - Find an anonymous child based on an attribute. An - unofficial convention is for anonymous nodes to have an - `anonid` attribute:: - - anon_child = client.find_element('id', 'parent').find_element('anon attribute', {'anonid': 'container'}) - - -.. _XUL: https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL -.. _XML binding language (XBL): https://developer.mozilla.org/en-US/docs/XBL -.. _anonymous content: https://developer.mozilla.org/en-US/docs/XBL/XBL_1.0_Reference/Anonymous_Content diff --git a/testing/marionette/client/docs/advanced/landing.rst b/testing/marionette/client/docs/advanced/landing.rst deleted file mode 100644 index 0a44de63d..000000000 --- a/testing/marionette/client/docs/advanced/landing.rst +++ /dev/null @@ -1,13 +0,0 @@ -Advanced Topics -=============== - -Here are a collection of articles explaining some of the more complicated -aspects of Marionette. - -.. toctree:: - :maxdepth: 1 - - findelement - stale - actions - debug diff --git a/testing/marionette/client/docs/advanced/stale.rst b/testing/marionette/client/docs/advanced/stale.rst deleted file mode 100644 index 0af576865..000000000 --- a/testing/marionette/client/docs/advanced/stale.rst +++ /dev/null @@ -1,71 +0,0 @@ -Dealing with Stale Elements -=========================== -.. py:currentmodule:: marionette - -Marionette does not keep a live representation of the DOM saved. All it can do -is send commands to the Marionette server which queries the DOM on the client's -behalf. References to elements are also not passed from server to client. A -unique id is generated for each element that gets referenced and a mapping of -id to element object is stored on the server. When commands such as -:func:`~HTMLElement.click()` are run, the client sends the element's id along -with the command. The server looks up the proper DOM element in its reference -table and executes the command on it. - -In practice this means that the DOM can change state and Marionette will never -know until it sends another query. For example, look at the following HTML:: - - <head> - <script type=text/javascript> - function addDiv() { - var div = document.createElement("div"); - document.getElementById("container").appendChild(div); - } - </script> - </head> - - <body> - <div id="container"> - </div> - <input id="button" type=button onclick="addDiv();"> - </body> - -Care needs to be taken as the DOM is being modified after the page has loaded. -The following code has a race condition:: - - button = client.find_element('id', 'button') - button.click() - assert len(client.find_elements('css selector', '#container div')) > 0 - - -Explicit Waiting and Expected Conditions ----------------------------------------- - -To avoid the above scenario, manual synchronisation is needed. Waits are used -to pause program execution until a given condition is true. This is a useful -technique to employ when documents load new content or change after -``Document.readyState``'s value changes to "complete". - -The :class:`Wait` helper class provided by Marionette avoids some of the -caveats of ``time.sleep(n)``. It will return immediately once the provided -condition evaluates to true. - -To avoid the race condition in the above example, one could do:: - - button = client.find_element('id', 'button') - button.click() - - def find_divs(): - return client.find_elements('css selector', '#container div') - - divs = Wait(client).until(find_divs) - assert len(divs) > 0 - -This avoids the race condition. Because finding elements is a common condition -to wait for, it is built in to Marionette. Instead of the above, you could -write:: - - button = client.find_element('id', 'button') - button.click() - assert len(Wait(client).until(expected.elements_present('css selector', '#container div'))) > 0 - -For a full list of built-in conditions, see :mod:`~marionette.expected`. diff --git a/testing/marionette/client/docs/basics.rst b/testing/marionette/client/docs/basics.rst deleted file mode 100644 index 0de72c625..000000000 --- a/testing/marionette/client/docs/basics.rst +++ /dev/null @@ -1,185 +0,0 @@ -.. py:currentmodule:: marionette_driver.marionette - -Marionette Python Client -======================== - -The Marionette Python client library allows you to remotely control a -Gecko-based browser or device which is running a Marionette_ -server. This includes Firefox Desktop and Firefox for Android. - -The Marionette server is built directly into Gecko and can be started by -passing in a command line option to Gecko, or by using a Marionette-enabled -build. The server listens for connections from various clients. Clients can -then control Gecko by sending commands to the server. - -This is the official Python client for Marionette. There also exists a -`NodeJS client`_ maintained by the Firefox OS automation team. - -.. _Marionette: https://developer.mozilla.org/en-US/docs/Marionette -.. _NodeJS client: https://github.com/mozilla-b2g/gaia/tree/master/tests/jsmarionette - -Getting the Client ------------------- - -The Python client is officially supported. To install it, first make sure you -have `pip installed`_ then run: - -.. parsed-literal:: - pip install marionette_driver - -It's highly recommended to use virtualenv_ when installing Marionette to avoid -package conflicts and other general nastiness. - -You should now be ready to start using Marionette. The best way to learn is to -play around with it. Start a `Marionette-enabled instance of Firefox`_, fire up -a python shell and follow along with the -:doc:`interactive tutorial <interactive>`! - -.. _pip installed: https://pip.pypa.io/en/latest/installing.html -.. _virtualenv: http://virtualenv.readthedocs.org/en/latest/ -.. _Marionette-enabled instance of Firefox: https://developer.mozilla.org/en-US/docs/Mozilla/QA/Marionette/Builds - -Using the Client for Testing ----------------------------- - -Please visit the `Marionette Tests`_ section on MDN for information regarding -testing with Marionette. - -.. _Marionette Tests: https://developer.mozilla.org/en/Marionette/Tests - -Session Management ------------------- -A session is a single instance of a Marionette client connected to a Marionette -server. Before you can start executing commands, you need to start a session -with :func:`start_session() <Marionette.start_session>`: - -.. parsed-literal:: - from marionette_driver.marionette import Marionette - - client = Marionette('localhost', port=2828) - client.start_session() - -This returns a session id and an object listing the capabilities of the -Marionette server. For example, a server running on Firefox Desktop will -have some features which a server running from Firefox Android won't. -It's also possible to access the capabilities using the -:attr:`~Marionette.session_capabilities` attribute. After finishing with a -session, you can delete it with :func:`~Marionette.delete_session()`. Note that -this will also happen automatically when the Marionette object is garbage -collected. - -Context Management ------------------- -Commands can only be executed in a single window, frame and scope at a time. In -order to run commands elsewhere, it's necessary to explicitly switch to the -appropriate context. - -Use :func:`~Marionette.switch_to_window` to execute commands in the context of a -new window: - -.. parsed-literal:: - original_window = client.current_window_handle - for handle in client.window_handles: - if handle != original_window: - client.switch_to_window(handle) - print("Switched to window with '{}' loaded.".format(client.get_url())) - client.switch_to_window(original_window) - -Similarly, use :func:`~Marionette.switch_to_frame` to execute commands in the -context of a new frame (e.g an <iframe> element): - -.. parsed-literal:: - iframe = client.find_element(By.TAG_NAME, 'iframe') - client.switch_to_frame(iframe) - assert iframe == client.get_active_frame() - -Finally Marionette can switch between `chrome` and `content` scope. Chrome is a -privileged scope where you can access things like the Firefox UI itself. -Content scope is where things like webpages live. You can switch between -`chrome` and `content` using the :func:`~Marionette.set_context` and :func:`~Marionette.using_context` functions: - -.. parsed-literal:: - client.set_context(client.CONTEXT_CONTENT) - # content scope - with client.using_context(client.CONTEXT_CHROME): - #chrome scope - ... do stuff ... - # content scope restored - - -Navigation ----------- - -Use :func:`~Marionette.navigate` to open a new website. It's also possible to -move through the back/forward cache using :func:`~Marionette.go_forward` and -:func:`~Marionette.go_back` respectively. To retrieve the currently -open website, use :func:`~Marionette.get_url`: - -.. parsed-literal:: - url = 'http://mozilla.org' - client.navigate(url) - client.go_back() - client.go_forward() - assert client.get_url() == url - - -DOM Elements ------------- - -In order to inspect or manipulate actual DOM elements, they must first be found -using the :func:`~Marionette.find_element` or :func:`~Marionette.find_elements` -methods: - -.. parsed-literal:: - from marionette import HTMLElement - element = client.find_element(By.ID, 'my-id') - assert type(element) == HTMLElement - elements = client.find_elements(By.TAG_NAME, 'a') - assert type(elements) == list - -For a full list of valid search strategies, see :doc:`advanced/findelement`. - -Now that an element has been found, it's possible to manipulate it: - -.. parsed-literal:: - element.click() - element.send_keys('hello!') - print(element.get_attribute('style')) - -For the full list of possible commands, see the :class:`HTMLElement` -reference. - -Be warned that a reference to an element object can become stale if it was -modified or removed from the document. See :doc:`advanced/stale` for tips -on working around this limitation. - -Script Execution ----------------- - -Sometimes Marionette's provided APIs just aren't enough and it is necessary to -run arbitrary javascript. This is accomplished with the -:func:`~Marionette.execute_script` and :func:`~Marionette.execute_async_script` -functions. They accomplish what their names suggest, the former executes some -synchronous JavaScript, while the latter provides a callback mechanism for -running asynchronous JavaScript: - -.. parsed-literal:: - result = client.execute_script("return arguments[0] + arguments[1];", - script_args=[2, 3]) - assert result == 5 - -The async method works the same way, except it won't return until a special -`marionetteScriptFinished()` function is called: - -.. parsed-literal:: - result = client.execute_async_script(""" - setTimeout(function() { - marionetteScriptFinished("all done"); - }, arguments[0]); - """, script_args=[1000]) - assert result == "all done" - -Beware that running asynchronous scripts can potentially hang the program -indefinitely if they are not written properly. It is generally a good idea to -set a script timeout using :func:`~Marionette.timeout.script` and handling -`ScriptTimeoutException`. diff --git a/testing/marionette/client/docs/conf.py b/testing/marionette/client/docs/conf.py deleted file mode 100644 index c3a74eef6..000000000 --- a/testing/marionette/client/docs/conf.py +++ /dev/null @@ -1,259 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Marionette Python Client documentation build configuration file, created by -# sphinx-quickstart on Tue Aug 6 13:54:46 2013. -# -# This file is execfile()d with the current directory set to its containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -import os -import sys - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# sys.path.insert(0, os.path.abspath('.')) - -here = os.path.dirname(os.path.abspath(__file__)) -parent = os.path.dirname(here) -sys.path.insert(0, parent) - -# -- General configuration ----------------------------------------------------- - -# If your documentation needs a minimal Sphinx version, state it here. -# needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be extensions -# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc'] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The encoding of source files. -# source_encoding = 'utf-8-sig' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'Marionette Python Client' -copyright = u'2013, Mozilla Automation and Tools and individual contributors' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -# version = '0' -# The full version, including alpha/beta/rc tags. -# release = '0' - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -# today = '' -# Else, today_fmt is used as the format for a strftime call. -# today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = ['_build'] - -# The reST default role (used for this markup: `text`) to use for all documents. -# default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -# add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -# add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -# show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# A list of ignored prefixes for module index sorting. -# modindex_common_prefix = [] - - -# -- Options for HTML output --------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. - -html_theme = 'default' - -on_rtd = os.environ.get('READTHEDOCS', None) == 'True' - -if not on_rtd: - try: - import sphinx_rtd_theme - html_theme = 'sphinx_rtd_theme' - html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] - except ImportError: - pass - - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -# html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. -# html_theme_path = [] - -# The name for this set of Sphinx documents. If None, it defaults to -# "<project> v<release> documentation". -# html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -# html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -# html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -# html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -# html_static_path = ['_static'] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -# html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -# html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -# html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -# html_additional_pages = {} - -# If false, no module index is generated. -# html_domain_indices = True - -# If false, no index is generated. -# html_use_index = True - -# If true, the index is split into individual pages for each letter. -# html_split_index = False - -# If true, links to the reST sources are added to the pages. -# html_show_sourcelink = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -# html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -# html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a <link> tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -# html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -# html_file_suffix = None - -# Output file base name for HTML help builder. -htmlhelp_basename = 'MarionettePythonClientdoc' - - -# -- Options for LaTeX output -------------------------------------------------- - -latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - # 'papersize': 'letterpaper', - - # The font size ('10pt', '11pt' or '12pt'). - # 'pointsize': '10pt', - - # Additional stuff for the LaTeX preamble. - # 'preamble': '', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, documentclass [howto/manual]). -latex_documents = [ - ('index', 'MarionettePythonClient.tex', u'Marionette Python Client Documentation', - u'Mozilla Automation and Tools team', 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -# latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -# latex_use_parts = False - -# If true, show page references after internal links. -# latex_show_pagerefs = False - -# If true, show URL addresses after external links. -# latex_show_urls = False - -# Documents to append as an appendix to all manuals. -# latex_appendices = [] - -# If false, no module index is generated. -# latex_domain_indices = True - - -# -- Options for manual page output -------------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - ('index', 'marionettepythonclient', u'Marionette Python Client Documentation', - [u'Mozilla Automation and Tools team'], 1) -] - -# If true, show URL addresses after external links. -# man_show_urls = False - - -# -- Options for Texinfo output ------------------------------------------------ - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - ('index', 'MarionettePythonClient', 'Marionette Python Client Documentation', - 'Mozilla Automation and Tools team', 'MarionettePythonClient', - 'One line description of project.', 'Miscellaneous'), -] - -# Documents to append as an appendix to all manuals. -# texinfo_appendices = [] - -# If false, no module index is generated. -# texinfo_domain_indices = True - -# How to display URL addresses: 'footnote', 'no', or 'inline'. -# texinfo_show_urls = 'footnote' diff --git a/testing/marionette/client/docs/index.rst b/testing/marionette/client/docs/index.rst deleted file mode 100644 index b1f266726..000000000 --- a/testing/marionette/client/docs/index.rst +++ /dev/null @@ -1,16 +0,0 @@ -.. include:: basics.rst - -Indices and tables ------------------- - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` - -.. toctree:: - :hidden: - - Getting Started <basics> - Interactive Tutorial <interactive> - advanced/landing - reference diff --git a/testing/marionette/client/docs/interactive.rst b/testing/marionette/client/docs/interactive.rst deleted file mode 100644 index e6d755613..000000000 --- a/testing/marionette/client/docs/interactive.rst +++ /dev/null @@ -1,55 +0,0 @@ -Using the Client Interactively -============================== - -Once you installed the client and have Marionette running, you can fire -up your favourite interactive python environment and start playing with -Marionette. Let's use a typical python shell: - -.. parsed-literal:: - - python - -First, import Marionette: - -.. parsed-literal:: - from marionette import Marionette - -Now create the client for this session. Assuming you're using the default -port on a Marionette instance running locally: - -.. parsed-literal:: - - client = Marionette(host='localhost', port=2828) - client.start_session() - -This will return some id representing your session id. Now that you've -established a connection, let's start doing interesting things: - -.. parsed-literal:: - - client.execute_script("alert('o hai there!');") - -You should now see this alert pop up! How exciting! Okay, let's do -something practical. Close the dialog and try this: - -.. parsed-literal:: - - client.navigate("http://www.mozilla.org") - -Now you're at mozilla.org! You can even verify it using the following: - -.. parsed-literal:: - client.get_url() - -You can even find an element and click on it. Let's say you want to get -the first link: - -.. parsed-literal:: - from marionette import By - first_link = client.find_element(By.TAG_NAME, "a") - -first_link now holds a reference to the first link on the page. You can click it: - -.. parsed-literal:: - first_link.click() - diff --git a/testing/marionette/client/docs/make.bat b/testing/marionette/client/docs/make.bat deleted file mode 100644 index fb02fc1a8..000000000 --- a/testing/marionette/client/docs/make.bat +++ /dev/null @@ -1,190 +0,0 @@ -@ECHO OFF - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set BUILDDIR=_build -set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . -set I18NSPHINXOPTS=%SPHINXOPTS% . -if NOT "%PAPER%" == "" ( - set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% - set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% -) - -if "%1" == "" goto help - -if "%1" == "help" ( - :help - echo.Please use `make ^<target^>` where ^<target^> is one of - echo. html to make standalone HTML files - echo. dirhtml to make HTML files named index.html in directories - echo. singlehtml to make a single large HTML file - echo. pickle to make pickle files - echo. json to make JSON files - echo. htmlhelp to make HTML files and a HTML help project - echo. qthelp to make HTML files and a qthelp project - echo. devhelp to make HTML files and a Devhelp project - echo. epub to make an epub - echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter - echo. text to make text files - echo. man to make manual pages - echo. texinfo to make Texinfo files - echo. gettext to make PO message catalogs - echo. changes to make an overview over all changed/added/deprecated items - echo. linkcheck to check all external links for integrity - echo. doctest to run all doctests embedded in the documentation if enabled - goto end -) - -if "%1" == "clean" ( - for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i - del /q /s %BUILDDIR%\* - goto end -) - -if "%1" == "html" ( - %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/html. - goto end -) - -if "%1" == "dirhtml" ( - %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. - goto end -) - -if "%1" == "singlehtml" ( - %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. - goto end -) - -if "%1" == "pickle" ( - %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can process the pickle files. - goto end -) - -if "%1" == "json" ( - %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can process the JSON files. - goto end -) - -if "%1" == "htmlhelp" ( - %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can run HTML Help Workshop with the ^ -.hhp project file in %BUILDDIR%/htmlhelp. - goto end -) - -if "%1" == "qthelp" ( - %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can run "qcollectiongenerator" with the ^ -.qhcp project file in %BUILDDIR%/qthelp, like this: - echo.^> qcollectiongenerator %BUILDDIR%\qthelp\MarionettePythonClient.qhcp - echo.To view the help file: - echo.^> assistant -collectionFile %BUILDDIR%\qthelp\MarionettePythonClient.ghc - goto end -) - -if "%1" == "devhelp" ( - %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. - goto end -) - -if "%1" == "epub" ( - %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The epub file is in %BUILDDIR%/epub. - goto end -) - -if "%1" == "latex" ( - %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. - goto end -) - -if "%1" == "text" ( - %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The text files are in %BUILDDIR%/text. - goto end -) - -if "%1" == "man" ( - %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The manual pages are in %BUILDDIR%/man. - goto end -) - -if "%1" == "texinfo" ( - %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. - goto end -) - -if "%1" == "gettext" ( - %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The message catalogs are in %BUILDDIR%/locale. - goto end -) - -if "%1" == "changes" ( - %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes - if errorlevel 1 exit /b 1 - echo. - echo.The overview file is in %BUILDDIR%/changes. - goto end -) - -if "%1" == "linkcheck" ( - %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck - if errorlevel 1 exit /b 1 - echo. - echo.Link check complete; look for any errors in the above output ^ -or in %BUILDDIR%/linkcheck/output.txt. - goto end -) - -if "%1" == "doctest" ( - %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest - if errorlevel 1 exit /b 1 - echo. - echo.Testing of doctests in the sources finished, look at the ^ -results in %BUILDDIR%/doctest/output.txt. - goto end -) - -:end diff --git a/testing/marionette/client/docs/reference.rst b/testing/marionette/client/docs/reference.rst deleted file mode 100644 index 7d2fc5b00..000000000 --- a/testing/marionette/client/docs/reference.rst +++ /dev/null @@ -1,51 +0,0 @@ -============= -API Reference -============= - -Marionette ----------- -.. autoclass:: marionette_driver.marionette.Marionette - :members: - -HTMLElement ------------ -.. autoclass:: marionette_driver.marionette.HTMLElement - :members: - -DateTimeValue -------------- -.. autoclass:: marionette_driver.DateTimeValue - :members: - -Actions -------- -.. autoclass:: marionette_driver.marionette.Actions - :members: - -MultiActions ------------- -.. autoclass:: marionette_driver.marionette.MultiActions - :members: - -Wait ----- -.. autoclass:: marionette_driver.Wait - :members: - :special-members: -.. autoattribute marionette_driver.wait.DEFAULT_TIMEOUT -.. autoattribute marionette_driver.wait.DEFAULT_INTERVAL - -Built-in Conditions -^^^^^^^^^^^^^^^^^^^ -.. automodule:: marionette_driver.expected - :members: - -Addons ------- -.. autoclass:: marionette_driver.addons.Addons - :members: - -Localization ------------- -.. autoclass:: marionette_driver.localization.L10n - :members: diff --git a/testing/marionette/client/marionette_driver/__init__.py b/testing/marionette/client/marionette_driver/__init__.py deleted file mode 100644 index d947d9c27..000000000 --- a/testing/marionette/client/marionette_driver/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -# 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/. - -__version__ = '2.2.0' - -from marionette_driver import ( - addons, - by, - date_time_value, - decorators, - errors, - expected, - geckoinstance, - gestures, - keys, - localization, - marionette, - selection, - wait, -) -from marionette_driver.by import By -from marionette_driver.date_time_value import DateTimeValue -from marionette_driver.gestures import smooth_scroll, pinch -from marionette_driver.marionette import Actions -from marionette_driver.wait import Wait diff --git a/testing/marionette/client/marionette_driver/addons.py b/testing/marionette/client/marionette_driver/addons.py deleted file mode 100644 index 0a8baef4f..000000000 --- a/testing/marionette/client/marionette_driver/addons.py +++ /dev/null @@ -1,70 +0,0 @@ -# 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 . import errors - -__all__ = ["Addons", "AddonInstallException"] - - -class AddonInstallException(errors.MarionetteException): - pass - - -class Addons(object): - """An API for installing and inspecting addons during Gecko - runtime. This is a partially implemented wrapper around Gecko's - `AddonManager API`_. - - For example:: - - from marionette_driver.addons import Addons - addons = Addons(marionette) - addons.install("/path/to/extension.xpi") - - .. _AddonManager API: https://developer.mozilla.org/en-US/Add-ons/Add-on_Manager - - """ - def __init__(self, marionette): - self._mn = marionette - - def install(self, path, temp=False): - """Install a Firefox addon. - - If the addon is restartless, it can be used right away. Otherwise - a restart using :func:`~marionette_driver.marionette.Marionette.restart` - will be needed. - - :param path: A file path to the extension to be installed. - :param temp: Install a temporary addon. Temporary addons will - automatically be uninstalled on shutdown and do not need - to be signed, though they must be restartless. - - :returns: The addon ID string of the newly installed addon. - - :raises: :exc:`AddonInstallException` - - """ - body = {"path": path, "temporary": temp} - try: - return self._mn._send_message("addon:install", body, key="value") - except errors.UnknownException as e: - raise AddonInstallException(e) - - def uninstall(self, addon_id): - """Uninstall a Firefox addon. - - If the addon is restartless, it will be uninstalled right away. - Otherwise a restart using :func:`~marionette_driver.marionette.Marionette.restart` - will be needed. - - If the call to uninstall is resulting in a `ScriptTimeoutException`, - an invalid ID is likely being passed in. Unfortunately due to - AddonManager's implementation, it's hard to retrieve this error from - Python. - - :param addon_id: The addon ID string to uninstall. - - """ - body = {"id": addon_id} - self._mn._send_message("addon:uninstall", body) diff --git a/testing/marionette/client/marionette_driver/by.py b/testing/marionette/client/marionette_driver/by.py deleted file mode 100644 index 8232be145..000000000 --- a/testing/marionette/client/marionette_driver/by.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright 2008-2009 WebDriver committers -# Copyright 2008-2009 Google Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -class By(object): - ID = "id" - XPATH = "xpath" - LINK_TEXT = "link text" - PARTIAL_LINK_TEXT = "partial link text" - NAME = "name" - TAG_NAME = "tag name" - CLASS_NAME = "class name" - CSS_SELECTOR = "css selector" - ANON_ATTRIBUTE = "anon attribute" - ANON = "anon" diff --git a/testing/marionette/client/marionette_driver/date_time_value.py b/testing/marionette/client/marionette_driver/date_time_value.py deleted file mode 100644 index 35c541dc0..000000000 --- a/testing/marionette/client/marionette_driver/date_time_value.py +++ /dev/null @@ -1,49 +0,0 @@ -# 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/. - - -class DateTimeValue(object): - """ - Interface for setting the value of HTML5 "date" and "time" input elements. - - Simple usage example: - - :: - - element = marionette.find_element(By.ID, "date-test") - dt_value = DateTimeValue(element) - dt_value.date = datetime(1998, 6, 2) - - """ - - def __init__(self, element): - self.element = element - - @property - def date(self): - """ - Retrieve the element's string value - """ - return self.element.get_attribute('value') - - # As per the W3C "date" element specification - # (http://dev.w3.org/html5/markup/input.date.html), this value is formatted - # according to RFC 3339: http://tools.ietf.org/html/rfc3339#section-5.6 - @date.setter - def date(self, date_value): - self.element.send_keys(date_value.strftime('%Y-%m-%d')) - - @property - def time(self): - """ - Retrieve the element's string value - """ - return self.element.get_attribute('value') - - # As per the W3C "time" element specification - # (http://dev.w3.org/html5/markup/input.time.html), this value is formatted - # according to RFC 3339: http://tools.ietf.org/html/rfc3339#section-5.6 - @time.setter - def time(self, time_value): - self.element.send_keys(time_value.strftime('%H:%M:%S')) diff --git a/testing/marionette/client/marionette_driver/decorators.py b/testing/marionette/client/marionette_driver/decorators.py deleted file mode 100644 index f65152471..000000000 --- a/testing/marionette/client/marionette_driver/decorators.py +++ /dev/null @@ -1,69 +0,0 @@ -# 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 functools import wraps -import socket - - -def _find_marionette_in_args(*args, **kwargs): - try: - m = [a for a in args + tuple(kwargs.values()) if hasattr(a, 'session')][0] - except IndexError: - print("Can only apply decorator to function using a marionette object") - raise - return m - - -def do_process_check(func): - """Decorator which checks the process status after the function has run.""" - @wraps(func) - def _(*args, **kwargs): - try: - return func(*args, **kwargs) - except (socket.error, socket.timeout): - # In case of socket failures which will also include crashes of the - # application, make sure to handle those correctly. - m = _find_marionette_in_args(*args, **kwargs) - m._handle_socket_failure() - - return _ - - -def uses_marionette(func): - """Decorator which creates a marionette session and deletes it - afterwards if one doesn't already exist. - """ - @wraps(func) - def _(*args, **kwargs): - m = _find_marionette_in_args(*args, **kwargs) - delete_session = False - if not m.session: - delete_session = True - m.start_session() - - m.set_context(m.CONTEXT_CHROME) - ret = func(*args, **kwargs) - - if delete_session: - m.delete_session() - - return ret - return _ - - -def using_context(context): - """Decorator which allows a function to execute in certain scope - using marionette.using_context functionality and returns to old - scope once the function exits. - :param context: Either 'chrome' or 'content'. - """ - def wrap(func): - @wraps(func) - def inner(*args, **kwargs): - m = _find_marionette_in_args(*args, **kwargs) - with m.using_context(context): - return func(*args, **kwargs) - - return inner - return wrap diff --git a/testing/marionette/client/marionette_driver/errors.py b/testing/marionette/client/marionette_driver/errors.py deleted file mode 100644 index 8fb1d564e..000000000 --- a/testing/marionette/client/marionette_driver/errors.py +++ /dev/null @@ -1,179 +0,0 @@ -# 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/. - -import traceback - - -class MarionetteException(Exception): - - """Raised when a generic non-recoverable exception has occured.""" - - status = "webdriver error" - - def __init__(self, message=None, cause=None, stacktrace=None): - """Construct new MarionetteException instance. - - :param message: An optional exception message. - - :param cause: An optional tuple of three values giving - information about the root exception cause. Expected - tuple values are (type, value, traceback). - - :param stacktrace: Optional string containing a stacktrace - (typically from a failed JavaScript execution) that will - be displayed in the exception's string representation. - - """ - self.cause = cause - self.stacktrace = stacktrace - - super(MarionetteException, self).__init__(message) - - def __str__(self): - msg = str(self.message) - tb = None - - if self.cause: - if type(self.cause) is tuple: - msg += ", caused by {0!r}".format(self.cause[0]) - tb = self.cause[2] - else: - msg += ", caused by {}".format(self.cause) - if self.stacktrace: - st = "".join(["\t{}\n".format(x) - for x in self.stacktrace.splitlines()]) - msg += "\nstacktrace:\n{}".format(st) - - if tb: - msg += ': ' + "".join(traceback.format_tb(tb)) - - return msg - - -class ElementNotSelectableException(MarionetteException): - status = "element not selectable" - - -class ElementClickInterceptedException(MarionetteException): - status = "element click intercepted" - - -class InsecureCertificateException(MarionetteException): - status = "insecure certificate" - - -class InvalidArgumentException(MarionetteException): - status = "invalid argument" - - -class InvalidSessionIdException(MarionetteException): - status = "invalid session id" - - -class TimeoutException(MarionetteException): - status = "timeout" - - -class JavascriptException(MarionetteException): - status = "javascript error" - - -class NoSuchElementException(MarionetteException): - status = "no such element" - - -class NoSuchWindowException(MarionetteException): - status = "no such window" - - -class StaleElementException(MarionetteException): - status = "stale element reference" - - -class ScriptTimeoutException(MarionetteException): - status = "script timeout" - - -class ElementNotVisibleException(MarionetteException): - """Deprecated. Will be removed with the release of Firefox 54.""" - - status = "element not visible" - - def __init__(self, - message="Element is not currently visible and may not be manipulated", - stacktrace=None, cause=None): - super(ElementNotVisibleException, self).__init__( - message, cause=cause, stacktrace=stacktrace) - - -class ElementNotAccessibleException(MarionetteException): - status = "element not accessible" - - -class ElementNotInteractableException(MarionetteException): - status = "element not interactable" - - -class NoSuchFrameException(MarionetteException): - status = "no such frame" - - -class InvalidElementStateException(MarionetteException): - status = "invalid element state" - - -class NoAlertPresentException(MarionetteException): - status = "no such alert" - - -class InvalidCookieDomainException(MarionetteException): - status = "invalid cookie domain" - - -class UnableToSetCookieException(MarionetteException): - status = "unable to set cookie" - - -class InvalidElementCoordinates(MarionetteException): - status = "invalid element coordinates" - - -class InvalidSelectorException(MarionetteException): - status = "invalid selector" - - -class MoveTargetOutOfBoundsException(MarionetteException): - status = "move target out of bounds" - - -class SessionNotCreatedException(MarionetteException): - status = "session not created" - - -class UnexpectedAlertOpen(MarionetteException): - status = "unexpected alert open" - - -class UnknownCommandException(MarionetteException): - status = "unknown command" - - -class UnknownException(MarionetteException): - status = "unknown error" - - -class UnsupportedOperationException(MarionetteException): - status = "unsupported operation" - - -es_ = [e for e in locals().values() if type(e) == type and issubclass(e, MarionetteException)] -by_string = {e.status: e for e in es_} - - -def lookup(identifier): - """Finds error exception class by associated Selenium JSON wire - protocol number code, or W3C WebDriver protocol string. - - """ - return by_string.get(identifier, MarionetteException) diff --git a/testing/marionette/client/marionette_driver/expected.py b/testing/marionette/client/marionette_driver/expected.py deleted file mode 100644 index d8c47e708..000000000 --- a/testing/marionette/client/marionette_driver/expected.py +++ /dev/null @@ -1,311 +0,0 @@ -# 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/. - -import errors -import types - -from marionette import HTMLElement - -"""This file provides a set of expected conditions for common use -cases when writing Marionette tests. - -The conditions rely on explicit waits that retries conditions a number -of times until they are either successfully met, or they time out. - -""" - - -class element_present(object): - """Checks that a web element is present in the DOM of the current - context. This does not necessarily mean that the element is - visible. - - You can select which element to be checked for presence by - supplying a locator:: - - el = Wait(marionette).until(expected.element_present(By.ID, "foo")) - - Or by using a function/lambda returning an element:: - - el = Wait(marionette).\ - until(expected.element_present(lambda m: m.find_element(By.ID, "foo"))) - - :param args: locator or function returning web element - :returns: the web element once it is located, or False - - """ - - def __init__(self, *args): - if len(args) == 1 and isinstance(args[0], types.FunctionType): - self.locator = args[0] - else: - self.locator = lambda m: m.find_element(*args) - - def __call__(self, marionette): - return _find(marionette, self.locator) - - -class element_not_present(element_present): - """Checks that a web element is not present in the DOM of the current - context. - - You can select which element to be checked for lack of presence by - supplying a locator:: - - r = Wait(marionette).until(expected.element_not_present(By.ID, "foo")) - - Or by using a function/lambda returning an element:: - - r = Wait(marionette).\ - until(expected.element_present(lambda m: m.find_element(By.ID, "foo"))) - - :param args: locator or function returning web element - :returns: True if element is not present, or False if it is present - - """ - - def __init__(self, *args): - super(element_not_present, self).__init__(*args) - - def __call__(self, marionette): - return not super(element_not_present, self).__call__(marionette) - - -class element_stale(object): - """Check that the given element is no longer attached to DOM of the - current context. - - This can be useful for waiting until an element is no longer - present. - - Sample usage:: - - el = marionette.find_element(By.ID, "foo") - # ... - Wait(marionette).until(expected.element_stale(el)) - - :param element: the element to wait for - :returns: False if the element is still attached to the DOM, True - otherwise - - """ - - def __init__(self, element): - self.el = element - - def __call__(self, marionette): - try: - # Calling any method forces a staleness check - self.el.is_enabled() - return False - except errors.StaleElementException: - return True - - -class elements_present(object): - """Checks that web elements are present in the DOM of the current - context. This does not necessarily mean that the elements are - visible. - - You can select which elements to be checked for presence by - supplying a locator:: - - els = Wait(marionette).until(expected.elements_present(By.TAG_NAME, "a")) - - Or by using a function/lambda returning a list of elements:: - - els = Wait(marionette).\ - until(expected.elements_present(lambda m: m.find_elements(By.TAG_NAME, "a"))) - - :param args: locator or function returning a list of web elements - :returns: list of web elements once they are located, or False - - """ - - def __init__(self, *args): - if len(args) == 1 and isinstance(args[0], types.FunctionType): - self.locator = args[0] - else: - self.locator = lambda m: m.find_elements(*args) - - def __call__(self, marionette): - return _find(marionette, self.locator) - - -class elements_not_present(elements_present): - """Checks that web elements are not present in the DOM of the - current context. - - You can select which elements to be checked for not being present - by supplying a locator:: - - r = Wait(marionette).until(expected.elements_not_present(By.TAG_NAME, "a")) - - Or by using a function/lambda returning a list of elements:: - - r = Wait(marionette).\ - until(expected.elements_not_present(lambda m: m.find_elements(By.TAG_NAME, "a"))) - - :param args: locator or function returning a list of web elements - :returns: True if elements are missing, False if one or more are - present - - """ - - def __init__(self, *args): - super(elements_not_present, self).__init__(*args) - - def __call__(self, marionette): - return not super(elements_not_present, self).__call__(marionette) - - -class element_displayed(object): - """An expectation for checking that an element is visible. - - Visibility means that the element is not only displayed, but also - has a height and width that is greater than 0 pixels. - - Stale elements, meaning elements that have been detached from the - DOM of the current context are treated as not being displayed, - meaning this expectation is not analogous to the behaviour of - calling `is_displayed()` on an `HTMLElement`. - - You can select which element to be checked for visibility by - supplying a locator:: - - displayed = Wait(marionette).until(expected.element_displayed(By.ID, "foo")) - - Or by supplying an element:: - - el = marionette.find_element(By.ID, "foo") - displayed = Wait(marionette).until(expected.element_displayed(el)) - - :param args: locator or web element - :returns: True if element is displayed, False if hidden - - """ - - def __init__(self, *args): - self.el = None - if len(args) == 1 and isinstance(args[0], HTMLElement): - self.el = args[0] - else: - self.locator = lambda m: m.find_element(*args) - - def __call__(self, marionette): - if self.el is None: - self.el = _find(marionette, self.locator) - if not self.el: - return False - try: - return self.el.is_displayed() - except errors.StaleElementException: - return False - - -class element_not_displayed(element_displayed): - """An expectation for checking that an element is not visible. - - Visibility means that the element is not only displayed, but also - has a height and width that is greater than 0 pixels. - - Stale elements, meaning elements that have been detached fom the - DOM of the current context are treated as not being displayed, - meaning this expectation is not analogous to the behaviour of - calling `is_displayed()` on an `HTMLElement`. - - You can select which element to be checked for visibility by - supplying a locator:: - - hidden = Wait(marionette).until(expected.element_not_displayed(By.ID, "foo")) - - Or by supplying an element:: - - el = marionette.find_element(By.ID, "foo") - hidden = Wait(marionette).until(expected.element_not_displayed(el)) - - :param args: locator or web element - :returns: True if element is hidden, False if displayed - - """ - - def __init__(self, *args): - super(element_not_displayed, self).__init__(*args) - - def __call__(self, marionette): - return not super(element_not_displayed, self).__call__(marionette) - - -class element_selected(object): - """An expectation for checking that the given element is selected. - - :param element: the element to be selected - :returns: True if element is selected, False otherwise - - """ - - def __init__(self, element): - self.el = element - - def __call__(self, marionette): - return self.el.is_selected() - - -class element_not_selected(element_selected): - """An expectation for checking that the given element is not - selected. - - :param element: the element to not be selected - :returns: True if element is not selected, False if selected - - """ - - def __init__(self, element): - super(element_not_selected, self).__init__(element) - - def __call__(self, marionette): - return not super(element_not_selected, self).__call__(marionette) - - -class element_enabled(object): - """An expectation for checking that the given element is enabled. - - :param element: the element to check if enabled - :returns: True if element is enabled, False otherwise - - """ - - def __init__(self, element): - self.el = element - - def __call__(self, marionette): - return self.el.is_enabled() - - -class element_not_enabled(element_enabled): - """An expectation for checking that the given element is disabled. - - :param element: the element to check if disabled - :returns: True if element is disabled, False if enabled - - """ - - def __init__(self, element): - super(element_not_enabled, self).__init__(element) - - def __call__(self, marionette): - return not super(element_not_enabled, self).__call__(marionette) - - -def _find(marionette, func): - el = None - - try: - el = func(marionette) - except errors.NoSuchElementException: - pass - - if el is None: - return False - return el diff --git a/testing/marionette/client/marionette_driver/geckoinstance.py b/testing/marionette/client/marionette_driver/geckoinstance.py deleted file mode 100644 index c59cc2ace..000000000 --- a/testing/marionette/client/marionette_driver/geckoinstance.py +++ /dev/null @@ -1,464 +0,0 @@ -# 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/ - -import os -import sys -import tempfile -import time - -from copy import deepcopy - -import mozversion - -from mozprofile import Profile -from mozrunner import Runner, FennecEmulatorRunner - - -class GeckoInstance(object): - required_prefs = { - # Increase the APZ content response timeout in tests to 1 minute. - # This is to accommodate the fact that test environments tends to be slower - # than production environments (with the b2g emulator being the slowest of them - # all), resulting in the production timeout value sometimes being exceeded - # and causing false-positive test failures. See bug 1176798, bug 1177018, - # bug 1210465. - "apz.content_response_timeout": 60000, - - # Do not send Firefox health reports to the production server - "datareporting.healthreport.documentServerURI": "http://%(server)s/dummy/healthreport/", - "datareporting.healthreport.about.reportUrl": "http://%(server)s/dummy/abouthealthreport/", - - # Do not show datareporting policy notifications which can interfer with tests - "datareporting.policy.dataSubmissionPolicyBypassNotification": True, - - # Until Bug 1238095 is fixed, we have to enable CPOWs in order - # for Marionette tests to work properly. - "dom.ipc.cpows.forbid-unsafe-from-browser": False, - "dom.ipc.reportProcessHangs": False, - - # No slow script dialogs - "dom.max_chrome_script_run_time": 0, - "dom.max_script_run_time": 0, - - # Only load extensions from the application and user profile - # AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_APPLICATION - "extensions.autoDisableScopes": 0, - "extensions.enabledScopes": 5, - # don't block add-ons for e10s - "extensions.e10sBlocksEnabling": False, - # Disable metadata caching for installed add-ons by default - "extensions.getAddons.cache.enabled": False, - # Disable intalling any distribution add-ons - "extensions.installDistroAddons": False, - "extensions.showMismatchUI": False, - # Turn off extension updates so they don't bother tests - "extensions.update.enabled": False, - "extensions.update.notifyUser": False, - # Make sure opening about:addons won"t hit the network - "extensions.webservice.discoverURL": "http://%(server)s/dummy/discoveryURL", - - # Allow the application to have focus even it runs in the background - "focusmanager.testmode": True, - - # Disable useragent updates - "general.useragent.updates.enabled": False, - - # Always use network provider for geolocation tests - # so we bypass the OSX dialog raised by the corelocation provider - "geo.provider.testing": True, - # Do not scan Wifi - "geo.wifi.scan": False, - - # No hang monitor - "hangmonitor.timeout": 0, - - "javascript.options.showInConsole": True, - "marionette.defaultPrefs.enabled": True, - "media.volume_scale": "0.01", - - # Do not prompt for temporary redirects - "network.http.prompt-temp-redirect": False, - # Disable speculative connections so they aren"t reported as leaking when they"re - # hanging around - "network.http.speculative-parallel-limit": 0, - # Do not automatically switch between offline and online - "network.manage-offline-status": False, - # Make sure SNTP requests don't hit the network - "network.sntp.pools": "%(server)s", - - # Tests don't wait for the notification button security delay - "security.notification_enable_delay": 0, - - # Ensure blocklist updates don't hit the network - "services.settings.server": "http://%(server)s/dummy/blocklist/", - - # Disable password capture, so that tests that include forms aren"t - # influenced by the presence of the persistent doorhanger notification - "signon.rememberSignons": False, - - # Prevent starting into safe mode after application crashes - "toolkit.startup.max_resumed_crashes": -1, - - # We want to collect telemetry, but we don't want to send in the results - "toolkit.telemetry.server": "https://%(server)s/dummy/telemetry/", - } - - def __init__(self, host=None, port=None, bin=None, profile=None, addons=None, - app_args=None, symbols_path=None, gecko_log=None, prefs=None, - workspace=None, verbose=0): - self.runner_class = Runner - self.app_args = app_args or [] - self.runner = None - self.symbols_path = symbols_path - self.binary = bin - - self.marionette_host = host - self.marionette_port = port - # Alternative to default temporary directory - self.workspace = workspace - self.addons = addons - # Check if it is a Profile object or a path to profile - self.profile = None - if isinstance(profile, Profile): - self.profile = profile - else: - self.profile_path = profile - self.prefs = prefs - self.required_prefs = deepcopy(self.required_prefs) - if prefs: - self.required_prefs.update(prefs) - - self._gecko_log_option = gecko_log - self._gecko_log = None - self.verbose = verbose - - @property - def gecko_log(self): - if self._gecko_log: - return self._gecko_log - - path = self._gecko_log_option - if path != "-": - if path is None: - path = "gecko.log" - elif os.path.isdir(path): - fname = "gecko-{}.log".format(time.time()) - path = os.path.join(path, fname) - - path = os.path.realpath(path) - if os.access(path, os.F_OK): - os.remove(path) - - self._gecko_log = path - return self._gecko_log - - def _update_profile(self): - profile_args = {"preferences": deepcopy(self.required_prefs)} - profile_args["preferences"]["marionette.defaultPrefs.port"] = self.marionette_port - if self.prefs: - profile_args["preferences"].update(self.prefs) - if self.verbose: - level = "TRACE" if self.verbose >= 2 else "DEBUG" - profile_args["preferences"]["marionette.logging"] = level - if "-jsdebugger" in self.app_args: - profile_args["preferences"].update({ - "devtools.browsertoolbox.panel": "jsdebugger", - "devtools.debugger.remote-enabled": True, - "devtools.chrome.enabled": True, - "devtools.debugger.prompt-connection": False, - "marionette.debugging.clicktostart": True, - }) - if self.addons: - profile_args["addons"] = self.addons - - if hasattr(self, "profile_path") and self.profile is None: - if not self.profile_path: - if self.workspace: - profile_args["profile"] = tempfile.mkdtemp( - suffix=".mozrunner-{:.0f}".format(time.time()), - dir=self.workspace) - self.profile = Profile(**profile_args) - else: - profile_args["path_from"] = self.profile_path - profile_name = "{}-{:.0f}".format( - os.path.basename(self.profile_path), - time.time() - ) - if self.workspace: - profile_args["path_to"] = os.path.join(self.workspace, - profile_name) - self.profile = Profile.clone(**profile_args) - - @classmethod - def create(cls, app=None, *args, **kwargs): - try: - if not app and kwargs["bin"] is not None: - app_id = mozversion.get_version(binary=kwargs["bin"])["application_id"] - app = app_ids[app_id] - - instance_class = apps[app] - except (IOError, KeyError): - exc, val, tb = sys.exc_info() - msg = 'Application "{0}" unknown (should be one of {1})' - raise NotImplementedError, msg.format(app, apps.keys()), tb - - return instance_class(*args, **kwargs) - - def start(self): - self._update_profile() - self.runner = self.runner_class(**self._get_runner_args()) - self.runner.start() - - def _get_runner_args(self): - process_args = { - "processOutputLine": [NullOutput()], - } - - if self.gecko_log == "-": - process_args["stream"] = sys.stdout - else: - process_args["logfile"] = self.gecko_log - - env = os.environ.copy() - - # environment variables needed for crashreporting - # https://developer.mozilla.org/docs/Environment_variables_affecting_crash_reporting - env.update({"MOZ_CRASHREPORTER_NO_REPORT": "1", - "MOZ_CRASHREPORTER_SHUTDOWN": "1", - }) - - return { - "binary": self.binary, - "profile": self.profile, - "cmdargs": ["-no-remote", "-marionette"] + self.app_args, - "env": env, - "symbols_path": self.symbols_path, - "process_args": process_args - } - - def close(self, restart=False): - if not restart: - self.profile = None - - if self.runner: - self.runner.stop() - self.runner.cleanup() - - def restart(self, prefs=None, clean=True): - self.close(restart=True) - - if clean and self.profile: - self.profile.cleanup() - self.profile = None - - if prefs: - self.prefs = prefs - else: - self.prefs = None - self.start() - - -class FennecInstance(GeckoInstance): - fennec_prefs = { - # Enable output of dump() - "browser.dom.window.dump.enabled": True, - - # Disable Android snippets - "browser.snippets.enabled": False, - "browser.snippets.syncPromo.enabled": False, - "browser.snippets.firstrunHomepage.enabled": False, - - # Disable safebrowsing components - "browser.safebrowsing.downloads.enabled": False, - - # Do not restore the last open set of tabs if the browser has crashed - "browser.sessionstore.resume_from_crash": False, - - # Disable e10s by default - "browser.tabs.remote.autostart.1": False, - "browser.tabs.remote.autostart.2": False, - "browser.tabs.remote.autostart": False, - - # Do not allow background tabs to be zombified, otherwise for tests that - # open additional tabs, the test harness tab itself might get unloaded - "browser.tabs.disableBackgroundZombification": True, - } - - def __init__(self, emulator_binary=None, avd_home=None, avd=None, - adb_path=None, serial=None, connect_to_running_emulator=False, - package_name=None, *args, **kwargs): - super(FennecInstance, self).__init__(*args, **kwargs) - self.required_prefs.update(FennecInstance.fennec_prefs) - - self.runner_class = FennecEmulatorRunner - # runner args - self._package_name = package_name - self.emulator_binary = emulator_binary - self.avd_home = avd_home - self.adb_path = adb_path - self.avd = avd - self.serial = serial - self.connect_to_running_emulator = connect_to_running_emulator - - @property - def package_name(self): - """ - Name of app to run on emulator. - - Note that FennecInstance does not use self.binary - """ - if self._package_name is None: - self._package_name = "org.mozilla.fennec" - user = os.getenv("USER") - if user: - self._package_name += "_" + user - return self._package_name - - def start(self): - self._update_profile() - self.runner = self.runner_class(**self._get_runner_args()) - try: - if self.connect_to_running_emulator: - self.runner.device.connect() - self.runner.start() - except Exception as e: - exc, val, tb = sys.exc_info() - message = "Error possibly due to runner or device args: {}" - raise exc, message.format(e.message), tb - # gecko_log comes from logcat when running with device/emulator - logcat_args = { - "filterspec": "Gecko", - "serial": self.runner.device.dm._deviceSerial - } - if self.gecko_log == "-": - logcat_args["stream"] = sys.stdout - else: - logcat_args["logfile"] = self.gecko_log - self.runner.device.start_logcat(**logcat_args) - - # forward marionette port (localhost:2828) - self.runner.device.dm.forward( - local="tcp:{}".format(self.marionette_port), - remote="tcp:{}".format(self.marionette_port)) - - def _get_runner_args(self): - process_args = { - "processOutputLine": [NullOutput()], - } - - runner_args = { - "app": self.package_name, - "avd_home": self.avd_home, - "adb_path": self.adb_path, - "binary": self.emulator_binary, - "profile": self.profile, - "cmdargs": ["-marionette"] + self.app_args, - "symbols_path": self.symbols_path, - "process_args": process_args, - "logdir": self.workspace or os.getcwd(), - "serial": self.serial, - } - if self.avd: - runner_args["avd"] = self.avd - - return runner_args - - def close(self, restart=False): - super(FennecInstance, self).close(restart) - if self.runner and self.runner.device.connected: - self.runner.device.dm.remove_forward( - "tcp:{}".format(self.marionette_port)) - - -class DesktopInstance(GeckoInstance): - desktop_prefs = { - # Disable application updates - "app.update.enabled": False, - - # Enable output of dump() - "browser.dom.window.dump.enabled": True, - - # Indicate that the download panel has been shown once so that whichever - # download test runs first doesn"t show the popup inconsistently - "browser.download.panel.shown": True, - - # Do not show the EULA notification which can interfer with tests - "browser.EULA.override": True, - - # Turn off about:newtab and make use of about:blank instead for new opened tabs - "browser.newtabpage.enabled": False, - # Assume the about:newtab page"s intro panels have been shown to not depend on - # which test runs first and happens to open about:newtab - "browser.newtabpage.introShown": True, - - # Background thumbnails in particular cause grief, and disabling thumbnails - # in general can"t hurt - we re-enable them when tests need them - "browser.pagethumbnails.capturing_disabled": True, - - # Avoid performing Reader Mode intros during tests - "browser.reader.detectedFirstArticle": True, - - # Disable safebrowsing components - "browser.safebrowsing.blockedURIs.enabled": False, - "browser.safebrowsing.downloads.enabled": False, - "browser.safebrowsing.forbiddenURIs.enabled": False, - "browser.safebrowsing.malware.enabled": False, - "browser.safebrowsing.phishing.enabled": False, - - # Disable updates to search engines - "browser.search.update": False, - - # Do not restore the last open set of tabs if the browser has crashed - "browser.sessionstore.resume_from_crash": False, - - # Don't check for the default web browser during startup - "browser.shell.checkDefaultBrowser": False, - - # Disable e10s by default - "browser.tabs.remote.autostart.1": False, - "browser.tabs.remote.autostart.2": False, - "browser.tabs.remote.autostart": False, - - # Needed for branded builds to prevent opening a second tab on startup - "browser.startup.homepage_override.mstone": "ignore", - # Start with a blank page by default - "browser.startup.page": 0, - - # Disable tab animation - "browser.tabs.animate": False, - - # Do not warn on exit when multiple tabs are open - "browser.tabs.warnOnClose": False, - # Do not warn when closing all other open tabs - "browser.tabs.warnOnCloseOtherTabs": False, - # Do not warn when multiple tabs will be opened - "browser.tabs.warnOnOpen": False, - - # Disable the UI tour - "browser.uitour.enabled": False, - - # Disable first-run welcome page - "startup.homepage_welcome_url": "about:blank", - "startup.homepage_welcome_url.additional": "", - } - - def __init__(self, *args, **kwargs): - super(DesktopInstance, self).__init__(*args, **kwargs) - self.required_prefs.update(DesktopInstance.desktop_prefs) - - -class NullOutput(object): - def __call__(self, line): - pass - - -apps = { - 'fennec': FennecInstance, - 'fxdesktop': DesktopInstance, -} - -app_ids = { - '{aa3c5121-dab2-40e2-81ca-7ea25febc110}': 'fennec', - '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}': 'fxdesktop', -} diff --git a/testing/marionette/client/marionette_driver/gestures.py b/testing/marionette/client/marionette_driver/gestures.py deleted file mode 100644 index 55b27ec83..000000000 --- a/testing/marionette/client/marionette_driver/gestures.py +++ /dev/null @@ -1,93 +0,0 @@ -# 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 import MultiActions, Actions - - -def smooth_scroll(marionette_session, start_element, axis, direction, - length, increments=None, wait_period=None, scroll_back=None): - """ - :param axis: y or x - :param direction: 0 for positive, and -1 for negative - :param length: total length of scroll scroll - :param increments: Amount to be moved per scrolling - :param wait_period: Seconds to wait between scrolling - :param scroll_back: Scroll back to original view? - """ - if axis not in ["x", "y"]: - raise Exception("Axis must be either 'x' or 'y'") - if direction not in [-1, 0]: - raise Exception("Direction must either be -1 negative or 0 positive") - increments = increments or 100 - wait_period = wait_period or 0.05 - scroll_back = scroll_back or False - current = 0 - if axis is "x": - if direction is -1: - offset = [-increments, 0] - else: - offset = [increments, 0] - else: - if direction is -1: - offset = [0, -increments] - else: - offset = [0, increments] - action = Actions(marionette_session) - action.press(start_element) - while (current < length): - current += increments - action.move_by_offset(*offset).wait(wait_period) - if scroll_back: - offset = [-value for value in offset] - while (current > 0): - current -= increments - action.move_by_offset(*offset).wait(wait_period) - action.release() - action.perform() - - -def pinch(marionette_session, element, x1, y1, x2, y2, x3, y3, x4, y4, duration=200): - """ - :param element: target - :param x1, y1: 1st finger starting position relative to the target - :param x3, y3: 1st finger ending position relative to the target - :param x2, y2: 2nd finger starting position relative to the target - :param x4, y4: 2nd finger ending position relative to the target - :param duration: Amount of time in milliseconds to complete the pinch. - """ - time = 0 - time_increment = 10 - if time_increment >= duration: - time_increment = duration - move_x1 = time_increment*1.0/duration * (x3 - x1) - move_y1 = time_increment*1.0/duration * (y3 - y1) - move_x2 = time_increment*1.0/duration * (x4 - x2) - move_y2 = time_increment*1.0/duration * (y4 - y2) - multiAction = MultiActions(marionette_session) - action1 = Actions(marionette_session) - action2 = Actions(marionette_session) - action1.press(element, x1, y1) - action2.press(element, x2, y2) - while (time < duration): - time += time_increment - action1.move_by_offset(move_x1, move_y1).wait(time_increment/1000) - action2.move_by_offset(move_x2, move_y2).wait(time_increment/1000) - action1.release() - action2.release() - multiAction.add(action1).add(action2).perform() - - -def long_press_without_contextmenu(marionette_session, element, time_in_seconds, x=None, y=None): - """ - :param element: The element to press. - :param time_in_seconds: Time in seconds to wait before releasing the press. - #x: Optional, x-coordinate to tap, relative to the top-left corner of the element. - #y: Optional, y-coordinate to tap, relative to the top-leftcorner of the element. - """ - action = Actions(marionette_session) - action.press(element, x, y) - action.move_by_offset(0, 0) - action.wait(time_in_seconds) - action.release() - action.perform() diff --git a/testing/marionette/client/marionette_driver/keys.py b/testing/marionette/client/marionette_driver/keys.py deleted file mode 100644 index 2a998c089..000000000 --- a/testing/marionette/client/marionette_driver/keys.py +++ /dev/null @@ -1,84 +0,0 @@ -# copyright 2008-2009 WebDriver committers -# Copyright 2008-2009 Google Inc. -# -# Licensed under the Apache License Version 2.0 = uthe "License") -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http //www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing software -# distributed under the License is distributed on an "AS IS" BASIS -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -class Keys(object): - - NULL = u'\ue000' - CANCEL = u'\ue001' # ^break - HELP = u'\ue002' - BACK_SPACE = u'\ue003' - TAB = u'\ue004' - CLEAR = u'\ue005' - RETURN = u'\ue006' - ENTER = u'\ue007' - SHIFT = u'\ue008' - LEFT_SHIFT = u'\ue008' # alias - CONTROL = u'\ue009' - LEFT_CONTROL = u'\ue009' # alias - ALT = u'\ue00a' - LEFT_ALT = u'\ue00a' # alias - PAUSE = u'\ue00b' - ESCAPE = u'\ue00c' - SPACE = u'\ue00d' - PAGE_UP = u'\ue00e' - PAGE_DOWN = u'\ue00f' - END = u'\ue010' - HOME = u'\ue011' - LEFT = u'\ue012' - ARROW_LEFT = u'\ue012' # alias - UP = u'\ue013' - ARROW_UP = u'\ue013' # alias - RIGHT = u'\ue014' - ARROW_RIGHT = u'\ue014' # alias - DOWN = u'\ue015' - ARROW_DOWN = u'\ue015' # alias - INSERT = u'\ue016' - DELETE = u'\ue017' - SEMICOLON = u'\ue018' - EQUALS = u'\ue019' - - NUMPAD0 = u'\ue01a' # numbe pad keys - NUMPAD1 = u'\ue01b' - NUMPAD2 = u'\ue01c' - NUMPAD3 = u'\ue01d' - NUMPAD4 = u'\ue01e' - NUMPAD5 = u'\ue01f' - NUMPAD6 = u'\ue020' - NUMPAD7 = u'\ue021' - NUMPAD8 = u'\ue022' - NUMPAD9 = u'\ue023' - MULTIPLY = u'\ue024' - ADD = u'\ue025' - SEPARATOR = u'\ue026' - SUBTRACT = u'\ue027' - DECIMAL = u'\ue028' - DIVIDE = u'\ue029' - - F1 = u'\ue031' # function keys - F2 = u'\ue032' - F3 = u'\ue033' - F4 = u'\ue034' - F5 = u'\ue035' - F6 = u'\ue036' - F7 = u'\ue037' - F8 = u'\ue038' - F9 = u'\ue039' - F10 = u'\ue03a' - F11 = u'\ue03b' - F12 = u'\ue03c' - - META = u'\ue03d' - COMMAND = u'\ue03d' diff --git a/testing/marionette/client/marionette_driver/localization.py b/testing/marionette/client/marionette_driver/localization.py deleted file mode 100644 index 7b0d91b44..000000000 --- a/testing/marionette/client/marionette_driver/localization.py +++ /dev/null @@ -1,54 +0,0 @@ -# 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/. - - -class L10n(object): - """An API which allows Marionette to handle localized content. - - The `localization`_ of UI elements in Gecko based applications is done via - entities and properties. For static values entities are used, which are located - in .dtd files. Whereby for dynamically updated content the values come from - .property files. Both types of elements can be identifed via a unique id, - and the translated content retrieved. - - For example:: - - from marionette_driver.localization import L10n - l10n = L10n(marionette) - - l10n.localize_entity(["chrome://global/locale/about.dtd"], "about.version") - l10n.localize_property(["chrome://global/locale/findbar.properties"], "FastFind")) - - .. _localization: https://mzl.la/2eUMjyF - """ - - def __init__(self, marionette): - self._marionette = marionette - - def localize_entity(self, dtd_urls, entity_id): - """Retrieve the localized string for the specified entity id. - - :param dtd_urls: List of .dtd URLs which will be used to search for the entity. - :param entity_id: ID of the entity to retrieve the localized string for. - - :returns: The localized string for the requested entity. - :raises: :exc:`NoSuchElementException` - """ - body = {"urls": dtd_urls, "id": entity_id} - return self._marionette._send_message("localization:l10n:localizeEntity", - body, key="value") - - def localize_property(self, properties_urls, property_id): - """Retrieve the localized string for the specified property id. - - :param properties_urls: List of .properties URLs which will be used to - search for the property. - :param property_id: ID of the property to retrieve the localized string for. - - :returns: The localized string for the requested property. - :raises: :exc:`NoSuchElementException` - """ - body = {"urls": properties_urls, "id": property_id} - return self._marionette._send_message("localization:l10n:localizeProperty", - body, key="value") diff --git a/testing/marionette/client/marionette_driver/marionette.py b/testing/marionette/client/marionette_driver/marionette.py deleted file mode 100644 index 7450d1fbb..000000000 --- a/testing/marionette/client/marionette_driver/marionette.py +++ /dev/null @@ -1,2153 +0,0 @@ -# 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/. - -import base64 -import datetime -import json -import os -import socket -import sys -import time -import traceback -import warnings - -from contextlib import contextmanager - -import errors -import transport - -from .decorators import do_process_check -from .geckoinstance import GeckoInstance -from .keys import Keys -from .timeout import Timeouts - -WEBELEMENT_KEY = "ELEMENT" -W3C_WEBELEMENT_KEY = "element-6066-11e4-a52e-4f735466cecf" - - -class HTMLElement(object): - """Represents a DOM Element.""" - - def __init__(self, marionette, id): - self.marionette = marionette - assert(id is not None) - self.id = id - - def __str__(self): - return self.id - - def __eq__(self, other_element): - return self.id == other_element.id - - def find_element(self, method, target): - """Returns an ``HTMLElement`` instance that matches the specified - method and target, relative to the current element. - - For more details on this function, see the `find_element` method - in the Marionette class. - """ - return self.marionette.find_element(method, target, self.id) - - def find_elements(self, method, target): - """Returns a list of all ``HTMLElement`` instances that match the - specified method and target in the current context. - - For more details on this function, see the find_elements method - in the Marionette class. - """ - return self.marionette.find_elements(method, target, self.id) - - def get_attribute(self, name): - """Returns the requested attribute, or None if no attribute - is set. - """ - body = {"id": self.id, "name": name} - return self.marionette._send_message("getElementAttribute", body, key="value") - - def get_property(self, name): - """Returns the requested property, or None if the property is - not set. - """ - try: - body = {"id": self.id, "name": name} - return self.marionette._send_message("getElementProperty", body, key="value") - except errors.UnknownCommandException: - # Keep backward compatibility for code which uses get_attribute() to - # also retrieve element properties. - # Remove when Firefox 55 is stable. - return self.get_attribute(name) - - def click(self): - self.marionette._send_message("clickElement", {"id": self.id}) - - def tap(self, x=None, y=None): - """Simulates a set of tap events on the element. - - :param x: X coordinate of tap event. If not given, default to - the centre of the element. - :param y: Y coordinate of tap event. If not given, default to - the centre of the element. - """ - body = {"id": self.id, "x": x, "y": y} - self.marionette._send_message("singleTap", body) - - @property - def text(self): - """Returns the visible text of the element, and its child elements.""" - body = {"id": self.id} - return self.marionette._send_message("getElementText", body, key="value") - - def send_keys(self, *string): - """Sends the string via synthesized keypresses to the element.""" - keys = Marionette.convert_keys(*string) - body = {"id": self.id, "value": keys} - self.marionette._send_message("sendKeysToElement", body) - - def clear(self): - """Clears the input of the element.""" - self.marionette._send_message("clearElement", {"id": self.id}) - - def is_selected(self): - """Returns True if the element is selected.""" - body = {"id": self.id} - return self.marionette._send_message("isElementSelected", body, key="value") - - def is_enabled(self): - """This command will return False if all the following criteria - are met otherwise return True: - - * A form control is disabled. - * A ``HTMLElement`` has a disabled boolean attribute. - """ - body = {"id": self.id} - return self.marionette._send_message("isElementEnabled", body, key="value") - - def is_displayed(self): - """Returns True if the element is displayed, False otherwise.""" - body = {"id": self.id} - return self.marionette._send_message("isElementDisplayed", body, key="value") - - @property - def size(self): - """A dictionary with the size of the element.""" - warnings.warn("The size property has been deprecated and will be removed in a future version. \ - Please use HTMLElement#rect", DeprecationWarning) - rect = self.rect - return {"width": rect["width"], "height": rect["height"]} - - @property - def tag_name(self): - """The tag name of the element.""" - body = {"id": self.id} - return self.marionette._send_message("getElementTagName", body, key="value") - - @property - def location(self): - """Get an element's location on the page. - - The returned point will contain the x and y coordinates of the - top left-hand corner of the given element. The point (0,0) - refers to the upper-left corner of the document. - - :returns: a dictionary containing x and y as entries - """ - warnings.warn("The location property has been deprecated and will be removed in a future version. \ - Please use HTMLElement#rect", DeprecationWarning) - rect = self.rect - return {"x": rect["x"], "y": rect["y"]} - - @property - def rect(self): - """Gets the element's bounding rectangle. - - This will return a dictionary with the following: - - * x and y represent the top left coordinates of the ``HTMLElement`` - relative to top left corner of the document. - * height and the width will contain the height and the width - of the DOMRect of the ``HTMLElement``. - """ - body = {"id": self.id} - return self.marionette._send_message( - "getElementRect", body, key="value" if self.marionette.protocol == 1 else None) - - def value_of_css_property(self, property_name): - """Gets the value of the specified CSS property name. - - :param property_name: Property name to get the value of. - """ - body = {"id": self.id, "propertyName": property_name} - return self.marionette._send_message( - "getElementValueOfCssProperty", body, key="value") - - -class MouseButton(object): - """Enum-like class for mouse button constants.""" - LEFT = 0 - MIDDLE = 1 - RIGHT = 2 - - -class Actions(object): - ''' - An Action object represents a set of actions that are executed in a particular order. - - All action methods (press, etc.) return the Actions object itself, to make - it easy to create a chain of events. - - Example usage: - - :: - - # get html file - testAction = marionette.absolute_url("testFool.html") - # navigate to the file - marionette.navigate(testAction) - # find element1 and element2 - element1 = marionette.find_element(By.ID, "element1") - element2 = marionette.find_element(By.ID, "element2") - # create action object - action = Actions(marionette) - # add actions (press, wait, move, release) into the object - action.press(element1).wait(5). move(element2).release() - # fire all the added events - action.perform() - ''' - - def __init__(self, marionette): - self.action_chain = [] - self.marionette = marionette - self.current_id = None - - def press(self, element, x=None, y=None): - ''' - Sends a 'touchstart' event to this element. - - If no coordinates are given, it will be targeted at the center of the - element. If given, it will be targeted at the (x,y) coordinates - relative to the top-left corner of the element. - - :param element: The element to press on. - :param x: Optional, x-coordinate to tap, relative to the top-left - corner of the element. - :param y: Optional, y-coordinate to tap, relative to the top-left - corner of the element. - ''' - element = element.id - self.action_chain.append(['press', element, x, y]) - return self - - def release(self): - ''' - Sends a 'touchend' event to this element. - - May only be called if press() has already be called on this element. - - If press and release are chained without a move action between them, - then it will be processed as a 'tap' event, and will dispatch the - expected mouse events ('mousemove' (if necessary), 'mousedown', - 'mouseup', 'mouseclick') after the touch events. If there is a wait - period between press and release that will trigger a contextmenu, - then the 'contextmenu' menu event will be fired instead of the - touch/mouse events. - ''' - self.action_chain.append(['release']) - return self - - def move(self, element): - ''' - Sends a 'touchmove' event at the center of the target element. - - :param element: Element to move towards. - - May only be called if press() has already be called. - ''' - element = element.id - self.action_chain.append(['move', element]) - return self - - def move_by_offset(self, x, y): - ''' - Sends 'touchmove' event to the given x, y coordinates relative to the - top-left of the currently touched element. - - May only be called if press() has already be called. - - :param x: Specifies x-coordinate of move event, relative to the - top-left corner of the element. - :param y: Specifies y-coordinate of move event, relative to the - top-left corner of the element. - ''' - self.action_chain.append(['moveByOffset', x, y]) - return self - - def wait(self, time=None): - ''' - Waits for specified time period. - - :param time: Time in seconds to wait. If time is None then this has no effect - for a single action chain. If used inside a multi-action chain, - then time being None indicates that we should wait for all other - currently executing actions that are part of the chain to complete. - ''' - self.action_chain.append(['wait', time]) - return self - - def cancel(self): - ''' - Sends 'touchcancel' event to the target of the original 'touchstart' event. - - May only be called if press() has already be called. - ''' - self.action_chain.append(['cancel']) - return self - - def tap(self, element, x=None, y=None): - ''' - Performs a quick tap on the target element. - - :param element: The element to tap. - :param x: Optional, x-coordinate of tap, relative to the top-left - corner of the element. If not specified, default to center of - element. - :param y: Optional, y-coordinate of tap, relative to the top-left - corner of the element. If not specified, default to center of - element. - - This is equivalent to calling: - - :: - - action.press(element, x, y).release() - ''' - element = element.id - self.action_chain.append(['press', element, x, y]) - self.action_chain.append(['release']) - return self - - def double_tap(self, element, x=None, y=None): - ''' - Performs a double tap on the target element. - - :param element: The element to double tap. - :param x: Optional, x-coordinate of double tap, relative to the - top-left corner of the element. - :param y: Optional, y-coordinate of double tap, relative to the - top-left corner of the element. - ''' - element = element.id - self.action_chain.append(['press', element, x, y]) - self.action_chain.append(['release']) - self.action_chain.append(['press', element, x, y]) - self.action_chain.append(['release']) - return self - - def click(self, element, button=MouseButton.LEFT, count=1): - ''' - Performs a click with additional parameters to allow for double clicking, - right click, middle click, etc. - - :param element: The element to click. - :param button: The mouse button to click (indexed from 0, left to right). - :param count: Optional, the count of clicks to synthesize (for double - click events). - ''' - el = element.id - self.action_chain.append(['click', el, button, count]) - return self - - def context_click(self, element): - ''' - Performs a context click on the specified element. - - :param element: The element to context click. - ''' - return self.click(element, button=MouseButton.RIGHT) - - def middle_click(self, element): - ''' - Performs a middle click on the specified element. - - :param element: The element to middle click. - ''' - return self.click(element, button=MouseButton.MIDDLE) - - def double_click(self, element): - ''' - Performs a double click on the specified element. - - :param element: The element to double click. - ''' - return self.click(element, count=2) - - def flick(self, element, x1, y1, x2, y2, duration=200): - ''' - Performs a flick gesture on the target element. - - :param element: The element to perform the flick gesture on. - :param x1: Starting x-coordinate of flick, relative to the top left - corner of the element. - :param y1: Starting y-coordinate of flick, relative to the top left - corner of the element. - :param x2: Ending x-coordinate of flick, relative to the top left - corner of the element. - :param y2: Ending y-coordinate of flick, relative to the top left - corner of the element. - :param duration: Time needed for the flick gesture for complete (in - milliseconds). - ''' - element = element.id - elapsed = 0 - time_increment = 10 - if time_increment >= duration: - time_increment = duration - move_x = time_increment*1.0/duration * (x2 - x1) - move_y = time_increment*1.0/duration * (y2 - y1) - self.action_chain.append(['press', element, x1, y1]) - while elapsed < duration: - elapsed += time_increment - self.action_chain.append(['moveByOffset', move_x, move_y]) - self.action_chain.append(['wait', time_increment/1000]) - self.action_chain.append(['release']) - return self - - def long_press(self, element, time_in_seconds, x=None, y=None): - ''' - Performs a long press gesture on the target element. - - :param element: The element to press. - :param time_in_seconds: Time in seconds to wait before releasing the press. - :param x: Optional, x-coordinate to tap, relative to the top-left - corner of the element. - :param y: Optional, y-coordinate to tap, relative to the top-left - corner of the element. - - This is equivalent to calling: - - :: - - action.press(element, x, y).wait(time_in_seconds).release() - - ''' - element = element.id - self.action_chain.append(['press', element, x, y]) - self.action_chain.append(['wait', time_in_seconds]) - self.action_chain.append(['release']) - return self - - def key_down(self, key_code): - """ - Perform a "keyDown" action for the given key code. Modifier keys are - respected by the server for the course of an action chain. - - :param key_code: The key to press as a result of this action. - """ - self.action_chain.append(['keyDown', key_code]) - return self - - def key_up(self, key_code): - """ - Perform a "keyUp" action for the given key code. Modifier keys are - respected by the server for the course of an action chain. - :param key_up: The key to release as a result of this action. - """ - self.action_chain.append(['keyUp', key_code]) - return self - - def perform(self): - """Sends the action chain built so far to the server side for - execution and clears the current chain of actions.""" - body = {"chain": self.action_chain, "nextId": self.current_id} - self.current_id = self.marionette._send_message("actionChain", body, key="value") - self.action_chain = [] - return self - - -class MultiActions(object): - ''' - A MultiActions object represents a sequence of actions that may be - performed at the same time. Its intent is to allow the simulation - of multi-touch gestures. - Usage example: - - :: - - # create multiaction object - multitouch = MultiActions(marionette) - # create several action objects - action_1 = Actions(marionette) - action_2 = Actions(marionette) - # add actions to each action object/finger - action_1.press(element1).move_to(element2).release() - action_2.press(element3).wait().release(element3) - # fire all the added events - multitouch.add(action_1).add(action_2).perform() - ''' - - def __init__(self, marionette): - self.multi_actions = [] - self.max_length = 0 - self.marionette = marionette - - def add(self, action): - ''' - Adds a set of actions to perform. - - :param action: An Actions object. - ''' - self.multi_actions.append(action.action_chain) - if len(action.action_chain) > self.max_length: - self.max_length = len(action.action_chain) - return self - - def perform(self): - """Perform all the actions added to this object.""" - body = {"value": self.multi_actions, "max_length": self.max_length} - self.marionette._send_message("multiAction", body) - - -class Alert(object): - """A class for interacting with alerts. - - :: - - Alert(marionette).accept() - Alert(merionette).dismiss() - """ - - def __init__(self, marionette): - self.marionette = marionette - - def accept(self): - """Accept a currently displayed modal dialog.""" - self.marionette._send_message("acceptDialog") - - def dismiss(self): - """Dismiss a currently displayed modal dialog.""" - self.marionette._send_message("dismissDialog") - - @property - def text(self): - """Return the currently displayed text in a tab modal.""" - return self.marionette._send_message("getTextFromDialog", key="value") - - def send_keys(self, *string): - """Send keys to the currently displayed text input area in an open - tab modal dialog.""" - body = {"value": Marionette.convert_keys(*string)} - self.marionette._send_message("sendKeysToDialog", body) - - -class Marionette(object): - """Represents a Marionette connection to a browser or device.""" - - CONTEXT_CHROME = "chrome" # non-browser content: windows, dialogs, etc. - CONTEXT_CONTENT = "content" # browser content: iframes, divs, etc. - DEFAULT_STARTUP_TIMEOUT = 120 - DEFAULT_SHUTDOWN_TIMEOUT = 65 # Firefox will kill hanging threads after 60s - - # Bug 1336953 - Until we can remove the socket timeout parameter it has to be - # set a default value which is larger than the longest timeout as defined by the - # WebDriver spec. In that case its 300s for page load. Also add another minute - # so that slow builds have enough time to send the timeout error to the client. - DEFAULT_SOCKET_TIMEOUT = 360 - - def __init__(self, host="localhost", port=2828, app=None, bin=None, - baseurl=None, socket_timeout=DEFAULT_SOCKET_TIMEOUT, - startup_timeout=None, **instance_args): - """Construct a holder for the Marionette connection. - - Remember to call ``start_session`` in order to initiate the - connection and start a Marionette session. - - :param host: Host where the Marionette server listens. - Defaults to localhost. - :param port: Port where the Marionette server listens. - Defaults to port 2828. - :param baseurl: Where to look for files served from Marionette's - www directory. - :param socket_timeout: Timeout for Marionette socket operations. - :param startup_timeout: Seconds to wait for a connection with - binary. - :param bin: Path to browser binary. If any truthy value is given - this will attempt to start a Gecko instance with the specified - `app`. - :param app: Type of ``instance_class`` to use for managing app - instance. See ``marionette_driver.geckoinstance``. - :param instance_args: Arguments to pass to ``instance_class``. - - """ - self.host = host - self.port = self.local_port = int(port) - self.bin = bin - self.client = None - self.instance = None - self.session = None - self.session_id = None - self.process_id = None - self.profile = None - self.window = None - self.chrome_window = None - self.baseurl = baseurl - self._test_name = None - self.socket_timeout = socket_timeout - self.crashed = 0 - - self.startup_timeout = int(startup_timeout or self.DEFAULT_STARTUP_TIMEOUT) - if self.bin: - if not Marionette.is_port_available(self.port, host=self.host): - ex_msg = "{0}:{1} is unavailable.".format(self.host, self.port) - raise errors.MarionetteException(message=ex_msg) - - self.instance = GeckoInstance.create( - app, host=self.host, port=self.port, bin=self.bin, **instance_args) - self.instance.start() - self.raise_for_port(timeout=self.startup_timeout) - - self.timeout = Timeouts(self) - - @property - def profile_path(self): - if self.instance and self.instance.profile: - return self.instance.profile.profile - - def cleanup(self): - if self.session: - try: - self.delete_session() - except (errors.MarionetteException, IOError): - # These exceptions get thrown if the Marionette server - # hit an exception/died or the connection died. We can - # do no further server-side cleanup in this case. - pass - if self.instance: - self.instance.close() - - def __del__(self): - self.cleanup() - - @staticmethod - def is_port_available(port, host=''): - port = int(port) - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - try: - s.bind((host, port)) - return True - except socket.error: - return False - finally: - s.close() - - def wait_for_port(self, timeout=None): - """Wait until Marionette server has been created the communication socket. - - :param timeout: Timeout in seconds for the server to be ready. - - """ - if timeout is None: - timeout = self.DEFAULT_STARTUP_TIMEOUT - - runner = None - if self.instance is not None: - runner = self.instance.runner - - poll_interval = 0.1 - starttime = datetime.datetime.now() - - while datetime.datetime.now() - starttime < datetime.timedelta(seconds=timeout): - # If the instance we want to connect to is not running return immediately - if runner is not None and not runner.is_running(): - return False - - sock = None - try: - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - sock.settimeout(0.5) - sock.connect((self.host, self.port)) - data = sock.recv(16) - if ":" in data: - return True - except socket.error: - pass - finally: - if sock is not None: - sock.close() - - time.sleep(poll_interval) - - return False - - @do_process_check - def raise_for_port(self, timeout=None): - """Raise socket.timeout if no connection can be established. - - :param timeout: Timeout in seconds for the server to be ready. - - """ - if not self.wait_for_port(timeout): - raise socket.timeout("Timed out waiting for connection on {0}:{1}!".format( - self.host, self.port)) - - @do_process_check - def _send_message(self, name, params=None, key=None): - """Send a blocking message to the server. - - Marionette provides an asynchronous, non-blocking interface and - this attempts to paper over this by providing a synchronous API - to the user. - - :param name: Requested command key. - :param params: Optional dictionary of key/value arguments. - :param key: Optional key to extract from response. - - :returns: Full response from the server, or if `key` is given, - the value of said key in the response. - """ - - if not self.session_id and name != "newSession": - raise errors.MarionetteException("Please start a session") - - try: - if self.protocol < 3: - data = {"name": name} - if params: - data["parameters"] = params - self.client.send(data) - msg = self.client.receive() - - else: - msg = self.client.request(name, params) - - except IOError: - self.delete_session(send_request=False) - raise - - res, err = msg.result, msg.error - if err: - self._handle_error(err) - - if key is not None: - return self._unwrap_response(res.get(key)) - else: - return self._unwrap_response(res) - - def _unwrap_response(self, value): - if isinstance(value, dict) and (WEBELEMENT_KEY in value or - W3C_WEBELEMENT_KEY in value): - if value.get(WEBELEMENT_KEY): - return HTMLElement(self, value.get(WEBELEMENT_KEY)) - else: - return HTMLElement(self, value.get(W3C_WEBELEMENT_KEY)) - elif isinstance(value, list): - return list(self._unwrap_response(item) for item in value) - else: - return value - - def _handle_error(self, obj): - if self.protocol == 1: - if "error" not in obj or not isinstance(obj["error"], dict): - raise errors.MarionetteException( - "Malformed packet, expected key 'error' to be a dict: {}".format(obj)) - error = obj["error"].get("status") - message = obj["error"].get("message") - stacktrace = obj["error"].get("stacktrace") - - else: - error = obj["error"] - message = obj["message"] - stacktrace = obj["stacktrace"] - - raise errors.lookup(error)(message, stacktrace=stacktrace) - - def check_for_crash(self): - """Check if the process crashed. - - :returns: True, if a crash happened since the method has been called the last time. - """ - crash_count = 0 - - if self.instance: - name = self.test_name or 'marionette.py' - crash_count = self.instance.runner.check_for_crashes(test_name=name) - self.crashed = self.crashed + crash_count - - return crash_count > 0 - - def _handle_socket_failure(self): - """Handle socket failures for the currently connected application. - - If the application crashed then clean-up internal states, or in case of a content - crash also kill the process. If there are other reasons for a socket failure, - wait for the process to shutdown itself, or force kill it. - - Please note that the method expects an exception to be handled on the current stack - frame, and is only called via the `@do_process_check` decorator. - - """ - exc, val, tb = sys.exc_info() - - # If the application hasn't been launched by Marionette no further action can be done. - # In such cases we simply re-throw the exception. - if not self.instance: - raise exc, val, tb - - else: - # Somehow the socket disconnected. Give the application some time to shutdown - # itself before killing the process. - returncode = self.instance.runner.wait(timeout=self.DEFAULT_SHUTDOWN_TIMEOUT) - - if returncode is None: - message = ('Process killed because the connection to Marionette server is ' - 'lost. Check gecko.log for errors') - # This will force-close the application without sending any other message. - self.cleanup() - else: - # If Firefox quit itself check if there was a crash - crash_count = self.check_for_crash() - - if crash_count > 0: - if returncode == 0: - message = 'Content process crashed' - else: - message = 'Process crashed (Exit code: {returncode})' - else: - message = 'Process has been unexpectedly closed (Exit code: {returncode})' - - self.delete_session(send_request=False, reset_session_id=True) - - message += ' (Reason: {reason})' - - raise IOError, message.format(returncode=returncode, reason=val), tb - - @staticmethod - def convert_keys(*string): - typing = [] - for val in string: - if isinstance(val, Keys): - typing.append(val) - elif isinstance(val, int): - val = str(val) - for i in range(len(val)): - typing.append(val[i]) - else: - for i in range(len(val)): - typing.append(val[i]) - return typing - - def get_permission(self, perm): - script = """ - let value = { - 'url': document.nodePrincipal.URI.spec, - 'appId': document.nodePrincipal.appId, - 'isInIsolatedMozBrowserElement': document.nodePrincipal.isInIsolatedMozBrowserElement, - 'type': arguments[0] - }; - return value;""" - with self.using_context("content"): - value = self.execute_script(script, script_args=(perm,), sandbox="system") - - with self.using_context("chrome"): - permission = self.execute_script(""" - Components.utils.import("resource://gre/modules/Services.jsm"); - let perm = arguments[0]; - let secMan = Services.scriptSecurityManager; - let attrs = {appId: perm.appId, - inIsolatedMozBrowser: perm.isInIsolatedMozBrowserElement}; - let principal = secMan.createCodebasePrincipal( - Services.io.newURI(perm.url, null, null), - attrs); - let testPerm = Services.perms.testPermissionFromPrincipal( - principal, perm.type); - return testPerm; - """, script_args=(value,)) - return permission - - def push_permission(self, perm, allow): - script = """ - let allow = arguments[0]; - if (typeof(allow) == "boolean") { - if (allow) { - allow = Components.interfaces.nsIPermissionManager.ALLOW_ACTION; - } - else { - allow = Components.interfaces.nsIPermissionManager.DENY_ACTION; - } - } - let perm_type = arguments[1]; - - Components.utils.import("resource://gre/modules/Services.jsm"); - window.wrappedJSObject.permChanged = false; - window.wrappedJSObject.permObserver = function(subject, topic, data) { - if (topic == "perm-changed") { - let permission = subject.QueryInterface(Components.interfaces.nsIPermission); - if (perm_type == permission.type) { - Services.obs.removeObserver(window.wrappedJSObject.permObserver, - "perm-changed"); - window.wrappedJSObject.permChanged = true; - } - } - }; - Services.obs.addObserver(window.wrappedJSObject.permObserver, - "perm-changed", false); - - let value = { - 'url': document.nodePrincipal.URI.spec, - 'appId': document.nodePrincipal.appId, - 'isInIsolatedMozBrowserElement': document.nodePrincipal.isInIsolatedMozBrowserElement, - 'type': perm_type, - 'action': allow - }; - return value; - """ - with self.using_context("content"): - perm = self.execute_script(script, script_args=(allow, perm,), sandbox="system") - - current_perm = self.get_permission(perm["type"]) - if current_perm == perm["action"]: - with self.using_context("content"): - self.execute_script(""" - Components.utils.import("resource://gre/modules/Services.jsm"); - Services.obs.removeObserver(window.wrappedJSObject.permObserver, - "perm-changed"); - """, sandbox="system") - return - - with self.using_context("chrome"): - self.execute_script(""" - Components.utils.import("resource://gre/modules/Services.jsm"); - let perm = arguments[0]; - let secMan = Services.scriptSecurityManager; - let attrs = {appId: perm.appId, - inIsolatedMozBrowser: perm.isInIsolatedMozBrowserElement}; - let principal = secMan.createCodebasePrincipal(Services.io.newURI(perm.url, - null, null), - attrs); - Services.perms.addFromPrincipal(principal, perm.type, perm.action); - return true; - """, script_args=(perm,)) - - with self.using_context("content"): - self.execute_async_script(""" - let wait = function() { - if (window.wrappedJSObject.permChanged) { - marionetteScriptFinished(); - } else { - window.setTimeout(wait, 100); - } - }(); - """, sandbox="system") - - @contextmanager - def using_permissions(self, perms): - ''' - Sets permissions for code being executed in a `with` block, - and restores them on exit. - - :param perms: A dict containing one or more perms and their - values to be set. - - Usage example:: - - with marionette.using_permissions({'systemXHR': True}): - ... do stuff ... - ''' - original_perms = {} - for perm in perms: - original_perms[perm] = self.get_permission(perm) - self.push_permission(perm, perms[perm]) - - try: - yield - finally: - for perm in original_perms: - self.push_permission(perm, original_perms[perm]) - - def clear_pref(self, pref): - """Clear the user-defined value from the specified preference. - - :param pref: Name of the preference. - """ - with self.using_context(self.CONTEXT_CHROME): - self.execute_script(""" - Components.utils.import("resource://gre/modules/Preferences.jsm"); - Preferences.reset(arguments[0]); - """, script_args=(pref,)) - - def get_pref(self, pref, default_branch=False, value_type="nsISupportsString"): - """Get the value of the specified preference. - - :param pref: Name of the preference. - :param default_branch: Optional, if `True` the preference value will be read - from the default branch. Otherwise the user-defined - value if set is returned. Defaults to `False`. - :param value_type: Optional, XPCOM interface of the pref's complex value. - Defaults to `nsISupportsString`. Other possible values are: - `nsILocalFile`, and `nsIPrefLocalizedString`. - - Usage example:: - marionette.get_pref("browser.tabs.warnOnClose") - - """ - with self.using_context(self.CONTEXT_CHROME): - pref_value = self.execute_script(""" - Components.utils.import("resource://gre/modules/Preferences.jsm"); - - let pref = arguments[0]; - let defaultBranch = arguments[1]; - let valueType = arguments[2]; - - prefs = new Preferences({defaultBranch: defaultBranch}); - return prefs.get(pref, null, Components.interfaces[valueType]); - """, script_args=(pref, default_branch, value_type)) - return pref_value - - def set_pref(self, pref, value, default_branch=False): - """Set the value of the specified preference. - - :param pref: Name of the preference. - :param value: The value to set the preference to. If the value is None, - reset the preference to its default value. If no default - value exists, the preference will cease to exist. - :param default_branch: Optional, if `True` the preference value will - be written to the default branch, and will remain until - the application gets restarted. Otherwise a user-defined - value is set. Defaults to `False`. - - Usage example:: - marionette.set_pref("browser.tabs.warnOnClose", True) - - """ - with self.using_context(self.CONTEXT_CHROME): - if value is None: - self.clear_pref(pref) - return - - self.execute_script(""" - Components.utils.import("resource://gre/modules/Preferences.jsm"); - - let pref = arguments[0]; - let value = arguments[1]; - let defaultBranch = arguments[2]; - - prefs = new Preferences({defaultBranch: defaultBranch}); - prefs.set(pref, value); - """, script_args=(pref, value, default_branch)) - - def set_prefs(self, prefs, default_branch=False): - """Set the value of a list of preferences. - - :param prefs: A dict containing one or more preferences and their values - to be set. See `set_pref` for further details. - :param default_branch: Optional, if `True` the preference value will - be written to the default branch, and will remain until - the application gets restarted. Otherwise a user-defined - value is set. Defaults to `False`. - - Usage example:: - - marionette.set_prefs({"browser.tabs.warnOnClose": True}) - - """ - for pref, value in prefs.items(): - self.set_pref(pref, value, default_branch=default_branch) - - @contextmanager - def using_prefs(self, prefs, default_branch=False): - """Set preferences for code executed in a `with` block, and restores them on exit. - - :param prefs: A dict containing one or more preferences and their values - to be set. See `set_prefs` for further details. - :param default_branch: Optional, if `True` the preference value will - be written to the default branch, and will remain until - the application gets restarted. Otherwise a user-defined - value is set. Defaults to `False`. - - Usage example:: - - with marionette.using_prefs({"browser.tabs.warnOnClose": True}): - # ... do stuff ... - - """ - original_prefs = {p: self.get_pref(p) for p in prefs} - self.set_prefs(prefs, default_branch=default_branch) - - try: - yield - finally: - self.set_prefs(original_prefs, default_branch=default_branch) - - @do_process_check - def enforce_gecko_prefs(self, prefs): - """Checks if the running instance has the given prefs. If not, - it will kill the currently running instance, and spawn a new - instance with the requested preferences. - - : param prefs: A dictionary whose keys are preference names. - """ - if not self.instance: - raise errors.MarionetteException("enforce_gecko_prefs() can only be called " - "on Gecko instances launched by Marionette") - pref_exists = True - with self.using_context(self.CONTEXT_CHROME): - for pref, value in prefs.iteritems(): - if type(value) is not str: - value = json.dumps(value) - pref_exists = self.execute_script(""" - let prefInterface = Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefBranch); - let pref = '{0}'; - let value = '{1}'; - let type = prefInterface.getPrefType(pref); - switch(type) {{ - case prefInterface.PREF_STRING: - return value == prefInterface.getCharPref(pref).toString(); - case prefInterface.PREF_BOOL: - return value == prefInterface.getBoolPref(pref).toString(); - case prefInterface.PREF_INT: - return value == prefInterface.getIntPref(pref).toString(); - case prefInterface.PREF_INVALID: - return false; - }} - """.format(pref, value)) - if not pref_exists: - break - - if not pref_exists: - context = self._send_message("getContext", key="value") - self.delete_session() - self.instance.restart(prefs) - self.raise_for_port() - self.start_session() - - # Restore the context as used before the restart - self.set_context(context) - - def _request_in_app_shutdown(self, shutdown_flags=None): - """Terminate the currently running instance from inside the application. - - :param shutdown_flags: If specified use additional flags for the shutdown - of the application. Possible values here correspond - to constants in nsIAppStartup: http://mzl.la/1X0JZsC. - """ - flags = set([]) - if shutdown_flags: - flags.add(shutdown_flags) - - # Trigger a 'quit-application-requested' observer notification so that - # components can safely shutdown before quitting the application. - with self.using_context("chrome"): - canceled = self.execute_script(""" - Components.utils.import("resource://gre/modules/Services.jsm"); - let cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"]. - createInstance(Components.interfaces.nsISupportsPRBool); - Services.obs.notifyObservers(cancelQuit, "quit-application-requested", null); - return cancelQuit.data; - """) - if canceled: - raise errors.MarionetteException("Something canceled the quit application request") - - self._send_message("quitApplication", {"flags": list(flags)}) - - @do_process_check - def quit(self, in_app=False, callback=None): - """Terminate the currently running instance. - - This command will delete the active marionette session. It also allows - manipulation of eg. the profile data while the application is not running. - To start the application again, start_session() has to be called. - - :param in_app: If True, marionette will cause a quit from within the - browser. Otherwise the browser will be quit immediately - by killing the process. - :param callback: If provided and `in_app` is True, the callback will - be used to trigger the shutdown. - """ - if not self.instance: - raise errors.MarionetteException("quit() can only be called " - "on Gecko instances launched by Marionette") - - if in_app: - if callable(callback): - self._send_message("acceptConnections", {"value": False}) - callback() - else: - self._request_in_app_shutdown() - - # Ensure to explicitely mark the session as deleted - self.delete_session(send_request=False, reset_session_id=True) - - # Give the application some time to shutdown - self.instance.runner.wait(timeout=self.DEFAULT_SHUTDOWN_TIMEOUT) - else: - self.delete_session(reset_session_id=True) - self.instance.close() - - @do_process_check - def restart(self, clean=False, in_app=False, callback=None): - """ - This will terminate the currently running instance, and spawn a new instance - with the same profile and then reuse the session id when creating a session again. - - :param clean: If False the same profile will be used after the restart. Note - that the in app initiated restart always maintains the same - profile. - :param in_app: If True, marionette will cause a restart from within the - browser. Otherwise the browser will be restarted immediately - by killing the process. - :param callback: If provided and `in_app` is True, the callback will be - used to trigger the restart. - """ - if not self.instance: - raise errors.MarionetteException("restart() can only be called " - "on Gecko instances launched by Marionette") - - context = self._send_message("getContext", key="value") - session_id = self.session_id - - if in_app: - if clean: - raise ValueError("An in_app restart cannot be triggered with the clean flag set") - - if callable(callback): - self._send_message("acceptConnections", {"value": False}) - callback() - else: - self._request_in_app_shutdown("eRestart") - - # Ensure to explicitely mark the session as deleted - self.delete_session(send_request=False, reset_session_id=True) - - try: - self.raise_for_port() - except socket.timeout: - if self.instance.runner.returncode is not None: - exc, val, tb = sys.exc_info() - self.cleanup() - raise exc, "Requested restart of the application was aborted", tb - - else: - self.delete_session() - self.instance.restart(clean=clean) - self.raise_for_port() - - self.start_session(session_id=session_id) - - # Restore the context as used before the restart - self.set_context(context) - - if in_app and self.process_id: - # In some cases Firefox restarts itself by spawning into a new process group. - # As long as mozprocess cannot track that behavior (bug 1284864) we assist by - # informing about the new process id. - self.instance.runner.process_handler.check_for_detached(self.process_id) - - def absolute_url(self, relative_url): - ''' - Returns an absolute url for files served from Marionette's www directory. - - :param relative_url: The url of a static file, relative to Marionette's www directory. - ''' - return "{0}{1}".format(self.baseurl, relative_url) - - @do_process_check - def start_session(self, capabilities=None, session_id=None, timeout=60): - """Create a new Marionette session. - - This method must be called before performing any other action. - - :param capabilities: An optional dict of desired or required capabilities. - :param timeout: Timeout in seconds for the server to be ready. - :param session_id: unique identifier for the session. If no session id is - passed in then one will be generated by the marionette server. - - :returns: A dict of the capabilities offered. - - """ - self.crashed = 0 - - if self.instance: - returncode = self.instance.runner.returncode - if returncode is not None: - # We're managing a binary which has terminated, so restart it. - self.instance.restart() - - self.client = transport.TcpTransport( - self.host, - self.port, - self.socket_timeout) - - # Call wait_for_port() before attempting to connect in - # the event gecko hasn't started yet. - timeout = timeout or self.startup_timeout - self.wait_for_port(timeout=timeout) - self.protocol, _ = self.client.connect() - - body = {"capabilities": capabilities, "sessionId": session_id} - resp = self._send_message("newSession", body) - - self.session_id = resp["sessionId"] - self.session = resp["value"] if self.protocol == 1 else resp["capabilities"] - # fallback to processId can be removed in Firefox 55 - self.process_id = self.session.get("moz:processID", self.session.get("processId")) - self.profile = self.session.get("moz:profile") - - return self.session - - @property - def test_name(self): - return self._test_name - - @test_name.setter - def test_name(self, test_name): - self._send_message("setTestName", {"value": test_name}) - self._test_name = test_name - - def delete_session(self, send_request=True, reset_session_id=False): - """Close the current session and disconnect from the server. - - :param send_request: Optional, if `True` a request to close the session on - the server side will be send. Use `False` in case of eg. in_app restart() - or quit(), which trigger a deletion themselves. Defaults to `True`. - :param reset_session_id: Optional, if `True` the current session id will - be reset, which will require an explicit call to `start_session()` before - the test can continue. Defaults to `False`. - """ - try: - if send_request: - self._send_message("deleteSession") - finally: - if reset_session_id: - self.session_id = None - self.session = None - self.process_id = None - self.profile = None - self.window = None - - if self.client is not None: - self.client.close() - - @property - def session_capabilities(self): - """A JSON dictionary representing the capabilities of the - current session. - - """ - return self.session - - def set_script_timeout(self, timeout): - """Sets the maximum number of ms that an asynchronous script is - allowed to run. - - If a script does not return in the specified amount of time, - a ScriptTimeoutException is raised. - - :param timeout: The maximum number of milliseconds an asynchronous - script can run without causing an ScriptTimeoutException to - be raised - - .. note:: `set_script_timeout` is deprecated, please use - `timeout.script` setter. - - """ - warnings.warn( - "set_script_timeout is deprecated, please use timeout.script setter", - DeprecationWarning) - self.timeout.script = timeout / 1000 - - def set_search_timeout(self, timeout): - """Sets a timeout for the find methods. - - When searching for an element using - either :class:`Marionette.find_element` or - :class:`Marionette.find_elements`, the method will continue - trying to locate the element for up to timeout ms. This can be - useful if, for example, the element you're looking for might - not exist immediately, because it belongs to a page which is - currently being loaded. - - :param timeout: Timeout in milliseconds. - - .. note:: `set_search_timeout` is deprecated, please use - `timeout.implicit` setter. - - """ - warnings.warn( - "set_search_timeout is deprecated, please use timeout.implicit setter", - DeprecationWarning) - self.timeout.implicit = timeout / 1000 - - def set_page_load_timeout(self, timeout): - """Sets a timeout for loading pages. - - A page load timeout specifies the amount of time the Marionette - instance should wait for a page load operation to complete. A - ``TimeoutException`` is returned if this limit is exceeded. - - :param timeout: Timeout in milliseconds. - - .. note:: `set_page_load_timeout` is deprecated, please use - `timeout.page_load` setter. - - """ - warnings.warn( - "set_page_load_timeout is deprecated, please use timeout.page_load setter", - DeprecationWarning) - self.timeout.page_load = timeout / 1000 - - @property - def current_window_handle(self): - """Get the current window's handle. - - Returns an opaque server-assigned identifier to this window - that uniquely identifies it within this Marionette instance. - This can be used to switch to this window at a later point. - - :returns: unique window handle - :rtype: string - """ - self.window = self._send_message("getWindowHandle", key="value") - return self.window - - @property - def current_chrome_window_handle(self): - """Get the current chrome window's handle. Corresponds to - a chrome window that may itself contain tabs identified by - window_handles. - - Returns an opaque server-assigned identifier to this window - that uniquely identifies it within this Marionette instance. - This can be used to switch to this window at a later point. - - :returns: unique window handle - :rtype: string - """ - self.chrome_window = self._send_message( - "getCurrentChromeWindowHandle", key="value") - return self.chrome_window - - def get_window_position(self): - """Get the current window's position. - - :returns: a dictionary with x and y - """ - return self._send_message( - "getWindowPosition", key="value" if self.protocol == 1 else None) - - def set_window_position(self, x, y): - """Set the position of the current window - - :param x: x coordinate for the top left of the window - :param y: y coordinate for the top left of the window - """ - self._send_message("setWindowPosition", {"x": x, "y": y}) - - @property - def title(self): - """Current title of the active window.""" - return self._send_message("getTitle", key="value") - - @property - def window_handles(self): - """Get list of windows in the current context. - - If called in the content context it will return a list of - references to all available browser windows. Called in the - chrome context, it will list all available windows, not just - browser windows (e.g. not just navigator.browser). - - Each window handle is assigned by the server, and the list of - strings returned does not have a guaranteed ordering. - - :returns: Unordered list of unique window handles as strings - """ - return self._send_message( - "getWindowHandles", key="value" if self.protocol == 1 else None) - - @property - def chrome_window_handles(self): - """Get a list of currently open chrome windows. - - Each window handle is assigned by the server, and the list of - strings returned does not have a guaranteed ordering. - - :returns: Unordered list of unique chrome window handles as strings - """ - return self._send_message( - "getChromeWindowHandles", key="value" if self.protocol == 1 else None) - - @property - def page_source(self): - """A string representation of the DOM.""" - return self._send_message("getPageSource", key="value") - - def close(self): - """Close the current window, ending the session if it's the last - window currently open. - - :returns: Unordered list of remaining unique window handles as strings - """ - return self._send_message("close") - - def close_chrome_window(self): - """Close the currently selected chrome window, ending the session - if it's the last window open. - - :returns: Unordered list of remaining unique chrome window handles as strings - """ - return self._send_message("closeChromeWindow") - - def set_context(self, context): - """Sets the context that Marionette commands are running in. - - :param context: Context, may be one of the class properties - `CONTEXT_CHROME` or `CONTEXT_CONTENT`. - - Usage example:: - - marionette.set_context(marionette.CONTEXT_CHROME) - """ - if context not in [self.CONTEXT_CHROME, self.CONTEXT_CONTENT]: - raise ValueError("Unknown context: {}".format(context)) - self._send_message("setContext", {"value": context}) - - @contextmanager - def using_context(self, context): - """Sets the context that Marionette commands are running in using - a `with` statement. The state of the context on the server is - saved before entering the block, and restored upon exiting it. - - :param context: Context, may be one of the class properties - `CONTEXT_CHROME` or `CONTEXT_CONTENT`. - - Usage example:: - - with marionette.using_context(marionette.CONTEXT_CHROME): - # chrome scope - ... do stuff ... - """ - scope = self._send_message("getContext", key="value") - self.set_context(context) - try: - yield - finally: - self.set_context(scope) - - def switch_to_alert(self): - """Returns an Alert object for interacting with a currently - displayed alert. - - :: - - alert = self.marionette.switch_to_alert() - text = alert.text - alert.accept() - """ - return Alert(self) - - def switch_to_window(self, window_id, focus=True): - """Switch to the specified window; subsequent commands will be - directed at the new window. - - :param window_id: The id or name of the window to switch to. - - :param focus: A boolean value which determins whether to focus - the window that we just switched to. - """ - body = {"focus": focus, "name": window_id} - self._send_message("switchToWindow", body) - self.window = window_id - - def get_active_frame(self): - """Returns an HTMLElement representing the frame Marionette is - currently acting on.""" - return self._send_message("getActiveFrame", key="value") - - def switch_to_default_content(self): - """Switch the current context to page's default content.""" - return self.switch_to_frame() - - def switch_to_parent_frame(self): - """ - Switch to the Parent Frame - """ - self._send_message("switchToParentFrame") - - def switch_to_frame(self, frame=None, focus=True): - """Switch the current context to the specified frame. Subsequent - commands will operate in the context of the specified frame, - if applicable. - - :param frame: A reference to the frame to switch to. This can - be an ``HTMLElement``, an integer index, string name, or an - ID attribute. If you call ``switch_to_frame`` without an - argument, it will switch to the top-level frame. - - :param focus: A boolean value which determins whether to focus - the frame that we just switched to. - """ - body = {"focus": focus} - if isinstance(frame, HTMLElement): - body["element"] = frame.id - elif frame is not None: - body["id"] = frame - self._send_message("switchToFrame", body) - - def switch_to_shadow_root(self, host=None): - """Switch the current context to the specified host's Shadow DOM. - Subsequent commands will operate in the context of the specified Shadow - DOM, if applicable. - - :param host: A reference to the host element containing Shadow DOM. - This can be an ``HTMLElement``. If you call - ``switch_to_shadow_root`` without an argument, it will switch to the - parent Shadow DOM or the top-level frame. - """ - body = {} - if isinstance(host, HTMLElement): - body["id"] = host.id - return self._send_message("switchToShadowRoot", body) - - def get_url(self): - """Get a string representing the current URL. - - On Desktop this returns a string representation of the URL of - the current top level browsing context. This is equivalent to - document.location.href. - - When in the context of the chrome, this returns the canonical - URL of the current resource. - - :returns: string representation of URL - """ - return self._send_message("getCurrentUrl", key="value") - - def get_window_type(self): - """Gets the windowtype attribute of the window Marionette is - currently acting on. - - This command only makes sense in a chrome context. You might use this - method to distinguish a browser window from an editor window. - """ - return self._send_message("getWindowType", key="value") - - def navigate(self, url): - """Navigate to given `url`. - - Navigates the current top-level browsing context's content - frame to the given URL and waits for the document to load or - the session's page timeout duration to elapse before returning. - - The command will return with a failure if there is an error - loading the document or the URL is blocked. This can occur if - it fails to reach the host, the URL is malformed, the page is - restricted (about:* pages), or if there is a certificate issue - to name some examples. - - The document is considered successfully loaded when the - `DOMContentLoaded` event on the frame element associated with the - `window` triggers and `document.readState` is "complete". - - In chrome context it will change the current `window`'s location - to the supplied URL and wait until `document.readState` equals - "complete" or the page timeout duration has elapsed. - - :param url: The URL to navigate to. - """ - self._send_message("get", {"url": url}) - - def go_back(self): - """Causes the browser to perform a back navigation.""" - self._send_message("goBack") - - def go_forward(self): - """Causes the browser to perform a forward navigation.""" - self._send_message("goForward") - - def refresh(self): - """Causes the browser to perform to refresh the current page.""" - self._send_message("refresh") - - def _to_json(self, args): - if isinstance(args, list) or isinstance(args, tuple): - wrapped = [] - for arg in args: - wrapped.append(self._to_json(arg)) - elif isinstance(args, dict): - wrapped = {} - for arg in args: - wrapped[arg] = self._to_json(args[arg]) - elif type(args) == HTMLElement: - wrapped = {W3C_WEBELEMENT_KEY: args.id, - WEBELEMENT_KEY: args.id} - elif (isinstance(args, bool) or isinstance(args, basestring) or - isinstance(args, int) or isinstance(args, float) or args is None): - wrapped = args - return wrapped - - def _from_json(self, value): - if isinstance(value, list): - unwrapped = [] - for item in value: - unwrapped.append(self._from_json(item)) - elif isinstance(value, dict): - unwrapped = {} - for key in value: - if key == W3C_WEBELEMENT_KEY: - unwrapped = HTMLElement(self, value[key]) - break - elif key == WEBELEMENT_KEY: - unwrapped = HTMLElement(self, value[key]) - break - else: - unwrapped[key] = self._from_json(value[key]) - else: - unwrapped = value - return unwrapped - - def execute_js_script(self, script, script_args=(), async=True, - new_sandbox=True, script_timeout=None, - inactivity_timeout=None, filename=None, - sandbox='default'): - args = self._to_json(script_args) - body = {"script": script, - "args": args, - "async": async, - "newSandbox": new_sandbox, - "scriptTimeout": script_timeout, - "inactivityTimeout": inactivity_timeout, - "filename": filename, - "line": None} - rv = self._send_message("executeJSScript", body, key="value") - return self._from_json(rv) - - def execute_script(self, script, script_args=(), new_sandbox=True, - sandbox="default", script_timeout=None): - """Executes a synchronous JavaScript script, and returns the - result (or None if the script does return a value). - - The script is executed in the context set by the most recent - set_context() call, or to the CONTEXT_CONTENT context if set_context() - has not been called. - - :param script: A string containing the JavaScript to execute. - :param script_args: An interable of arguments to pass to the script. - :param sandbox: A tag referring to the sandbox you wish to use; - if you specify a new tag, a new sandbox will be created. - If you use the special tag `system`, the sandbox will - be created using the system principal which has elevated - privileges. - :param new_sandbox: If False, preserve global variables from - the last execute_*script call. This is True by default, in which - case no globals are preserved. - - Simple usage example: - - :: - - result = marionette.execute_script("return 1;") - assert result == 1 - - You can use the `script_args` parameter to pass arguments to the - script: - - :: - - result = marionette.execute_script("return arguments[0] + arguments[1];", - script_args=(2, 3,)) - assert result == 5 - some_element = marionette.find_element(By.ID, "someElement") - sid = marionette.execute_script("return arguments[0].id;", script_args=(some_element,)) - assert some_element.get_attribute("id") == sid - - Scripts wishing to access non-standard properties of the window - object must use window.wrappedJSObject: - - :: - - result = marionette.execute_script(''' - window.wrappedJSObject.test1 = "foo"; - window.wrappedJSObject.test2 = "bar"; - return window.wrappedJSObject.test1 + window.wrappedJSObject.test2; - ''') - assert result == "foobar" - - Global variables set by individual scripts do not persist between - script calls by default. If you wish to persist data between - script calls, you can set new_sandbox to False on your next call, - and add any new variables to a new 'global' object like this: - - :: - - marionette.execute_script("global.test1 = 'foo';") - result = self.marionette.execute_script("return global.test1;", new_sandbox=False) - assert result == "foo" - - """ - args = self._to_json(script_args) - stack = traceback.extract_stack() - frame = stack[-2:-1][0] # grab the second-to-last frame - body = {"script": script, - "args": args, - "newSandbox": new_sandbox, - "sandbox": sandbox, - "scriptTimeout": script_timeout, - "line": int(frame[1]), - "filename": os.path.basename(frame[0])} - rv = self._send_message("executeScript", body, key="value") - return self._from_json(rv) - - def execute_async_script(self, script, script_args=(), new_sandbox=True, - sandbox="default", script_timeout=None, - debug_script=False): - """Executes an asynchronous JavaScript script, and returns the - result (or None if the script does return a value). - - The script is executed in the context set by the most recent - set_context() call, or to the CONTEXT_CONTENT context if - set_context() has not been called. - - :param script: A string containing the JavaScript to execute. - :param script_args: An interable of arguments to pass to the script. - :param sandbox: A tag referring to the sandbox you wish to use; if - you specify a new tag, a new sandbox will be created. If you - use the special tag `system`, the sandbox will be created - using the system principal which has elevated privileges. - :param new_sandbox: If False, preserve global variables from - the last execute_*script call. This is True by default, - in which case no globals are preserved. - :param debug_script: Capture javascript exceptions when in - `CONTEXT_CHROME` context. - - Usage example: - - :: - - marionette.timeout.script = 10 - result = self.marionette.execute_async_script(''' - // this script waits 5 seconds, and then returns the number 1 - setTimeout(function() { - marionetteScriptFinished(1); - }, 5000); - ''') - assert result == 1 - """ - args = self._to_json(script_args) - stack = traceback.extract_stack() - frame = stack[-2:-1][0] # grab the second-to-last frame - body = {"script": script, - "args": args, - "newSandbox": new_sandbox, - "sandbox": sandbox, - "scriptTimeout": script_timeout, - "line": int(frame[1]), - "filename": os.path.basename(frame[0]), - "debug_script": debug_script} - rv = self._send_message("executeAsyncScript", body, key="value") - return self._from_json(rv) - - def find_element(self, method, target, id=None): - """Returns an HTMLElement instances that matches the specified - method and target in the current context. - - An HTMLElement instance may be used to call other methods on the - element, such as click(). If no element is immediately found, the - attempt to locate an element will be repeated for up to the amount of - time set by ``timeout.implicit``. If multiple elements match the given - criteria, only the first is returned. If no element matches, a - NoSuchElementException will be raised. - - :param method: The method to use to locate the element; one of: - "id", "name", "class name", "tag name", "css selector", - "link text", "partial link text", "xpath", "anon" and "anon - attribute". Note that the "name", "link text" and "partial - link test" methods are not supported in the chrome DOM. - :param target: The target of the search. For example, if method = - "tag", target might equal "div". If method = "id", target would - be an element id. - :param id: If specified, search for elements only inside the element - with the specified id. - """ - body = {"value": target, "using": method} - if id: - body["element"] = id - return self._send_message("findElement", body, key="value") - - def find_elements(self, method, target, id=None): - """Returns a list of all HTMLElement instances that match the - specified method and target in the current context. - - An HTMLElement instance may be used to call other methods on the - element, such as click(). If no element is immediately found, - the attempt to locate an element will be repeated for up to the - amount of time set by ``timeout.implicit``. - - :param method: The method to use to locate the elements; one - of: "id", "name", "class name", "tag name", "css selector", - "link text", "partial link text", "xpath", "anon" and "anon - attribute". Note that the "name", "link text" and "partial link - test" methods are not supported in the chrome DOM. - :param target: The target of the search. For example, if method = - "tag", target might equal "div". If method = "id", target would be - an element id. - :param id: If specified, search for elements only inside the element - with the specified id. - """ - body = {"value": target, "using": method} - if id: - body["element"] = id - return self._send_message( - "findElements", body, key="value" if self.protocol == 1 else None) - - def get_active_element(self): - el_or_ref = self._send_message("getActiveElement", key="value") - if self.protocol < 3: - return HTMLElement(self, el_or_ref) - return el_or_ref - - def log(self, msg, level="INFO"): - """Stores a timestamped log message in the Marionette server - for later retrieval. - - :param msg: String with message to log. - :param level: String with log level (e.g. "INFO" or "DEBUG"). - Defaults to "INFO". - """ - body = {"value": msg, "level": level} - self._send_message("log", body) - - def get_logs(self): - """Returns the list of logged messages. - - Each log message is an array with three string elements: the level, - the message, and a date. - - Usage example:: - - marionette.log("I AM INFO") - marionette.log("I AM ERROR", "ERROR") - logs = marionette.get_logs() - assert logs[0][1] == "I AM INFO" - assert logs[1][1] == "I AM ERROR" - """ - return self._send_message("getLogs", - key="value" if self.protocol == 1 else None) - - def import_script(self, js_file): - """Imports a script into the scope of the execute_script and - execute_async_script calls. - - This is particularly useful if you wish to import your own - libraries. - - :param js_file: Filename of JavaScript file to import. - - For example, Say you have a script, importfunc.js, that contains: - - :: - - let testFunc = function() { return "i'm a test function!";}; - - Assuming this file is in the same directory as the test, you - could do something like: - - :: - - js = os.path.abspath(os.path.join(__file__, os.path.pardir, "importfunc.js")) - marionette.import_script(js) - assert "i'm a test function!" == self.marionette.execute_script("return testFunc();") - """ - js = "" - with open(js_file, "r") as f: - js = f.read() - body = {"script": js} - self._send_message("importScript", body) - - def clear_imported_scripts(self): - """Clears all imported scripts in this context, ie: calling - clear_imported_scripts in chrome context will clear only scripts - you imported in chrome, and will leave the scripts you imported - in content context. - """ - self._send_message("clearImportedScripts") - - def add_cookie(self, cookie): - """Adds a cookie to your current session. - - :param cookie: A dictionary object, with required keys - "name" - and "value"; optional keys - "path", "domain", "secure", - "expiry". - - Usage example: - - :: - - driver.add_cookie({"name": "foo", "value": "bar"}) - driver.add_cookie({"name": "foo", "value": "bar", "path": "/"}) - driver.add_cookie({"name": "foo", "value": "bar", "path": "/", - "secure": True}) - """ - body = {"cookie": cookie} - self._send_message("addCookie", body) - - def delete_all_cookies(self): - """Delete all cookies in the scope of the current session. - - Usage example: - - :: - - driver.delete_all_cookies() - """ - self._send_message("deleteAllCookies") - - def delete_cookie(self, name): - """Delete a cookie by its name. - - :param name: Name of cookie to delete. - - Usage example: - - :: - - driver.delete_cookie("foo") - """ - self._send_message("deleteCookie", {"name": name}) - - def get_cookie(self, name): - """Get a single cookie by name. Returns the cookie if found, - None if not. - - :param name: Name of cookie to get. - """ - cookies = self.get_cookies() - for cookie in cookies: - if cookie["name"] == name: - return cookie - return None - - def get_cookies(self): - """Get all the cookies for the current domain. - - This is the equivalent of calling `document.cookie` and - parsing the result. - - :returns: A list of cookies for the current domain. - """ - return self._send_message("getCookies", key="value" if self.protocol == 1 else None) - - def screenshot(self, element=None, highlights=None, format="base64", - full=True, scroll=True): - """Takes a screenshot of a web element or the current frame. - - The screen capture is returned as a lossless PNG image encoded - as a base 64 string by default. If the `element` argument is defined the - capture area will be limited to the bounding box of that - element. Otherwise, the capture area will be the bounding box - of the current frame. - - :param element: The element to take a screenshot of. If None, will - take a screenshot of the current frame. - - :param highlights: A list of HTMLElement objects to draw a red - box around in the returned screenshot. - - :param format: if "base64" (the default), returns the screenshot - as a base64-string. If "binary", the data is decoded and - returned as raw binary. If "hash", the data is hashed using - the SHA-256 algorithm and the result is returned as a hex digest. - - :param full: If True (the default), the capture area will be the - complete frame. Else only the viewport is captured. Only applies - when `element` is None. - - :param scroll: When `element` is provided, scroll to it before - taking the screenshot (default). Otherwise, avoid scrolling - `element` into view. - """ - - if element: - element = element.id - lights = None - if highlights: - lights = [highlight.id for highlight in highlights] - - body = {"id": element, - "highlights": lights, - "full": full, - "hash": False, - "scroll": scroll} - if format == "hash": - body["hash"] = True - data = self._send_message("takeScreenshot", body, key="value") - - if format == "base64" or format == "hash": - return data - elif format == "binary": - return base64.b64decode(data.encode("ascii")) - else: - raise ValueError("format parameter must be either 'base64'" - " or 'binary', not {0}".format(repr(format))) - - @property - def orientation(self): - """Get the current browser orientation. - - Will return one of the valid primary orientation values - portrait-primary, landscape-primary, portrait-secondary, or - landscape-secondary. - """ - return self._send_message("getScreenOrientation", key="value") - - def set_orientation(self, orientation): - """Set the current browser orientation. - - The supplied orientation should be given as one of the valid - orientation values. If the orientation is unknown, an error - will be raised. - - Valid orientations are "portrait" and "landscape", which fall - back to "portrait-primary" and "landscape-primary" - respectively, and "portrait-secondary" as well as - "landscape-secondary". - - :param orientation: The orientation to lock the screen in. - """ - body = {"orientation": orientation} - self._send_message("setScreenOrientation", body) - - @property - def window_size(self): - """Get the current browser window size. - - Will return the current browser window size in pixels. Refers to - window outerWidth and outerHeight values, which include scroll bars, - title bars, etc. - - :returns: dictionary representation of current window width and height - """ - return self._send_message("getWindowSize", - key="value" if self.protocol == 1 else None) - - def set_window_size(self, width, height): - """Resize the browser window currently in focus. - - The supplied width and height values refer to the window outerWidth - and outerHeight values, which include scroll bars, title bars, etc. - - An error will be returned if the requested window size would result - in the window being in the maximised state. - - :param width: The width to resize the window to. - :param height: The height to resize the window to. - - """ - body = {"width": width, "height": height} - return self._send_message("setWindowSize", body) - - def maximize_window(self): - """ Resize the browser window currently receiving commands. The action - should be equivalent to the user pressing the the maximize button - """ - return self._send_message("maximizeWindow") diff --git a/testing/marionette/client/marionette_driver/selection.py b/testing/marionette/client/marionette_driver/selection.py deleted file mode 100644 index 30e66deaa..000000000 --- a/testing/marionette/client/marionette_driver/selection.py +++ /dev/null @@ -1,227 +0,0 @@ -# -*- coding: utf-8 -*- -# 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/. - - -class SelectionManager(object): - '''Interface for manipulating the selection and carets of the element. - - We call the blinking cursor (nsCaret) as cursor, and call AccessibleCaret as - caret for short. - - Simple usage example: - - :: - - element = marionette.find_element(By.ID, 'input') - sel = SelectionManager(element) - sel.move_caret_to_front() - - ''' - - def __init__(self, element): - self.element = element - - def _input_or_textarea(self): - '''Return True if element is either <input> or <textarea>.''' - return self.element.tag_name in ('input', 'textarea') - - def js_selection_cmd(self): - '''Return a command snippet to get selection object. - - If the element is <input> or <textarea>, return the selection object - associated with it. Otherwise, return the current selection object. - - Note: "element" must be provided as the first argument to - execute_script(). - - ''' - if self._input_or_textarea(): - # We must unwrap sel so that DOMRect could be returned to Python - # side. - return '''var sel = arguments[0].editor.selection;''' - else: - return '''var sel = window.getSelection();''' - - def move_cursor_by_offset(self, offset, backward=False): - '''Move cursor in the element by character offset. - - :param offset: Move the cursor to the direction by offset characters. - :param backward: Optional, True to move backward; Default to False to - move forward. - - ''' - cmd = self.js_selection_cmd() + ''' - for (let i = 0; i < {0}; ++i) {{ - sel.modify("move", "{1}", "character"); - }} - '''.format(offset, 'backward' if backward else 'forward') - - self.element.marionette.execute_script( - cmd, script_args=[self.element], sandbox='system') - - def move_cursor_to_front(self): - '''Move cursor in the element to the front of the content.''' - if self._input_or_textarea(): - cmd = '''arguments[0].setSelectionRange(0, 0);''' - else: - cmd = '''var sel = window.getSelection(); - sel.collapse(arguments[0].firstChild, 0);''' - - self.element.marionette.execute_script(cmd, script_args=[self.element]) - - def move_cursor_to_end(self): - '''Move cursor in the element to the end of the content.''' - if self._input_or_textarea(): - cmd = '''var len = arguments[0].value.length; - arguments[0].setSelectionRange(len, len);''' - else: - cmd = '''var sel = window.getSelection(); - sel.collapse(arguments[0].lastChild, arguments[0].lastChild.length);''' - - self.element.marionette.execute_script(cmd, script_args=[self.element]) - - def selection_rect_list(self, idx): - '''Return the selection's DOMRectList object for the range at given idx. - - If the element is either <input> or <textarea>, return the DOMRectList of - the range at given idx of the selection within the element. Otherwise, - return the DOMRectList of the of the range at given idx of current selection. - - ''' - cmd = self.js_selection_cmd() +\ - '''return sel.getRangeAt({}).getClientRects();'''.format(idx) - return self.element.marionette.execute_script(cmd, - script_args=[self.element], - sandbox='system') - - def range_count(self): - '''Get selection's range count''' - cmd = self.js_selection_cmd() +\ - '''return sel.rangeCount;''' - return self.element.marionette.execute_script(cmd, - script_args=[self.element], - sandbox='system') - - def _selection_location_helper(self, location_type): - '''Return the start and end location of the selection in the element. - - Return a tuple containing two pairs of (x, y) coordinates of the start - and end locations in the element. The coordinates are relative to the - top left-hand corner of the element. Both ltr and rtl directions are - considered. - - ''' - range_count = self.range_count() - first_rect_list = self.selection_rect_list(0) - last_rect_list = self.selection_rect_list(range_count - 1) - last_list_length = last_rect_list['length'] - first_rect, last_rect = first_rect_list['0'], last_rect_list[str(last_list_length - 1)] - origin_x, origin_y = self.element.rect['x'], self.element.rect['y'] - - if self.element.get_property('dir') == 'rtl': # such as Arabic - start_pos, end_pos = 'right', 'left' - else: - start_pos, end_pos = 'left', 'right' - - # Calculate y offset according to different needs. - if location_type == 'center': - start_y_offset = first_rect['height'] / 2.0 - end_y_offset = last_rect['height'] / 2.0 - elif location_type == 'caret': - # Selection carets' tip are below the bottom of the two ends of the - # selection. Add 5px to y should be sufficient to locate them. - caret_tip_y_offset = 5 - start_y_offset = first_rect['height'] + caret_tip_y_offset - end_y_offset = last_rect['height'] + caret_tip_y_offset - else: - start_y_offset = end_y_offset = 0 - - caret1_x = first_rect[start_pos] - origin_x - caret1_y = first_rect['top'] + start_y_offset - origin_y - caret2_x = last_rect[end_pos] - origin_x - caret2_y = last_rect['top'] + end_y_offset - origin_y - - return ((caret1_x, caret1_y), (caret2_x, caret2_y)) - - def selection_location(self): - '''Return the start and end location of the selection in the element. - - Return a tuple containing two pairs of (x, y) coordinates of the start - and end of the selection. The coordinates are relative to the top - left-hand corner of the element. Both ltr and rtl direction are - considered. - - ''' - return self._selection_location_helper('center') - - def carets_location(self): - '''Return a pair of the two carets' location. - - Return a tuple containing two pairs of (x, y) coordinates of the two - carets' tip. The coordinates are relative to the top left-hand corner of - the element. Both ltr and rtl direction are considered. - - ''' - return self._selection_location_helper('caret') - - def cursor_location(self): - '''Return the blanking cursor's center location within the element. - - Return (x, y) coordinates of the cursor's center relative to the top - left-hand corner of the element. - - ''' - return self._selection_location_helper('center')[0] - - def first_caret_location(self): - '''Return the first caret's location. - - Return (x, y) coordinates of the first caret's tip relative to the top - left-hand corner of the element. - - ''' - return self.carets_location()[0] - - def second_caret_location(self): - '''Return the second caret's location. - - Return (x, y) coordinates of the second caret's tip relative to the top - left-hand corner of the element. - - ''' - return self.carets_location()[1] - - def select_all(self): - '''Select all the content in the element.''' - if self._input_or_textarea(): - cmd = '''var len = arguments[0].value.length; - arguments[0].focus(); - arguments[0].setSelectionRange(0, len);''' - else: - cmd = '''var range = document.createRange(); - range.setStart(arguments[0].firstChild, 0); - range.setEnd(arguments[0].lastChild, arguments[0].lastChild.length); - var sel = window.getSelection(); - sel.removeAllRanges(); - sel.addRange(range);''' - - self.element.marionette.execute_script(cmd, script_args=[self.element]) - - @property - def content(self): - '''Return all the content of the element.''' - if self._input_or_textarea(): - return self.element.get_property('value') - else: - return self.element.text - - @property - def selected_content(self): - '''Return the selected portion of the content in the element.''' - cmd = self.js_selection_cmd() +\ - '''return sel.toString();''' - return self.element.marionette.execute_script(cmd, - script_args=[self.element], - sandbox='system') diff --git a/testing/marionette/client/marionette_driver/timeout.py b/testing/marionette/client/marionette_driver/timeout.py deleted file mode 100644 index e2fa94f4f..000000000 --- a/testing/marionette/client/marionette_driver/timeout.py +++ /dev/null @@ -1,98 +0,0 @@ -# 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/. - - -import errors - - -DEFAULT_SCRIPT_TIMEOUT = 30 -DEFAULT_PAGE_LOAD_TIMEOUT = 300 -DEFAULT_IMPLICIT_WAIT_TIMEOUT = 0 - - -class Timeouts(object): - """Manage timeout settings in the Marionette session. - - Usage:: - - marionette = Marionette(...) - marionette.start_session() - marionette.timeout.page_load = 10 - marionette.timeout.page_load - # => 10 - - """ - - def __init__(self, marionette): - self._marionette = marionette - - def _set(self, name, sec): - ms = sec * 1000 - try: - self._marionette._send_message("setTimeouts", {name: ms}) - except errors.UnknownCommandException: - # remove when 55 is stable - self._marionette._send_message("timeouts", {"type": name, "ms": ms}) - - def _get(self, name): - ms = self._marionette._send_message("getTimeouts", key=name) - return ms / 1000 - - @property - def script(self): - """Get the session's script timeout. This specifies the time - to wait for injected scripts to finished before interrupting - them. It is by default 30 seconds. - - """ - return self._get("script") - - @script.setter - def script(self, sec): - """Set the session's script timeout. This specifies the time - to wait for injected scripts to finish before interrupting them. - - """ - self._set("script", sec) - - @property - def page_load(self): - """Get the session's page load timeout. This specifies the time - to wait for the page loading to complete. It is by default 5 - minutes (or 300 seconds). - - """ - return self._get("page load") - - @page_load.setter - def page_load(self, sec): - """Set the session's page load timeout. This specifies the time - to wait for the page loading to complete. - - """ - self._set("page load", sec) - - @property - def implicit(self): - """Get the session's implicit wait timeout. This specifies the - time to wait for the implicit element location strategy when - retrieving elements. It is by default disabled (0 seconds). - - """ - return self._get("implicit") - - @implicit.setter - def implicit(self, sec): - """Set the session's implicit wait timeout. This specifies the - time to wait for the implicit element location strategy when - retrieving elements. - - """ - self._set("implicit", sec) - - def reset(self): - """Resets timeouts to their default values.""" - self.script = DEFAULT_SCRIPT_TIMEOUT - self.page_load = DEFAULT_PAGE_LOAD_TIMEOUT - self.implicit = DEFAULT_IMPLICIT_WAIT_TIMEOUT diff --git a/testing/marionette/client/marionette_driver/transport.py b/testing/marionette/client/marionette_driver/transport.py deleted file mode 100644 index 82828fdef..000000000 --- a/testing/marionette/client/marionette_driver/transport.py +++ /dev/null @@ -1,300 +0,0 @@ -# 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/. - -import json -import socket -import time - - -class SocketTimeout(object): - def __init__(self, socket, timeout): - self.sock = socket - self.timeout = timeout - self.old_timeout = None - - def __enter__(self): - self.old_timeout = self.sock.gettimeout() - self.sock.settimeout(self.timeout) - - def __exit__(self, *args, **kwargs): - self.sock.settimeout(self.old_timeout) - - -class Message(object): - def __init__(self, msgid): - self.id = msgid - - def __eq__(self, other): - return self.id == other.id - - def __ne__(self, other): - return not self.__eq__(other) - - -class Command(Message): - TYPE = 0 - - def __init__(self, msgid, name, params): - Message.__init__(self, msgid) - self.name = name - self.params = params - - def __str__(self): - return "<Command id={0}, name={1}, params={2}>".format(self.id, self.name, self.params) - - def to_msg(self): - msg = [Command.TYPE, self.id, self.name, self.params] - return json.dumps(msg) - - @staticmethod - def from_msg(payload): - data = json.loads(payload) - assert data[0] == Command.TYPE - cmd = Command(data[1], data[2], data[3]) - return cmd - - -class Response(Message): - TYPE = 1 - - def __init__(self, msgid, error, result): - Message.__init__(self, msgid) - self.error = error - self.result = result - - def __str__(self): - return "<Response id={0}, error={1}, result={2}>".format(self.id, self.error, self.result) - - def to_msg(self): - msg = [Response.TYPE, self.id, self.error, self.result] - return json.dumps(msg) - - @staticmethod - def from_msg(payload): - data = json.loads(payload) - assert data[0] == Response.TYPE - return Response(data[1], data[2], data[3]) - - -class Proto2Command(Command): - """Compatibility shim that marshals messages from a protocol level - 2 and below remote into ``Command`` objects. - """ - - def __init__(self, name, params): - Command.__init__(self, None, name, params) - - -class Proto2Response(Response): - """Compatibility shim that marshals messages from a protocol level - 2 and below remote into ``Response`` objects. - """ - - def __init__(self, error, result): - Response.__init__(self, None, error, result) - - @staticmethod - def from_data(data): - err, res = None, None - if "error" in data: - err = data - else: - res = data - return Proto2Response(err, res) - - -class TcpTransport(object): - """Socket client that communciates with Marionette via TCP. - - It speaks the protocol of the remote debugger in Gecko, in which - messages are always preceded by the message length and a colon, e.g.: - - 7:MESSAGE - - On top of this protocol it uses a Marionette message format, that - depending on the protocol level offered by the remote server, varies. - Supported protocol levels are 1 and above. - """ - max_packet_length = 4096 - - def __init__(self, addr, port, socket_timeout=60.0): - """If `socket_timeout` is `0` or `0.0`, non-blocking socket mode - will be used. Setting it to `1` or `None` disables timeouts on - socket operations altogether. - """ - self.addr = addr - self.port = port - self._socket_timeout = socket_timeout - - self.protocol = 1 - self.application_type = None - self.last_id = 0 - self.expected_response = None - self.sock = None - - @property - def socket_timeout(self): - return self._socket_timeout - - @socket_timeout.setter - def socket_timeout(self, value): - if self.sock: - self.sock.settimeout(value) - self._socket_timeout = value - - def _unmarshal(self, packet): - msg = None - - # protocol 3 and above - if self.protocol >= 3: - typ = int(packet[1]) - if typ == Command.TYPE: - msg = Command.from_msg(packet) - elif typ == Response.TYPE: - msg = Response.from_msg(packet) - - # protocol 2 and below - else: - data = json.loads(packet) - - msg = Proto2Response.from_data(data) - - return msg - - def receive(self, unmarshal=True): - """Wait for the next complete response from the remote. - - :param unmarshal: Default is to deserialise the packet and - return a ``Message`` type. Setting this to false will return - the raw packet. - """ - now = time.time() - data = "" - bytes_to_recv = 10 - - while self.socket_timeout is None or (time.time() - now < self.socket_timeout): - try: - chunk = self.sock.recv(bytes_to_recv) - data += chunk - except socket.timeout: - pass - else: - if not chunk: - raise socket.error("No data received over socket") - - sep = data.find(":") - if sep > -1: - length = data[0:sep] - remaining = data[sep + 1:] - - if len(remaining) == int(length): - if unmarshal: - msg = self._unmarshal(remaining) - self.last_id = msg.id - - if self.protocol >= 3: - self.last_id = msg.id - - # keep reading incoming responses until - # we receive the user's expected response - if isinstance(msg, Response) and msg != self.expected_response: - return self.receive(unmarshal) - - return msg - - else: - return remaining - - bytes_to_recv = int(length) - len(remaining) - - raise socket.timeout("Connection timed out after {}s".format(self.socket_timeout)) - - def connect(self): - """Connect to the server and process the hello message we expect - to receive in response. - - Returns a tuple of the protocol level and the application type. - """ - try: - self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.sock.settimeout(self.socket_timeout) - - self.sock.connect((self.addr, self.port)) - except: - # Unset self.sock so that the next attempt to send will cause - # another connection attempt. - self.sock = None - raise - - with SocketTimeout(self.sock, 2.0): - # first packet is always a JSON Object - # which we can use to tell which protocol level we are at - raw = self.receive(unmarshal=False) - hello = json.loads(raw) - self.protocol = hello.get("marionetteProtocol", 1) - self.application_type = hello.get("applicationType") - - return (self.protocol, self.application_type) - - def send(self, obj): - """Send message to the remote server. Allowed input is a - ``Message`` instance or a JSON serialisable object. - """ - if not self.sock: - self.connect() - - if isinstance(obj, Message): - data = obj.to_msg() - if isinstance(obj, Command): - self.expected_response = obj - else: - data = json.dumps(obj) - payload = "{0}:{1}".format(len(data), data) - - totalsent = 0 - while totalsent < len(payload): - sent = self.sock.send(payload[totalsent:]) - if sent == 0: - raise IOError("Socket error after sending {0} of {1} bytes" - .format(totalsent, len(payload))) - else: - totalsent += sent - - def respond(self, obj): - """Send a response to a command. This can be an arbitrary JSON - serialisable object or an ``Exception``. - """ - res, err = None, None - if isinstance(obj, Exception): - err = obj - else: - res = obj - msg = Response(self.last_id, err, res) - self.send(msg) - return self.receive() - - def request(self, name, params): - """Sends a message to the remote server and waits for a response - to come back. - """ - self.last_id = self.last_id + 1 - cmd = Command(self.last_id, name, params) - self.send(cmd) - return self.receive() - - def close(self): - """Close the socket.""" - if self.sock: - try: - self.sock.shutdown(socket.SHUT_RDWR) - except IOError as exc: - # Errno 57 is "socket not connected", which we don't care about here. - if exc.errno != 57: - raise - - self.sock.close() - self.sock = None - - def __del__(self): - self.close() diff --git a/testing/marionette/client/marionette_driver/wait.py b/testing/marionette/client/marionette_driver/wait.py deleted file mode 100644 index c89465ce4..000000000 --- a/testing/marionette/client/marionette_driver/wait.py +++ /dev/null @@ -1,167 +0,0 @@ -# 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/. - -import collections -import errors -import sys -import time - -DEFAULT_TIMEOUT = 5 -DEFAULT_INTERVAL = 0.1 - - -class Wait(object): - - """An explicit conditional utility class for waiting until a condition - evaluates to true or not null. - - This will repeatedly evaluate a condition in anticipation for a - truthy return value, or its timeout to expire, or its waiting - predicate to become true. - - A `Wait` instance defines the maximum amount of time to wait for a - condition, as well as the frequency with which to check the - condition. Furthermore, the user may configure the wait to ignore - specific types of exceptions whilst waiting, such as - `errors.NoSuchElementException` when searching for an element on - the page. - - """ - - def __init__(self, marionette, timeout=DEFAULT_TIMEOUT, - interval=DEFAULT_INTERVAL, ignored_exceptions=None, - clock=None): - """Configure the Wait instance to have a custom timeout, interval, and - list of ignored exceptions. Optionally a different time - implementation than the one provided by the standard library - (time) can also be provided. - - Sample usage:: - - # Wait 30 seconds for window to open, checking for its presence once - # every 5 seconds. - wait = Wait(marionette, timeout=30, interval=5, - ignored_exceptions=errors.NoSuchWindowException) - window = wait.until(lambda m: m.switch_to_window(42)) - - :param marionette: The input value to be provided to - conditions, usually a Marionette instance. - - :param timeout: How long to wait for the evaluated condition - to become true. The default timeout is - `wait.DEFAULT_TIMEOUT`. - - :param interval: How often the condition should be evaluated. - In reality the interval may be greater as the cost of - evaluating the condition function. If that is not the case the - interval for the next condition function call is shortend to keep - the original interval sequence as best as possible. - The default polling interval is `wait.DEFAULT_INTERVAL`. - - :param ignored_exceptions: Ignore specific types of exceptions - whilst waiting for the condition. Any exceptions not - whitelisted will be allowed to propagate, terminating the - wait. - - :param clock: Allows overriding the use of the runtime's - default time library. See `wait.SystemClock` for - implementation details. - - """ - - self.marionette = marionette - self.timeout = timeout - self.clock = clock or SystemClock() - self.end = self.clock.now + self.timeout - self.interval = interval - - exceptions = [] - if ignored_exceptions is not None: - if isinstance(ignored_exceptions, collections.Iterable): - exceptions.extend(iter(ignored_exceptions)) - else: - exceptions.append(ignored_exceptions) - self.exceptions = tuple(set(exceptions)) - - def until(self, condition, is_true=None, message=""): - """Repeatedly runs condition until its return value evaluates to true, - or its timeout expires or the predicate evaluates to true. - - This will poll at the given interval until the given timeout - is reached, or the predicate or conditions returns true. A - condition that returns null or does not evaluate to true will - fully elapse its timeout before raising an - `errors.TimeoutException`. - - If an exception is raised in the condition function and it's - not ignored, this function will raise immediately. If the - exception is ignored, it will continue polling for the - condition until it returns successfully or a - `TimeoutException` is raised. - - :param condition: A callable function whose return value will - be returned by this function if it evaluates to true. - - :param is_true: An optional predicate that will terminate and - return when it evaluates to False. It should be a - function that will be passed clock and an end time. The - default predicate will terminate a wait when the clock - elapses the timeout. - - :param message: An optional message to include in the - exception's message if this function times out. - - """ - - rv = None - last_exc = None - until = is_true or until_pred - start = self.clock.now - - while not until(self.clock, self.end): - try: - next = self.clock.now + self.interval - rv = condition(self.marionette) - except (KeyboardInterrupt, SystemExit): - raise - except self.exceptions: - last_exc = sys.exc_info() - - # Re-adjust the interval depending on how long the callback - # took to evaluate the condition - interval_new = max(next - self.clock.now, 0) - - if not rv: - self.clock.sleep(interval_new) - continue - - if rv is not None: - return rv - - self.clock.sleep(interval_new) - - if message: - message = " with message: {}".format(message) - - raise errors.TimeoutException( - "Timed out after {0} seconds{1}".format(round((self.clock.now - start), 1), - message if message else ""), - cause=last_exc) - - -def until_pred(clock, end): - return clock.now >= end - - -class SystemClock(object): - - def __init__(self): - self._time = time - - def sleep(self, duration): - self._time.sleep(duration) - - @property - def now(self): - return self._time.time() diff --git a/testing/marionette/client/requirements.txt b/testing/marionette/client/requirements.txt deleted file mode 100644 index a06a719ac..000000000 --- a/testing/marionette/client/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -mozrunner >= 6.13 -mozversion >= 1.1 diff --git a/testing/marionette/client/setup.py b/testing/marionette/client/setup.py deleted file mode 100644 index b73b17d08..000000000 --- a/testing/marionette/client/setup.py +++ /dev/null @@ -1,49 +0,0 @@ -# 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/. - -import os -import re -from setuptools import setup, find_packages - -THIS_DIR = os.path.dirname(os.path.realpath(__name__)) - - -def read(*parts): - with open(os.path.join(THIS_DIR, *parts)) as f: - return f.read() - - -def get_version(): - return re.findall("__version__ = '([\d\.]+)'", - read('marionette_driver', '__init__.py'), re.M)[0] - - -setup(name='marionette_driver', - version=get_version(), - description="Marionette Driver", - long_description='See http://marionette-client.readthedocs.org/en/latest/', - # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers - classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)', - 'Operating System :: MacOS :: MacOS X', - 'Operating System :: Microsoft :: Windows', - 'Operating System :: POSIX', - 'Topic :: Software Development :: Quality Assurance', - 'Topic :: Software Development :: Testing', - 'Topic :: Utilities', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2.7', - ], - keywords='mozilla', - author='Auto-tools', - author_email='tools-marionette@lists.mozilla.org', - url='https://wiki.mozilla.org/Auto-tools/Projects/Marionette', - license='MPL', - packages=find_packages(), - include_package_data=True, - zip_safe=False, - install_requires=read('requirements.txt').splitlines(), - ) diff --git a/testing/marionette/components/marionette.js b/testing/marionette/components/marionette.js deleted file mode 100644 index 252252823..000000000 --- a/testing/marionette/components/marionette.js +++ /dev/null @@ -1,237 +0,0 @@ -/* 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/. */ - -"use strict"; - -const {Constructor: CC, interfaces: Ci, utils: Cu, classes: Cc} = Components; - -const MARIONETTE_CONTRACTID = "@mozilla.org/marionette;1"; -const MARIONETTE_CID = Components.ID("{786a1369-dca5-4adc-8486-33d23c88010a}"); - -const DEFAULT_PORT = 2828; -const ENABLED_PREF = "marionette.defaultPrefs.enabled"; -const PORT_PREF = "marionette.defaultPrefs.port"; -const FORCELOCAL_PREF = "marionette.force-local"; -const LOG_PREF = "marionette.logging"; - -/** - * Besides starting based on existing prefs in a profile and a commandline flag, - * we also support inheriting prefs out of an env var, and to start marionette - * that way. - * This allows marionette prefs to persist when we do a restart into a - * different profile in order to test things like Firefox refresh. - * The env var itself, if present, is interpreted as a JSON structure, with the - * keys mapping to preference names in the "marionette." branch, and the values - * to the values of those prefs. So something like {"defaultPrefs.enabled": true} - * in the env var would result in the marionette.defaultPrefs.enabled pref being - * set to true, thus triggering marionette being enabled for that startup. - */ -const ENV_PREF_VAR = "MOZ_MARIONETTE_PREF_STATE_ACROSS_RESTARTS"; - -const ServerSocket = CC("@mozilla.org/network/server-socket;1", - "nsIServerSocket", - "initSpecialConnection"); - -Cu.import("resource://gre/modules/Log.jsm"); -Cu.import("resource://gre/modules/Preferences.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -function MarionetteComponent() { - this.loaded_ = false; - this.observerService = Services.obs; - this.logger = this.setupLogger_(this.determineLoggingLevel_()); -} - -MarionetteComponent.prototype = { - classDescription: "Marionette component", - classID: MARIONETTE_CID, - contractID: MARIONETTE_CONTRACTID, - QueryInterface: XPCOMUtils.generateQI([Ci.nsICommandLineHandler, Ci.nsIObserver]), - _xpcom_categories: [ - {category: "command-line-handler", entry: "b-marionette"}, - {category: "profile-after-change", service: true} - ], - enabled: false, - finalUiStartup: false, - server: null, -}; - -MarionetteComponent.prototype.setupLogger_ = function (level) { - let log = Log.repository.getLogger("Marionette"); - log.level = level; - log.addAppender(new Log.DumpAppender()); - return log; -}; - -MarionetteComponent.prototype.determineLoggingLevel_ = function() { - let level = Log.Level.Info; - - // marionette.logging pref can override default - // with an entry from the Log.Level enum - if (Preferences.has(LOG_PREF)) { - let p = Preferences.get(LOG_PREF); - - switch (typeof p) { - // Gecko >= 46 - case "string": - let s = p.toLowerCase(); - s = s.charAt(0).toUpperCase() + s.slice(1); - level = Log.Level[s]; - break; - - // Gecko <= 45 - case "boolean": - if (p) { - level = Log.Level.Trace; - } - break; - } - } - - return level; -}; - -MarionetteComponent.prototype.onSocketAccepted = function( - socket, transport) { - this.logger.info("onSocketAccepted for Marionette dummy socket"); -}; - -MarionetteComponent.prototype.onStopListening = function (socket, status) { - this.logger.info(`onStopListening for Marionette dummy socket, code ${status}`); - socket.close(); -}; - -/** Check cmdLine argument for {@code --marionette}. */ -MarionetteComponent.prototype.handle = function (cmdLine) { - // if the CLI is there then lets do work otherwise nothing to see - if (cmdLine.handleFlag("marionette", false)) { - this.enabled = true; - this.logger.debug("Marionette enabled via command-line flag"); - this.init(); - } -}; - -MarionetteComponent.prototype.observe = function (subj, topic, data) { - switch (topic) { - case "profile-after-change": - this.maybeReadPrefsFromEnvironment(); - // Using final-ui-startup as the xpcom category doesn't seem to work, - // so we wait for that by adding an observer here. - this.observerService.addObserver(this, "final-ui-startup", false); -#ifdef ENABLE_MARIONETTE - this.enabled = Preferences.get(ENABLED_PREF, false); - if (this.enabled) { - this.logger.debug("Marionette enabled via build flag and pref"); - - // We want to suppress the modal dialog that's shown - // when starting up in safe-mode to enable testing. - if (Services.appinfo.inSafeMode) { - this.observerService.addObserver(this, "domwindowopened", false); - } - } -#endif - break; - - case "final-ui-startup": - this.finalUiStartup = true; - this.observerService.removeObserver(this, topic); - this.observerService.addObserver(this, "xpcom-shutdown", false); - this.init(); - break; - - case "domwindowopened": - this.observerService.removeObserver(this, topic); - this.suppressSafeModeDialog_(subj); - break; - - case "xpcom-shutdown": - this.observerService.removeObserver(this, "xpcom-shutdown"); - this.uninit(); - break; - } -}; - -MarionetteComponent.prototype.maybeReadPrefsFromEnvironment = function() { - let env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment); - if (env.exists(ENV_PREF_VAR)) { - let prefStr = env.get(ENV_PREF_VAR); - let prefs; - try { - prefs = JSON.parse(prefStr); - } catch (ex) { - Cu.reportError("Invalid marionette prefs in environment; prefs won't have been applied."); - Cu.reportError(ex); - } - if (prefs) { - for (let prefName of Object.keys(prefs)) { - Preferences.set("marionette." + prefName, prefs[prefName]); - } - } - } -} - -MarionetteComponent.prototype.suppressSafeModeDialog_ = function (win) { - // Wait for the modal dialog to finish loading. - win.addEventListener("load", function onload() { - win.removeEventListener("load", onload); - - if (win.document.getElementById("safeModeDialog")) { - // Accept the dialog to start in safe-mode - win.setTimeout(() => { - win.document.documentElement.getButton("accept").click(); - }); - } - }); -}; - -MarionetteComponent.prototype.init = function() { - if (this.loaded_ || !this.enabled || !this.finalUiStartup) { - return; - } - - this.loaded_ = true; - - let forceLocal = Preferences.get(FORCELOCAL_PREF, - Services.appinfo.name == "B2G" ? false : true); - Preferences.set(FORCELOCAL_PREF, forceLocal); - - if (!forceLocal) { - // See bug 800138. Because the first socket that opens with - // force-local=false fails, we open a dummy socket that will fail. - // keepWhenOffline=true so that it still work when offline (local). - // This allows the following attempt by Marionette to open a socket - // to succeed. - let insaneSacrificialGoat = - new ServerSocket(666, Ci.nsIServerSocket.KeepWhenOffline, 4); - insaneSacrificialGoat.asyncListen(this); - } - - let port = Preferences.get(PORT_PREF, DEFAULT_PORT); - - let s; - try { - Cu.import("chrome://marionette/content/server.js"); - s = new MarionetteServer(port, forceLocal); - s.start(); - this.logger.info(`Listening on port ${s.port}`); - } catch (e) { - this.logger.error(`Error on starting server: ${e}`); - dump(e.toString() + "\n" + e.stack + "\n"); - } finally { - if (s) { - this.server = s; - } - } -}; - -MarionetteComponent.prototype.uninit = function() { - if (!this.loaded_) { - return; - } - this.server.stop(); - this.loaded_ = false; -}; - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([MarionetteComponent]); diff --git a/testing/marionette/components/marionette.manifest b/testing/marionette/components/marionette.manifest deleted file mode 100644 index ebe904960..000000000 --- a/testing/marionette/components/marionette.manifest +++ /dev/null @@ -1,4 +0,0 @@ -component {786a1369-dca5-4adc-8486-33d23c88010a} marionette.js -contract @mozilla.org/marionette;1 {786a1369-dca5-4adc-8486-33d23c88010a} -category command-line-handler b-marionette @mozilla.org/marionette;1 -category profile-after-change Marionette @mozilla.org/marionette;1 diff --git a/testing/marionette/components/moz.build b/testing/marionette/components/moz.build deleted file mode 100644 index 7fc4113a5..000000000 --- a/testing/marionette/components/moz.build +++ /dev/null @@ -1,9 +0,0 @@ -# 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/. - -EXTRA_COMPONENTS += ["marionette.manifest"] -EXTRA_PP_COMPONENTS += ["marionette.js"] - -if CONFIG["ENABLE_MARIONETTE"]: - DEFINES["ENABLE_MARIONETTE"] = 1 diff --git a/testing/marionette/cookies.js b/testing/marionette/cookies.js deleted file mode 100644 index 5bd385aa2..000000000 --- a/testing/marionette/cookies.js +++ /dev/null @@ -1,131 +0,0 @@ -/* 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/. */ - -"use strict"; - -const {classes: Cc, interfaces: Ci, utils: Cu} = Components; - -Cu.import("resource://gre/modules/Log.jsm"); -Cu.import("chrome://marionette/content/error.js"); - -const logger = Log.repository.getLogger("Marionette"); - -this.EXPORTED_SYMBOLS = ["Cookies"]; - -const IPV4_PORT_EXPR = /:\d+$/; - -/** - * Interface for manipulating cookies from content space. - */ -this.Cookies = class { - - /** - * @param {function(): Document} documentFn - * Closure that returns the current content document. - * @param {Proxy(SyncChromeSender)} chromeProxy - * A synchronous proxy interface to chrome space. - */ - constructor(documentFn, chromeProxy) { - this.documentFn_ = documentFn; - this.chrome = chromeProxy; - } - - get document() { - return this.documentFn_(); - } - - [Symbol.iterator]() { - let path = this.document.location.pathname || "/"; - let cs = this.chrome.getVisibleCookies(path, this.document.location.hostname)[0]; - return cs[Symbol.iterator](); - } - - /** - * Add a new cookie to a content document. - * - * @param {string} name - * Cookie key. - * @param {string} value - * Cookie value. - * @param {Object.<string, ?>} opts - * An object with the optional fields {@code domain}, {@code path}, - * {@code secure}, {@code httpOnly}, and {@code expiry}. - * - * @return {Object.<string, ?>} - * A serialisation of the cookie that was added. - * - * @throws UnableToSetCookieError - * If the document's content type isn't HTML, the current document's - * domain is a mismatch to the cookie's provided domain, or there - * otherwise was issues with the input data. - */ - add(name, value, opts={}) { - if (typeof this.document == "undefined" || !this.document.contentType.match(/html/i)) { - throw new UnableToSetCookieError( - "You may only set cookies on HTML documents: " + this.document.contentType); - } - - if (!opts.expiry) { - // date twenty years into future, in seconds - let date = new Date(); - let now = new Date(Date.now()); - date.setYear(now.getFullYear() + 20); - opts.expiry = date.getTime() / 1000; - } - - if (!opts.domain) { - opts.domain = this.document.location.host; - } else if (this.document.location.host.indexOf(opts.domain) < 0) { - throw new InvalidCookieDomainError( - "You may only set cookies for the current domain"); - } - - // remove port from domain, if present. - // unfortunately this catches IPv6 addresses by mistake - // TODO: Bug 814416 - opts.domain = opts.domain.replace(IPV4_PORT_EXPR, ""); - - let cookie = { - domain: opts.domain, - path: opts.path, - name: name, - value: value, - secure: opts.secure, - httpOnly: opts.httpOnly, - session: false, - expiry: opts.expiry, - }; - if (!this.chrome.addCookie(cookie)) { - throw new UnableToSetCookieError(); - } - - return cookie; - } - - /** - * Delete cookie by reference or by name. - * - * @param {(string|Object.<string, ?>)} cookie - * Name of cookie or cookie object. - * - * @throws {UnknownError} - * If unable to delete the cookie. - */ - delete(cookie) { - let name; - if (cookie.hasOwnProperty("name")) { - name = cookie.name; - } else { - name = cookie; - } - - for (let candidate of this) { - if (candidate.name == name) { - if (!this.chrome.deleteCookie(candidate)) { - throw new UnknownError("Unable to delete cookie by name: " + name); - } - } - } - } -}; diff --git a/testing/marionette/dispatcher.js b/testing/marionette/dispatcher.js deleted file mode 100644 index 1f09ef8bf..000000000 --- a/testing/marionette/dispatcher.js +++ /dev/null @@ -1,228 +0,0 @@ -/* 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/. */ - -"use strict"; - -const {interfaces: Ci, utils: Cu} = Components; - -Cu.import("resource://gre/modules/Log.jsm"); -Cu.import("resource://gre/modules/Preferences.jsm"); -Cu.import("resource://gre/modules/Task.jsm"); - -Cu.import("chrome://marionette/content/assert.js"); -Cu.import("chrome://marionette/content/driver.js"); -Cu.import("chrome://marionette/content/error.js"); -Cu.import("chrome://marionette/content/message.js"); - -this.EXPORTED_SYMBOLS = ["Dispatcher"]; - -const PROTOCOL_VERSION = 3; - -const logger = Log.repository.getLogger("Marionette"); - -/** - * Manages a Marionette connection, and dispatches packets received to - * their correct destinations. - * - * @param {number} connId - * Unique identifier of the connection this dispatcher should handle. - * @param {DebuggerTransport} transport - * Debugger transport connection to the client. - * @param {function(): GeckoDriver} driverFactory - * A factory function that produces a GeckoDriver. - */ -this.Dispatcher = function (connId, transport, driverFactory) { - this.connId = connId; - this.conn = transport; - - // transport hooks are Dispatcher#onPacket - // and Dispatcher#onClosed - this.conn.hooks = this; - - // callback for when connection is closed - this.onclose = null; - - // last received/sent message ID - this.lastId = 0; - - this.driver = driverFactory(); - - // lookup of commands sent by server to client by message ID - this.commands_ = new Map(); -}; - -/** - * Debugger transport callback that cleans up - * after a connection is closed. - */ -Dispatcher.prototype.onClosed = function (reason) { - this.driver.deleteSession(); - if (this.onclose) { - this.onclose(this); - } -}; - -/** - * Callback that receives data packets from the client. - * - * If the message is a Response, we look up the command previously issued - * to the client and run its callback, if any. In case of a Command, - * the corresponding is executed. - * - * @param {Array.<number, number, ?, ?>} data - * A four element array where the elements, in sequence, signifies - * message type, message ID, method name or error, and parameters - * or result. - */ -Dispatcher.prototype.onPacket = function (data) { - let msg = Message.fromMsg(data); - msg.origin = MessageOrigin.Client; - this.log_(msg); - - if (msg instanceof Response) { - let cmd = this.commands_.get(msg.id); - this.commands_.delete(msg.id); - cmd.onresponse(msg); - } else if (msg instanceof Command) { - this.lastId = msg.id; - this.execute(msg); - } -}; - -/** - * Executes a WebDriver command and sends back a response when it has - * finished executing. - * - * Commands implemented in GeckoDriver and registered in its - * {@code GeckoDriver.commands} attribute. The return values from - * commands are expected to be Promises. If the resolved value of said - * promise is not an object, the response body will be wrapped in an object - * under a "value" field. - * - * If the command implementation sends the response itself by calling - * {@code resp.send()}, the response is guaranteed to not be sent twice. - * - * Errors thrown in commands are marshaled and sent back, and if they - * are not WebDriverError instances, they are additionally propagated and - * reported to {@code Components.utils.reportError}. - * - * @param {Command} cmd - * The requested command to execute. - */ -Dispatcher.prototype.execute = function (cmd) { - let resp = new Response(cmd.id, this.send.bind(this)); - let sendResponse = () => resp.sendConditionally(resp => !resp.sent); - let sendError = resp.sendError.bind(resp); - - let req = Task.spawn(function*() { - let fn = this.driver.commands[cmd.name]; - if (typeof fn == "undefined") { - throw new UnknownCommandError(cmd.name); - } - - if (cmd.name !== "newSession") { - assert.session(this.driver); - } - - let rv = yield fn.bind(this.driver)(cmd, resp); - - if (typeof rv != "undefined") { - if (typeof rv != "object") { - resp.body = {value: rv}; - } else { - resp.body = rv; - } - } - }.bind(this)); - - req.then(sendResponse, sendError).catch(error.report); -}; - -Dispatcher.prototype.sendError = function (err, cmdId) { - let resp = new Response(cmdId, this.send.bind(this)); - resp.sendError(err); -}; - -// Convenience methods: - -/** - * When a client connects we send across a JSON Object defining the - * protocol level. - * - * This is the only message sent by Marionette that does not follow - * the regular message format. - */ -Dispatcher.prototype.sayHello = function() { - let whatHo = { - applicationType: "gecko", - marionetteProtocol: PROTOCOL_VERSION, - }; - this.sendRaw(whatHo); -}; - - -/** - * Delegates message to client based on the provided {@code cmdId}. - * The message is sent over the debugger transport socket. - * - * The command ID is a unique identifier assigned to the client's request - * that is used to distinguish the asynchronous responses. - * - * Whilst responses to commands are synchronous and must be sent in the - * correct order. - * - * @param {Command,Response} msg - * The command or response to send. - */ -Dispatcher.prototype.send = function (msg) { - msg.origin = MessageOrigin.Server; - if (msg instanceof Command) { - this.commands_.set(msg.id, msg); - this.sendToEmulator(msg); - } else if (msg instanceof Response) { - this.sendToClient(msg); - } -}; - -// Low-level methods: - -/** - * Send given response to the client over the debugger transport socket. - * - * @param {Response} resp - * The response to send back to the client. - */ -Dispatcher.prototype.sendToClient = function (resp) { - this.driver.responseCompleted(); - this.sendMessage(resp); -}; - -/** - * Marshal message to the Marionette message format and send it. - * - * @param {Command,Response} msg - * The message to send. - */ -Dispatcher.prototype.sendMessage = function (msg) { - this.log_(msg); - let payload = msg.toMsg(); - this.sendRaw(payload); -}; - -/** - * Send the given payload over the debugger transport socket to the - * connected client. - * - * @param {Object} payload - * The payload to ship. - */ -Dispatcher.prototype.sendRaw = function (payload) { - this.conn.send(payload); -}; - -Dispatcher.prototype.log_ = function (msg) { - let a = (msg.origin == MessageOrigin.Client ? " -> " : " <- "); - let s = JSON.stringify(msg.toMsg()); - logger.trace(this.connId + a + s); -}; diff --git a/testing/marionette/driver.js b/testing/marionette/driver.js deleted file mode 100644 index 7ef72d3bb..000000000 --- a/testing/marionette/driver.js +++ /dev/null @@ -1,2907 +0,0 @@ -/* 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/. */ - -"use strict"; - -var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; - -var loader = Cc["@mozilla.org/moz/jssubscript-loader;1"] - .getService(Ci.mozIJSSubScriptLoader); - -Cu.import("resource://gre/modules/Log.jsm"); -Cu.import("resource://gre/modules/Preferences.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -XPCOMUtils.defineLazyServiceGetter( - this, "cookieManager", "@mozilla.org/cookiemanager;1", "nsICookieManager2"); - -Cu.import("chrome://marionette/content/accessibility.js"); -Cu.import("chrome://marionette/content/addon.js"); -Cu.import("chrome://marionette/content/assert.js"); -Cu.import("chrome://marionette/content/atom.js"); -Cu.import("chrome://marionette/content/browser.js"); -Cu.import("chrome://marionette/content/capture.js"); -Cu.import("chrome://marionette/content/cert.js"); -Cu.import("chrome://marionette/content/element.js"); -Cu.import("chrome://marionette/content/error.js"); -Cu.import("chrome://marionette/content/evaluate.js"); -Cu.import("chrome://marionette/content/event.js"); -Cu.import("chrome://marionette/content/interaction.js"); -Cu.import("chrome://marionette/content/l10n.js"); -Cu.import("chrome://marionette/content/legacyaction.js"); -Cu.import("chrome://marionette/content/logging.js"); -Cu.import("chrome://marionette/content/modal.js"); -Cu.import("chrome://marionette/content/proxy.js"); -Cu.import("chrome://marionette/content/session.js"); -Cu.import("chrome://marionette/content/simpletest.js"); - -this.EXPORTED_SYMBOLS = ["GeckoDriver", "Context"]; - -var FRAME_SCRIPT = "chrome://marionette/content/listener.js"; -const BROWSER_STARTUP_FINISHED = "browser-delayed-startup-finished"; -const CLICK_TO_START_PREF = "marionette.debugging.clicktostart"; -const CONTENT_LISTENER_PREF = "marionette.contentListener"; - -const SUPPORTED_STRATEGIES = new Set([ - element.Strategy.ClassName, - element.Strategy.Selector, - element.Strategy.ID, - element.Strategy.TagName, - element.Strategy.XPath, - element.Strategy.Anon, - element.Strategy.AnonAttribute, -]); - -const logger = Log.repository.getLogger("Marionette"); -const globalMessageManager = Cc["@mozilla.org/globalmessagemanager;1"] - .getService(Ci.nsIMessageBroadcaster); - -// This is used to prevent newSession from returning before the telephony -// API's are ready; see bug 792647. This assumes that marionette-server.js -// will be loaded before the 'system-message-listener-ready' message -// is fired. If this stops being true, this approach will have to change. -var systemMessageListenerReady = false; -Services.obs.addObserver(function() { - systemMessageListenerReady = true; -}, "system-message-listener-ready", false); - -// This is used on desktop to prevent newSession from returning before a page -// load initiated by the Firefox command line has completed. -var delayedBrowserStarted = false; -Services.obs.addObserver(function () { - delayedBrowserStarted = true; -}, BROWSER_STARTUP_FINISHED, false); - -this.Context = { - CHROME: "chrome", - CONTENT: "content", -}; - -this.Context.fromString = function (s) { - s = s.toUpperCase(); - if (s in this) { - return this[s]; - } - return null; -}; - -/** - * Implements (parts of) the W3C WebDriver protocol. GeckoDriver lives - * in chrome space and mediates calls to the message listener of the current - * browsing context's content frame message listener via ListenerProxy. - * - * Throughout this prototype, functions with the argument {@code cmd}'s - * documentation refers to the contents of the {@code cmd.parameters} - * object. - * - * @param {string} appName - * Description of the product, for example "B2G" or "Firefox". - * @param {MarionetteServer} server - * The instance of Marionette server. - */ -this.GeckoDriver = function (appName, server) { - this.appName = appName; - this._server = server; - - this.sessionId = null; - this.wins = new browser.Windows(); - this.browsers = {}; - // points to current browser - this.curBrowser = null; - // topmost chrome frame - this.mainFrame = null; - // chrome iframe that currently has focus - this.curFrame = null; - this.mainContentFrameId = null; - this.mozBrowserClose = null; - this.currentFrameElement = null; - // frame ID of the current remote frame, used for mozbrowserclose events - this.oopFrameId = null; - this.observing = null; - this._browserIds = new WeakMap(); - - // The curent context decides if commands should affect chrome- or - // content space. - this.context = Context.CONTENT; - - this.importedScripts = new evaluate.ScriptStorageService( - [Context.CHROME, Context.CONTENT]); - this.sandboxes = new Sandboxes(() => this.getCurrentWindow()); - this.legacyactions = new legacyaction.Chain(); - - this.timer = null; - this.inactivityTimer = null; - - this.marionetteLog = new logging.ContentLogger(); - this.testName = null; - - this.capabilities = new session.Capabilities(); - - this.mm = globalMessageManager; - this.listener = proxy.toListener(() => this.mm, this.sendAsync.bind(this)); - - // always keep weak reference to current dialogue - this.dialog = null; - let handleDialog = (subject, topic) => { - let winr; - if (topic == modal.COMMON_DIALOG_LOADED) { - winr = Cu.getWeakReference(subject); - } - this.dialog = new modal.Dialog(() => this.curBrowser, winr); - }; - modal.addHandler(handleDialog); -}; - -Object.defineProperty(GeckoDriver.prototype, "a11yChecks", { - get: function () { - return this.capabilities.get("moz:accessibilityChecks"); - } -}); - -Object.defineProperty(GeckoDriver.prototype, "proxy", { - get: function () { - return this.capabilities.get("proxy"); - } -}); - -Object.defineProperty(GeckoDriver.prototype, "secureTLS", { - get: function () { - return !this.capabilities.get("acceptInsecureCerts"); - } -}); - -Object.defineProperty(GeckoDriver.prototype, "timeouts", { - get: function () { - return this.capabilities.get("timeouts"); - }, - - set: function (newTimeouts) { - this.capabilities.set("timeouts", newTimeouts); - }, -}); - -Object.defineProperty(GeckoDriver.prototype, "windowHandles", { - get: function () { - let hs = []; - let winEn = Services.wm.getEnumerator(null); - - while (winEn.hasMoreElements()) { - let win = winEn.getNext(); - let tabBrowser = browser.getTabBrowser(win); - - if (tabBrowser) { - tabBrowser.tabs.forEach(tab => { - let winId = this.getIdForBrowser(browser.getBrowserForTab(tab)); - if (winId !== null) { - hs.push(winId); - } - }); - } else { - // For other chrome windows beside the browser window, only add the window itself. - hs.push(getOuterWindowId(win)); - } - } - - return hs; - }, -}); - -Object.defineProperty(GeckoDriver.prototype, "chromeWindowHandles", { - get : function () { - let hs = []; - let winEn = Services.wm.getEnumerator(null); - - while (winEn.hasMoreElements()) { - hs.push(getOuterWindowId(winEn.getNext())); - } - - return hs; - }, -}); - -GeckoDriver.prototype.QueryInterface = XPCOMUtils.generateQI([ - Ci.nsIMessageListener, - Ci.nsIObserver, - Ci.nsISupportsWeakReference, -]); - -/** - * Switches to the global ChromeMessageBroadcaster, potentially replacing - * a frame-specific ChromeMessageSender. Has no effect if the global - * ChromeMessageBroadcaster is already in use. If this replaces a - * frame-specific ChromeMessageSender, it removes the message listeners - * from that sender, and then puts the corresponding frame script "to - * sleep", which removes most of the message listeners from it as well. - */ -GeckoDriver.prototype.switchToGlobalMessageManager = function() { - if (this.curBrowser && this.curBrowser.frameManager.currentRemoteFrame !== null) { - this.curBrowser.frameManager.removeMessageManagerListeners(this.mm); - this.sendAsync("sleepSession"); - this.curBrowser.frameManager.currentRemoteFrame = null; - } - this.mm = globalMessageManager; -}; - -/** - * Helper method to send async messages to the content listener. - * Correct usage is to pass in the name of a function in listener.js, - * a serialisable object, and optionally the current command's ID - * when not using the modern dispatching technique. - * - * @param {string} name - * Suffix of the targetted message listener - * ({@code Marionette:<suffix>}). - * @param {Object=} msg - * Optional JSON serialisable object to send to the listener. - * @param {number=} commandID - * Optional command ID to ensure synchronisity. - */ -GeckoDriver.prototype.sendAsync = function (name, data, commandID) { - name = "Marionette:" + name; - let payload = copy(data); - - // TODO(ato): When proxy.AsyncMessageChannel - // is used for all chrome <-> content communication - // this can be removed. - if (commandID) { - payload.command_id = commandID; - } - - if (!this.curBrowser.frameManager.currentRemoteFrame) { - this.broadcastDelayedAsyncMessage_(name, payload); - } else { - this.sendTargettedAsyncMessage_(name, payload); - } -}; - -GeckoDriver.prototype.broadcastDelayedAsyncMessage_ = function (name, payload) { - this.curBrowser.executeWhenReady(() => { - if (this.curBrowser.curFrameId) { - const target = name + this.curBrowser.curFrameId; - this.mm.broadcastAsyncMessage(target, payload); - } else { - throw new NoSuchWindowError( - "No such content frame; perhaps the listener was not registered?"); - } - }); -}; - -GeckoDriver.prototype.sendTargettedAsyncMessage_ = function (name, payload) { - const curRemoteFrame = this.curBrowser.frameManager.currentRemoteFrame; - const target = name + curRemoteFrame.targetFrameId; - - try { - this.mm.sendAsyncMessage(target, payload); - } catch (e) { - switch (e.result) { - case Cr.NS_ERROR_FAILURE: - case Cr.NS_ERROR_NOT_INITIALIZED: - throw new NoSuchWindowError(); - - default: - throw new WebDriverError(e); - } - } -}; - -/** - * Gets the current active window. - * - * @return {nsIDOMWindow} - */ -GeckoDriver.prototype.getCurrentWindow = function() { - let typ = null; - if (this.curFrame === null) { - if (this.curBrowser === null) { - if (this.context == Context.CONTENT) { - typ = "navigator:browser"; - } - return Services.wm.getMostRecentWindow(typ); - } else { - return this.curBrowser.window; - } - } else { - return this.curFrame; - } -}; - -GeckoDriver.prototype.addFrameCloseListener = function (action) { - let win = this.getCurrentWindow(); - this.mozBrowserClose = e => { - if (e.target.id == this.oopFrameId) { - win.removeEventListener("mozbrowserclose", this.mozBrowserClose, true); - this.switchToGlobalMessageManager(); - throw new NoSuchWindowError("The window closed during action: " + action); - } - }; - win.addEventListener("mozbrowserclose", this.mozBrowserClose, true); -}; - -/** - * Create a new browsing context for window and add to known browsers. - * - * @param {nsIDOMWindow} win - * Window for which we will create a browsing context. - * - * @return {string} - * Returns the unique server-assigned ID of the window. - */ -GeckoDriver.prototype.addBrowser = function (win) { - let bc = new browser.Context(win, this); - let winId = getOuterWindowId(win); - - this.browsers[winId] = bc; - this.curBrowser = this.browsers[winId]; - if (!this.wins.has(winId)) { - // add this to seenItems so we can guarantee - // the user will get winId as this window's id - this.wins.set(winId, win); - } -}; - -/** - * Registers a new browser, win, with Marionette. - * - * If we have not seen the browser content window before, the listener - * frame script will be loaded into it. If isNewSession is true, we will - * switch focus to the start frame when it registers. - * - * @param {nsIDOMWindow} win - * Window whose browser we need to access. - * @param {boolean=false} isNewSession - * True if this is the first time we're talking to this browser. - */ -GeckoDriver.prototype.startBrowser = function (win, isNewSession = false) { - this.mainFrame = win; - this.curFrame = null; - this.addBrowser(win); - this.curBrowser.isNewSession = isNewSession; - this.curBrowser.startSession(isNewSession, win, this.whenBrowserStarted.bind(this)); -}; - -/** - * Callback invoked after a new session has been started in a browser. - * Loads the Marionette frame script into the browser if needed. - * - * @param {nsIDOMWindow} win - * Window whose browser we need to access. - * @param {boolean} isNewSession - * True if this is the first time we're talking to this browser. - */ -GeckoDriver.prototype.whenBrowserStarted = function (win, isNewSession) { - let mm = win.window.messageManager; - if (mm) { - if (!isNewSession) { - // Loading the frame script corresponds to a situation we need to - // return to the server. If the messageManager is a message broadcaster - // with no children, we don't have a hope of coming back from this call, - // so send the ack here. Otherwise, make a note of how many child scripts - // will be loaded so we known when it's safe to return. - // Child managers may not have child scripts yet (e.g. socialapi), only - // count child managers that have children, but only count the top level - // children as they are the ones that we expect a response from. - if (mm.childCount !== 0) { - this.curBrowser.frameRegsPending = 0; - for (let i = 0; i < mm.childCount; i++) { - if (mm.getChildAt(i).childCount !== 0) { - this.curBrowser.frameRegsPending += 1; - } - } - } - } - - if (!Preferences.get(CONTENT_LISTENER_PREF) || !isNewSession) { - // load listener into the remote frame - // and any applicable new frames - // opened after this call - mm.loadFrameScript(FRAME_SCRIPT, true); - Preferences.set(CONTENT_LISTENER_PREF, true); - } - } else { - logger.error( - `Could not load listener into content for page ${win.location.href}`); - } -}; - -/** - * Recursively get all labeled text. - * - * @param {nsIDOMElement} el - * The parent element. - * @param {Array.<string>} lines - * Array that holds the text lines. - */ -GeckoDriver.prototype.getVisibleText = function (el, lines) { - try { - if (atom.isElementDisplayed(el, this.getCurrentWindow())) { - if (el.value) { - lines.push(el.value); - } - for (let child in el.childNodes) { - this.getVisibleText(el.childNodes[child], lines); - } - } - } catch (e) { - if (el.nodeName == "#text") { - lines.push(el.textContent); - } - } -}; - -/** - * Handles registration of new content listener browsers. Depending on - * their type they are either accepted or ignored. - */ -GeckoDriver.prototype.registerBrowser = function (id, be) { - let nullPrevious = this.curBrowser.curFrameId === null; - let listenerWindow = Services.wm.getOuterWindowWithId(id); - - // go in here if we're already in a remote frame - if (this.curBrowser.frameManager.currentRemoteFrame !== null && - (!listenerWindow || this.mm == this.curBrowser.frameManager - .currentRemoteFrame.messageManager.get())) { - // The outerWindowID from an OOP frame will not be meaningful to - // the parent process here, since each process maintains its own - // independent window list. So, it will either be null (!listenerWindow) - // if we're already in a remote frame, or it will point to some - // random window, which will hopefully cause an href mismatch. - // Currently this only happens in B2G for OOP frames registered in - // Marionette:switchToFrame, so we'll acknowledge the switchToFrame - // message here. - // - // TODO: Should have a better way of determining that this message - // is from a remote frame. - this.curBrowser.frameManager.currentRemoteFrame.targetFrameId = - this.generateFrameId(id); - } - - let reg = {}; - // this will be sent to tell the content process if it is the main content - let mainContent = this.curBrowser.mainContentId === null; - if (be.getAttribute("type") != "content") { - // curBrowser holds all the registered frames in knownFrames - let uid = this.generateFrameId(id); - reg.id = uid; - reg.remotenessChange = this.curBrowser.register(uid, be); - } - - // set to true if we updated mainContentId - mainContent = mainContent && this.curBrowser.mainContentId !== null; - if (mainContent) { - this.mainContentFrameId = this.curBrowser.curFrameId; - } - - this.wins.set(reg.id, listenerWindow); - if (nullPrevious && (this.curBrowser.curFrameId !== null)) { - this.sendAsync( - "newSession", - this.capabilities.toJSON(), - this.newSessionCommandId); - if (this.curBrowser.isNewSession) { - this.newSessionCommandId = null; - } - } - - return [reg, mainContent, this.capabilities.toJSON()]; -}; - -GeckoDriver.prototype.registerPromise = function () { - const li = "Marionette:register"; - - return new Promise(resolve => { - let cb = msg => { - let wid = msg.json.value; - let be = msg.target; - let rv = this.registerBrowser(wid, be); - - if (this.curBrowser.frameRegsPending > 0) { - this.curBrowser.frameRegsPending--; - } - - if (this.curBrowser.frameRegsPending === 0) { - this.mm.removeMessageListener(li, cb); - resolve(); - } - - // this is a sync message and listeners expect the ID back - return rv; - }; - this.mm.addMessageListener(li, cb); - }); -}; - -GeckoDriver.prototype.listeningPromise = function () { - const li = "Marionette:listenersAttached"; - return new Promise(resolve => { - let cb = () => { - this.mm.removeMessageListener(li, cb); - resolve(); - }; - this.mm.addMessageListener(li, cb); - }); -}; - -/** Create a new session. */ -GeckoDriver.prototype.newSession = function* (cmd, resp) { - if (this.sessionId) { - throw new SessionNotCreatedError("Maximum number of active sessions"); - } - - this.sessionId = cmd.parameters.sessionId || - cmd.parameters.session_id || - element.generateUUID(); - this.newSessionCommandId = cmd.id; - - try { - this.capabilities = session.Capabilities.fromJSON( - cmd.parameters.capabilities, {merge: true}); - logger.config("Matched capabilities: " + - JSON.stringify(this.capabilities)); - } catch (e) { - throw new SessionNotCreatedError(e); - } - - if (!this.secureTLS) { - logger.warn("TLS certificate errors will be ignored for this session"); - let acceptAllCerts = new cert.InsecureSweepingOverride(); - cert.installOverride(acceptAllCerts); - } - - if (this.proxy.init()) { - logger.info("Proxy settings initialised: " + JSON.stringify(this.proxy)); - } - - // If we are testing accessibility with marionette, start a11y service in - // chrome first. This will ensure that we do not have any content-only - // services hanging around. - if (this.a11yChecks && accessibility.service) { - logger.info("Preemptively starting accessibility service in Chrome"); - } - - let registerBrowsers = this.registerPromise(); - let browserListening = this.listeningPromise(); - - let waitForWindow = function() { - let win = this.getCurrentWindow(); - if (!win) { - // if the window isn't even created, just poll wait for it - let checkTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - checkTimer.initWithCallback(waitForWindow.bind(this), 100, - Ci.nsITimer.TYPE_ONE_SHOT); - } else if (win.document.readyState != "complete") { - // otherwise, wait for it to be fully loaded before proceeding - let listener = ev => { - // ensure that we proceed, on the top level document load event - // (not an iframe one...) - if (ev.target != win.document) { - return; - } - win.removeEventListener("load", listener); - waitForWindow.call(this); - }; - win.addEventListener("load", listener, true); - } else { - let clickToStart = Preferences.get(CLICK_TO_START_PREF); - if (clickToStart && (this.appName != "B2G")) { - let pService = Cc["@mozilla.org/embedcomp/prompt-service;1"] - .getService(Ci.nsIPromptService); - pService.alert(win, "", "Click to start execution of marionette tests"); - } - this.startBrowser(win, true); - } - }; - - let runSessionStart = function() { - if (!Preferences.get(CONTENT_LISTENER_PREF)) { - waitForWindow.call(this); - } else if (this.appName != "Firefox" && this.curBrowser === null) { - // if there is a content listener, then we just wake it up - this.addBrowser(this.getCurrentWindow()); - this.curBrowser.startSession(this.whenBrowserStarted.bind(this)); - this.mm.broadcastAsyncMessage("Marionette:restart", {}); - } else { - throw new WebDriverError("Session already running"); - } - this.switchToGlobalMessageManager(); - }; - - if (!delayedBrowserStarted && this.appName != "B2G") { - let self = this; - Services.obs.addObserver(function onStart() { - Services.obs.removeObserver(onStart, BROWSER_STARTUP_FINISHED); - runSessionStart.call(self); - }, BROWSER_STARTUP_FINISHED, false); - } else { - runSessionStart.call(this); - } - - yield registerBrowsers; - yield browserListening; - - if (this.curBrowser.tab) { - browser.getBrowserForTab(this.curBrowser.tab).focus(); - } - - return { - sessionId: this.sessionId, - capabilities: this.capabilities, - }; -}; - -/** - * Send the current session's capabilities to the client. - * - * Capabilities informs the client of which WebDriver features are - * supported by Firefox and Marionette. They are immutable for the - * length of the session. - * - * The return value is an immutable map of string keys - * ("capabilities") to values, which may be of types boolean, - * numerical or string. - */ -GeckoDriver.prototype.getSessionCapabilities = function (cmd, resp) { - resp.body.capabilities = this.capabilities; -}; - -/** - * Log message. Accepts user defined log-level. - * - * @param {string} value - * Log message. - * @param {string} level - * Arbitrary log level. - */ -GeckoDriver.prototype.log = function (cmd, resp) { - // if level is null, we want to use ContentLogger#send's default - this.marionetteLog.log( - cmd.parameters.value, - cmd.parameters.level || undefined); -}; - -/** Return all logged messages. */ -GeckoDriver.prototype.getLogs = function (cmd, resp) { - resp.body = this.marionetteLog.get(); -}; - -/** - * Sets the context of the subsequent commands to be either "chrome" or - * "content". - * - * @param {string} value - * Name of the context to be switched to. Must be one of "chrome" or - * "content". - */ -GeckoDriver.prototype.setContext = function (cmd, resp) { - let val = cmd.parameters.value; - let ctx = Context.fromString(val); - if (ctx === null) { - throw new WebDriverError(`Invalid context: ${val}`); - } - this.context = ctx; -}; - -/** Gets the context of the server, either "chrome" or "content". */ -GeckoDriver.prototype.getContext = function (cmd, resp) { - resp.body.value = this.context.toString(); -}; - -/** - * Executes a JavaScript function in the context of the current browsing - * context, if in content space, or in chrome space otherwise, and returns - * the return value of the function. - * - * It is important to note that if the {@code sandboxName} parameter - * is left undefined, the script will be evaluated in a mutable sandbox, - * causing any change it makes on the global state of the document to have - * lasting side-effects. - * - * @param {string} script - * Script to evaluate as a function body. - * @param {Array.<(string|boolean|number|object|WebElement)>} args - * Arguments exposed to the script in {@code arguments}. The array - * items must be serialisable to the WebDriver protocol. - * @param {number} scriptTimeout - * Duration in milliseconds of when to interrupt and abort the - * script evaluation. - * @param {string=} sandbox - * Name of the sandbox to evaluate the script in. The sandbox is - * cached for later re-use on the same Window object if - * {@code newSandbox} is false. If he parameter is undefined, - * the script is evaluated in a mutable sandbox. If the parameter - * is "system", it will be evaluted in a sandbox with elevated system - * privileges, equivalent to chrome space. - * @param {boolean=} newSandbox - * Forces the script to be evaluated in a fresh sandbox. Note that if - * it is undefined, the script will normally be evaluted in a fresh - * sandbox. - * @param {string=} filename - * Filename of the client's program where this script is evaluated. - * @param {number=} line - * Line in the client's program where this script is evaluated. - * @param {boolean=} debug_script - * Attach an {@code onerror} event handler on the Window object. - * It does not differentiate content errors from chrome errors. - * @param {boolean=} directInject - * Evaluate the script without wrapping it in a function. - * - * @return {(string|boolean|number|object|WebElement)} - * Return value from the script, or null which signifies either the - * JavaScript notion of null or undefined. - * - * @throws ScriptTimeoutError - * If the script was interrupted due to reaching the {@code - * scriptTimeout} or default timeout. - * @throws JavaScriptError - * If an Error was thrown whilst evaluating the script. - */ -GeckoDriver.prototype.executeScript = function*(cmd, resp) { - let {script, args, scriptTimeout} = cmd.parameters; - scriptTimeout = scriptTimeout || this.timeouts.script; - - let opts = { - sandboxName: cmd.parameters.sandbox, - newSandbox: !!(typeof cmd.parameters.newSandbox == "undefined") || - cmd.parameters.newSandbox, - filename: cmd.parameters.filename, - line: cmd.parameters.line, - debug: cmd.parameters.debug_script, - }; - - resp.body.value = yield this.execute_(script, args, scriptTimeout, opts); -}; - -/** - * Executes a JavaScript function in the context of the current browsing - * context, if in content space, or in chrome space otherwise, and returns - * the object passed to the callback. - * - * The callback is always the last argument to the {@code arguments} - * list passed to the function scope of the script. It can be retrieved - * as such: - * - * let callback = arguments[arguments.length - 1]; - * callback("foo"); - * // "foo" is returned - * - * It is important to note that if the {@code sandboxName} parameter - * is left undefined, the script will be evaluated in a mutable sandbox, - * causing any change it makes on the global state of the document to have - * lasting side-effects. - * - * @param {string} script - * Script to evaluate as a function body. - * @param {Array.<(string|boolean|number|object|WebElement)>} args - * Arguments exposed to the script in {@code arguments}. The array - * items must be serialisable to the WebDriver protocol. - * @param {number} scriptTimeout - * Duration in milliseconds of when to interrupt and abort the - * script evaluation. - * @param {string=} sandbox - * Name of the sandbox to evaluate the script in. The sandbox is - * cached for later re-use on the same Window object if - * {@code newSandbox} is false. If the parameter is undefined, - * the script is evaluated in a mutable sandbox. If the parameter - * is "system", it will be evaluted in a sandbox with elevated system - * privileges, equivalent to chrome space. - * @param {boolean=} newSandbox - * Forces the script to be evaluated in a fresh sandbox. Note that if - * it is undefined, the script will normally be evaluted in a fresh - * sandbox. - * @param {string=} filename - * Filename of the client's program where this script is evaluated. - * @param {number=} line - * Line in the client's program where this script is evaluated. - * @param {boolean=} debug_script - * Attach an {@code onerror} event handler on the Window object. - * It does not differentiate content errors from chrome errors. - * @param {boolean=} directInject - * Evaluate the script without wrapping it in a function. - * - * @return {(string|boolean|number|object|WebElement)} - * Return value from the script, or null which signifies either the - * JavaScript notion of null or undefined. - * - * @throws ScriptTimeoutError - * If the script was interrupted due to reaching the {@code - * scriptTimeout} or default timeout. - * @throws JavaScriptError - * If an Error was thrown whilst evaluating the script. - */ -GeckoDriver.prototype.executeAsyncScript = function* (cmd, resp) { - let {script, args, scriptTimeout} = cmd.parameters; - scriptTimeout = scriptTimeout || this.timeouts.script; - - let opts = { - sandboxName: cmd.parameters.sandbox, - newSandbox: !!(typeof cmd.parameters.newSandbox == "undefined") || - cmd.parameters.newSandbox, - filename: cmd.parameters.filename, - line: cmd.parameters.line, - debug: cmd.parameters.debug_script, - async: true, - }; - - resp.body.value = yield this.execute_(script, args, scriptTimeout, opts); -}; - -GeckoDriver.prototype.execute_ = function (script, args, timeout, opts = {}) { - switch (this.context) { - case Context.CONTENT: - // evaluate in content with lasting side-effects - if (!opts.sandboxName) { - return this.listener.execute(script, args, timeout, opts); - - // evaluate in content with sandbox - } else { - return this.listener.executeInSandbox(script, args, timeout, opts); - } - - case Context.CHROME: - let sb = this.sandboxes.get(opts.sandboxName, opts.newSandbox); - if (opts.sandboxName) { - sb = sandbox.augment(sb, new logging.Adapter(this.marionetteLog)); - sb = sandbox.augment(sb, {global: sb}); - } - - opts.timeout = timeout; - script = this.importedScripts.for(Context.CHROME).concat(script); - let wargs = element.fromJson(args, this.curBrowser.seenEls, sb.window); - let evaluatePromise = evaluate.sandbox(sb, script, wargs, opts); - return evaluatePromise.then(res => element.toJson(res, this.curBrowser.seenEls)); - } -}; - -/** - * Execute pure JavaScript. Used to execute simpletest harness tests, - * which are like mochitests only injected using Marionette. - * - * Scripts are expected to call the {@code finish} global when done. - */ -GeckoDriver.prototype.executeJSScript = function* (cmd, resp) { - let {script, args, scriptTimeout} = cmd.parameters; - scriptTimeout = scriptTimeout || this.timeouts.script; - - let opts = { - filename: cmd.parameters.filename, - line: cmd.parameters.line, - async: cmd.parameters.async, - }; - - switch (this.context) { - case Context.CHROME: - let win = this.getCurrentWindow(); - let wargs = element.fromJson(args, this.curBrowser.seenEls, win); - let harness = new simpletest.Harness( - win, - Context.CHROME, - this.marionetteLog, - scriptTimeout, - function() {}, - this.testName); - - let sb = sandbox.createSimpleTest(win, harness); - // TODO(ato): Not sure this is needed: - sb = sandbox.augment(sb, new logging.Adapter(this.marionetteLog)); - - let res = yield evaluate.sandbox(sb, script, wargs, opts); - resp.body.value = element.toJson(res, this.curBrowser.seenEls); - break; - - case Context.CONTENT: - resp.body.value = yield this.listener.executeSimpleTest(script, args, scriptTimeout, opts); - break; - } -}; - -/** - * Navigate to given URL. - * - * Navigates the current browsing context to the given URL and waits for - * the document to load or the session's page timeout duration to elapse - * before returning. - * - * The command will return with a failure if there is an error loading - * the document or the URL is blocked. This can occur if it fails to - * reach host, the URL is malformed, or if there is a certificate issue - * to name some examples. - * - * The document is considered successfully loaded when the - * DOMContentLoaded event on the frame element associated with the - * current window triggers and document.readyState is "complete". - * - * In chrome context it will change the current window's location to - * the supplied URL and wait until document.readyState equals "complete" - * or the page timeout duration has elapsed. - * - * @param {string} url - * URL to navigate to. - */ -GeckoDriver.prototype.get = function*(cmd, resp) { - assert.content(this.context); - - let url = cmd.parameters.url; - - let get = this.listener.get({url: url, pageTimeout: this.timeouts.pageLoad}); - - // If a remoteness update interrupts our page load, this will never return - // We need to re-issue this request to correctly poll for readyState and - // send errors. - this.curBrowser.pendingCommands.push(() => { - let parameters = { - // TODO(ato): Bug 1242595 - command_id: this.listener.activeMessageId, - pageTimeout: this.timeouts.pageLoad, - startTime: new Date().getTime(), - }; - this.mm.broadcastAsyncMessage( - "Marionette:pollForReadyState" + this.curBrowser.curFrameId, - parameters); - }); - - yield get; - browser.getBrowserForTab(this.curBrowser.tab).focus(); -}; - -/** - * Get a string representing the current URL. - * - * On Desktop this returns a string representation of the URL of the - * current top level browsing context. This is equivalent to - * document.location.href. - * - * When in the context of the chrome, this returns the canonical URL - * of the current resource. - */ -GeckoDriver.prototype.getCurrentUrl = function (cmd) { - switch (this.context) { - case Context.CHROME: - return this.getCurrentWindow().location.href; - - case Context.CONTENT: - let isB2G = this.appName == "B2G"; - return this.listener.getCurrentUrl(isB2G); - } -}; - -/** Gets the current title of the window. */ -GeckoDriver.prototype.getTitle = function* (cmd, resp) { - switch (this.context) { - case Context.CHROME: - let win = this.getCurrentWindow(); - resp.body.value = win.document.documentElement.getAttribute("title"); - break; - - case Context.CONTENT: - resp.body.value = yield this.listener.getTitle(); - break; - } -}; - -/** Gets the current type of the window. */ -GeckoDriver.prototype.getWindowType = function (cmd, resp) { - let win = this.getCurrentWindow(); - resp.body.value = win.document.documentElement.getAttribute("windowtype"); -}; - -/** Gets the page source of the content document. */ -GeckoDriver.prototype.getPageSource = function* (cmd, resp) { - switch (this.context) { - case Context.CHROME: - let win = this.getCurrentWindow(); - let s = new win.XMLSerializer(); - resp.body.value = s.serializeToString(win.document); - break; - - case Context.CONTENT: - resp.body.value = yield this.listener.getPageSource(); - break; - } -}; - -/** - * Cause the browser to traverse one step backward in the joint history - * of the current browsing context. - */ -GeckoDriver.prototype.goBack = function* (cmd, resp) { - assert.content(this.context); - - if (!this.curBrowser.tab) { - // Navigation does not work for non-browser windows - return; - } - - let contentBrowser = browser.getBrowserForTab(this.curBrowser.tab) - if (!contentBrowser.webNavigation.canGoBack) { - return; - } - - let currentURL = yield this.listener.getCurrentUrl(); - let goBack = this.listener.goBack({pageTimeout: this.timeouts.pageLoad}); - - // If a remoteness update interrupts our page load, this will never return - // We need to re-issue this request to correctly poll for readyState and - // send errors. - this.curBrowser.pendingCommands.push(() => { - let parameters = { - // TODO(ato): Bug 1242595 - command_id: this.listener.activeMessageId, - lastSeenURL: currentURL, - pageTimeout: this.timeouts.pageLoad, - startTime: new Date().getTime(), - }; - this.mm.broadcastAsyncMessage( - // TODO: combine with - // "Marionette:pollForReadyState" + this.curBrowser.curFrameId, - "Marionette:pollForReadyState" + this.curBrowser.curFrameId, - parameters); - }); - - yield goBack; -}; - -/** - * Cause the browser to traverse one step forward in the joint history - * of the current browsing context. - */ -GeckoDriver.prototype.goForward = function* (cmd, resp) { - assert.content(this.context); - - if (!this.curBrowser.tab) { - // Navigation does not work for non-browser windows - return; - } - - let contentBrowser = browser.getBrowserForTab(this.curBrowser.tab) - if (!contentBrowser.webNavigation.canGoForward) { - return; - } - - let currentURL = yield this.listener.getCurrentUrl(); - let goForward = this.listener.goForward({pageTimeout: this.timeouts.pageLoad}); - - // If a remoteness update interrupts our page load, this will never return - // We need to re-issue this request to correctly poll for readyState and - // send errors. - this.curBrowser.pendingCommands.push(() => { - let parameters = { - // TODO(ato): Bug 1242595 - command_id: this.listener.activeMessageId, - lastSeenURL: currentURL, - pageTimeout: this.timeouts.pageLoad, - startTime: new Date().getTime(), - }; - this.mm.broadcastAsyncMessage( - // TODO: combine with - // "Marionette:pollForReadyState" + this.curBrowser.curFrameId, - "Marionette:pollForReadyState" + this.curBrowser.curFrameId, - parameters); - }); - - yield goForward; -}; - -/** Refresh the page. */ -GeckoDriver.prototype.refresh = function*(cmd, resp) { - assert.content(this.context); - - yield this.listener.refresh(); -}; - -/** - * Forces an update for the given browser's id. - */ -GeckoDriver.prototype.updateIdForBrowser = function (browser, newId) { - this._browserIds.set(browser.permanentKey, newId); -}; - -/** - * Retrieves a listener id for the given xul browser element. In case - * the browser is not known, an attempt is made to retrieve the id from - * a CPOW, and null is returned if this fails. - */ -GeckoDriver.prototype.getIdForBrowser = function (browser) { - if (browser === null) { - return null; - } - let permKey = browser.permanentKey; - if (this._browserIds.has(permKey)) { - return this._browserIds.get(permKey); - } - - let winId = browser.outerWindowID; - if (winId) { - winId = winId.toString(); - this._browserIds.set(permKey, winId); - return winId; - } - return null; -}, - -/** - * Get the current window's handle. On desktop this typically corresponds - * to the currently selected tab. - * - * Return an opaque server-assigned identifier to this window that - * uniquely identifies it within this Marionette instance. This can - * be used to switch to this window at a later point. - * - * @return {string} - * Unique window handle. - */ -GeckoDriver.prototype.getWindowHandle = function (cmd, resp) { - // curFrameId always holds the current tab. - if (this.curBrowser.curFrameId) { - resp.body.value = this.curBrowser.curFrameId; - return; - } - - for (let i in this.browsers) { - if (this.curBrowser == this.browsers[i]) { - resp.body.value = i; - return; - } - } -}; - -/** - * Get a list of top-level browsing contexts. On desktop this typically - * corresponds to the set of open tabs for browser windows, or the window itself - * for non-browser chrome windows. - * - * Each window handle is assigned by the server and is guaranteed unique, - * however the return array does not have a specified ordering. - * - * @return {Array.<string>} - * Unique window handles. - */ -GeckoDriver.prototype.getWindowHandles = function (cmd, resp) { - return this.windowHandles; -} - -/** - * Get the current window's handle. This corresponds to a window that - * may itself contain tabs. - * - * Return an opaque server-assigned identifier to this window that - * uniquely identifies it within this Marionette instance. This can - * be used to switch to this window at a later point. - * - * @return {string} - * Unique window handle. - */ -GeckoDriver.prototype.getChromeWindowHandle = function (cmd, resp) { - for (let i in this.browsers) { - if (this.curBrowser == this.browsers[i]) { - resp.body.value = i; - return; - } - } -}; - -/** - * Returns identifiers for each open chrome window for tests interested in - * managing a set of chrome windows and tabs separately. - * - * @return {Array.<string>} - * Unique window handles. - */ -GeckoDriver.prototype.getChromeWindowHandles = function (cmd, resp) { - return this.chromeWindowHandles; -} - -/** - * Get the current window position. - * - * @return {Object.<string, number>} - * Object with |x| and |y| coordinates. - */ -GeckoDriver.prototype.getWindowPosition = function (cmd, resp) { - return this.curBrowser.position; -}; - -/** - * Set the window position of the browser on the OS Window Manager - * - * @param {number} x - * X coordinate of the top/left of the window that it will be - * moved to. - * @param {number} y - * Y coordinate of the top/left of the window that it will be - * moved to. - * - * @return {Object.<string, number>} - * Object with |x| and |y| coordinates. - */ -GeckoDriver.prototype.setWindowPosition = function (cmd, resp) { - assert.firefox() - - let {x, y} = cmd.parameters; - assert.positiveInteger(x); - assert.positiveInteger(y); - - let win = this.getCurrentWindow(); - win.moveTo(x, y); - - return this.curBrowser.position; -}; - -/** - * Switch current top-level browsing context by name or server-assigned ID. - * Searches for windows by name, then ID. Content windows take precedence. - * - * @param {string} name - * Target name or ID of the window to switch to. - * @param {boolean=} focus - * A boolean value which determines whether to focus - * the window. Defaults to true. - */ -GeckoDriver.prototype.switchToWindow = function* (cmd, resp) { - let switchTo = cmd.parameters.name; - let focus = (cmd.parameters.focus !== undefined) ? cmd.parameters.focus : true; - let found; - - let byNameOrId = function (name, windowId) { - return switchTo === name || switchTo === windowId; - }; - - let winEn = Services.wm.getEnumerator(null); - while (winEn.hasMoreElements()) { - let win = winEn.getNext(); - let outerId = getOuterWindowId(win); - let tabBrowser = browser.getTabBrowser(win); - - if (byNameOrId(win.name, outerId)) { - // In case the wanted window is a chrome window, we are done. - found = {win: win, outerId: outerId, hasTabBrowser: !!tabBrowser}; - break; - - } else if (tabBrowser) { - // Otherwise check if the chrome window has a tab browser, and that it - // contains a tab with the wanted window handle. - for (let i = 0; i < tabBrowser.tabs.length; ++i) { - let contentBrowser = browser.getBrowserForTab(tabBrowser.tabs[i]); - let contentWindowId = this.getIdForBrowser(contentBrowser); - - if (byNameOrId(win.name, contentWindowId)) { - found = { - win: win, - outerId: outerId, - hasTabBrowser: true, - tabIndex: i, - }; - break; - } - } - } - } - - if (found) { - if (!(found.outerId in this.browsers)) { - // Initialise Marionette if the current chrome window has not been seen - // before. Also register the initial tab, if one exists. - let registerBrowsers, browserListening; - - if (found.hasTabBrowser) { - registerBrowsers = this.registerPromise(); - browserListening = this.listeningPromise(); - } - - this.startBrowser(found.win, false /* isNewSession */); - - if (registerBrowsers && browserListening) { - yield registerBrowsers; - yield browserListening; - } - - } else { - // Otherwise switch to the known chrome window, and activate the tab - // if it's a content browser. - this.curBrowser = this.browsers[found.outerId]; - - if ("tabIndex" in found) { - this.curBrowser.switchToTab(found.tabIndex, found.win, focus); - } - } - } else { - throw new NoSuchWindowError(`Unable to locate window: ${switchTo}`); - } -}; - -GeckoDriver.prototype.getActiveFrame = function (cmd, resp) { - switch (this.context) { - case Context.CHROME: - // no frame means top-level - resp.body.value = null; - if (this.curFrame) { - let elRef = this.curBrowser.seenEls - .add(this.curFrame.frameElement); - let el = element.makeWebElement(elRef); - resp.body.value = el; - } - break; - - case Context.CONTENT: - resp.body.value = null; - if (this.currentFrameElement !== null) { - let el = element.makeWebElement(this.currentFrameElement); - resp.body.value = el; - } - break; - } -}; - -GeckoDriver.prototype.switchToParentFrame = function*(cmd, resp) { - let res = yield this.listener.switchToParentFrame(); -}; - -/** - * Switch to a given frame within the current window. - * - * @param {Object} element - * A web element reference to the element to switch to. - * @param {(string|number)} id - * If element is not defined, then this holds either the id, name, - * or index of the frame to switch to. - */ -GeckoDriver.prototype.switchToFrame = function* (cmd, resp) { - let {id, element, focus} = cmd.parameters; - - const otherErrorsExpr = /about:.+(error)|(blocked)\?/; - const checkTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - - let curWindow = this.getCurrentWindow(); - - let checkLoad = function() { - let win = this.getCurrentWindow(); - if (win.document.readyState == "complete") { - return; - } else if (win.document.readyState == "interactive") { - let baseURI = win.document.baseURI; - if (baseURI.startsWith("about:certerror")) { - throw new InsecureCertificateError(); - } else if (otherErrorsExpr.exec(win.document.baseURI)) { - throw new UnknownError("Error loading page"); - } - } - - checkTimer.initWithCallback(checkLoad.bind(this), 100, Ci.nsITimer.TYPE_ONE_SHOT); - }; - - if (this.context == Context.CHROME) { - let foundFrame = null; - - // just focus - if (typeof id == "undefined" && typeof element == "undefined") { - this.curFrame = null; - if (focus) { - this.mainFrame.focus(); - } - checkTimer.initWithCallback(checkLoad.bind(this), 100, Ci.nsITimer.TYPE_ONE_SHOT); - return; - } - - // by element - if (this.curBrowser.seenEls.has(element)) { - // HTMLIFrameElement - let wantedFrame = this.curBrowser.seenEls.get(element, {frame: curWindow}); - // Deal with an embedded xul:browser case - if (wantedFrame.tagName == "xul:browser" || wantedFrame.tagName == "browser") { - curWindow = wantedFrame.contentWindow; - this.curFrame = curWindow; - if (focus) { - this.curFrame.focus(); - } - checkTimer.initWithCallback(checkLoad.bind(this), 100, Ci.nsITimer.TYPE_ONE_SHOT); - return; - } - - // Check if the frame is XBL anonymous - let parent = curWindow.document.getBindingParent(wantedFrame); - // Shadow nodes also show up in getAnonymousNodes, we should ignore them. - if (parent && !(parent.shadowRoot && parent.shadowRoot.contains(wantedFrame))) { - let anonNodes = [...curWindow.document.getAnonymousNodes(parent) || []]; - if (anonNodes.length > 0) { - let el = wantedFrame; - while (el) { - if (anonNodes.indexOf(el) > -1) { - curWindow = wantedFrame.contentWindow; - this.curFrame = curWindow; - if (focus) { - this.curFrame.focus(); - } - checkTimer.initWithCallback(checkLoad.bind(this), 100, Ci.nsITimer.TYPE_ONE_SHOT); - return; - } - el = el.parentNode; - } - } - } - - // else, assume iframe - let frames = curWindow.document.getElementsByTagName("iframe"); - let numFrames = frames.length; - for (let i = 0; i < numFrames; i++) { - if (new XPCNativeWrapper(frames[i]) == new XPCNativeWrapper(wantedFrame)) { - curWindow = frames[i].contentWindow; - this.curFrame = curWindow; - if (focus) { - this.curFrame.focus(); - } - checkTimer.initWithCallback(checkLoad.bind(this), 100, Ci.nsITimer.TYPE_ONE_SHOT); - return; - } - } - } - - switch (typeof id) { - case "string" : - let foundById = null; - let frames = curWindow.document.getElementsByTagName("iframe"); - let numFrames = frames.length; - for (let i = 0; i < numFrames; i++) { - //give precedence to name - let frame = frames[i]; - if (frame.getAttribute("name") == id) { - foundFrame = i; - curWindow = frame.contentWindow; - break; - } else if (foundById === null && frame.id == id) { - foundById = i; - } - } - if (foundFrame === null && foundById !== null) { - foundFrame = foundById; - curWindow = frames[foundById].contentWindow; - } - break; - case "number": - if (typeof curWindow.frames[id] != "undefined") { - foundFrame = id; - curWindow = curWindow.frames[foundFrame].frameElement.contentWindow; - } - break; - } - - if (foundFrame !== null) { - this.curFrame = curWindow; - if (focus) { - this.curFrame.focus(); - } - checkTimer.initWithCallback(checkLoad.bind(this), 100, Ci.nsITimer.TYPE_ONE_SHOT); - } else { - throw new NoSuchFrameError(`Unable to locate frame: ${id}`); - } - - } else if (this.context == Context.CONTENT) { - if (!id && !element && - this.curBrowser.frameManager.currentRemoteFrame !== null) { - // We're currently using a ChromeMessageSender for a remote frame, so this - // request indicates we need to switch back to the top-level (parent) frame. - // We'll first switch to the parent's (global) ChromeMessageBroadcaster, so - // we send the message to the right listener. - this.switchToGlobalMessageManager(); - } - cmd.command_id = cmd.id; - - let res = yield this.listener.switchToFrame(cmd.parameters); - if (res) { - let {win: winId, frame: frameId} = res; - this.mm = this.curBrowser.frameManager.getFrameMM(winId, frameId); - - let registerBrowsers = this.registerPromise(); - let browserListening = this.listeningPromise(); - - this.oopFrameId = - this.curBrowser.frameManager.switchToFrame(winId, frameId); - - yield registerBrowsers; - yield browserListening; - } - } -}; - -GeckoDriver.prototype.getTimeouts = function (cmd, resp) { - return this.timeouts; -}; - -/** - * Set timeout for page loading, searching, and scripts. - * - * @param {Object.<string, number>} - * Dictionary of timeout types and their new value, where all timeout - * types are optional. - * - * @throws {InvalidArgumentError} - * If timeout type key is unknown, or the value provided with it is - * not an integer. - */ -GeckoDriver.prototype.setTimeouts = function (cmd, resp) { - // backwards compatibility with old API - // that accepted a dictionary {type: <string>, ms: <number>} - let json = {}; - if (typeof cmd.parameters == "object" && - "type" in cmd.parameters && - "ms" in cmd.parameters) { - logger.warn("Using deprecated data structure for setting timeouts"); - json = {[cmd.parameters.type]: parseInt(cmd.parameters.ms)}; - } else { - json = cmd.parameters; - } - - // merge with existing timeouts - let merged = Object.assign(this.timeouts.toJSON(), json); - this.timeouts = session.Timeouts.fromJSON(merged); -}; - -/** Single tap. */ -GeckoDriver.prototype.singleTap = function*(cmd, resp) { - let {id, x, y} = cmd.parameters; - - switch (this.context) { - case Context.CHROME: - throw new UnsupportedOperationError( - "Command 'singleTap' is not yet available in chrome context"); - - case Context.CONTENT: - this.addFrameCloseListener("tap"); - yield this.listener.singleTap(id, x, y); - break; - } -}; - -/** - * Perform a series of grouped actions at the specified points in time. - * - * @param {Array.<?>} actions - * Array of objects that each represent an action sequence. - * - * @throws {UnsupportedOperationError} - * If the command is made in chrome context. - */ -GeckoDriver.prototype.performActions = function(cmd, resp) { - switch (this.context) { - case Context.CHROME: - throw new UnsupportedOperationError( - "Command 'performActions' is not yet available in chrome context"); - - case Context.CONTENT: - return this.listener.performActions({"actions": cmd.parameters.actions}); - } -}; - -/** - * Release all the keys and pointer buttons that are currently depressed. - */ -GeckoDriver.prototype.releaseActions = function(cmd, resp) { - switch (this.context) { - case Context.CHROME: - throw new UnsupportedOperationError( - "Command 'releaseActions' is not yet available in chrome context"); - - case Context.CONTENT: - return this.listener.releaseActions(); - } -}; - -/** - * An action chain. - * - * @param {Object} value - * A nested array where the inner array represents each event, - * and the outer array represents a collection of events. - * - * @return {number} - * Last touch ID. - */ -GeckoDriver.prototype.actionChain = function*(cmd, resp) { - let {chain, nextId} = cmd.parameters; - - switch (this.context) { - case Context.CHROME: - // be conservative until this has a use case and is established - // to work as expected in Fennec - assert.firefox() - - let win = this.getCurrentWindow(); - resp.body.value = yield this.legacyactions.dispatchActions( - chain, nextId, {frame: win}, this.curBrowser.seenEls); - break; - - case Context.CONTENT: - this.addFrameCloseListener("action chain"); - resp.body.value = yield this.listener.actionChain(chain, nextId); - break; - } -}; - -/** - * A multi-action chain. - * - * @param {Object} value - * A nested array where the inner array represents eache vent, - * the middle array represents a collection of events for each - * finger, and the outer array represents all fingers. - */ -GeckoDriver.prototype.multiAction = function*(cmd, resp) { - switch (this.context) { - case Context.CHROME: - throw new UnsupportedOperationError( - "Command 'multiAction' is not yet available in chrome context"); - - case Context.CONTENT: - this.addFrameCloseListener("multi action chain"); - yield this.listener.multiAction(cmd.parameters.value, cmd.parameters.max_length); - break; - } -}; - -/** - * Find an element using the indicated search strategy. - * - * @param {string} using - * Indicates which search method to use. - * @param {string} value - * Value the client is looking for. - */ -GeckoDriver.prototype.findElement = function*(cmd, resp) { - let strategy = cmd.parameters.using; - let expr = cmd.parameters.value; - let opts = { - startNode: cmd.parameters.element, - timeout: this.timeouts.implicit, - all: false, - }; - - switch (this.context) { - case Context.CHROME: - if (!SUPPORTED_STRATEGIES.has(strategy)) { - throw new InvalidSelectorError(`Strategy not supported: ${strategy}`); - } - - let container = {frame: this.getCurrentWindow()}; - if (opts.startNode) { - opts.startNode = this.curBrowser.seenEls.get(opts.startNode, container); - } - let el = yield element.find(container, strategy, expr, opts); - let elRef = this.curBrowser.seenEls.add(el); - let webEl = element.makeWebElement(elRef); - - resp.body.value = webEl; - break; - - case Context.CONTENT: - resp.body.value = yield this.listener.findElementContent( - strategy, - expr, - opts); - break; - } -}; - -/** - * Find elements using the indicated search strategy. - * - * @param {string} using - * Indicates which search method to use. - * @param {string} value - * Value the client is looking for. - */ -GeckoDriver.prototype.findElements = function*(cmd, resp) { - let strategy = cmd.parameters.using; - let expr = cmd.parameters.value; - let opts = { - startNode: cmd.parameters.element, - timeout: this.timeouts.implicit, - all: true, - }; - - switch (this.context) { - case Context.CHROME: - if (!SUPPORTED_STRATEGIES.has(strategy)) { - throw new InvalidSelectorError(`Strategy not supported: ${strategy}`); - } - - let container = {frame: this.getCurrentWindow()}; - if (opts.startNode) { - opts.startNode = this.curBrowser.seenEls.get(opts.startNode, container); - } - let els = yield element.find(container, strategy, expr, opts); - - let elRefs = this.curBrowser.seenEls.addAll(els); - let webEls = elRefs.map(element.makeWebElement); - resp.body = webEls; - break; - - case Context.CONTENT: - resp.body = yield this.listener.findElementsContent( - cmd.parameters.using, - cmd.parameters.value, - opts); - break; - } -}; - -/** Return the active element on the page. */ -GeckoDriver.prototype.getActiveElement = function*(cmd, resp) { - switch (this.context) { - case Context.CHROME: - throw new UnsupportedOperationError( - "Command 'getActiveElement' is not yet available in chrome context"); - - case Context.CONTENT: - resp.body.value = yield this.listener.getActiveElement(); - break; - } -}; - -/** - * Send click event to element. - * - * @param {string} id - * Reference ID to the element that will be clicked. - */ -GeckoDriver.prototype.clickElement = function*(cmd, resp) { - let id = cmd.parameters.id; - - switch (this.context) { - case Context.CHROME: - let win = this.getCurrentWindow(); - let el = this.curBrowser.seenEls.get(id, {frame: win}); - yield interaction.clickElement(el, this.a11yChecks); - break; - - case Context.CONTENT: - // We need to protect against the click causing an OOP frame to close. - // This fires the mozbrowserclose event when it closes so we need to - // listen for it and then just send an error back. The person making the - // call should be aware something isnt right and handle accordingly - this.addFrameCloseListener("click"); - yield this.listener.clickElement(id); - break; - } -}; - -/** - * Get a given attribute of an element. - * - * @param {string} id - * Web element reference ID to the element that will be inspected. - * @param {string} name - * Name of the attribute which value to retrieve. - * - * @return {string} - * Value of the attribute. - */ -GeckoDriver.prototype.getElementAttribute = function*(cmd, resp) { - let {id, name} = cmd.parameters; - - switch (this.context) { - case Context.CHROME: - let win = this.getCurrentWindow(); - let el = this.curBrowser.seenEls.get(id, {frame: win}); - - resp.body.value = el.getAttribute(name); - break; - - case Context.CONTENT: - resp.body.value = yield this.listener.getElementAttribute(id, name); - break; - } -}; - -/** - * Returns the value of a property associated with given element. - * - * @param {string} id - * Web element reference ID to the element that will be inspected. - * @param {string} name - * Name of the property which value to retrieve. - * - * @return {string} - * Value of the property. - */ -GeckoDriver.prototype.getElementProperty = function*(cmd, resp) { - let {id, name} = cmd.parameters; - - switch (this.context) { - case Context.CHROME: - let win = this.getCurrentWindow(); - let el = this.curBrowser.seenEls.get(id, {frame: win}); - resp.body.value = el[name]; - break; - - case Context.CONTENT: - resp.body.value = yield this.listener.getElementProperty(id, name); - break; - } -}; - -/** - * Get the text of an element, if any. Includes the text of all child - * elements. - * - * @param {string} id - * Reference ID to the element that will be inspected. - */ -GeckoDriver.prototype.getElementText = function*(cmd, resp) { - let id = cmd.parameters.id; - - switch (this.context) { - case Context.CHROME: - // for chrome, we look at text nodes, and any node with a "label" field - let win = this.getCurrentWindow(); - let el = this.curBrowser.seenEls.get(id, {frame: win}); - let lines = []; - this.getVisibleText(el, lines); - resp.body.value = lines.join("\n"); - break; - - case Context.CONTENT: - resp.body.value = yield this.listener.getElementText(id); - break; - } -}; - -/** - * Get the tag name of the element. - * - * @param {string} id - * Reference ID to the element that will be inspected. - */ -GeckoDriver.prototype.getElementTagName = function*(cmd, resp) { - let id = cmd.parameters.id; - - switch (this.context) { - case Context.CHROME: - let win = this.getCurrentWindow(); - let el = this.curBrowser.seenEls.get(id, {frame: win}); - resp.body.value = el.tagName.toLowerCase(); - break; - - case Context.CONTENT: - resp.body.value = yield this.listener.getElementTagName(id); - break; - } -}; - -/** - * Check if element is displayed. - * - * @param {string} id - * Reference ID to the element that will be inspected. - */ -GeckoDriver.prototype.isElementDisplayed = function*(cmd, resp) { - let id = cmd.parameters.id; - - switch (this.context) { - case Context.CHROME: - let win = this.getCurrentWindow(); - let el = this.curBrowser.seenEls.get(id, {frame: win}); - resp.body.value = yield interaction.isElementDisplayed( - el, this.a11yChecks); - break; - - case Context.CONTENT: - resp.body.value = yield this.listener.isElementDisplayed(id); - break; - } -}; - -/** - * Return the property of the computed style of an element. - * - * @param {string} id - * Reference ID to the element that will be checked. - * @param {string} propertyName - * CSS rule that is being requested. - */ -GeckoDriver.prototype.getElementValueOfCssProperty = function*(cmd, resp) { - let {id, propertyName: prop} = cmd.parameters; - - switch (this.context) { - case Context.CHROME: - let win = this.getCurrentWindow(); - let el = this.curBrowser.seenEls.get(id, {frame: win}); - let sty = win.document.defaultView.getComputedStyle(el, null); - resp.body.value = sty.getPropertyValue(prop); - break; - - case Context.CONTENT: - resp.body.value = yield this.listener.getElementValueOfCssProperty(id, prop); - break; - } -}; - -/** - * Check if element is enabled. - * - * @param {string} id - * Reference ID to the element that will be checked. - */ -GeckoDriver.prototype.isElementEnabled = function*(cmd, resp) { - let id = cmd.parameters.id; - - switch (this.context) { - case Context.CHROME: - // Selenium atom doesn't quite work here - let win = this.getCurrentWindow(); - let el = this.curBrowser.seenEls.get(id, {frame: win}); - resp.body.value = yield interaction.isElementEnabled( - el, this.a11yChecks); - break; - - case Context.CONTENT: - resp.body.value = yield this.listener.isElementEnabled(id); - break; - } -}, - -/** - * Check if element is selected. - * - * @param {string} id - * Reference ID to the element that will be checked. - */ -GeckoDriver.prototype.isElementSelected = function*(cmd, resp) { - let id = cmd.parameters.id; - - switch (this.context) { - case Context.CHROME: - // Selenium atom doesn't quite work here - let win = this.getCurrentWindow(); - let el = this.curBrowser.seenEls.get(id, {frame: win}); - resp.body.value = yield interaction.isElementSelected( - el, this.a11yChecks); - break; - - case Context.CONTENT: - resp.body.value = yield this.listener.isElementSelected(id); - break; - } -}; - -GeckoDriver.prototype.getElementRect = function*(cmd, resp) { - let id = cmd.parameters.id; - - switch (this.context) { - case Context.CHROME: - let win = this.getCurrentWindow(); - let el = this.curBrowser.seenEls.get(id, {frame: win}); - let rect = el.getBoundingClientRect(); - resp.body = { - x: rect.x + win.pageXOffset, - y: rect.y + win.pageYOffset, - width: rect.width, - height: rect.height - }; - break; - - case Context.CONTENT: - resp.body = yield this.listener.getElementRect(id); - break; - } -}; - -/** - * Send key presses to element after focusing on it. - * - * @param {string} id - * Reference ID to the element that will be checked. - * @param {string} value - * Value to send to the element. - */ -GeckoDriver.prototype.sendKeysToElement = function*(cmd, resp) { - let {id, value} = cmd.parameters; - assert.defined(value, `Expected character sequence: ${value}`); - - switch (this.context) { - case Context.CHROME: - let win = this.getCurrentWindow(); - let el = this.curBrowser.seenEls.get(id, {frame: win}); - yield interaction.sendKeysToElement( - el, value, true, this.a11yChecks); - break; - - case Context.CONTENT: - yield this.listener.sendKeysToElement(id, value); - break; - } -}; - -/** Sets the test name. The test name is used for logging purposes. */ -GeckoDriver.prototype.setTestName = function*(cmd, resp) { - let val = cmd.parameters.value; - this.testName = val; - yield this.listener.setTestName({value: val}); -}; - -/** - * Clear the text of an element. - * - * @param {string} id - * Reference ID to the element that will be cleared. - */ -GeckoDriver.prototype.clearElement = function*(cmd, resp) { - let id = cmd.parameters.id; - - switch (this.context) { - case Context.CHROME: - // the selenium atom doesn't work here - let win = this.getCurrentWindow(); - let el = this.curBrowser.seenEls.get(id, {frame: win}); - if (el.nodeName == "textbox") { - el.value = ""; - } else if (el.nodeName == "checkbox") { - el.checked = false; - } - break; - - case Context.CONTENT: - yield this.listener.clearElement(id); - break; - } -}; - -/** - * Switch to shadow root of the given host element. - * - * @param {string} id element id. - */ -GeckoDriver.prototype.switchToShadowRoot = function*(cmd, resp) { - assert.content(this.context) - - let id; - if (cmd.parameters) { id = cmd.parameters.id; } - yield this.listener.switchToShadowRoot(id); -}; - -/** Add a cookie to the document. */ -GeckoDriver.prototype.addCookie = function*(cmd, resp) { - assert.content(this.context) - - let cb = msg => { - this.mm.removeMessageListener("Marionette:addCookie", cb); - let cookie = msg.json; - Services.cookies.add( - cookie.domain, - cookie.path, - cookie.name, - cookie.value, - cookie.secure, - cookie.httpOnly, - cookie.session, - cookie.expiry, - {}); // originAttributes - return true; - }; - - this.mm.addMessageListener("Marionette:addCookie", cb); - yield this.listener.addCookie(cmd.parameters.cookie); -}; - -/** - * Get all the cookies for the current domain. - * - * This is the equivalent of calling {@code document.cookie} and parsing - * the result. - */ -GeckoDriver.prototype.getCookies = function*(cmd, resp) { - assert.content(this.context) - - resp.body = yield this.listener.getCookies(); -}; - -/** Delete all cookies that are visible to a document. */ -GeckoDriver.prototype.deleteAllCookies = function*(cmd, resp) { - assert.content(this.context) - - let cb = msg => { - let cookie = msg.json; - cookieManager.remove( - cookie.host, - cookie.name, - cookie.path, - false, - cookie.originAttributes); - return true; - }; - - this.mm.addMessageListener("Marionette:deleteCookie", cb); - yield this.listener.deleteAllCookies(); - this.mm.removeMessageListener("Marionette:deleteCookie", cb); -}; - -/** Delete a cookie by name. */ -GeckoDriver.prototype.deleteCookie = function*(cmd, resp) { - assert.content(this.context) - - let cb = msg => { - this.mm.removeMessageListener("Marionette:deleteCookie", cb); - let cookie = msg.json; - cookieManager.remove( - cookie.host, - cookie.name, - cookie.path, - false, - cookie.originAttributes); - return true; - }; - - this.mm.addMessageListener("Marionette:deleteCookie", cb); - yield this.listener.deleteCookie(cmd.parameters.name); -}; - -/** - * Close the currently selected tab/window. - * - * With multiple open tabs present the currently selected tab will be closed. - * Otherwise the window itself will be closed. If it is the last window - * currently open, the window will not be closed to prevent a shutdown of the - * application. Instead the returned list of window handles is empty. - * - * @return {Array.<string>} - * Unique window handles of remaining windows. - */ -GeckoDriver.prototype.close = function (cmd, resp) { - let nwins = 0; - let winEn = Services.wm.getEnumerator(null); - - while (winEn.hasMoreElements()) { - let win = winEn.getNext(); - - // For browser windows count the tabs. Otherwise take the window itself. - let tabbrowser = browser.getTabBrowser(win); - if (tabbrowser) { - nwins += tabbrowser.tabs.length; - } else { - nwins++; - } - } - - // If there is only 1 window left, do not close it. Instead return a faked - // empty array of window handles. This will instruct geckodriver to terminate - // the application. - if (nwins == 1) { - return []; - } - - if (this.mm != globalMessageManager) { - this.mm.removeDelayedFrameScript(FRAME_SCRIPT); - } - - return this.curBrowser.closeTab().then(() => this.windowHandles); -}; - -/** - * Close the currently selected chrome window. - * - * If it is the last window currently open, the chrome window will not be - * closed to prevent a shutdown of the application. Instead the returned - * list of chrome window handles is empty. - * - * @return {Array.<string>} - * Unique chrome window handles of remaining chrome windows. - */ -GeckoDriver.prototype.closeChromeWindow = function (cmd, resp) { - assert.firefox(); - - // Get the total number of windows - let nwins = 0; - let winEn = Services.wm.getEnumerator(null); - - while (winEn.hasMoreElements()) { - nwins++; - winEn.getNext(); - } - - // If there is only 1 window left, do not close it. Instead return a faked - // empty array of window handles. This will instruct geckodriver to terminate - // the application. - if (nwins == 1) { - return []; - } - - // reset frame to the top-most frame - this.curFrame = null; - - if (this.mm != globalMessageManager) { - this.mm.removeDelayedFrameScript(FRAME_SCRIPT); - } - - return this.curBrowser.closeWindow().then(() => this.chromeWindowHandles); -}; - -/** Delete Marionette session. */ -GeckoDriver.prototype.deleteSession = function (cmd, resp) { - if (this.curBrowser !== null) { - // frame scripts can be safely reused - Preferences.set(CONTENT_LISTENER_PREF, false); - - // delete session in each frame in each browser - for (let win in this.browsers) { - let browser = this.browsers[win]; - for (let i in browser.knownFrames) { - globalMessageManager.broadcastAsyncMessage( - "Marionette:deleteSession" + browser.knownFrames[i], {}); - } - } - - let winEn = Services.wm.getEnumerator(null); - while (winEn.hasMoreElements()) { - let win = winEn.getNext(); - if (win.messageManager) { - win.messageManager.removeDelayedFrameScript(FRAME_SCRIPT); - } else { - logger.error( - `Could not remove listener from page ${win.location.href}`); - } - } - - this.curBrowser.frameManager.removeMessageManagerListeners( - globalMessageManager); - } - - this.switchToGlobalMessageManager(); - - // reset frame to the top-most frame - this.curFrame = null; - if (this.mainFrame) { - try { - this.mainFrame.focus(); - } catch (e) { - this.mainFrame = null; - } - } - - if (this.observing !== null) { - for (let topic in this.observing) { - Services.obs.removeObserver(this.observing[topic], topic); - } - this.observing = null; - } - - this.sandboxes.clear(); - cert.uninstallOverride(); - - this.sessionId = null; - this.capabilities = new session.Capabilities(); -}; - -/** Returns the current status of the Application Cache. */ -GeckoDriver.prototype.getAppCacheStatus = function* (cmd, resp) { - switch (this.context) { - case Context.CHROME: - throw new UnsupportedOperationError( - "Command 'getAppCacheStatus' is not yet available in chrome context"); - - case Context.CONTENT: - resp.body.value = yield this.listener.getAppCacheStatus(); - break; - } -}; - -/** - * Import script to the JS evaluation runtime. - * - * Imported scripts are exposed in the contexts of all subsequent - * calls to {@code executeScript}, {@code executeAsyncScript}, and - * {@code executeJSScript} by prepending them to the evaluated script. - * - * Scripts can be cleared with the {@code clearImportedScripts} command. - * - * @param {string} script - * Script to include. If the script is byte-by-byte equal to an - * existing imported script, it is not imported. - */ -GeckoDriver.prototype.importScript = function*(cmd, resp) { - let script = cmd.parameters.script; - this.importedScripts.for(this.context).add(script); -}; - -/** - * Clear all scripts that are imported into the JS evaluation runtime. - * - * Scripts can be imported using the {@code importScript} command. - */ -GeckoDriver.prototype.clearImportedScripts = function*(cmd, resp) { - this.importedScripts.for(this.context).clear(); -}; - -/** - * Takes a screenshot of a web element, current frame, or viewport. - * - * The screen capture is returned as a lossless PNG image encoded as - * a base 64 string. - * - * If called in the content context, the <code>id</code> argument is not null - * and refers to a present and visible web element's ID, the capture area - * will be limited to the bounding box of that element. Otherwise, the - * capture area will be the bounding box of the current frame. - * - * If called in the chrome context, the screenshot will always represent the - * entire viewport. - * - * @param {string=} id - * Optional web element reference to take a screenshot of. - * If undefined, a screenshot will be taken of the document element. - * @param {Array.<string>=} highlights - * List of web elements to highlight. - * @param {boolean} full - * True to take a screenshot of the entire document element. Is not - * considered if {@code id} is not defined. Defaults to true. - * @param {boolean=} hash - * True if the user requests a hash of the image data. - * @param {boolean=} scroll - * Scroll to element if |id| is provided. If undefined, it will - * scroll to the element. - * - * @return {string} - * If {@code hash} is false, PNG image encoded as base64 encoded string. If - * 'hash' is True, hex digest of the SHA-256 hash of the base64 encoded - * string. - */ -GeckoDriver.prototype.takeScreenshot = function (cmd, resp) { - let {id, highlights, full, hash, scroll} = cmd.parameters; - highlights = highlights || []; - let format = hash ? capture.Format.Hash : capture.Format.Base64; - - switch (this.context) { - case Context.CHROME: - let container = {frame: this.getCurrentWindow().document.defaultView}; - if (!container.frame) { - throw new NoSuchWindowError("Unable to locate window"); - } - - let highlightEls = highlights.map( - ref => this.curBrowser.seenEls.get(ref, container)); - - // viewport - let canvas; - if (!id && !full) { - canvas = capture.viewport(container.frame, highlightEls); - - // element or full document element - } else { - let node; - if (id) { - node = this.curBrowser.seenEls.get(id, container); - } else { - node = container.frame.document.documentElement; - } - - canvas = capture.element(node, highlightEls); - } - - switch (format) { - case capture.Format.Hash: - return capture.toHash(canvas); - - case capture.Format.Base64: - return capture.toBase64(canvas); - } - break; - - case Context.CONTENT: - return this.listener.takeScreenshot(format, cmd.parameters); - } -}; - -/** - * Get the current browser orientation. - * - * Will return one of the valid primary orientation values - * portrait-primary, landscape-primary, portrait-secondary, or - * landscape-secondary. - */ -GeckoDriver.prototype.getScreenOrientation = function (cmd, resp) { - assert.fennec(); - - resp.body.value = this.getCurrentWindow().screen.mozOrientation; -}; - -/** - * Set the current browser orientation. - * - * The supplied orientation should be given as one of the valid - * orientation values. If the orientation is unknown, an error will - * be raised. - * - * Valid orientations are "portrait" and "landscape", which fall - * back to "portrait-primary" and "landscape-primary" respectively, - * and "portrait-secondary" as well as "landscape-secondary". - */ -GeckoDriver.prototype.setScreenOrientation = function (cmd, resp) { - assert.fennec(); - - const ors = [ - "portrait", "landscape", - "portrait-primary", "landscape-primary", - "portrait-secondary", "landscape-secondary", - ]; - - let or = String(cmd.parameters.orientation); - assert.string(or); - let mozOr = or.toLowerCase(); - if (!ors.includes(mozOr)) { - throw new InvalidArgumentError(`Unknown screen orientation: ${or}`); - } - - let win = this.getCurrentWindow(); - if (!win.screen.mozLockOrientation(mozOr)) { - throw new WebDriverError(`Unable to set screen orientation: ${or}`); - } -}; - -/** - * Get the size of the browser window currently in focus. - * - * Will return the current browser window size in pixels. Refers to - * window outerWidth and outerHeight values, which include scroll bars, - * title bars, etc. - */ -GeckoDriver.prototype.getWindowSize = function (cmd, resp) { - let win = this.getCurrentWindow(); - resp.body.width = win.outerWidth; - resp.body.height = win.outerHeight; -}; - -/** - * Set the size of the browser window currently in focus. - * - * Not supported on B2G. The supplied width and height values refer to - * the window outerWidth and outerHeight values, which include scroll - * bars, title bars, etc. - */ -GeckoDriver.prototype.setWindowSize = function (cmd, resp) { - assert.firefox() - - let {width, height} = cmd.parameters; - let win = this.getCurrentWindow(); - win.resizeTo(width, height); - this.getWindowSize(cmd, resp); -}; - -/** - * Maximizes the user agent window as if the user pressed the maximise - * button. - * - * Not Supported on B2G or Fennec. - */ -GeckoDriver.prototype.maximizeWindow = function (cmd, resp) { - assert.firefox() - - let win = this.getCurrentWindow(); - win.maximize() -}; - -/** - * Dismisses a currently displayed tab modal, or returns no such alert if - * no modal is displayed. - */ -GeckoDriver.prototype.dismissDialog = function (cmd, resp) { - this._checkIfAlertIsPresent(); - - let {button0, button1} = this.dialog.ui; - (button1 ? button1 : button0).click(); - this.dialog = null; -}; - -/** - * Accepts a currently displayed tab modal, or returns no such alert if - * no modal is displayed. - */ -GeckoDriver.prototype.acceptDialog = function (cmd, resp) { - this._checkIfAlertIsPresent(); - - let {button0} = this.dialog.ui; - button0.click(); - this.dialog = null; -}; - -/** - * Returns the message shown in a currently displayed modal, or returns a no such - * alert error if no modal is currently displayed. - */ -GeckoDriver.prototype.getTextFromDialog = function (cmd, resp) { - this._checkIfAlertIsPresent(); - - let {infoBody} = this.dialog.ui; - resp.body.value = infoBody.textContent; -}; - -/** - * Sends keys to the input field of a currently displayed modal, or - * returns a no such alert error if no modal is currently displayed. If - * a tab modal is currently displayed but has no means for text input, - * an element not visible error is returned. - */ -GeckoDriver.prototype.sendKeysToDialog = function (cmd, resp) { - this._checkIfAlertIsPresent(); - - // see toolkit/components/prompts/content/commonDialog.js - let {loginContainer, loginTextbox} = this.dialog.ui; - if (loginContainer.hidden) { - throw new ElementNotInteractableError( - "This prompt does not accept text input"); - } - - let win = this.dialog.window ? this.dialog.window : this.getCurrentWindow(); - event.sendKeysToElement( - cmd.parameters.value, - loginTextbox, - {ignoreVisibility: true}, - win); -}; - -GeckoDriver.prototype._checkIfAlertIsPresent = function() { - if (!this.dialog || !this.dialog.ui) { - throw new NoAlertOpenError( - "No tab modal was open when attempting to get the dialog text"); - } -}; - -/** - * Enables or disables accepting new socket connections. - * - * By calling this method with `false` the server will not accept any further - * connections, but existing connections will not be forcible closed. Use `true` - * to re-enable accepting connections. - * - * Please note that when closing the connection via the client you can end-up in - * a non-recoverable state if it hasn't been enabled before. - * - * This method is used for custom in application shutdowns via marionette.quit() - * or marionette.restart(), like File -> Quit. - * - * @param {boolean} state - * True if the server should accept new socket connections. - */ -GeckoDriver.prototype.acceptConnections = function (cmd, resp) { - assert.boolean(cmd.parameters.value); - this._server.acceptConnections = cmd.parameters.value; -} - -/** - * Quits Firefox with the provided flags and tears down the current - * session. - */ -GeckoDriver.prototype.quitApplication = function (cmd, resp) { - assert.firefox("Bug 1298921 - In app initiated quit not yet available beside Firefox") - - let flags = Ci.nsIAppStartup.eAttemptQuit; - for (let k of cmd.parameters.flags || []) { - flags |= Ci.nsIAppStartup[k]; - } - - this._server.acceptConnections = false; - resp.send(); - - this.deleteSession(); - Services.startup.quit(flags); -}; - -GeckoDriver.prototype.installAddon = function (cmd, resp) { - assert.firefox() - - let path = cmd.parameters.path; - let temp = cmd.parameters.temporary || false; - if (typeof path == "undefined" || typeof path != "string" || - typeof temp != "boolean") { - throw InvalidArgumentError(); - } - - return addon.install(path, temp); -}; - -GeckoDriver.prototype.uninstallAddon = function (cmd, resp) { - assert.firefox() - - let id = cmd.parameters.id; - if (typeof id == "undefined" || typeof id != "string") { - throw new InvalidArgumentError(); - } - - return addon.uninstall(id); -}; - -/** - * Helper function to convert an outerWindowID into a UID that Marionette - * tracks. - */ -GeckoDriver.prototype.generateFrameId = function (id) { - let uid = id + (this.appName == "B2G" ? "-b2g" : ""); - return uid; -}; - -/** Receives all messages from content messageManager. */ -GeckoDriver.prototype.receiveMessage = function (message) { - switch (message.name) { - case "Marionette:ok": - case "Marionette:done": - case "Marionette:error": - // check if we need to remove the mozbrowserclose listener - if (this.mozBrowserClose !== null) { - let win = this.getCurrentWindow(); - win.removeEventListener("mozbrowserclose", this.mozBrowserClose, true); - this.mozBrowserClose = null; - } - break; - - case "Marionette:log": - // log server-side messages - logger.info(message.json.message); - break; - - case "Marionette:shareData": - // log messages from tests - if (message.json.log) { - this.marionetteLog.addAll(message.json.log); - } - break; - - case "Marionette:switchToModalOrigin": - this.curBrowser.frameManager.switchToModalOrigin(message); - this.mm = this.curBrowser.frameManager - .currentRemoteFrame.messageManager.get(); - break; - - case "Marionette:switchedToFrame": - if (message.json.restorePrevious) { - this.currentFrameElement = this.previousFrameElement; - } else { - // we don't arbitrarily save previousFrameElement, since - // we allow frame switching after modals appear, which would - // override this value and we'd lose our reference - if (message.json.storePrevious) { - this.previousFrameElement = this.currentFrameElement; - } - this.currentFrameElement = message.json.frameValue; - } - break; - - case "Marionette:getVisibleCookies": - let [currentPath, host] = message.json; - let isForCurrentPath = path => currentPath.indexOf(path) != -1; - let results = []; - - let en = cookieManager.getCookiesFromHost(host, {}); - while (en.hasMoreElements()) { - let cookie = en.getNext().QueryInterface(Ci.nsICookie2); - // take the hostname and progressively shorten - let hostname = host; - do { - if ((cookie.host == "." + hostname || cookie.host == hostname) && - isForCurrentPath(cookie.path)) { - results.push({ - "name": cookie.name, - "value": cookie.value, - "path": cookie.path, - "host": cookie.host, - "secure": cookie.isSecure, - "expiry": cookie.expires, - "httpOnly": cookie.isHttpOnly, - "originAttributes": cookie.originAttributes - }); - break; - } - hostname = hostname.replace(/^.*?\./, ""); - } while (hostname.indexOf(".") != -1); - } - return results; - - case "Marionette:emitTouchEvent": - globalMessageManager.broadcastAsyncMessage( - "MarionetteMainListener:emitTouchEvent", message.json); - break; - - case "Marionette:register": - let wid = message.json.value; - let be = message.target; - let rv = this.registerBrowser(wid, be); - return rv; - - case "Marionette:listenersAttached": - if (message.json.listenerId === this.curBrowser.curFrameId) { - // If remoteness gets updated we need to call newSession. In the case - // of desktop this just sets up a small amount of state that doesn't - // change over the course of a session. - this.sendAsync("newSession", this.capabilities); - this.curBrowser.flushPendingCommands(); - } - break; - } -}; - -GeckoDriver.prototype.responseCompleted = function () { - if (this.curBrowser !== null) { - this.curBrowser.pendingCommands = []; - } -}; - -/** - * Retrieve the localized string for the specified entity id. - * - * Example: - * localizeEntity(["chrome://global/locale/about.dtd"], "about.version") - * - * @param {Array.<string>} urls - * Array of .dtd URLs. - * @param {string} id - * The ID of the entity to retrieve the localized string for. - * - * @return {string} - * The localized string for the requested entity. - */ -GeckoDriver.prototype.localizeEntity = function (cmd, resp) { - let {urls, id} = cmd.parameters; - - if (!Array.isArray(urls)) { - throw new InvalidArgumentError("Value of `urls` should be of type 'Array'"); - } - if (typeof id != "string") { - throw new InvalidArgumentError("Value of `id` should be of type 'string'"); - } - - resp.body.value = l10n.localizeEntity(urls, id); -} - -/** - * Retrieve the localized string for the specified property id. - * - * Example: - * localizeProperty(["chrome://global/locale/findbar.properties"], "FastFind") - * - * @param {Array.<string>} urls - * Array of .properties URLs. - * @param {string} id - * The ID of the property to retrieve the localized string for. - * - * @return {string} - * The localized string for the requested property. - */ -GeckoDriver.prototype.localizeProperty = function (cmd, resp) { - let {urls, id} = cmd.parameters; - - if (!Array.isArray(urls)) { - throw new InvalidArgumentError("Value of `urls` should be of type 'Array'"); - } - if (typeof id != "string") { - throw new InvalidArgumentError("Value of `id` should be of type 'string'"); - } - - resp.body.value = l10n.localizeProperty(urls, id); -} - -GeckoDriver.prototype.commands = { - "getMarionetteID": GeckoDriver.prototype.getMarionetteID, - "sayHello": GeckoDriver.prototype.sayHello, - "newSession": GeckoDriver.prototype.newSession, - "getSessionCapabilities": GeckoDriver.prototype.getSessionCapabilities, - "log": GeckoDriver.prototype.log, - "getLogs": GeckoDriver.prototype.getLogs, - "setContext": GeckoDriver.prototype.setContext, - "getContext": GeckoDriver.prototype.getContext, - "executeScript": GeckoDriver.prototype.executeScript, - "getTimeouts": GeckoDriver.prototype.getTimeouts, - "timeouts": GeckoDriver.prototype.setTimeouts, // deprecated until Firefox 55 - "setTimeouts": GeckoDriver.prototype.setTimeouts, - "singleTap": GeckoDriver.prototype.singleTap, - "performActions": GeckoDriver.prototype.performActions, - "releaseActions": GeckoDriver.prototype.releaseActions, - "actionChain": GeckoDriver.prototype.actionChain, // deprecated - "multiAction": GeckoDriver.prototype.multiAction, // deprecated - "executeAsyncScript": GeckoDriver.prototype.executeAsyncScript, - "executeJSScript": GeckoDriver.prototype.executeJSScript, - "findElement": GeckoDriver.prototype.findElement, - "findElements": GeckoDriver.prototype.findElements, - "clickElement": GeckoDriver.prototype.clickElement, - "getElementAttribute": GeckoDriver.prototype.getElementAttribute, - "getElementProperty": GeckoDriver.prototype.getElementProperty, - "getElementText": GeckoDriver.prototype.getElementText, - "getElementTagName": GeckoDriver.prototype.getElementTagName, - "isElementDisplayed": GeckoDriver.prototype.isElementDisplayed, - "getElementValueOfCssProperty": GeckoDriver.prototype.getElementValueOfCssProperty, - "getElementRect": GeckoDriver.prototype.getElementRect, - "isElementEnabled": GeckoDriver.prototype.isElementEnabled, - "isElementSelected": GeckoDriver.prototype.isElementSelected, - "sendKeysToElement": GeckoDriver.prototype.sendKeysToElement, - "clearElement": GeckoDriver.prototype.clearElement, - "getTitle": GeckoDriver.prototype.getTitle, - "getWindowType": GeckoDriver.prototype.getWindowType, - "getPageSource": GeckoDriver.prototype.getPageSource, - "get": GeckoDriver.prototype.get, - "getCurrentUrl": GeckoDriver.prototype.getCurrentUrl, - "goBack": GeckoDriver.prototype.goBack, - "goForward": GeckoDriver.prototype.goForward, - "refresh": GeckoDriver.prototype.refresh, - "getWindowHandle": GeckoDriver.prototype.getWindowHandle, - "getChromeWindowHandle": GeckoDriver.prototype.getChromeWindowHandle, - "getCurrentChromeWindowHandle": GeckoDriver.prototype.getChromeWindowHandle, - "getWindowHandles": GeckoDriver.prototype.getWindowHandles, - "getChromeWindowHandles": GeckoDriver.prototype.getChromeWindowHandles, - "getWindowPosition": GeckoDriver.prototype.getWindowPosition, - "setWindowPosition": GeckoDriver.prototype.setWindowPosition, - "getActiveFrame": GeckoDriver.prototype.getActiveFrame, - "switchToFrame": GeckoDriver.prototype.switchToFrame, - "switchToParentFrame": GeckoDriver.prototype.switchToParentFrame, - "switchToWindow": GeckoDriver.prototype.switchToWindow, - "switchToShadowRoot": GeckoDriver.prototype.switchToShadowRoot, - "deleteSession": GeckoDriver.prototype.deleteSession, - "importScript": GeckoDriver.prototype.importScript, - "clearImportedScripts": GeckoDriver.prototype.clearImportedScripts, - "getAppCacheStatus": GeckoDriver.prototype.getAppCacheStatus, - "close": GeckoDriver.prototype.close, - "closeChromeWindow": GeckoDriver.prototype.closeChromeWindow, - "setTestName": GeckoDriver.prototype.setTestName, - "takeScreenshot": GeckoDriver.prototype.takeScreenshot, - "addCookie": GeckoDriver.prototype.addCookie, - "getCookies": GeckoDriver.prototype.getCookies, - "deleteAllCookies": GeckoDriver.prototype.deleteAllCookies, - "deleteCookie": GeckoDriver.prototype.deleteCookie, - "getActiveElement": GeckoDriver.prototype.getActiveElement, - "getScreenOrientation": GeckoDriver.prototype.getScreenOrientation, - "setScreenOrientation": GeckoDriver.prototype.setScreenOrientation, - "getWindowSize": GeckoDriver.prototype.getWindowSize, - "setWindowSize": GeckoDriver.prototype.setWindowSize, - "maximizeWindow": GeckoDriver.prototype.maximizeWindow, - "dismissDialog": GeckoDriver.prototype.dismissDialog, - "acceptDialog": GeckoDriver.prototype.acceptDialog, - "getTextFromDialog": GeckoDriver.prototype.getTextFromDialog, - "sendKeysToDialog": GeckoDriver.prototype.sendKeysToDialog, - "acceptConnections": GeckoDriver.prototype.acceptConnections, - "quitApplication": GeckoDriver.prototype.quitApplication, - - "localization:l10n:localizeEntity": GeckoDriver.prototype.localizeEntity, - "localization:l10n:localizeProperty": GeckoDriver.prototype.localizeProperty, - - "addon:install": GeckoDriver.prototype.installAddon, - "addon:uninstall": GeckoDriver.prototype.uninstallAddon, -}; - -function copy (obj) { - if (Array.isArray(obj)) { - return obj.slice(); - } else if (typeof obj == "object") { - return Object.assign({}, obj); - } - return obj; -} - -/** - * Get the outer window ID for the specified window. - * - * @param {nsIDOMWindow} win - * Window whose browser we need to access. - * - * @return {string} - * Returns the unique window ID. - */ -function getOuterWindowId(win) { - let id = win.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils) - .outerWindowID; - - return id.toString(); -} diff --git a/testing/marionette/element.js b/testing/marionette/element.js deleted file mode 100644 index 9687fb27d..000000000 --- a/testing/marionette/element.js +++ /dev/null @@ -1,1171 +0,0 @@ -/* 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/. */ - -"use strict"; - -const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; - -Cu.import("resource://gre/modules/Log.jsm"); - -Cu.import("chrome://marionette/content/assert.js"); -Cu.import("chrome://marionette/content/atom.js"); -Cu.import("chrome://marionette/content/error.js"); - -const logger = Log.repository.getLogger("Marionette"); - -/** - * This module provides shared functionality for dealing with DOM- - * and web elements in Marionette. - * - * A web element is an abstraction used to identify an element when it - * is transported across the protocol, between remote- and local ends. - * - * Each element has an associated web element reference (a UUID) that - * uniquely identifies the the element across all browsing contexts. The - * web element reference for every element representing the same element - * is the same. - * - * The @code{element.Store} provides a mapping between web element - * references and DOM elements for each browsing context. It also provides - * functionality for looking up and retrieving elements. - */ - -this.EXPORTED_SYMBOLS = ["element"]; - -const DOCUMENT_POSITION_DISCONNECTED = 1; -const XMLNS = "http://www.w3.org/1999/xhtml"; - -const uuidGen = Cc["@mozilla.org/uuid-generator;1"] - .getService(Ci.nsIUUIDGenerator); - -this.element = {}; - -element.Key = "element-6066-11e4-a52e-4f735466cecf"; -element.LegacyKey = "ELEMENT"; - -element.Strategy = { - ClassName: "class name", - Selector: "css selector", - ID: "id", - Name: "name", - LinkText: "link text", - PartialLinkText: "partial link text", - TagName: "tag name", - XPath: "xpath", - Anon: "anon", - AnonAttribute: "anon attribute", -}; - -/** - * Stores known/seen elements and their associated web element - * references. - * - * Elements are added by calling |add(el)| or |addAll(elements)|, and - * may be queried by their web element reference using |get(element)|. - */ -element.Store = class { - constructor() { - this.els = {}; - this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - } - - clear() { - this.els = {}; - } - - /** - * Make a collection of elements seen. - * - * The oder of the returned web element references is guaranteed to - * match that of the collection passed in. - * - * @param {NodeList} els - * Sequence of elements to add to set of seen elements. - * - * @return {Array.<WebElement>} - * List of the web element references associated with each element - * from |els|. - */ - addAll(els) { - let add = this.add.bind(this); - return [...els].map(add); - } - - /** - * Make an element seen. - * - * @param {nsIDOMElement} el - * Element to add to set of seen elements. - * - * @return {string} - * Web element reference associated with element. - */ - add(el) { - for (let i in this.els) { - let foundEl; - try { - foundEl = this.els[i].get(); - } catch (e) {} - - if (foundEl) { - if (new XPCNativeWrapper(foundEl) == new XPCNativeWrapper(el)) { - return i; - } - - // cleanup reference to gc'd element - } else { - delete this.els[i]; - } - } - - let id = element.generateUUID(); - this.els[id] = Cu.getWeakReference(el); - return id; - } - - /** - * Determine if the provided web element reference has been seen - * before/is in the element store. - * - * @param {string} uuid - * Element's associated web element reference. - * - * @return {boolean} - * True if element is in the store, false otherwise. - */ - has(uuid) { - return Object.keys(this.els).includes(uuid); - } - - /** - * Retrieve a DOM element by its unique web element reference/UUID. - * - * @param {string} uuid - * Web element reference, or UUID. - * @param {(nsIDOMWindow|ShadowRoot)} container - * Window and an optional shadow root that contains the element. - * - * @returns {nsIDOMElement} - * Element associated with reference. - * - * @throws {JavaScriptError} - * If the provided reference is unknown. - * @throws {StaleElementReferenceError} - * If element has gone stale, indicating it is no longer attached to - * the DOM provided in the container. - */ - get(uuid, container) { - let el = this.els[uuid]; - if (!el) { - throw new JavaScriptError(`Element reference not seen before: ${uuid}`); - } - - try { - el = el.get(); - } catch (e) { - el = null; - delete this.els[id]; - } - - // use XPCNativeWrapper to compare elements (see bug 834266) - let wrappedFrame = new XPCNativeWrapper(container.frame); - let wrappedShadowRoot; - if (container.shadowRoot) { - wrappedShadowRoot = new XPCNativeWrapper(container.shadowRoot); - } - let wrappedEl = new XPCNativeWrapper(el); - let wrappedContainer = { - frame: wrappedFrame, - shadowRoot: wrappedShadowRoot, - }; - if (!el || - !(wrappedEl.ownerDocument == wrappedFrame.document) || - element.isDisconnected(wrappedEl, wrappedContainer)) { - throw new StaleElementReferenceError( - error.pprint`The element reference of ${el} stale: ` + - "either the element is no longer attached to the DOM " + - "or the page has been refreshed"); - } - - return el; - } -}; - -/** - * Find a single element or a collection of elements starting at the - * document root or a given node. - * - * If |timeout| is above 0, an implicit search technique is used. - * This will wait for the duration of |timeout| for the element - * to appear in the DOM. - * - * See the |element.Strategy| enum for a full list of supported - * search strategies that can be passed to |strategy|. - * - * Available flags for |opts|: - * - * |all| - * If true, a multi-element search selector is used and a sequence - * of elements will be returned. Otherwise a single element. - * - * |timeout| - * Duration to wait before timing out the search. If |all| is - * false, a NoSuchElementError is thrown if unable to find - * the element within the timeout duration. - * - * |startNode| - * Element to use as the root of the search. - * - * @param {Object.<string, Window>} container - * Window object and an optional shadow root that contains the - * root shadow DOM element. - * @param {string} strategy - * Search strategy whereby to locate the element(s). - * @param {string} selector - * Selector search pattern. The selector must be compatible with - * the chosen search |strategy|. - * @param {Object.<string, ?>} opts - * Options. - * - * @return {Promise: (nsIDOMElement|Array<nsIDOMElement>)} - * Single element or a sequence of elements. - * - * @throws InvalidSelectorError - * If |strategy| is unknown. - * @throws InvalidSelectorError - * If |selector| is malformed. - * @throws NoSuchElementError - * If a single element is requested, this error will throw if the - * element is not found. - */ -element.find = function (container, strategy, selector, opts = {}) { - opts.all = !!opts.all; - opts.timeout = opts.timeout || 0; - - let searchFn; - if (opts.all) { - searchFn = findElements.bind(this); - } else { - searchFn = findElement.bind(this); - } - - return new Promise((resolve, reject) => { - let findElements = implicitlyWaitFor( - () => find_(container, strategy, selector, searchFn, opts), - opts.timeout); - - findElements.then(foundEls => { - // the following code ought to be moved into findElement - // and findElements when bug 1254486 is addressed - if (!opts.all && (!foundEls || foundEls.length == 0)) { - let msg; - switch (strategy) { - case element.Strategy.AnonAttribute: - msg = "Unable to locate anonymous element: " + JSON.stringify(selector); - break; - - default: - msg = "Unable to locate element: " + selector; - } - - reject(new NoSuchElementError(msg)); - } - - if (opts.all) { - resolve(foundEls); - } - resolve(foundEls[0]); - }, reject); - }); -}; - -function find_(container, strategy, selector, searchFn, opts) { - let rootNode = container.shadowRoot || container.frame.document; - let startNode; - - if (opts.startNode) { - startNode = opts.startNode; - } else { - switch (strategy) { - // For anonymous nodes the start node needs to be of type DOMElement, which - // will refer to :root in case of a DOMDocument. - case element.Strategy.Anon: - case element.Strategy.AnonAttribute: - if (rootNode instanceof Ci.nsIDOMDocument) { - startNode = rootNode.documentElement; - } - break; - - default: - startNode = rootNode; - } - } - - let res; - try { - res = searchFn(strategy, selector, rootNode, startNode); - } catch (e) { - throw new InvalidSelectorError( - `Given ${strategy} expression "${selector}" is invalid: ${e}`); - } - - if (res) { - if (opts.all) { - return res; - } - return [res]; - } - return []; -} - -/** - * Find a single element by XPath expression. - * - * @param {DOMElement} root - * Document root - * @param {DOMElement} startNode - * Where in the DOM hiearchy to begin searching. - * @param {string} expr - * XPath search expression. - * - * @return {DOMElement} - * First element matching expression. - */ -element.findByXPath = function (root, startNode, expr) { - let iter = root.evaluate(expr, startNode, null, - Ci.nsIDOMXPathResult.FIRST_ORDERED_NODE_TYPE, null); - return iter.singleNodeValue; -}; - -/** - * Find elements by XPath expression. - * - * @param {DOMElement} root - * Document root. - * @param {DOMElement} startNode - * Where in the DOM hierarchy to begin searching. - * @param {string} expr - * XPath search expression. - * - * @return {Array.<DOMElement>} - * Sequence of found elements matching expression. - */ -element.findByXPathAll = function (root, startNode, expr) { - let rv = []; - let iter = root.evaluate(expr, startNode, null, - Ci.nsIDOMXPathResult.ORDERED_NODE_ITERATOR_TYPE, null); - let el = iter.iterateNext(); - while (el) { - rv.push(el); - el = iter.iterateNext(); - } - return rv; -}; - -/** - * Find all hyperlinks dscendant of |node| which link text is |s|. - * - * @param {DOMElement} node - * Where in the DOM hierarchy to being searching. - * @param {string} s - * Link text to search for. - * - * @return {Array.<DOMAnchorElement>} - * Sequence of link elements which text is |s|. - */ -element.findByLinkText = function (node, s) { - return filterLinks(node, link => link.text.trim() === s); -}; - -/** - * Find all hyperlinks descendant of |node| which link text contains |s|. - * - * @param {DOMElement} node - * Where in the DOM hierachy to begin searching. - * @param {string} s - * Link text to search for. - * - * @return {Array.<DOMAnchorElement>} - * Sequence of link elements which text containins |s|. - */ -element.findByPartialLinkText = function (node, s) { - return filterLinks(node, link => link.text.indexOf(s) != -1); -}; - -/** - * Filters all hyperlinks that are descendant of |node| by |predicate|. - * - * @param {DOMElement} node - * Where in the DOM hierarchy to begin searching. - * @param {function(DOMAnchorElement): boolean} predicate - * Function that determines if given link should be included in - * return value or filtered away. - * - * @return {Array.<DOMAnchorElement>} - * Sequence of link elements matching |predicate|. - */ -function filterLinks(node, predicate) { - let rv = []; - for (let link of node.getElementsByTagName("a")) { - if (predicate(link)) { - rv.push(link); - } - } - return rv; -} - -/** - * Finds a single element. - * - * @param {element.Strategy} using - * Selector strategy to use. - * @param {string} value - * Selector expression. - * @param {DOMElement} rootNode - * Document root. - * @param {DOMElement=} startNode - * Optional node from which to start searching. - * - * @return {DOMElement} - * Found elements. - * - * @throws {InvalidSelectorError} - * If strategy |using| is not recognised. - * @throws {Error} - * If selector expression |value| is malformed. - */ -function findElement(using, value, rootNode, startNode) { - switch (using) { - case element.Strategy.ID: - if (startNode.getElementById) { - return startNode.getElementById(value); - } - return element.findByXPath(rootNode, startNode, `.//*[@id="${value}"]`); - - case element.Strategy.Name: - if (startNode.getElementsByName) { - return startNode.getElementsByName(value)[0]; - } - return element.findByXPath(rootNode, startNode, `.//*[@name="${value}"]`); - - case element.Strategy.ClassName: - // works for >= Firefox 3 - return startNode.getElementsByClassName(value)[0]; - - case element.Strategy.TagName: - // works for all elements - return startNode.getElementsByTagName(value)[0]; - - case element.Strategy.XPath: - return element.findByXPath(rootNode, startNode, value); - - case element.Strategy.LinkText: - for (let link of startNode.getElementsByTagName("a")) { - if (link.text.trim() === value) { - return link; - } - } - break; - - case element.Strategy.PartialLinkText: - for (let link of startNode.getElementsByTagName("a")) { - if (link.text.indexOf(value) != -1) { - return link; - } - } - break; - - case element.Strategy.Selector: - try { - return startNode.querySelector(value); - } catch (e) { - throw new InvalidSelectorError(`${e.message}: "${value}"`); - } - break; - - case element.Strategy.Anon: - return rootNode.getAnonymousNodes(startNode); - - case element.Strategy.AnonAttribute: - let attr = Object.keys(value)[0]; - return rootNode.getAnonymousElementByAttribute(startNode, attr, value[attr]); - - default: - throw new InvalidSelectorError(`No such strategy: ${using}`); - } -} - -/** - * Find multiple elements. - * - * @param {element.Strategy} using - * Selector strategy to use. - * @param {string} value - * Selector expression. - * @param {DOMElement} rootNode - * Document root. - * @param {DOMElement=} startNode - * Optional node from which to start searching. - * - * @return {DOMElement} - * Found elements. - * - * @throws {InvalidSelectorError} - * If strategy |using| is not recognised. - * @throws {Error} - * If selector expression |value| is malformed. - */ -function findElements(using, value, rootNode, startNode) { - switch (using) { - case element.Strategy.ID: - value = `.//*[@id="${value}"]`; - - // fall through - case element.Strategy.XPath: - return element.findByXPathAll(rootNode, startNode, value); - - case element.Strategy.Name: - if (startNode.getElementsByName) { - return startNode.getElementsByName(value); - } - return element.findByXPathAll(rootNode, startNode, `.//*[@name="${value}"]`); - - case element.Strategy.ClassName: - return startNode.getElementsByClassName(value); - - case element.Strategy.TagName: - return startNode.getElementsByTagName(value); - - case element.Strategy.LinkText: - return element.findByLinkText(startNode, value); - - case element.Strategy.PartialLinkText: - return element.findByPartialLinkText(startNode, value); - - case element.Strategy.Selector: - return startNode.querySelectorAll(value); - - case element.Strategy.Anon: - return rootNode.getAnonymousNodes(startNode); - - case element.Strategy.AnonAttribute: - let attr = Object.keys(value)[0]; - let el = rootNode.getAnonymousElementByAttribute(startNode, attr, value[attr]); - if (el) { - return [el]; - } - return []; - - default: - throw new InvalidSelectorError(`No such strategy: ${using}`); - } -} - -/** - * Runs function off the main thread until its return value is truthy - * or the provided timeout is reached. The function is guaranteed to be - * run at least once, irregardless of the timeout. - * - * A truthy return value constitutes a truthful boolean, positive number, - * object, or non-empty array. - * - * The |func| is evaluated every |interval| for as long as its runtime - * duration does not exceed |interval|. If the runtime evaluation duration - * of |func| is greater than |interval|, evaluations of |func| are queued. - * - * @param {function(): ?} func - * Function to run off the main thread. - * @param {number} timeout - * Desired timeout. If 0 or less than the runtime evaluation time - * of |func|, |func| is guaranteed to run at least once. - * @param {number=} interval - * Duration between each poll of |func| in milliseconds. Defaults to - * 100 milliseconds. - * - * @return {Promise} - * Yields the return value from |func|. The promise is rejected if - * |func| throws. - */ -function implicitlyWaitFor(func, timeout, interval = 100) { - let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - - return new Promise((resolve, reject) => { - let startTime = new Date().getTime(); - let endTime = startTime + timeout; - - let elementSearch = function() { - let res; - try { - res = func(); - } catch (e) { - reject(e); - } - - if ( - // collections that might contain web elements - // should be checked until they are not empty - (element.isCollection(res) && res.length > 0) - - // !![] (ensuring boolean type on empty array) always returns true - // and we can only use it on non-collections - || (!element.isCollection(res) && !!res) - - // return immediately if timeout is 0, - // allowing |func| to be evaluted at least once - || startTime == endTime - - // return if timeout has elapsed - || new Date().getTime() >= endTime - ) { - resolve(res); - } - }; - - // the repeating slack timer waits |interval| - // before invoking |elementSearch| - elementSearch(); - - timer.init(elementSearch, interval, Ci.nsITimer.TYPE_REPEATING_SLACK); - - // cancel timer and propagate result - }).then(res => { - timer.cancel(); - return res; - }, err => { - timer.cancel(); - throw err; - }); -} - -/** Determines if |obj| is an HTML or JS collection. */ -element.isCollection = function (seq) { - switch (Object.prototype.toString.call(seq)) { - case "[object Arguments]": - case "[object Array]": - case "[object FileList]": - case "[object HTMLAllCollection]": - case "[object HTMLCollection]": - case "[object HTMLFormControlsCollection]": - case "[object HTMLOptionsCollection]": - case "[object NodeList]": - return true; - - default: - return false; - } -}; - -element.makeWebElement = function (uuid) { - return { - [element.Key]: uuid, - [element.LegacyKey]: uuid, - }; -}; - -/** - * Checks if |ref| has either |element.Key| or |element.LegacyKey| as properties. - * - * @param {?} ref - * Object that represents a web element reference. - * @return {boolean} - * True if |ref| has either expected property. - */ -element.isWebElementReference = function (ref) { - let properties = Object.getOwnPropertyNames(ref); - return properties.includes(element.Key) || properties.includes(element.LegacyKey); -}; - -element.generateUUID = function() { - let uuid = uuidGen.generateUUID().toString(); - return uuid.substring(1, uuid.length - 1); -}; - -/** - * Convert any web elements in arbitrary objects to DOM elements by - * looking them up in the seen element store. - * - * @param {?} obj - * Arbitrary object containing web elements. - * @param {element.Store} seenEls - * Element store to use for lookup of web element references. - * @param {Window} win - * Window. - * @param {ShadowRoot} shadowRoot - * Shadow root. - * - * @return {?} - * Same object as provided by |obj| with the web elements replaced - * by DOM elements. - */ -element.fromJson = function ( - obj, seenEls, win, shadowRoot = undefined) { - switch (typeof obj) { - case "boolean": - case "number": - case "string": - return obj; - - case "object": - if (obj === null) { - return obj; - } - - // arrays - else if (Array.isArray(obj)) { - return obj.map(e => element.fromJson(e, seenEls, win, shadowRoot)); - } - - // web elements - else if (Object.keys(obj).includes(element.Key) || - Object.keys(obj).includes(element.LegacyKey)) { - let uuid = obj[element.Key] || obj[element.LegacyKey]; - let el = seenEls.get(uuid, {frame: win, shadowRoot: shadowRoot}); - if (!el) { - throw new WebDriverError(`Unknown element: ${uuid}`); - } - return el; - } - - // arbitrary objects - else { - let rv = {}; - for (let prop in obj) { - rv[prop] = element.fromJson(obj[prop], seenEls, win, shadowRoot); - } - return rv; - } - } -}; - -/** - * Convert arbitrary objects to JSON-safe primitives that can be - * transported over the Marionette protocol. - * - * Any DOM elements are converted to web elements by looking them up - * and/or adding them to the element store provided. - * - * @param {?} obj - * Object to be marshaled. - * @param {element.Store} seenEls - * Element store to use for lookup of web element references. - * - * @return {?} - * Same object as provided by |obj| with the elements replaced by - * web elements. - */ -element.toJson = function (obj, seenEls) { - let t = Object.prototype.toString.call(obj); - - // null - if (t == "[object Undefined]" || t == "[object Null]") { - return null; - } - - // literals - else if (t == "[object Boolean]" || t == "[object Number]" || t == "[object String]") { - return obj; - } - - // Array, NodeList, HTMLCollection, et al. - else if (element.isCollection(obj)) { - return [...obj].map(el => element.toJson(el, seenEls)); - } - - // HTMLElement - else if ("nodeType" in obj && obj.nodeType == 1) { - let uuid = seenEls.add(obj); - return element.makeWebElement(uuid); - } - - // arbitrary objects + files - else { - let rv = {}; - for (let prop in obj) { - try { - rv[prop] = element.toJson(obj[prop], seenEls); - } catch (e if (e.result == Cr.NS_ERROR_NOT_IMPLEMENTED)) { - logger.debug(`Skipping ${prop}: ${e.message}`); - } - } - return rv; - } -}; - -/** - * Check if the element is detached from the current frame as well as - * the optional shadow root (when inside a Shadow DOM context). - * - * @param {nsIDOMElement} el - * Element to be checked. - * @param {Container} container - * Container with |frame|, which is the window object that contains - * the element, and an optional |shadowRoot|. - * - * @return {boolean} - * Flag indicating that the element is disconnected. - */ -element.isDisconnected = function (el, container = {}) { - const {frame, shadowRoot} = container; - assert.defined(frame); - - // shadow dom - if (frame.ShadowRoot && shadowRoot) { - if (el.compareDocumentPosition(shadowRoot) & - DOCUMENT_POSITION_DISCONNECTED) { - return true; - } - - // looking for next possible ShadowRoot ancestor - let parent = shadowRoot.host; - while (parent && !(parent instanceof frame.ShadowRoot)) { - parent = parent.parentNode; - } - return element.isDisconnected( - shadowRoot.host, - {frame: frame, shadowRoot: parent}); - - // outside shadow dom - } else { - let docEl = frame.document.documentElement; - return el.compareDocumentPosition(docEl) & - DOCUMENT_POSITION_DISCONNECTED; - } -}; - -/** - * This function generates a pair of coordinates relative to the viewport - * given a target element and coordinates relative to that element's - * top-left corner. - * - * @param {Node} node - * Target node. - * @param {number=} xOffset - * Horizontal offset relative to target's top-left corner. - * Defaults to the centre of the target's bounding box. - * @param {number=} yOffset - * Vertical offset relative to target's top-left corner. Defaults to - * the centre of the target's bounding box. - * - * @return {Object.<string, number>} - * X- and Y coordinates. - * - * @throws TypeError - * If |xOffset| or |yOffset| are not numbers. - */ -element.coordinates = function ( - node, xOffset = undefined, yOffset = undefined) { - - let box = node.getBoundingClientRect(); - - if (typeof xOffset == "undefined" || xOffset === null) { - xOffset = box.width / 2.0; - } - if (typeof yOffset == "undefined" || yOffset === null) { - yOffset = box.height / 2.0; - } - - if (typeof yOffset != "number" || typeof xOffset != "number") { - throw new TypeError("Offset must be a number"); - } - - return { - x: box.left + xOffset, - y: box.top + yOffset, - }; -}; - -/** - * This function returns true if the node is in the viewport. - * - * @param {Element} el - * Target element. - * @param {number=} x - * Horizontal offset relative to target. Defaults to the centre of - * the target's bounding box. - * @param {number=} y - * Vertical offset relative to target. Defaults to the centre of - * the target's bounding box. - * - * @return {boolean} - * True if if |el| is in viewport, false otherwise. - */ -element.inViewport = function (el, x = undefined, y = undefined) { - let win = el.ownerDocument.defaultView; - let c = element.coordinates(el, x, y); - let vp = { - top: win.pageYOffset, - left: win.pageXOffset, - bottom: (win.pageYOffset + win.innerHeight), - right: (win.pageXOffset + win.innerWidth) - }; - - return (vp.left <= c.x + win.pageXOffset && - c.x + win.pageXOffset <= vp.right && - vp.top <= c.y + win.pageYOffset && - c.y + win.pageYOffset <= vp.bottom); -}; - -/** - * Gets the element's container element. - * - * An element container is defined by the WebDriver - * specification to be an <option> element in a valid element context - * (https://html.spec.whatwg.org/#concept-element-contexts), meaning - * that it has an ancestral element that is either <datalist> or <select>. - * - * If the element does not have a valid context, its container element - * is itself. - * - * @param {Element} el - * Element to get the container of. - * - * @return {Element} - * Container element of |el|. - */ -element.getContainer = function (el) { - if (el.localName != "option") { - return el; - } - - function validContext(ctx) { - return ctx.localName == "datalist" || ctx.localName == "select"; - } - - // does <option> have a valid context, - // meaning is it a child of <datalist> or <select>? - let parent = el; - while (parent.parentNode && !validContext(parent)) { - parent = parent.parentNode; - } - - if (!validContext(parent)) { - return el; - } - return parent; -}; - -/** - * An element is in view if it is a member of its own pointer-interactable - * paint tree. - * - * This means an element is considered to be in view, but not necessarily - * pointer-interactable, if it is found somewhere in the - * |elementsFromPoint| list at |el|'s in-view centre coordinates. - * - * Before running the check, we change |el|'s pointerEvents style property - * to "auto", since elements without pointer events enabled do not turn - * up in the paint tree we get from document.elementsFromPoint. This is - * a specialisation that is only relevant when checking if the element is - * in view. - * - * @param {Element} el - * Element to check if is in view. - * - * @return {boolean} - * True if |el| is inside the viewport, or false otherwise. - */ -element.isInView = function (el) { - let originalPointerEvents = el.style.pointerEvents; - try { - el.style.pointerEvents = "auto"; - const tree = element.getPointerInteractablePaintTree(el); - return tree.includes(el); - } finally { - el.style.pointerEvents = originalPointerEvents; - } -}; - -/** - * This function throws the visibility of the element error if the element is - * not displayed or the given coordinates are not within the viewport. - * - * @param {Element} el - * Element to check if visible. - * @param {number=} x - * Horizontal offset relative to target. Defaults to the centre of - * the target's bounding box. - * @param {number=} y - * Vertical offset relative to target. Defaults to the centre of - * the target's bounding box. - * - * @return {boolean} - * True if visible, false otherwise. - */ -element.isVisible = function (el, x = undefined, y = undefined) { - let win = el.ownerDocument.defaultView; - - // Bug 1094246: webdriver's isShown doesn't work with content xul - if (!element.isXULElement(el) && !atom.isElementDisplayed(el, win)) { - return false; - } - - if (el.tagName.toLowerCase() == "body") { - return true; - } - - if (!element.inViewport(el, x, y)) { - element.scrollIntoView(el); - if (!element.inViewport(el)) { - return false; - } - } - return true; -}; - -/** - * A pointer-interactable element is defined to be the first - * non-transparent element, defined by the paint order found at the centre - * point of its rectangle that is inside the viewport, excluding the size - * of any rendered scrollbars. - * - * @param {DOMElement} el - * Element determine if is pointer-interactable. - * - * @return {boolean} - * True if interactable, false otherwise. - */ -element.isPointerInteractable = function (el) { - let tree = element.getPointerInteractablePaintTree(el); - return tree[0] === el; -}; - -/** - * Calculate the in-view centre point of the area of the given DOM client - * rectangle that is inside the viewport. - * - * @param {DOMRect} rect - * Element off a DOMRect sequence produced by calling |getClientRects| - * on a |DOMElement|. - * @param {nsIDOMWindow} win - * Current browsing context. - * - * @return {Map.<string, number>} - * X and Y coordinates that denotes the in-view centre point of |rect|. - */ -element.getInViewCentrePoint = function (rect, win) { - const {max, min} = Math; - - let x = { - left: max(0, min(rect.x, rect.x + rect.width)), - right: min(win.innerWidth, max(rect.x, rect.x + rect.width)), - }; - let y = { - top: max(0, min(rect.y, rect.y + rect.height)), - bottom: min(win.innerHeight, max(rect.y, rect.y + rect.height)), - }; - - return { - x: (x.left + x.right) / 2, - y: (y.top + y.bottom) / 2, - }; -}; - -/** - * Produces a pointer-interactable elements tree from a given element. - * - * The tree is defined by the paint order found at the centre point of - * the element's rectangle that is inside the viewport, excluding the size - * of any rendered scrollbars. - * - * @param {DOMElement} el - * Element to determine if is pointer-interactable. - * - * @return {Array.<DOMElement>} - * Sequence of elements in paint order. - */ -element.getPointerInteractablePaintTree = function (el) { - const doc = el.ownerDocument; - const win = doc.defaultView; - - // pointer-interactable elements tree, step 1 - if (element.isDisconnected(el, {frame: win})) { - return []; - } - - // steps 2-3 - let rects = el.getClientRects(); - if (rects.length == 0) { - return []; - } - - // step 4 - let centre = element.getInViewCentrePoint(rects[0], win); - - // step 5 - return doc.elementsFromPoint(centre.x, centre.y); -}; - -// TODO(ato): Not implemented. -// In fact, it's not defined in the spec. -element.isKeyboardInteractable = function (el) { - return true; -}; - -/** - * Attempts to scroll into view |el|. - * - * @param {DOMElement} el - * Element to scroll into view. - */ -element.scrollIntoView = function (el) { - if (el.scrollIntoView) { - el.scrollIntoView({block: "end", inline: "nearest", behavior: "instant"}); - } -}; - -element.isXULElement = function (el) { - let ns = atom.getElementAttribute(el, "namespaceURI"); - return ns.indexOf("there.is.only.xul") >= 0; -}; - -const boolEls = { - audio: ["autoplay", "controls", "loop", "muted"], - button: ["autofocus", "disabled", "formnovalidate"], - details: ["open"], - dialog: ["open"], - fieldset: ["disabled"], - form: ["novalidate"], - iframe: ["allowfullscreen"], - img: ["ismap"], - input: ["autofocus", "checked", "disabled", "formnovalidate", "multiple", "readonly", "required"], - keygen: ["autofocus", "disabled"], - menuitem: ["checked", "default", "disabled"], - object: ["typemustmatch"], - ol: ["reversed"], - optgroup: ["disabled"], - option: ["disabled", "selected"], - script: ["async", "defer"], - select: ["autofocus", "disabled", "multiple", "required"], - textarea: ["autofocus", "disabled", "readonly", "required"], - track: ["default"], - video: ["autoplay", "controls", "loop", "muted"], -}; - -/** - * Tests if the attribute is a boolean attribute on element. - * - * @param {DOMElement} el - * Element to test if |attr| is a boolean attribute on. - * @param {string} attr - * Attribute to test is a boolean attribute. - * - * @return {boolean} - * True if the attribute is boolean, false otherwise. - */ -element.isBooleanAttribute = function (el, attr) { - if (el.namespaceURI !== XMLNS) { - return false; - } - - // global boolean attributes that apply to all HTML elements, - // except for custom elements - if ((attr == "hidden" || attr == "itemscope") && !el.localName.includes("-")) { - return true; - } - - if (!boolEls.hasOwnProperty(el.localName)) { - return false; - } - return boolEls[el.localName].includes(attr) -}; diff --git a/testing/marionette/error.js b/testing/marionette/error.js deleted file mode 100644 index c36dace25..000000000 --- a/testing/marionette/error.js +++ /dev/null @@ -1,500 +0,0 @@ -/* 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/. */ - -"use strict"; - -const {interfaces: Ci, utils: Cu} = Components; - -const ERRORS = new Set([ - "ElementClickInterceptedError", - "ElementNotAccessibleError", - "ElementNotInteractableError", - "InsecureCertificateError", - "InvalidArgumentError", - "InvalidElementStateError", - "InvalidSelectorError", - "InvalidSessionIDError", - "JavaScriptError", - "MoveTargetOutOfBoundsError", - "NoAlertOpenError", - "NoSuchElementError", - "NoSuchFrameError", - "NoSuchWindowError", - "ScriptTimeoutError", - "SessionNotCreatedError", - "StaleElementReferenceError", - "TimeoutError", - "UnableToSetCookieError", - "UnknownCommandError", - "UnknownError", - "UnsupportedOperationError", - "WebDriverError", -]); - -const BUILTIN_ERRORS = new Set([ - "Error", - "EvalError", - "InternalError", - "RangeError", - "ReferenceError", - "SyntaxError", - "TypeError", - "URIError", -]); - -this.EXPORTED_SYMBOLS = ["error"].concat(Array.from(ERRORS)); - -this.error = {}; - -/** - * Checks if obj is an instance of the Error prototype in a safe manner. - * Prefer using this over using instanceof since the Error prototype - * isn't unique across browsers, and XPCOM nsIException's are special - * snowflakes. - * - * @param {*} val - * Any value that should be undergo the test for errorness. - * @return {boolean} - * True if error, false otherwise. - */ -error.isError = function (val) { - if (val === null || typeof val != "object") { - return false; - } else if (val instanceof Ci.nsIException) { - return true; - } else { - // DOMRectList errors on string comparison - try { - let proto = Object.getPrototypeOf(val); - return BUILTIN_ERRORS.has(proto.toString()); - } catch (e) { - return false; - } - } -}; - -/** - * Checks if obj is an object in the WebDriverError prototypal chain. - */ -error.isWebDriverError = function (obj) { - return error.isError(obj) && - ("name" in obj && ERRORS.has(obj.name)); -}; - -/** - * Wraps any error as a WebDriverError. If the given error is already in - * the WebDriverError prototype chain, this function returns it - * unmodified. - */ -error.wrap = function (err) { - if (error.isWebDriverError(err)) { - return err; - } - return new WebDriverError(err); -}; - -/** - * Unhandled error reporter. Dumps the error and its stacktrace to console, - * and reports error to the Browser Console. - */ -error.report = function (err) { - let msg = "Marionette threw an error: " + error.stringify(err); - dump(msg + "\n"); - if (Cu.reportError) { - Cu.reportError(msg); - } -}; - -/** - * Prettifies an instance of Error and its stacktrace to a string. - */ -error.stringify = function (err) { - try { - let s = err.toString(); - if ("stack" in err) { - s += "\n" + err.stack; - } - return s; - } catch (e) { - return "<unprintable error>"; - } -}; - -/** - * Pretty-print values passed to template strings. - * - * Usage: - * - * let bool = {value: true}; - * error.pprint`Expected boolean, got ${bool}`; - * => 'Expected boolean, got [object Object] {"value": true}' - * - * let htmlElement = document.querySelector("input#foo"); - * error.pprint`Expected element ${htmlElement}`; - * => 'Expected element <input id="foo" class="bar baz">' - */ -error.pprint = function (ss, ...values) { - function prettyObject (obj) { - let proto = Object.prototype.toString.call(obj); - let s = ""; - try { - s = JSON.stringify(obj); - } catch (e if e instanceof TypeError) { - s = `<${e.message}>`; - } - return proto + " " + s; - } - - function prettyElement (el) { - let ident = []; - if (el.id) { - ident.push(`id="${el.id}"`); - } - if (el.classList.length > 0) { - ident.push(`class="${el.className}"`); - } - - let idents = ""; - if (ident.length > 0) { - idents = " " + ident.join(" "); - } - - return `<${el.localName}${idents}>`; - } - - let res = []; - for (let i = 0; i < ss.length; i++) { - res.push(ss[i]); - if (i < values.length) { - let val = values[i]; - let typ = Object.prototype.toString.call(val); - let s; - try { - if (val && val.nodeType === 1) { - s = prettyElement(val); - } else { - s = prettyObject(val); - } - } catch (e) { - s = typeof val; - } - res.push(s); - } - } - return res.join(""); -}; - -/** - * WebDriverError is the prototypal parent of all WebDriver errors. - * It should not be used directly, as it does not correspond to a real - * error in the specification. - */ -class WebDriverError extends Error { - /** - * @param {(string|Error)=} x - * Optional string describing error situation or Error instance - * to propagate. - */ - constructor (x) { - super(x); - this.name = this.constructor.name; - this.status = "webdriver error"; - - // Error's ctor does not preserve x' stack - if (error.isError(x)) { - this.stack = x.stack; - } - } - - toJSON () { - return { - error: this.status, - message: this.message || "", - stacktrace: this.stack || "", - } - } - - static fromJSON (json) { - if (typeof json.error == "undefined") { - let s = JSON.stringify(json); - throw new TypeError("Undeserialisable error type: " + s); - } - if (!STATUSES.has(json.error)) { - throw new TypeError("Not of WebDriverError descent: " + json.error); - } - - let cls = STATUSES.get(json.error); - let err = new cls(); - if ("message" in json) { - err.message = json.message; - } - if ("stacktrace" in json) { - err.stack = json.stacktrace; - } - return err; - } -} - -class ElementNotAccessibleError extends WebDriverError { - constructor (message) { - super(message); - this.status = "element not accessible"; - } -} - -/** - * An element click could not be completed because the element receiving - * the events is obscuring the element that was requested clicked. - * - * @param {Element=} obscuredEl - * Element obscuring the element receiving the click. Providing this - * is not required, but will produce a nicer error message. - * @param {Map.<string, number>} coords - * Original click location. Providing this is not required, but - * will produce a nicer error message. - */ -class ElementClickInterceptedError extends WebDriverError { - constructor (obscuredEl = undefined, coords = undefined) { - let msg = ""; - if (obscuredEl && coords) { - const doc = obscuredEl.ownerDocument; - const overlayingEl = doc.elementFromPoint(coords.x, coords.y); - - switch (obscuredEl.style.pointerEvents) { - case "none": - msg = error.pprint`Element ${obscuredEl} is not clickable ` + - `at point (${coords.x},${coords.y}) ` + - `because it does not have pointer events enabled, ` + - error.pprint`and element ${overlayingEl} ` + - `would receive the click instead`; - break; - - default: - msg = error.pprint`Element ${obscuredEl} is not clickable ` + - `at point (${coords.x},${coords.y}) ` + - error.pprint`because another element ${overlayingEl} ` + - `obscures it`; - break; - } - } - - super(msg); - this.status = "element click intercepted"; - } -} - -class ElementNotInteractableError extends WebDriverError { - constructor (message) { - super(message); - this.status = "element not interactable"; - } -} - -class InsecureCertificateError extends WebDriverError { - constructor (message) { - super(message); - this.status = "insecure certificate"; - } -} - -class InvalidArgumentError extends WebDriverError { - constructor (message) { - super(message); - this.status = "invalid argument"; - } -} - -class InvalidElementStateError extends WebDriverError { - constructor (message) { - super(message); - this.status = "invalid element state"; - } -} - -class InvalidSelectorError extends WebDriverError { - constructor (message) { - super(message); - this.status = "invalid selector"; - } -} - -class InvalidSessionIDError extends WebDriverError { - constructor (message) { - super(message); - this.status = "invalid session id"; - } -} - -/** - * Creates a richly annotated error for an error situation that occurred - * whilst evaluating injected scripts. - */ -class JavaScriptError extends WebDriverError { - /** - * @param {(string|Error)} x - * An Error object instance or a string describing the error - * situation. - * @param {string=} fnName - * Name of the function to use in the stack trace message. - * @param {string=} file - * Filename of the test file on the client. - * @param {number=} line - * Line number of |file|. - * @param {string=} script - * Script being executed, in text form. - */ - constructor ( - x, - fnName = undefined, - file = undefined, - line = undefined, - script = undefined) { - let msg = String(x); - let trace = ""; - - if (fnName) { - trace += fnName; - if (file) { - trace += ` @${file}`; - if (line) { - trace += `, line ${line}`; - } - } - } - - if (error.isError(x)) { - let jsStack = x.stack.split("\n"); - let match = jsStack[0].match(/:(\d+):\d+$/); - let jsLine = match ? parseInt(match[1]) : 0; - if (script) { - let src = script.split("\n")[jsLine]; - trace += "\n" + - `inline javascript, line ${jsLine}\n` + - `src: "${src}"`; - } - trace += "\nStack:\n" + x.stack; - } - - super(msg); - this.status = "javascript error"; - this.stack = trace; - } -} - -class MoveTargetOutOfBoundsError extends WebDriverError { - constructor (message) { - super(message); - this.status = "move target out of bounds"; - } -} - -class NoAlertOpenError extends WebDriverError { - constructor (message) { - super(message); - this.status = "no such alert"; - } -} - -class NoSuchElementError extends WebDriverError { - constructor (message) { - super(message); - this.status = "no such element"; - } -} - -class NoSuchFrameError extends WebDriverError { - constructor (message) { - super(message); - this.status = "no such frame"; - } -} - -class NoSuchWindowError extends WebDriverError { - constructor (message) { - super(message); - this.status = "no such window"; - } -} - -class ScriptTimeoutError extends WebDriverError { - constructor (message) { - super(message); - this.status = "script timeout"; - } -} - -class SessionNotCreatedError extends WebDriverError { - constructor (message) { - super(message); - this.status = "session not created"; - } -} - -class StaleElementReferenceError extends WebDriverError { - constructor (message) { - super(message); - this.status = "stale element reference"; - } -} - -class TimeoutError extends WebDriverError { - constructor (message) { - super(message); - this.status = "timeout"; - } -} - -class UnableToSetCookieError extends WebDriverError { - constructor (message) { - super(message); - this.status = "unable to set cookie"; - } -} - -class UnknownCommandError extends WebDriverError { - constructor (message) { - super(message); - this.status = "unknown command"; - } -} - -class UnknownError extends WebDriverError { - constructor (message) { - super(message); - this.status = "unknown error"; - } -} - -class UnsupportedOperationError extends WebDriverError { - constructor (message) { - super(message); - this.status = "unsupported operation"; - } -} - -const STATUSES = new Map([ - ["element not accessible", ElementNotAccessibleError], - ["element not interactable", ElementNotInteractableError], - ["element click intercepted", ElementClickInterceptedError], - ["insecure certificate", InsecureCertificateError], - ["invalid argument", InvalidArgumentError], - ["invalid element state", InvalidElementStateError], - ["invalid selector", InvalidSelectorError], - ["invalid session id", InvalidSessionIDError], - ["javascript error", JavaScriptError], - ["move target out of bounds", MoveTargetOutOfBoundsError], - ["no alert open", NoAlertOpenError], - ["no such element", NoSuchElementError], - ["no such frame", NoSuchFrameError], - ["no such window", NoSuchWindowError], - ["script timeout", ScriptTimeoutError], - ["session not created", SessionNotCreatedError], - ["stale element reference", StaleElementReferenceError], - ["timeout", TimeoutError], - ["unable to set cookie", UnableToSetCookieError], - ["unknown command", UnknownCommandError], - ["unknown error", UnknownError], - ["unsupported operation", UnsupportedOperationError], - ["webdriver error", WebDriverError], -]); diff --git a/testing/marionette/evaluate.js b/testing/marionette/evaluate.js deleted file mode 100644 index 38a80eb39..000000000 --- a/testing/marionette/evaluate.js +++ /dev/null @@ -1,494 +0,0 @@ -/* 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/. */ - -"use strict"; - -const {classes: Cc, interfaces: Ci, utils: Cu} = Components; - -Cu.import("resource://gre/modules/Log.jsm"); -Cu.import("resource://gre/modules/NetUtil.jsm"); -Cu.import("resource://gre/modules/Timer.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -Cu.import("chrome://marionette/content/error.js"); - -const logger = Log.repository.getLogger("Marionette"); - -this.EXPORTED_SYMBOLS = ["evaluate", "sandbox", "Sandboxes"]; - -const ARGUMENTS = "__webDriverArguments"; -const CALLBACK = "__webDriverCallback"; -const COMPLETE = "__webDriverComplete"; -const DEFAULT_TIMEOUT = 10000; // ms -const FINISH = "finish"; -const MARIONETTE_SCRIPT_FINISHED = "marionetteScriptFinished"; -const ELEMENT_KEY = "element"; -const W3C_ELEMENT_KEY = "element-6066-11e4-a52e-4f735466cecf"; - -this.evaluate = {}; - -/** - * Evaluate a script in given sandbox. - * - * If the option {@code directInject} is not specified, the script will - * be executed as a function with the {@code args} argument applied. - * - * The arguments provided by the {@code args} argument are exposed through - * the {@code arguments} object available in the script context, and if - * the script is executed asynchronously with the {@code async} - * option, an additional last argument that is synonymous to the - * {@code marionetteScriptFinished} global is appended, and can be - * accessed through {@code arguments[arguments.length - 1]}. - * - * The {@code timeout} option specifies the duration for how long the - * script should be allowed to run before it is interrupted and aborted. - * An interrupted script will cause a ScriptTimeoutError to occur. - * - * The {@code async} option indicates that the script will not return - * until the {@code marionetteScriptFinished} global callback is invoked, - * which is analogous to the last argument of the {@code arguments} - * object. - * - * The option {@code directInject} causes the script to be evaluated - * without being wrapped in a function and the provided arguments will - * be disregarded. This will cause such things as root scope return - * statements to throw errors because they are not used inside a function. - * - * The {@code filename} option is used in error messages to provide - * information on the origin script file in the local end. - * - * The {@code line} option is used in error messages, along with - * {@code filename}, to provide the line number in the origin script - * file on the local end. - * - * @param {nsISandbox) sb - * The sandbox the script will be evaluted in. - * @param {string}Â script - * The script to evaluate. - * @param {Array.<?>=} args - * A sequence of arguments to call the script with. - * @param {Object.<string, ?>=} opts - * Dictionary of options: - * - * async (boolean) - * Indicates if the script should return immediately or wait - * for the callback be invoked before returning. - * debug (boolean) - * Attaches an {@code onerror} event listener. - * directInject (boolean) - * Evaluates the script without wrapping it in a function. - * filename (string) - * File location of the program in the client. - * line (number) - * Line number of the program in the client. - * sandboxName (string) - * Name of the sandbox. Elevated system privileges, equivalent - * to chrome space, will be given if it is "system". - * timeout (boolean) - * Duration in milliseconds before interrupting the script. - * - * @return {Promise} - * A promise that when resolved will give you the return value from - * the script. Note that the return value requires serialisation before - * it can be sent to the client. - * - * @throws JavaScriptError - * If an Error was thrown whilst evaluating the script. - * @throws ScriptTimeoutError - * If the script was interrupted due to script timeout. - */ -evaluate.sandbox = function (sb, script, args = [], opts = {}) { - let scriptTimeoutID, timeoutHandler, unloadHandler; - - let promise = new Promise((resolve, reject) => { - let src = ""; - sb[COMPLETE] = resolve; - timeoutHandler = () => reject(new ScriptTimeoutError("Timed out")); - unloadHandler = () => reject( - new JavaScriptError("Document was unloaded during execution")); - - // wrap in function - if (!opts.directInject) { - if (opts.async) { - sb[CALLBACK] = sb[COMPLETE]; - } - sb[ARGUMENTS] = sandbox.cloneInto(args, sb); - - // callback function made private - // so that introspection is possible - // on the arguments object - if (opts.async) { - sb[CALLBACK] = sb[COMPLETE]; - src += `${ARGUMENTS}.push(rv => ${CALLBACK}(rv));`; - } - - src += `(function() { ${script} }).apply(null, ${ARGUMENTS})`; - - // marionetteScriptFinished is not WebDriver conformant, - // hence it is only exposed to immutable sandboxes - if (opts.sandboxName) { - sb[MARIONETTE_SCRIPT_FINISHED] = sb[CALLBACK]; - } - } - - // onerror is not hooked on by default because of the inability to - // differentiate content errors from chrome errors. - // - // see bug 1128760 for more details - if (opts.debug) { - sb.window.onerror = (msg, url, line) => { - let err = new JavaScriptError(`${msg} at ${url}:${line}`); - reject(err); - }; - } - - // timeout and unload handlers - scriptTimeoutID = setTimeout(timeoutHandler, opts.timeout || DEFAULT_TIMEOUT); - sb.window.onunload = sandbox.cloneInto(unloadHandler, sb); - - let res; - try { - res = Cu.evalInSandbox(src, sb, "1.8", opts.filename || "dummy file", 0); - } catch (e) { - let err = new JavaScriptError( - e, - "execute_script", - opts.filename, - opts.line, - script); - reject(err); - } - - if (!opts.async) { - resolve(res); - } - }); - - return promise.then(res => { - clearTimeout(scriptTimeoutID); - sb.window.removeEventListener("unload", unloadHandler); - return res; - }); -}; - -this.sandbox = {}; - -/** - * Provides a safe way to take an object defined in a privileged scope and - * create a structured clone of it in a less-privileged scope. It returns - * a reference to the clone. - * - * Unlike for |Components.utils.cloneInto|, |obj| may contain functions - * and DOM elemnets. - */ -sandbox.cloneInto = function (obj, sb) { - return Cu.cloneInto(obj, sb, {cloneFunctions: true, wrapReflectors: true}); -}; - -/** - * Augment given sandbox by an adapter that has an {@code exports} - * map property, or a normal map, of function names and function - * references. - * - * @param {Sandbox}Â sb - * The sandbox to augment. - * @param {Object} adapter - * Object that holds an {@code exports} property, or a map, of - * function names and function references. - * - * @return {Sandbox} - * The augmented sandbox. - */ -sandbox.augment = function (sb, adapter) { - function* entries(obj) { - for (let key of Object.keys(obj)) { - yield [key, obj[key]]; - } - } - - let funcs = adapter.exports || entries(adapter); - for (let [name, func] of funcs) { - sb[name] = func; - } - - return sb; -}; - -/** - * Creates a sandbox. - * - * @param {Window} window - * The DOM Window object. - * @param {nsIPrincipal=} principal - * An optional, custom principal to prefer over the Window. Useful if - * you need elevated security permissions. - * - * @return {Sandbox} - * The created sandbox. - */ -sandbox.create = function (window, principal = null, opts = {}) { - let p = principal || window; - opts = Object.assign({ - sameZoneAs: window, - sandboxPrototype: window, - wantComponents: true, - wantXrays: true, - }, opts); - return new Cu.Sandbox(p, opts); -}; - -/** - * Creates a mutable sandbox, where changes to the global scope - * will have lasting side-effects. - * - * @param {Window} window - * The DOM Window object. - * - * @return {Sandbox} - * The created sandbox. - */ -sandbox.createMutable = function (window) { - let opts = { - wantComponents: false, - wantXrays: false, - }; - return sandbox.create(window, null, opts); -}; - -sandbox.createSystemPrincipal = function (window) { - let principal = Cc["@mozilla.org/systemprincipal;1"] - .createInstance(Ci.nsIPrincipal); - return sandbox.create(window, principal); -}; - -sandbox.createSimpleTest = function (window, harness) { - let sb = sandbox.create(window); - sb = sandbox.augment(sb, harness); - sb[FINISH] = () => sb[COMPLETE](harness.generate_results()); - return sb; -}; - -/** - * Sandbox storage. When the user requests a sandbox by a specific name, - * if one exists in the storage this will be used as long as its window - * reference is still valid. - */ -this.Sandboxes = class { - /** - * @param {function(): Window} windowFn - * A function that returns the references to the current Window - * object. - */ - constructor(windowFn) { - this.windowFn_ = windowFn; - this.boxes_ = new Map(); - } - - get window_() { - return this.windowFn_(); - } - - /** - * Factory function for getting a sandbox by name, or failing that, - * creating a new one. - * - * If the sandbox' window does not match the provided window, a new one - * will be created. - * - * @param {string} name - * The name of the sandbox to get or create. - * @param {boolean} fresh - * Remove old sandbox by name first, if it exists. - * - * @return {Sandbox} - * A used or fresh sandbox. - */ - get(name = "default", fresh = false) { - let sb = this.boxes_.get(name); - if (sb) { - if (fresh || sb.window != this.window_) { - this.boxes_.delete(name); - return this.get(name, false); - } - } else { - if (name == "system") { - sb = sandbox.createSystemPrincipal(this.window_); - } else { - sb = sandbox.create(this.window_); - } - this.boxes_.set(name, sb); - } - return sb; - } - - /** - * Clears cache of sandboxes. - */ - clear() { - this.boxes_.clear(); - } -}; - -/** - * Stores scripts imported from the local end through the - * {@code GeckoDriver#importScript} command. - * - * Imported scripts are prepended to the script that is evaluated - * on each call to {@code GeckoDriver#executeScript}, - * {@code GeckoDriver#executeAsyncScript}, and - * {@code GeckoDriver#executeJSScript}. - * - * Usage: - * - * let importedScripts = new evaluate.ScriptStorage(); - * importedScripts.add(firstScript); - * importedScripts.add(secondScript); - * - * let scriptToEval = importedScripts.concat(script); - * // firstScript and secondScript are prepended to script - * - */ -evaluate.ScriptStorage = class extends Set { - - /** - * Produce a string of all stored scripts. - * - * The stored scripts are concatenated into a string, with optional - * additional scripts then appended. - * - * @param {...string} addional - * Optional scripts to include. - * - * @return {string} - * Concatenated string consisting of stored scripts and additional - * scripts, in that order. - */ - concat(...additional) { - let rv = ""; - for (let s of this) { - rv = s + rv; - } - for (let s of additional) { - rv = rv + s; - } - return rv; - } - - toJson() { - return Array.from(this); - } - -}; - -/** - * Service that enables the script storage service to be queried from - * content space. - * - * The storage can back multiple |ScriptStorage|, each typically belonging - * to a |Context|. Since imported scripts' scope are global and not - * scoped to the current browsing context, all imported scripts are stored - * in chrome space and fetched by content space as needed. - * - * Usage in chrome space: - * - * let service = new evaluate.ScriptStorageService( - * [Context.CHROME, Context.CONTENT]); - * let storage = service.for(Context.CHROME); - * let scriptToEval = storage.concat(script); - * - */ -evaluate.ScriptStorageService = class extends Map { - - /** - * Create the service. - * - * An optional array of names for script storages to initially create - * can be provided. - * - * @param {Array.<string>=} initialStorages - * List of names of the script storages to create initially. - */ - constructor(initialStorages = []) { - super(initialStorages.map(name => [name, new evaluate.ScriptStorage()])); - } - - /** - * Retrieve the scripts associated with the given context. - * - * @param {Context} context - * Context to retrieve the scripts from. - * - * @return {ScriptStorage} - * Scrips associated with given |context|. - */ - for(context) { - return this.get(context); - } - - processMessage(msg) { - switch (msg.name) { - case "Marionette:getImportedScripts": - let storage = this.for.apply(this, msg.json); - return storage.toJson(); - - default: - throw new TypeError("Unknown message: " + msg.name); - } - } - - // TODO(ato): The idea of services in chrome space - // can be generalised at some later time (see cookies.js:38). - receiveMessage(msg) { - try { - return this.processMessage(msg); - } catch (e) { - logger.error(e); - } - } - -}; - -evaluate.ScriptStorageService.prototype.QueryInterface = - XPCOMUtils.generateQI([ - Ci.nsIMessageListener, - Ci.nsISupportsWeakReference, - ]); - -/** - * Bridges the script storage in chrome space, to make it possible to - * retrieve a {@code ScriptStorage} associated with a given - * {@code Context} from content space. - * - * Usage in content space: - * - * let client = new evaluate.ScriptStorageServiceClient(chromeProxy); - * let storage = client.for(Context.CONTENT); - * let scriptToEval = storage.concat(script); - * - */ -evaluate.ScriptStorageServiceClient = class { - - /** - * @param {proxy.SyncChromeSender} chromeProxy - * Proxy for communicating with chrome space. - */ - constructor(chromeProxy) { - this.chrome = chromeProxy; - } - - /** - * Retrieve scripts associated with the given context. - * - * @param {Context} context - * Context to retrieve scripts from. - * - * @return {ScriptStorage} - * Scripts associated with given |context|. - */ - for(context) { - let scripts = this.chrome.getImportedScripts(context)[0]; - return new evaluate.ScriptStorage(scripts); - } - -}; diff --git a/testing/marionette/event.js b/testing/marionette/event.js deleted file mode 100644 index c60ca306b..000000000 --- a/testing/marionette/event.js +++ /dev/null @@ -1,1365 +0,0 @@ -/* 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/. */ - -// Provides functionality for creating and sending DOM events. - -"use strict"; - -const {interfaces: Ci, utils: Cu, classes: Cc} = Components; - -Cu.import("resource://gre/modules/Log.jsm"); -const logger = Log.repository.getLogger("Marionette"); - -Cu.import("chrome://marionette/content/element.js"); -Cu.import("chrome://marionette/content/error.js"); - -this.EXPORTED_SYMBOLS = ["event"]; - -// must be synchronised with nsIDOMWindowUtils -const COMPOSITION_ATTR_RAWINPUT = 0x02; -const COMPOSITION_ATTR_SELECTEDRAWTEXT = 0x03; -const COMPOSITION_ATTR_CONVERTEDTEXT = 0x04; -const COMPOSITION_ATTR_SELECTEDCONVERTEDTEXT = 0x05; - -// TODO(ato): Document! -let seenEvent = false; - -function getDOMWindowUtils(win) { - if (!win) { - win = window; - } - - // this assumes we are operating in chrome space - return win.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); -} - -this.event = {}; - -event.MouseEvents = { - click: 0, - dblclick: 1, - mousedown: 2, - mouseup: 3, - mouseover: 4, - mouseout: 5, -}; - -event.Modifiers = { - shiftKey: 0, - ctrlKey: 1, - altKey: 2, - metaKey: 3, -}; - -/** - * Sends a mouse event to given target. - * - * @param {nsIDOMMouseEvent} mouseEvent - * Event to send. - * @param {(DOMElement|string)} target - * Target of event. Can either be an element or the ID of an element. - * @param {Window=} window - * Window object. Defaults to the current window. - * - * @throws {TypeError} - * If the event is unsupported. - */ -event.sendMouseEvent = function (mouseEvent, target, window = undefined) { - if (!event.MouseEvents.hasOwnProperty(mouseEvent.type)) { - throw new TypeError("Unsupported event type: " + mouseEvent.type); - } - - if (!target.nodeType && typeof target != "string") { - throw new TypeError("Target can only be a DOM element or a string: " + target); - } - - if (!target.nodeType) { - target = window.document.getElementById(target); - } else { - window = window || target.ownerDocument.defaultView; - } - - let ev = window.document.createEvent("MouseEvent"); - - let type = mouseEvent.type; - let view = window; - - let detail = mouseEvent.detail; - if (!detail) { - if (mouseEvent.type in ["click", "mousedown", "mouseup"]) { - detail = 1; - } else if (mouseEvent.type == "dblclick") { - detail = 2; - } else { - detail = 0; - } - } - - let screenX = mouseEvent.screenX || 0; - let screenY = mouseEvent.screenY || 0; - let clientX = mouseEvent.clientX || 0; - let clientY = mouseEvent.clientY || 0; - let ctrlKey = mouseEvent.ctrlKey || false; - let altKey = mouseEvent.altKey || false; - let shiftKey = mouseEvent.shiftKey || false; - let metaKey = mouseEvent.metaKey || false; - let button = mouseEvent.button || 0; - let relatedTarget = mouseEvent.relatedTarget || null; - - ev.initMouseEvent( - mouseEvent.type, - /* canBubble */ true, - /* cancelable */ true, - view, - detail, - screenX, - screenY, - clientX, - clientY, - ctrlKey, - altKey, - shiftKey, - metaKey, - button, - relatedTarget); -}; - -/** - * Send character to the currently focused element. - * - * This function handles casing of characters (sends the right charcode, - * and sends a shift key for uppercase chars). No other modifiers are - * handled at this point. - * - * For now this method only works for English letters (lower and upper - * case) and the digits 0-9. - */ -event.sendChar = function (char, window = undefined) { - // DOM event charcodes match ASCII (JS charcodes) for a-zA-Z0-9 - let hasShift = (char == char.toUpperCase()); - event.synthesizeKey(char, {shiftKey: hasShift}, window); -}; - -/** - * Send string to the focused element. - * - * For now this method only works for English letters (lower and upper - * case) and the digits 0-9. - */ -event.sendString = function (string, window = undefined) { - for (let i = 0; i < string.length; ++i) { - event.sendChar(string.charAt(i), window); - } -}; - -/** - * Send the non-character key to the focused element. - * - * The name of the key should be the part that comes after "DOM_VK_" - * in the nsIDOMKeyEvent constant name for this key. No modifiers are - * handled at this point. - */ -event.sendKey = function (key, window = undefined) { - let keyName = "VK_" + key.toUpperCase(); - event.synthesizeKey(keyName, {shiftKey: false}, window); -}; - -// TODO(ato): Unexpose this when action.Chain#emitMouseEvent -// no longer emits its own events -event.parseModifiers_ = function (modifiers) { - let mval = 0; - if (modifiers.shiftKey) { - mval |= Ci.nsIDOMNSEvent.SHIFT_MASK; - } - if (modifiers.ctrlKey) { - mval |= Ci.nsIDOMNSEvent.CONTROL_MASK; - } - if (modifiers.altKey) { - mval |= Ci.nsIDOMNSEvent.ALT_MASK; - } - if (modifiers.metaKey) { - mval |= Ci.nsIDOMNSEvent.META_MASK; - } - if (modifiers.accelKey) { - if (navigator.platform.indexOf("Mac") >= 0) { - mval |= Ci.nsIDOMNSEvent.META_MASK; - } else { - mval |= Ci.nsIDOMNSEvent.CONTROL_MASK; - } - } - return mval; -}; - -/** - * Synthesise a mouse event on a target. - * - * The actual client point is determined by taking the aTarget's client - * box and offseting it by offsetX and offsetY. This allows mouse clicks - * to be simulated by calling this method. - * - * If the type is specified, an mouse event of that type is - * fired. Otherwise, a mousedown followed by a mouse up is performed. - * - * @param {Element} element - * Element to click. - * @param {number} offsetX - * Horizontal offset to click from the target's bounding box. - * @param {number} offsetY - * Vertical offset to click from the target's bounding box. - * @param {Object.<string, ?>} opts - * Object which may contain the properties "shiftKey", "ctrlKey", - * "altKey", "metaKey", "accessKey", "clickCount", "button", and - * "type". - * @param {Window=} window - * Window object. Defaults to the current window. - */ -event.synthesizeMouse = function ( - element, offsetX, offsetY, opts, window = undefined) { - let rect = element.getBoundingClientRect(); - event.synthesizeMouseAtPoint( - rect.left + offsetX, rect.top + offsetY, opts, window); -}; - -/* - * Synthesize a mouse event at a particular point in a window. - * - * If the type of the event is specified, a mouse event of that type is - * fired. Otherwise, a mousedown followed by a mouse up is performed. - * - * @param {number} left - * CSS pixels from the left document margin. - * @param {number} top - * CSS pixels from the top document margin. - * @param {Object.<string, ?>} opts - * Object which may contain the properties "shiftKey", "ctrlKey", - * "altKey", "metaKey", "accessKey", "clickCount", "button", and - * "type". - * @param {Window=} window - * Window object. Defaults to the current window. - */ -event.synthesizeMouseAtPoint = function ( - left, top, opts, window = undefined) { - - let domutils = getDOMWindowUtils(window); - - let button = opts.button || 0; - let clickCount = opts.clickCount || 1; - let modifiers = event.parseModifiers_(opts); - let pressure = ("pressure" in opts) ? opts.pressure : 0; - let inputSource = ("inputSource" in opts) ? opts.inputSource : - Ci.nsIDOMMouseEvent.MOZ_SOURCE_MOUSE; - let isDOMEventSynthesized = - ("isSynthesized" in opts) ? opts.isSynthesized : true; - let isWidgetEventSynthesized = - ("isWidgetEventSynthesized" in opts) ? opts.isWidgetEventSynthesized : false; - let buttons = ("buttons" in opts) ? opts.buttons : domutils.MOUSE_BUTTONS_NOT_SPECIFIED; - - if (("type" in opts) && opts.type) { - domutils.sendMouseEvent( - opts.type, left, top, button, clickCount, modifiers, false, pressure, inputSource, - isDOMEventSynthesized, isWidgetEventSynthesized, buttons); - } else { - domutils.sendMouseEvent( - "mousedown", left, top, button, clickCount, modifiers, false, pressure, inputSource, - isDOMEventSynthesized, isWidgetEventSynthesized, buttons); - domutils.sendMouseEvent( - "mouseup", left, top, button, clickCount, modifiers, false, pressure, inputSource, - isDOMEventSynthesized, isWidgetEventSynthesized, buttons); - } -}; - -/** - * Call event.synthesizeMouse with coordinates at the centre of the - * target. - */ -event.synthesizeMouseAtCenter = function (element, event, window) { - let rect = element.getBoundingClientRect(); - event.synthesizeMouse( - element, - rect.width / 2, - rect.height / 2, - event, - window); -}; - -/** - * Synthesise a mouse scroll event on a target. - * - * The actual client point is determined by taking the target's client - * box and offseting it by |offsetX| and |offsetY|. - * - * If the |type| property is specified for the |event| argument, a mouse - * scroll event of that type is fired. Otherwise, DOMMouseScroll is used. - * - * If the |axis| is specified, it must be one of "horizontal" or - * "vertical". If not specified, "vertical" is used. - * - * |delta| is the amount to scroll by (can be positive or negative). - * It must be specified. - * - * |hasPixels| specifies whether kHasPixels should be set in the - * |scrollFlags|. - * - * |isMomentum| specifies whether kIsMomentum should be set in the - * |scrollFlags|. - * - * @param {Element} target - * @param {number} offsetY - * @param {number} offsetY - * @param {Object.<string, ?>} event - * Object which may contain the properties shiftKey, ctrlKey, altKey, - * metaKey, accessKey, button, type, axis, delta, and hasPixels. - * @param {Window=} window - * Window object. Defaults to the current window. - */ -event.synthesizeMouseScroll = function ( - target, offsetX, offsetY, ev, window = undefined) { - - let domutils = getDOMWindowUtils(window); - - // see nsMouseScrollFlags in nsGUIEvent.h - const kIsVertical = 0x02; - const kIsHorizontal = 0x04; - const kHasPixels = 0x08; - const kIsMomentum = 0x40; - - let button = ev.button || 0; - let modifiers = event.parseModifiers_(ev); - - let rect = target.getBoundingClientRect(); - let left = rect.left; - let top = rect.top; - - let type = (("type" in ev) && ev.type) || "DOMMouseScroll"; - let axis = ev.axis || "vertical"; - let scrollFlags = (axis == "horizontal") ? kIsHorizontal : kIsVertical; - if (ev.hasPixels) { - scrollFlags |= kHasPixels; - } - if (ev.isMomentum) { - scrollFlags |= kIsMomentum; - } - - domutils.sendMouseScrollEvent( - type, - left + offsetX, - top + offsetY, - button, - scrollFlags, - ev.delta, - modifiers); -}; - -function computeKeyCodeFromChar_(char) { - if (char.length != 1) { - return 0; - } - - if (char >= "a" && char <= "z") { - return Ci.nsIDOMKeyEvent.DOM_VK_A + char.charCodeAt(0) - "a".charCodeAt(0); - } - if (char >= "A" && char <= "Z") { - return Ci.nsIDOMKeyEvent.DOM_VK_A + char.charCodeAt(0) - "A".charCodeAt(0); - } - if (char >= "0" && char <= "9") { - return Ci.nsIDOMKeyEvent.DOM_VK_0 + char.charCodeAt(0) - "0".charCodeAt(0); - } - - // returns US keyboard layout's keycode - switch (char) { - case "~": - case "`": - return Ci.nsIDOMKeyEvent.DOM_VK_BACK_QUOTE; - - case "!": - return Ci.nsIDOMKeyEvent.DOM_VK_1; - - case "@": - return Ci.nsIDOMKeyEvent.DOM_VK_2; - - case "#": - return Ci.nsIDOMKeyEvent.DOM_VK_3; - - case "$": - return Ci.nsIDOMKeyEvent.DOM_VK_4; - - case "%": - return Ci.nsIDOMKeyEvent.DOM_VK_5; - - case "^": - return Ci.nsIDOMKeyEvent.DOM_VK_6; - - case "&": - return Ci.nsIDOMKeyEvent.DOM_VK_7; - - case "*": - return Ci.nsIDOMKeyEvent.DOM_VK_8; - - case "(": - return Ci.nsIDOMKeyEvent.DOM_VK_9; - - case ")": - return Ci.nsIDOMKeyEvent.DOM_VK_0; - - case "-": - case "_": - return Ci.nsIDOMKeyEvent.DOM_VK_SUBTRACT; - - case "+": - case "=": - return Ci.nsIDOMKeyEvent.DOM_VK_EQUALS; - - case "{": - case "[": - return Ci.nsIDOMKeyEvent.DOM_VK_OPEN_BRACKET; - - case "}": - case "]": - return Ci.nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET; - - case "|": - case "\\": - return Ci.nsIDOMKeyEvent.DOM_VK_BACK_SLASH; - - case ":": - case ";": - return Ci.nsIDOMKeyEvent.DOM_VK_SEMICOLON; - - case "'": - case "\"": - return Ci.nsIDOMKeyEvent.DOM_VK_QUOTE; - - case "<": - case ",": - return Ci.nsIDOMKeyEvent.DOM_VK_COMMA; - - case ">": - case ".": - return Ci.nsIDOMKeyEvent.DOM_VK_PERIOD; - - case "?": - case "/": - return Ci.nsIDOMKeyEvent.DOM_VK_SLASH; - - case "\n": - return Ci.nsIDOMKeyEvent.DOM_VK_RETURN; - - default: - return 0; - } -} - -/** - * Returns true if the given key should cause keypress event when widget - * handles the native key event. Otherwise, false. - * - * The key code should be one of consts of nsIDOMKeyEvent.DOM_VK_*, - * or a key name begins with "VK_", or a character. - */ -event.isKeypressFiredKey = function (key) { - if (typeof key == "string") { - if (key.indexOf("VK_") === 0) { - key = Ci.nsIDOMKeyEvent["DOM_" + key]; - if (!key) { - throw new TypeError("Unknown key: " + key); - } - - // if key generates a character, it must cause a keypress event - } else { - return true; - } - } - - switch (key) { - case Ci.nsIDOMKeyEvent.DOM_VK_SHIFT: - case Ci.nsIDOMKeyEvent.DOM_VK_CONTROL: - case Ci.nsIDOMKeyEvent.DOM_VK_ALT: - case Ci.nsIDOMKeyEvent.DOM_VK_CAPS_LOCK: - case Ci.nsIDOMKeyEvent.DOM_VK_NUM_LOCK: - case Ci.nsIDOMKeyEvent.DOM_VK_SCROLL_LOCK: - case Ci.nsIDOMKeyEvent.DOM_VK_META: - return false; - - default: - return true; - } -}; - -/** - * Synthesise a key event. - * - * It is targeted at whatever would be targeted by an actual keypress - * by the user, typically the focused element. - * - * @param {string} key - * Key to synthesise. Should either be a character or a key code - * starting with "VK_" such as VK_RETURN, or a normalized key value. - * @param {Object.<string, ?>} event - * Object which may contain the properties shiftKey, ctrlKey, altKey, - * metaKey, accessKey, type. If the type is specified (keydown or keyup), - * a key event of that type is fired. Otherwise, a keydown, a keypress, - * and then a keyup event are fired in sequence. - * @param {Window=} window - * Window object. Defaults to the current window. - * - * @throws {TypeError} - * If unknown key. - */ -event.synthesizeKey = function (key, event, win = undefined) -{ - var TIP = getTIP_(win); - if (!TIP) { - return; - } - var KeyboardEvent = getKeyboardEvent_(win); - var modifiers = emulateToActivateModifiers_(TIP, event, win); - var keyEventDict = createKeyboardEventDictionary_(key, event, win); - var keyEvent = new KeyboardEvent("", keyEventDict.dictionary); - var dispatchKeydown = - !("type" in event) || event.type === "keydown" || !event.type; - var dispatchKeyup = - !("type" in event) || event.type === "keyup" || !event.type; - - try { - if (dispatchKeydown) { - TIP.keydown(keyEvent, keyEventDict.flags); - if ("repeat" in event && event.repeat > 1) { - keyEventDict.dictionary.repeat = true; - var repeatedKeyEvent = new KeyboardEvent("", keyEventDict.dictionary); - for (var i = 1; i < event.repeat; i++) { - TIP.keydown(repeatedKeyEvent, keyEventDict.flags); - } - } - } - if (dispatchKeyup) { - TIP.keyup(keyEvent, keyEventDict.flags); - } - } finally { - emulateToInactivateModifiers_(TIP, modifiers, win); - } -}; - -var TIPMap = new WeakMap(); - -function getTIP_(win, callback) -{ - if (!win) { - win = window; - } - var tip; - if (TIPMap.has(win)) { - tip = TIPMap.get(win); - } else { - tip = - Cc["@mozilla.org/text-input-processor;1"]. - createInstance(Ci.nsITextInputProcessor); - TIPMap.set(win, tip); - } - if (!tip.beginInputTransactionForTests(win, callback)) { - tip = null; - TIPMap.delete(win); - } - return tip; -} - -function getKeyboardEvent_(win = window) -{ - if (typeof KeyboardEvent != "undefined") { - try { - // See if the object can be instantiated; sometimes this yields - // 'TypeError: can't access dead object' or 'KeyboardEvent is not a constructor'. - new KeyboardEvent("", {}); - return KeyboardEvent; - } catch (ex) {} - } - if (typeof content != "undefined" && ("KeyboardEvent" in content)) { - return content.KeyboardEvent; - } - return win.KeyboardEvent; -} - -function createKeyboardEventDictionary_(key, keyEvent, win = window) { - var result = { dictionary: null, flags: 0 }; - var keyCodeIsDefined = "keyCode" in keyEvent; - var keyCode = - (keyCodeIsDefined && keyEvent.keyCode >= 0 && keyEvent.keyCode <= 255) ? - keyEvent.keyCode : 0; - var keyName = "Unidentified"; - if (key.indexOf("KEY_") == 0) { - keyName = key.substr("KEY_".length); - result.flags |= Ci.nsITextInputProcessor.KEY_NON_PRINTABLE_KEY; - } else if (key.indexOf("VK_") == 0) { - keyCode = Ci.nsIDOMKeyEvent["DOM_" + key]; - if (!keyCode) { - throw "Unknown key: " + key; - } - keyName = guessKeyNameFromKeyCode_(keyCode, win); - result.flags |= Ci.nsITextInputProcessor.KEY_NON_PRINTABLE_KEY; - } else if (key != "") { - keyName = key; - if (!keyCodeIsDefined) { - keyCode = computeKeyCodeFromChar_(key.charAt(0)); - } - if (!keyCode) { - result.flags |= Ci.nsITextInputProcessor.KEY_KEEP_KEYCODE_ZERO; - } - // keyName was already determined in keyEvent so no fall-back needed - if (!("key" in keyEvent && keyName == keyEvent.key)) { - result.flags |= Ci.nsITextInputProcessor.KEY_FORCE_PRINTABLE_KEY; - } - } - var locationIsDefined = "location" in keyEvent; - if (locationIsDefined && keyEvent.location === 0) { - result.flags |= Ci.nsITextInputProcessor.KEY_KEEP_KEY_LOCATION_STANDARD; - } - result.dictionary = { - key: keyName, - code: "code" in keyEvent ? keyEvent.code : "", - location: locationIsDefined ? keyEvent.location : 0, - repeat: "repeat" in keyEvent ? keyEvent.repeat === true : false, - keyCode: keyCode, - }; - return result; -} - -function emulateToActivateModifiers_(TIP, keyEvent, win = window) -{ - if (!keyEvent) { - return null; - } - var KeyboardEvent = getKeyboardEvent_(win); - var navigator = getNavigator_(win); - - var modifiers = { - normal: [ - { key: "Alt", attr: "altKey" }, - { key: "AltGraph", attr: "altGraphKey" }, - { key: "Control", attr: "ctrlKey" }, - { key: "Fn", attr: "fnKey" }, - { key: "Meta", attr: "metaKey" }, - { key: "OS", attr: "osKey" }, - { key: "Shift", attr: "shiftKey" }, - { key: "Symbol", attr: "symbolKey" }, - { key: isMac_(win) ? "Meta" : "Control", - attr: "accelKey" }, - ], - lockable: [ - { key: "CapsLock", attr: "capsLockKey" }, - { key: "FnLock", attr: "fnLockKey" }, - { key: "NumLock", attr: "numLockKey" }, - { key: "ScrollLock", attr: "scrollLockKey" }, - { key: "SymbolLock", attr: "symbolLockKey" }, - ] - } - - for (var i = 0; i < modifiers.normal.length; i++) { - if (!keyEvent[modifiers.normal[i].attr]) { - continue; - } - if (TIP.getModifierState(modifiers.normal[i].key)) { - continue; // already activated. - } - var event = new KeyboardEvent("", { key: modifiers.normal[i].key }); - TIP.keydown(event, - TIP.KEY_NON_PRINTABLE_KEY | TIP.KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT); - modifiers.normal[i].activated = true; - } - for (var i = 0; i < modifiers.lockable.length; i++) { - if (!keyEvent[modifiers.lockable[i].attr]) { - continue; - } - if (TIP.getModifierState(modifiers.lockable[i].key)) { - continue; // already activated. - } - var event = new KeyboardEvent("", { key: modifiers.lockable[i].key }); - TIP.keydown(event, - TIP.KEY_NON_PRINTABLE_KEY | TIP.KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT); - TIP.keyup(event, - TIP.KEY_NON_PRINTABLE_KEY | TIP.KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT); - modifiers.lockable[i].activated = true; - } - return modifiers; -} - -function emulateToInactivateModifiers_(TIP, modifiers, win = window) -{ - if (!modifiers) { - return; - } - var KeyboardEvent = getKeyboardEvent_(win); - for (var i = 0; i < modifiers.normal.length; i++) { - if (!modifiers.normal[i].activated) { - continue; - } - var event = new KeyboardEvent("", { key: modifiers.normal[i].key }); - TIP.keyup(event, - TIP.KEY_NON_PRINTABLE_KEY | TIP.KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT); - } - for (var i = 0; i < modifiers.lockable.length; i++) { - if (!modifiers.lockable[i].activated) { - continue; - } - if (!TIP.getModifierState(modifiers.lockable[i].key)) { - continue; // who already inactivated this? - } - var event = new KeyboardEvent("", { key: modifiers.lockable[i].key }); - TIP.keydown(event, - TIP.KEY_NON_PRINTABLE_KEY | TIP.KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT); - TIP.keyup(event, - TIP.KEY_NON_PRINTABLE_KEY | TIP.KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT); - } -} - -function getNavigator_(win = window) -{ - if (typeof navigator != "undefined") { - return navigator; - } - return win.navigator; -} - -function isMac_(win = window) { - if (win) { - try { - return win.navigator.platform.indexOf("Mac") > -1; - } catch (ex) {} - } - return navigator.platform.indexOf("Mac") > -1; -} - -function guessKeyNameFromKeyCode_(aKeyCode, win = window) -{ - var KeyboardEvent = getKeyboardEvent_(win); - switch (aKeyCode) { - case KeyboardEvent.DOM_VK_CANCEL: - return "Cancel"; - case KeyboardEvent.DOM_VK_HELP: - return "Help"; - case KeyboardEvent.DOM_VK_BACK_SPACE: - return "Backspace"; - case KeyboardEvent.DOM_VK_TAB: - return "Tab"; - case KeyboardEvent.DOM_VK_CLEAR: - return "Clear"; - case KeyboardEvent.DOM_VK_RETURN: - return "Enter"; - case KeyboardEvent.DOM_VK_SHIFT: - return "Shift"; - case KeyboardEvent.DOM_VK_CONTROL: - return "Control"; - case KeyboardEvent.DOM_VK_ALT: - return "Alt"; - case KeyboardEvent.DOM_VK_PAUSE: - return "Pause"; - case KeyboardEvent.DOM_VK_EISU: - return "Eisu"; - case KeyboardEvent.DOM_VK_ESCAPE: - return "Escape"; - case KeyboardEvent.DOM_VK_CONVERT: - return "Convert"; - case KeyboardEvent.DOM_VK_NONCONVERT: - return "NonConvert"; - case KeyboardEvent.DOM_VK_ACCEPT: - return "Accept"; - case KeyboardEvent.DOM_VK_MODECHANGE: - return "ModeChange"; - case KeyboardEvent.DOM_VK_PAGE_UP: - return "PageUp"; - case KeyboardEvent.DOM_VK_PAGE_DOWN: - return "PageDown"; - case KeyboardEvent.DOM_VK_END: - return "End"; - case KeyboardEvent.DOM_VK_HOME: - return "Home"; - case KeyboardEvent.DOM_VK_LEFT: - return "ArrowLeft"; - case KeyboardEvent.DOM_VK_UP: - return "ArrowUp"; - case KeyboardEvent.DOM_VK_RIGHT: - return "ArrowRight"; - case KeyboardEvent.DOM_VK_DOWN: - return "ArrowDown"; - case KeyboardEvent.DOM_VK_SELECT: - return "Select"; - case KeyboardEvent.DOM_VK_PRINT: - return "Print"; - case KeyboardEvent.DOM_VK_EXECUTE: - return "Execute"; - case KeyboardEvent.DOM_VK_PRINTSCREEN: - return "PrintScreen"; - case KeyboardEvent.DOM_VK_INSERT: - return "Insert"; - case KeyboardEvent.DOM_VK_DELETE: - return "Delete"; - case KeyboardEvent.DOM_VK_WIN: - return "OS"; - case KeyboardEvent.DOM_VK_CONTEXT_MENU: - return "ContextMenu"; - case KeyboardEvent.DOM_VK_SLEEP: - return "Standby"; - case KeyboardEvent.DOM_VK_F1: - return "F1"; - case KeyboardEvent.DOM_VK_F2: - return "F2"; - case KeyboardEvent.DOM_VK_F3: - return "F3"; - case KeyboardEvent.DOM_VK_F4: - return "F4"; - case KeyboardEvent.DOM_VK_F5: - return "F5"; - case KeyboardEvent.DOM_VK_F6: - return "F6"; - case KeyboardEvent.DOM_VK_F7: - return "F7"; - case KeyboardEvent.DOM_VK_F8: - return "F8"; - case KeyboardEvent.DOM_VK_F9: - return "F9"; - case KeyboardEvent.DOM_VK_F10: - return "F10"; - case KeyboardEvent.DOM_VK_F11: - return "F11"; - case KeyboardEvent.DOM_VK_F12: - return "F12"; - case KeyboardEvent.DOM_VK_F13: - return "F13"; - case KeyboardEvent.DOM_VK_F14: - return "F14"; - case KeyboardEvent.DOM_VK_F15: - return "F15"; - case KeyboardEvent.DOM_VK_F16: - return "F16"; - case KeyboardEvent.DOM_VK_F17: - return "F17"; - case KeyboardEvent.DOM_VK_F18: - return "F18"; - case KeyboardEvent.DOM_VK_F19: - return "F19"; - case KeyboardEvent.DOM_VK_F20: - return "F20"; - case KeyboardEvent.DOM_VK_F21: - return "F21"; - case KeyboardEvent.DOM_VK_F22: - return "F22"; - case KeyboardEvent.DOM_VK_F23: - return "F23"; - case KeyboardEvent.DOM_VK_F24: - return "F24"; - case KeyboardEvent.DOM_VK_NUM_LOCK: - return "NumLock"; - case KeyboardEvent.DOM_VK_SCROLL_LOCK: - return "ScrollLock"; - case KeyboardEvent.DOM_VK_VOLUME_MUTE: - return "AudioVolumeMute"; - case KeyboardEvent.DOM_VK_VOLUME_DOWN: - return "AudioVolumeDown"; - case KeyboardEvent.DOM_VK_VOLUME_UP: - return "AudioVolumeUp"; - case KeyboardEvent.DOM_VK_META: - return "Meta"; - case KeyboardEvent.DOM_VK_ALTGR: - return "AltGraph"; - case KeyboardEvent.DOM_VK_ATTN: - return "Attn"; - case KeyboardEvent.DOM_VK_CRSEL: - return "CrSel"; - case KeyboardEvent.DOM_VK_EXSEL: - return "ExSel"; - case KeyboardEvent.DOM_VK_EREOF: - return "EraseEof"; - case KeyboardEvent.DOM_VK_PLAY: - return "Play"; - default: - return "Unidentified"; - } -} - -/** - * Indicate that an event with an original target and type is expected - * to be fired, or not expected to be fired. - */ -function expectEvent_(expectedTarget, expectedEvent, testName) { - if (!expectedTarget || !expectedEvent) { - return null; - } - - seenEvent = false; - - let type; - if (expectedEvent.charAt(0) == "!") { - type = expectedEvent.substring(1); - } else { - type = expectedEvent; - } - - let handler = ev => { - let pass = (!seenEvent && ev.originalTarget == expectedTarget && ev.type == type); - is(pass, true, `${testName} ${type} event target ${seenEvent ? "twice" : ""}`); - seenEvent = true; - }; - - expectedTarget.addEventListener(type, handler, false); - return handler; -} - -/** - * Check if the event was fired or not. The provided event handler will - * be removed. - */ -function checkExpectedEvent_( - expectedTarget, expectedEvent, eventHandler, testName) { - - if (eventHandler) { - let expectEvent = (expectedEvent.charAt(0) != "!"); - let type = expectEvent; - if (!type) { - type = expectedEvent.substring(1); - } - expectedTarget.removeEventListener(type, eventHandler, false); - - let desc = `${type} event`; - if (!expectEvent) { - desc += " not"; - } - is(seenEvent, expectEvent, `${testName} ${desc} fired`); - } - - seenEvent = false; -} - -/** - * Similar to event.synthesizeMouse except that a test is performed to - * see if an event is fired at the right target as a result. - * - * To test that an event is not fired, use an expected type preceded by - * an exclamation mark, such as "!select". This might be used to test that - * a click on a disabled element doesn't fire certain events for instance. - * - * @param {Element} target - * Synthesise the mouse event on this target. - * @param {number} offsetX - * Horizontal offset from the target's bounding box. - * @param {number} offsetY - * Vertical offset from the target's bounding box. - * @param {Object.<string, ?>} ev - * Object which may contain the properties shiftKey, ctrlKey, altKey, - * metaKey, accessKey, type. - * @param {Element} expectedTarget - * Expected originalTarget of the event. - * @param {DOMEvent} expectedEvent - * Expected type of the event, such as "select". - * @param {string} testName - * Test name when outputing results. - * @param {Window=} window - * Window object. Defaults to the current window. - */ -event.synthesizeMouseExpectEvent = function ( - target, offsetX, offsetY, ev, expectedTarget, expectedEvent, - testName, window = undefined) { - - let eventHandler = expectEvent_( - expectedTarget, - expectedEvent, - testName); - event.synthesizeMouse(target, offsetX, offsetY, ev, window); - checkExpectedEvent_( - expectedTarget, - expectedEvent, - eventHandler, - testName); -}; - -/** - * Similar to synthesizeKey except that a test is performed to see if - * an event is fired at the right target as a result. - * - * @param {string} key - * Key to synthesise. - * @param {Object.<string, ?>} ev - * Object which may contain the properties shiftKey, ctrlKey, altKey, - * metaKey, accessKey, type. - * @param {Element} expectedTarget - * Expected originalTarget of the event. - * @param {DOMEvent} expectedEvent - * Expected type of the event, such as "select". - * @param {string} testName - * Test name when outputing results - * @param {Window=} window - * Window object. Defaults to the current window. - * - * To test that an event is not fired, use an expected type preceded by an - * exclamation mark, such as "!select". - * - * aWindow is optional, and defaults to the current window object. - */ -event.synthesizeKeyExpectEvent = function ( - key, ev, expectedTarget, expectedEvent, testName, - window = undefined) { - - let eventHandler = expectEvent_( - expectedTarget, - expectedEvent, - testName); - event.synthesizeKey(key, ev, window); - checkExpectedEvent_( - expectedTarget, - expectedEvent, - eventHandler, - testName); -}; - -/** - * Synthesize a composition event. - * - * @param {DOMEvent} ev - * The composition event information. This must have |type| - * member. The value must be "compositionstart", "compositionend" or - * "compositionupdate". And also this may have |data| and |locale| - * which would be used for the value of each property of the - * composition event. Note that the data would be ignored if the - * event type were "compositionstart". - * @param {Window=} window - * Window object. Defaults to the current window. - */ -event.synthesizeComposition = function (ev, window = undefined) { - let domutils = getDOMWindowUtils(window); - domutils.sendCompositionEvent(ev.type, ev.data || "", ev.locale || ""); -}; - -/** - * Synthesize a text event. - * - * The text event's information, this has |composition| and |caret| - * members. |composition| has |string| and |clauses| members. |clauses| - * must be array object. Each object has |length| and |attr|. - * And |caret| has |start| and |length|. See the following tree image. - * - * ev - * +-- composition - * | +-- string - * | +-- clauses[] - * | +-- length - * | +-- attr - * +-- caret - * +-- start - * +-- length - * - * Set the composition string to |composition.string|. Set its clauses - * information to the |clauses| array. - * - * When it's composing, set the each clauses' length - * to the |composition.clauses[n].length|. The sum - * of the all length values must be same as the length of - * |composition.string|. Set nsIDOMWindowUtils.COMPOSITION_ATTR_* to the - * |composition.clauses[n].attr|. - * - * When it's not composing, set 0 to the |composition.clauses[0].length| - * and |composition.clauses[0].attr|. - * - * Set caret position to the |caret.start|. Its offset from the start of - * the composition string. Set caret length to |caret.length|. If it's - * larger than 0, it should be wide caret. However, current nsEditor - * doesn't support wide caret, therefore, you should always set 0 now. - * - * @param {Object.<string, ?>} ev - * The text event's information, - * @param {Window=} window - * Window object. Defaults to the current window. - */ -event.synthesizeText = function (ev, window = undefined) { - let domutils = getDOMWindowUtils(window); - - if (!ev.composition || - !ev.composition.clauses || - !ev.composition.clauses[0]) { - return; - } - - let firstClauseLength = ev.composition.clauses[0].length; - let firstClauseAttr = ev.composition.clauses[0].attr; - let secondClauseLength = 0; - let secondClauseAttr = 0; - let thirdClauseLength = 0; - let thirdClauseAttr = 0; - if (ev.composition.clauses[1]) { - secondClauseLength = ev.composition.clauses[1].length; - secondClauseAttr = ev.composition.clauses[1].attr; - if (event.composition.clauses[2]) { - thirdClauseLength = ev.composition.clauses[2].length; - thirdClauseAttr = ev.composition.clauses[2].attr; - } - } - - let caretStart = -1; - let caretLength = 0; - if (event.caret) { - caretStart = ev.caret.start; - caretLength = ev.caret.length; - } - - domutils.sendTextEvent( - ev.composition.string, - firstClauseLength, - firstClauseAttr, - secondClauseLength, - secondClauseAttr, - thirdClauseLength, - thirdClauseAttr, - caretStart, - caretLength); -}; - -/** - * Synthesize a query selected text event. - * - * @param {Window=} - * Window object. Defaults to the current window. - * - * @return {(nsIQueryContentEventResult|null)} - * Event's result, or null if it failed. - */ -event.synthesizeQuerySelectedText = function (window = undefined) { - let domutils = getDOMWindowUtils(window); - return domutils.sendQueryContentEvent( - domutils.QUERY_SELECTED_TEXT, 0, 0, 0, 0); -}; - -/** - * Synthesize a selection set event. - * - * @param {number} offset - * Character offset. 0 means the first character in the selection - * root. - * @param {number} length - * Length of the text. If the length is too long, the extra length - * is ignored. - * @param {boolean} reverse - * If true, the selection is from |aOffset + aLength| to |aOffset|. - * Otherwise, from |aOffset| to |aOffset + aLength|. - * @param {Window=} window - * Window object. Defaults to the current window. - * - * @return True, if succeeded. Otherwise false. - */ -event.synthesizeSelectionSet = function ( - offset, length, reverse, window = undefined) { - let domutils = getDOMWindowUtils(window); - return domutils.sendSelectionSetEvent(offset, length, reverse); -}; - -const KEYCODES_LOOKUP = { - "VK_SHIFT": "shiftKey", - "VK_CONTROL": "ctrlKey", - "VK_ALT": "altKey", - "VK_META": "metaKey", -}; - -const VIRTUAL_KEYCODE_LOOKUP = { - "\uE001": "VK_CANCEL", - "\uE002": "VK_HELP", - "\uE003": "VK_BACK_SPACE", - "\uE004": "VK_TAB", - "\uE005": "VK_CLEAR", - "\uE006": "VK_RETURN", - "\uE007": "VK_RETURN", - "\uE008": "VK_SHIFT", - "\uE009": "VK_CONTROL", - "\uE00A": "VK_ALT", - "\uE03D": "VK_META", - "\uE00B": "VK_PAUSE", - "\uE00C": "VK_ESCAPE", - "\uE00D": "VK_SPACE", // printable - "\uE00E": "VK_PAGE_UP", - "\uE00F": "VK_PAGE_DOWN", - "\uE010": "VK_END", - "\uE011": "VK_HOME", - "\uE012": "VK_LEFT", - "\uE013": "VK_UP", - "\uE014": "VK_RIGHT", - "\uE015": "VK_DOWN", - "\uE016": "VK_INSERT", - "\uE017": "VK_DELETE", - "\uE018": "VK_SEMICOLON", - "\uE019": "VK_EQUALS", - "\uE01A": "VK_NUMPAD0", - "\uE01B": "VK_NUMPAD1", - "\uE01C": "VK_NUMPAD2", - "\uE01D": "VK_NUMPAD3", - "\uE01E": "VK_NUMPAD4", - "\uE01F": "VK_NUMPAD5", - "\uE020": "VK_NUMPAD6", - "\uE021": "VK_NUMPAD7", - "\uE022": "VK_NUMPAD8", - "\uE023": "VK_NUMPAD9", - "\uE024": "VK_MULTIPLY", - "\uE025": "VK_ADD", - "\uE026": "VK_SEPARATOR", - "\uE027": "VK_SUBTRACT", - "\uE028": "VK_DECIMAL", - "\uE029": "VK_DIVIDE", - "\uE031": "VK_F1", - "\uE032": "VK_F2", - "\uE033": "VK_F3", - "\uE034": "VK_F4", - "\uE035": "VK_F5", - "\uE036": "VK_F6", - "\uE037": "VK_F7", - "\uE038": "VK_F8", - "\uE039": "VK_F9", - "\uE03A": "VK_F10", - "\uE03B": "VK_F11", - "\uE03C": "VK_F12", -}; - -function getKeyCode(c) { - if (c in VIRTUAL_KEYCODE_LOOKUP) { - return VIRTUAL_KEYCODE_LOOKUP[c]; - } - return c; -} - -event.sendKeyDown = function (keyToSend, modifiers, document) { - modifiers.type = "keydown"; - event.sendSingleKey(keyToSend, modifiers, document); - // TODO This doesn't do anything since |synthesizeKeyEvent| ignores explicit - // keypress request, and instead figures out itself when to send keypress - if (["VK_SHIFT", "VK_CONTROL", "VK_ALT", "VK_META"].indexOf(getKeyCode(keyToSend)) < 0) { - modifiers.type = "keypress"; - event.sendSingleKey(keyToSend, modifiers, document); - } - delete modifiers.type; -}; - -event.sendKeyUp = function (keyToSend, modifiers, window = undefined) { - modifiers.type = "keyup"; - event.sendSingleKey(keyToSend, modifiers, window); - delete modifiers.type; -}; - -/** - * Synthesize a key event for a single key. - * - * @param {string} keyToSend - * Code point or normalized key value - * @param {?} modifiers - * Object with properties used in KeyboardEvent (shiftkey, repeat, ...) - * as well as, the event |type| such as keydown. All properties are optional. - * @param {Window=} window - * Window object. If |window| is undefined, the event is synthesized in - * current window. - */ -event.sendSingleKey = function (keyToSend, modifiers, window = undefined) { - let keyCode = getKeyCode(keyToSend); - if (keyCode in KEYCODES_LOOKUP) { - // We assume that if |keyToSend| is a raw code point (like "\uE009") then - // |modifiers| does not already have correct value for corresponding - // |modName| attribute (like ctrlKey), so that value needs to be flipped - let modName = KEYCODES_LOOKUP[keyCode]; - modifiers[modName] = !modifiers[modName]; - } else if (modifiers.shiftKey && keyCode != "Shift") { - keyCode = keyCode.toUpperCase(); - } - event.synthesizeKey(keyCode, modifiers, window); -}; - -/** - * Focus element and, if a textual input field and no previous selection - * state exists, move the caret to the end of the input field. - * - * @param {Element} element - * Element to focus. - */ -function focusElement(element) { - let t = element.type; - if (t && (t == "text" || t == "textarea")) { - if (element.selectionEnd == 0) { - let len = element.value.length; - element.setSelectionRange(len, len); - } - } - element.focus(); -} - -/** - * @param {Array.<string>} keySequence - * @param {Element} element - * @param {Object.<string, boolean>=} opts - * @param {Window=} window - */ -event.sendKeysToElement = function ( - keySequence, el, opts = {}, window = undefined) { - - if (opts.ignoreVisibility || element.isVisible(el)) { - focusElement(el); - - // make Object.<modifier, false> map - let modifiers = Object.create(event.Modifiers); - for (let modifier in event.Modifiers) { - modifiers[modifier] = false; - } - - let value = keySequence.join(""); - for (let i = 0; i < value.length; i++) { - let c = value.charAt(i); - event.sendSingleKey(c, modifiers, window); - } - - } else { - throw new ElementNotInteractableError("Element is not visible"); - } -}; - -event.sendEvent = function (eventType, el, modifiers = {}, opts = {}) { - opts.canBubble = opts.canBubble || true; - - let doc = el.ownerDocument || el.document; - let ev = doc.createEvent("Event"); - - ev.shiftKey = modifiers["shift"]; - ev.metaKey = modifiers["meta"]; - ev.altKey = modifiers["alt"]; - ev.ctrlKey = modifiers["ctrl"]; - - ev.initEvent(eventType, opts.canBubble, true); - el.dispatchEvent(ev); -}; - -event.focus = function (el, opts = {}) { - opts.canBubble = opts.canBubble || true; - let doc = el.ownerDocument || el.document; - let win = doc.defaultView; - - let ev = new win.FocusEvent(el); - ev.initEvent("focus", opts.canBubble, true); - el.dispatchEvent(ev); -}; - -event.mouseover = function (el, modifiers = {}, opts = {}) { - return event.sendEvent("mouseover", el, modifiers, opts); -}; - -event.mousemove = function (el, modifiers = {}, opts = {}) { - return event.sendEvent("mousemove", el, modifiers, opts); -}; - -event.mousedown = function (el, modifiers = {}, opts = {}) { - return event.sendEvent("mousedown", el, modifiers, opts); -}; - -event.mouseup = function (el, modifiers = {}, opts = {}) { - return event.sendEvent("mouseup", el, modifiers, opts); -}; - -event.click = function (el, modifiers = {}, opts = {}) { - return event.sendEvent("click", el, modifiers, opts); -}; - -event.change = function (el, modifiers = {}, opts = {}) { - return event.sendEvent("change", el, modifiers, opts); -}; - -event.input = function (el, modifiers = {}, opts = {}) { - return event.sendEvent("input", el, modifiers, opts); -}; diff --git a/testing/marionette/frame.js b/testing/marionette/frame.js deleted file mode 100644 index fc713eb97..000000000 --- a/testing/marionette/frame.js +++ /dev/null @@ -1,260 +0,0 @@ -/* 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/. */ - -"use strict"; - -const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components; - -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -this.EXPORTED_SYMBOLS = ["frame"]; - -this.frame = {}; - -const FRAME_SCRIPT = "chrome://marionette/content/listener.js"; - -// list of OOP frames that has the frame script loaded -var remoteFrames = []; - -/** - * An object representing a frame that Marionette has loaded a - * frame script in. - */ -frame.RemoteFrame = function (windowId, frameId) { - // outerWindowId relative to main process - this.windowId = windowId; - // actual frame relative to the windowId's frames list - this.frameId = frameId; - // assigned frame ID, used for messaging - this.targetFrameId = this.frameId; - // list of OOP frames that has the frame script loaded - this.remoteFrames = []; -}; - -/** - * The FrameManager will maintain the list of Out Of Process (OOP) - * frames and will handle frame switching between them. - * - * It handles explicit frame switching (switchToFrame), and implicit - * frame switching, which occurs when a modal dialog is triggered in B2G. - * - * @param {GeckoDriver} driver - * Reference to the driver instance. - */ -frame.Manager = class { - constructor(driver) { - // messageManager maintains the messageManager - // for the current process' chrome frame or the global message manager - - // holds a member of the remoteFrames (for an OOP frame) - // or null (for the main process) - this.currentRemoteFrame = null; - // frame we'll need to restore once interrupt is gone - this.previousRemoteFrame = null; - // set to true when we have been interrupted by a modal - this.handledModal = false; - this.driver = driver; - } - - /** - * Receives all messages from content messageManager. - */ - receiveMessage(message) { - switch (message.name) { - case "MarionetteFrame:getInterruptedState": - // this will return true if the calling frame was interrupted by a modal dialog - if (this.previousRemoteFrame) { - // get the frame window of the interrupted frame - let interruptedFrame = Services.wm.getOuterWindowWithId( - this.previousRemoteFrame.windowId); - - if (this.previousRemoteFrame.frameId !== null) { - // find OOP frame - let iframes = interruptedFrame.document.getElementsByTagName("iframe"); - interruptedFrame = iframes[this.previousRemoteFrame.frameId]; - } - - // check if the interrupted frame is the same as the calling frame - if (interruptedFrame.src == message.target.src) { - return {value: this.handledModal}; - } - - // we get here if previousRemoteFrame and currentRemoteFrame are null, - // i.e. if we're in a non-OOP process, or we haven't switched into an OOP frame, - // in which case, handledModal can't be set to true - } else if (this.currentRemoteFrame === null) { - return {value: this.handledModal}; - } - return {value: false}; - - // handleModal is called when we need to switch frames to the main - // process due to a modal dialog interrupt - case "MarionetteFrame:handleModal": - // If previousRemoteFrame was set, that means we switched into a - // remote frame. If this is the case, then we want to switch back - // into the system frame. If it isn't the case, then we're in a - // non-OOP environment, so we don't need to handle remote frames. - let isLocal = true; - if (this.currentRemoteFrame !== null) { - isLocal = false; - this.removeMessageManagerListeners( - this.currentRemoteFrame.messageManager.get()); - - // store the previous frame so we can switch back to it when - // the modal is dismissed - this.previousRemoteFrame = this.currentRemoteFrame; - - // by setting currentRemoteFrame to null, - // it signifies we're in the main process - this.currentRemoteFrame = null; - this.driver.messageManager = Cc["@mozilla.org/globalmessagemanager;1"] - .getService(Ci.nsIMessageBroadcaster); - } - - this.handledModal = true; - this.driver.sendOk(this.driver.command_id); - return {value: isLocal}; - - case "MarionetteFrame:getCurrentFrameId": - if (this.currentRemoteFrame !== null) { - return this.currentRemoteFrame.frameId; - } - } - } - - getOopFrame(winId, frameId) { - // get original frame window - let outerWin = Services.wm.getOuterWindowWithId(winId); - // find the OOP frame - let f = outerWin.document.getElementsByTagName("iframe")[frameId]; - return f; - } - - getFrameMM(winId, frameId) { - let oopFrame = this.getOopFrame(winId, frameId); - let mm = oopFrame.QueryInterface(Ci.nsIFrameLoaderOwner) - .frameLoader.messageManager; - return mm; - } - - /** - * Switch to OOP frame. We're handling this here so we can maintain - * a list of remote frames. - */ - switchToFrame(winId, frameId) { - let oopFrame = this.getOopFrame(winId, frameId); - let mm = this.getFrameMM(winId, frameId); - - // see if this frame already has our frame script loaded in it; - // if so, just wake it up - for (let i = 0; i < remoteFrames.length; i++) { - let f = remoteFrames[i]; - let fmm = f.messageManager.get(); - try { - fmm.sendAsyncMessage("aliveCheck", {}); - } catch (e) { - if (e.result == Cr.NS_ERROR_NOT_INITIALIZED) { - remoteFrames.splice(i--, 1); - continue; - } - } - - if (fmm == mm) { - this.currentRemoteFrame = f; - this.addMessageManagerListeners(mm); - - mm.sendAsyncMessage("Marionette:restart"); - return oopFrame.id; - } - } - - // if we get here, then we need to load the frame script in this frame, - // and set the frame's ChromeMessageSender as the active message manager - // the driver will listen to. - this.addMessageManagerListeners(mm); - let f = new frame.RemoteFrame(winId, frameId); - f.messageManager = Cu.getWeakReference(mm); - remoteFrames.push(f); - this.currentRemoteFrame = f; - - mm.loadFrameScript(FRAME_SCRIPT, true, true); - - return oopFrame.id; - } - - /* - * This function handles switching back to the frame that was - * interrupted by the modal dialog. It gets called by the interrupted - * frame once the dialog is dismissed and the frame resumes its process. - */ - switchToModalOrigin() { - // only handle this if we indeed switched out of the modal's - // originating frame - if (this.previousRemoteFrame !== null) { - this.currentRemoteFrame = this.previousRemoteFrame; - let mm = this.currentRemoteFrame.messageManager.get(); - this.addMessageManagerListeners(mm); - } - this.handledModal = false; - } - - /** - * Adds message listeners to the driver, listening for - * messages from content frame scripts. It also adds a - * MarionetteFrame:getInterruptedState message listener to the - * FrameManager, so the frame manager's state can be checked by the frame. - * - * @param {nsIMessageListenerManager} mm - * The message manager object, typically - * ChromeMessageBroadcaster or ChromeMessageSender. - */ - addMessageManagerListeners(mm) { - mm.addWeakMessageListener("Marionette:ok", this.driver); - mm.addWeakMessageListener("Marionette:done", this.driver); - mm.addWeakMessageListener("Marionette:error", this.driver); - mm.addWeakMessageListener("Marionette:emitTouchEvent", this.driver); - mm.addWeakMessageListener("Marionette:log", this.driver); - mm.addWeakMessageListener("Marionette:shareData", this.driver); - mm.addWeakMessageListener("Marionette:switchToModalOrigin", this.driver); - mm.addWeakMessageListener("Marionette:switchedToFrame", this.driver); - mm.addWeakMessageListener("Marionette:getVisibleCookies", this.driver); - mm.addWeakMessageListener("Marionette:getImportedScripts", this.driver.importedScripts); - mm.addWeakMessageListener("Marionette:register", this.driver); - mm.addWeakMessageListener("Marionette:listenersAttached", this.driver); - mm.addWeakMessageListener("MarionetteFrame:handleModal", this); - mm.addWeakMessageListener("MarionetteFrame:getCurrentFrameId", this); - mm.addWeakMessageListener("MarionetteFrame:getInterruptedState", this); - } - - /** - * Removes listeners for messages from content frame scripts. - * We do not remove the MarionetteFrame:getInterruptedState or - * the Marionette:switchToModalOrigin message listener, because we - * want to allow all known frames to contact the frame manager so - * that it can check if it was interrupted, and if so, it will call - * switchToModalOrigin when its process gets resumed. - * - * @param {nsIMessageListenerManager} mm - * The message manager object, typically - * ChromeMessageBroadcaster or ChromeMessageSender. - */ - removeMessageManagerListeners(mm) { - mm.removeWeakMessageListener("Marionette:ok", this.driver); - mm.removeWeakMessageListener("Marionette:done", this.driver); - mm.removeWeakMessageListener("Marionette:error", this.driver); - mm.removeWeakMessageListener("Marionette:log", this.driver); - mm.removeWeakMessageListener("Marionette:shareData", this.driver); - mm.removeWeakMessageListener("Marionette:switchedToFrame", this.driver); - mm.removeWeakMessageListener("Marionette:getVisibleCookies", this.driver); - mm.removeWeakMessageListener("Marionette:getImportedScripts", this.driver.importedScripts); - mm.removeWeakMessageListener("Marionette:listenersAttached", this.driver); - mm.removeWeakMessageListener("Marionette:register", this.driver); - mm.removeWeakMessageListener("MarionetteFrame:handleModal", this); - mm.removeWeakMessageListener("MarionetteFrame:getCurrentFrameId", this); - } -}; - -frame.Manager.prototype.QueryInterface = XPCOMUtils.generateQI( - [Ci.nsIMessageListener, Ci.nsISupportsWeakReference]); diff --git a/testing/marionette/harness/.flake8 b/testing/marionette/harness/.flake8 deleted file mode 100644 index 23c7990dc..000000000 --- a/testing/marionette/harness/.flake8 +++ /dev/null @@ -1,3 +0,0 @@ -[flake8] -max-line-length = 99 -exclude = __init__.py,disti/*,build/*,marionette_harness/runner/mixins/*, marionette_harness/tests/* diff --git a/testing/marionette/harness/MANIFEST.in b/testing/marionette/harness/MANIFEST.in deleted file mode 100644 index 1e39ed0e4..000000000 --- a/testing/marionette/harness/MANIFEST.in +++ /dev/null @@ -1,3 +0,0 @@ -exclude MANIFEST.in -include requirements.txt -recursive-include marionette_harness/www * diff --git a/testing/marionette/harness/README.rst b/testing/marionette/harness/README.rst deleted file mode 100644 index 3f8865603..000000000 --- a/testing/marionette/harness/README.rst +++ /dev/null @@ -1,30 +0,0 @@ -marionette-harness -================== - -Marionette is an automation driver for Mozilla's Gecko engine. It can remotely -control either the UI or the internal JavaScript of a Gecko platform, such as -Firefox. It can control both the chrome (i.e. menus and functions) or the -content (the webpage loaded inside the browsing context), giving a high level -of control and ability to replicate user actions. In addition to performing -actions on the browser, Marionette can also read the properties and attributes -of the DOM. - -The marionette_harness package contains the test runner for Marionette, and -allows you to run automated tests written in Python for Gecko based -applications. Therefore it offers the necessary testcase classes, which are -based on the unittest framework. - -For more information and the repository please checkout: - -- home and docs: https://developer.mozilla.org/en-US/docs/Mozilla/QA/Marionette - - -Example -------- - -The following command will run the tests as specified via a manifest file, or -test path, or test folder in Firefox: - - marionette --binary %path_to_firefox% [manifest_file | test_file | test_folder] - -To get an overview about all possible option run `marionette --help`. diff --git a/testing/marionette/harness/marionette_harness/__init__.py b/testing/marionette/harness/marionette_harness/__init__.py deleted file mode 100644 index 9ae4e1b29..000000000 --- a/testing/marionette/harness/marionette_harness/__init__.py +++ /dev/null @@ -1,35 +0,0 @@ -# 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/. - -__version__ = '4.0.0' - -from .marionette_test import ( - CommonTestCase, - expectedFailure, - MarionetteTestCase, - parameterized, - run_if_e10s, - run_if_manage_instance, - skip, - skip_if_chrome, - skip_if_desktop, - skip_if_e10s, - skip_if_mobile, - SkipTest, - skip_unless_protocol, -) -from .runner import ( - BaseMarionetteArguments, - BaseMarionetteTestRunner, - BrowserMobProxyArguments, - BrowserMobProxyTestCaseMixin, - Marionette, - MarionetteTest, - MarionetteTestResult, - MarionetteTextTestRunner, - TestManifest, - TestResult, - TestResultCollection, - WindowManagerMixin, -) diff --git a/testing/marionette/harness/marionette_harness/marionette_test/__init__.py b/testing/marionette/harness/marionette_harness/marionette_test/__init__.py deleted file mode 100644 index efcf1d38e..000000000 --- a/testing/marionette/harness/marionette_harness/marionette_test/__init__.py +++ /dev/null @@ -1,31 +0,0 @@ -# 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/. - -__version__ = '3.1.0' - - -from unittest.case import ( - expectedFailure, - skip, - SkipTest, -) - -from .decorators import ( - parameterized, - run_if_e10s, - run_if_manage_instance, - skip_if_chrome, - skip_if_desktop, - skip_if_e10s, - skip_if_mobile, - skip_unless_browser_pref, - skip_unless_protocol, - with_parameters, -) - -from .testcases import ( - CommonTestCase, - MarionetteTestCase, - MetaParameterized, -) diff --git a/testing/marionette/harness/marionette_harness/marionette_test/decorators.py b/testing/marionette/harness/marionette_harness/marionette_test/decorators.py deleted file mode 100644 index 63f947ea2..000000000 --- a/testing/marionette/harness/marionette_harness/marionette_test/decorators.py +++ /dev/null @@ -1,239 +0,0 @@ -# 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/. - -import functools -import types - -from unittest.case import ( - SkipTest, -) - - -def parameterized(func_suffix, *args, **kwargs): - r"""Decorator which generates methods given a base method and some data. - - **func_suffix** is used as a suffix for the new created method and must be - unique given a base method. if **func_suffix** countains characters that - are not allowed in normal python function name, these characters will be - replaced with "_". - - This decorator can be used more than once on a single base method. The class - must have a metaclass of :class:`MetaParameterized`. - - Example:: - - # This example will generate two methods: - # - # - MyTestCase.test_it_1 - # - MyTestCase.test_it_2 - # - class MyTestCase(MarionetteTestCase): - @parameterized("1", 5, named='name') - @parameterized("2", 6, named='name2') - def test_it(self, value, named=None): - print value, named - - :param func_suffix: will be used as a suffix for the new method - :param \*args: arguments to pass to the new method - :param \*\*kwargs: named arguments to pass to the new method - """ - def wrapped(func): - if not hasattr(func, 'metaparameters'): - func.metaparameters = [] - func.metaparameters.append((func_suffix, args, kwargs)) - return func - return wrapped - - -def run_if_e10s(reason): - """Decorator which runs a test if e10s mode is active.""" - def decorator(test_item): - if not isinstance(test_item, types.FunctionType): - raise Exception('Decorator only supported for functions') - - @functools.wraps(test_item) - def skip_wrapper(self, *args, **kwargs): - with self.marionette.using_context('chrome'): - multi_process_browser = not self.marionette.execute_script(""" - try { - return Services.appinfo.browserTabsRemoteAutostart; - } catch (e) { - return false; - } - """) - if multi_process_browser: - raise SkipTest(reason) - return test_item(self, *args, **kwargs) - return skip_wrapper - return decorator - - -def run_if_manage_instance(reason): - """Decorator which runs a test if Marionette manages the application instance.""" - def decorator(test_item): - if not isinstance(test_item, types.FunctionType): - raise Exception('Decorator only supported for functions') - - @functools.wraps(test_item) - def skip_wrapper(self, *args, **kwargs): - if self.marionette.instance is None: - raise SkipTest(reason) - return test_item(self, *args, **kwargs) - return skip_wrapper - return decorator - - -def skip_if_chrome(reason): - """Decorator which skips a test if chrome context is active.""" - def decorator(test_item): - if not isinstance(test_item, types.FunctionType): - raise Exception('Decorator only supported for functions') - - @functools.wraps(test_item) - def skip_wrapper(self, *args, **kwargs): - if self.marionette._send_message('getContext', key='value') == 'chrome': - raise SkipTest(reason) - return test_item(self, *args, **kwargs) - return skip_wrapper - return decorator - - -def skip_if_desktop(reason): - """Decorator which skips a test if run on desktop.""" - def decorator(test_item): - if not isinstance(test_item, types.FunctionType): - raise Exception('Decorator only supported for functions') - - @functools.wraps(test_item) - def skip_wrapper(self, *args, **kwargs): - if self.marionette.session_capabilities.get('browserName') == 'firefox': - raise SkipTest(reason) - return test_item(self, *args, **kwargs) - return skip_wrapper - return decorator - - -def skip_if_e10s(reason): - """Decorator which skips a test if e10s mode is active.""" - def decorator(test_item): - if not isinstance(test_item, types.FunctionType): - raise Exception('Decorator only supported for functions') - - @functools.wraps(test_item) - def skip_wrapper(self, *args, **kwargs): - with self.marionette.using_context('chrome'): - multi_process_browser = self.marionette.execute_script(""" - try { - return Services.appinfo.browserTabsRemoteAutostart; - } catch (e) { - return false; - } - """) - if multi_process_browser: - raise SkipTest(reason) - return test_item(self, *args, **kwargs) - return skip_wrapper - return decorator - - -def skip_if_mobile(reason): - """Decorator which skips a test if run on mobile.""" - def decorator(test_item): - if not isinstance(test_item, types.FunctionType): - raise Exception('Decorator only supported for functions') - - @functools.wraps(test_item) - def skip_wrapper(self, *args, **kwargs): - if self.marionette.session_capabilities.get('browserName') == 'fennec': - raise SkipTest(reason) - return test_item(self, *args, **kwargs) - return skip_wrapper - return decorator - - -def skip_unless_browser_pref(reason, pref, predicate=bool): - """Decorator which skips a test based on the value of a browser preference. - - :param reason: Message describing why the test need to be skipped. - :param pref: the preference name - :param predicate: a function that should return false to skip the test. - The function takes one parameter, the preference value. - Defaults to the python built-in bool function. - - Note that the preference must exist, else a failure is raised. - - Example: :: - - class TestSomething(MarionetteTestCase): - @skip_unless_browser_pref("Sessionstore needs to be enabled for crashes", - "browser.sessionstore.resume_from_crash", - lambda value: value is True, - ) - def test_foo(self): - pass # test implementation here - - """ - def decorator(test_item): - if not isinstance(test_item, types.FunctionType): - raise Exception('Decorator only supported for functions') - if not callable(predicate): - raise ValueError('predicate must be callable') - - @functools.wraps(test_item) - def skip_wrapper(self, *args, **kwargs): - value = self.marionette.get_pref(pref) - if value is None: - self.fail("No such browser preference: {0!r}".format(pref)) - if not predicate(value): - raise SkipTest(reason) - return test_item(self, *args, **kwargs) - return skip_wrapper - return decorator - - -def skip_unless_protocol(reason, predicate): - """Decorator which skips a test if the predicate does not match the current protocol level.""" - def decorator(test_item): - if not isinstance(test_item, types.FunctionType): - raise Exception('Decorator only supported for functions') - if not callable(predicate): - raise ValueError('predicate must be callable') - - @functools.wraps(test_item) - def skip_wrapper(self, *args, **kwargs): - level = self.marionette.client.protocol - if not predicate(level): - raise SkipTest(reason) - return test_item(self, *args, **kwargs) - return skip_wrapper - return decorator - - -def with_parameters(parameters): - """Decorator which generates methods given a base method and some data. - - Acts like :func:`parameterized`, but define all methods in one call. - - Example:: - - # This example will generate two methods: - # - # - MyTestCase.test_it_1 - # - MyTestCase.test_it_2 - # - - DATA = [("1", [5], {'named':'name'}), ("2", [6], {'named':'name2'})] - - class MyTestCase(MarionetteTestCase): - @with_parameters(DATA) - def test_it(self, value, named=None): - print value, named - - :param parameters: list of tuples (**func_suffix**, **args**, **kwargs**) - defining parameters like in :func:`todo`. - """ - def wrapped(func): - func.metaparameters = parameters - return func - return wrapped diff --git a/testing/marionette/harness/marionette_harness/marionette_test/testcases.py b/testing/marionette/harness/marionette_harness/marionette_test/testcases.py deleted file mode 100644 index 5051e3351..000000000 --- a/testing/marionette/harness/marionette_harness/marionette_test/testcases.py +++ /dev/null @@ -1,504 +0,0 @@ -# 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/. - -import imp -import os -import re -import sys -import time -import types -import unittest -import warnings -import weakref - -from unittest.case import ( - _ExpectedFailure, - _UnexpectedSuccess, - SkipTest, -) - -from marionette_driver.errors import ( - MarionetteException, - ScriptTimeoutException, - TimeoutException, -) -from mozlog import get_default_logger - - -def _wraps_parameterized(func, func_suffix, args, kwargs): - """Internal: Decorator used in class MetaParameterized.""" - def wrapper(self): - return func(self, *args, **kwargs) - wrapper.__name__ = func.__name__ + '_' + str(func_suffix) - wrapper.__doc__ = '[{0}] {1}'.format(func_suffix, func.__doc__) - return wrapper - - -class MetaParameterized(type): - """ - A metaclass that allow a class to use decorators. - - It can be used like :func:`parameterized` - or :func:`with_parameters` to generate new methods. - """ - - RE_ESCAPE_BAD_CHARS = re.compile(r'[\.\(\) -/]') - - def __new__(cls, name, bases, attrs): - for k, v in attrs.items(): - if callable(v) and hasattr(v, 'metaparameters'): - for func_suffix, args, kwargs in v.metaparameters: - func_suffix = cls.RE_ESCAPE_BAD_CHARS.sub('_', func_suffix) - wrapper = _wraps_parameterized(v, func_suffix, args, kwargs) - if wrapper.__name__ in attrs: - raise KeyError("{0} is already a defined method on {1}" - .format(wrapper.__name__, name)) - attrs[wrapper.__name__] = wrapper - del attrs[k] - - return type.__new__(cls, name, bases, attrs) - - -class JSTest: - head_js_re = re.compile(r"MARIONETTE_HEAD_JS(\s*)=(\s*)['|\"](.*?)['|\"];") - context_re = re.compile(r"MARIONETTE_CONTEXT(\s*)=(\s*)['|\"](.*?)['|\"];") - timeout_re = re.compile(r"MARIONETTE_TIMEOUT(\s*)=(\s*)(\d+);") - inactivity_timeout_re = re.compile(r"MARIONETTE_INACTIVITY_TIMEOUT(\s*)=(\s*)(\d+);") - - -class CommonTestCase(unittest.TestCase): - - __metaclass__ = MetaParameterized - match_re = None - failureException = AssertionError - pydebugger = None - - def __init__(self, methodName, marionette_weakref, fixtures, **kwargs): - super(CommonTestCase, self).__init__(methodName) - self.methodName = methodName - - self._marionette_weakref = marionette_weakref - self.fixtures = fixtures - - self.loglines = [] - self.duration = 0 - self.start_time = 0 - self.expected = kwargs.pop('expected', 'pass') - self.logger = get_default_logger() - - def _enter_pm(self): - if self.pydebugger: - self.pydebugger.post_mortem(sys.exc_info()[2]) - - def _addSkip(self, result, reason): - addSkip = getattr(result, 'addSkip', None) - if addSkip is not None: - addSkip(self, reason) - else: - warnings.warn("TestResult has no addSkip method, skips not reported", - RuntimeWarning, 2) - result.addSuccess(self) - - def run(self, result=None): - # Bug 967566 suggests refactoring run, which would hopefully - # mean getting rid of this inner function, which only sits - # here to reduce code duplication: - def expected_failure(result, exc_info): - addExpectedFailure = getattr(result, "addExpectedFailure", None) - if addExpectedFailure is not None: - addExpectedFailure(self, exc_info) - else: - warnings.warn("TestResult has no addExpectedFailure method, " - "reporting as passes", RuntimeWarning) - result.addSuccess(self) - - self.start_time = time.time() - orig_result = result - if result is None: - result = self.defaultTestResult() - startTestRun = getattr(result, 'startTestRun', None) - if startTestRun is not None: - startTestRun() - - result.startTest(self) - - testMethod = getattr(self, self._testMethodName) - if (getattr(self.__class__, "__unittest_skip__", False) or - getattr(testMethod, "__unittest_skip__", False)): - # If the class or method was skipped. - try: - skip_why = (getattr(self.__class__, '__unittest_skip_why__', '') or - getattr(testMethod, '__unittest_skip_why__', '')) - self._addSkip(result, skip_why) - finally: - result.stopTest(self) - self.stop_time = time.time() - return - try: - success = False - try: - if self.expected == "fail": - try: - self.setUp() - except Exception: - raise _ExpectedFailure(sys.exc_info()) - else: - self.setUp() - except SkipTest as e: - self._addSkip(result, str(e)) - except KeyboardInterrupt: - raise - except _ExpectedFailure as e: - expected_failure(result, e.exc_info) - except: - self._enter_pm() - result.addError(self, sys.exc_info()) - else: - try: - if self.expected == 'fail': - try: - testMethod() - except: - raise _ExpectedFailure(sys.exc_info()) - raise _UnexpectedSuccess - else: - testMethod() - except self.failureException: - self._enter_pm() - result.addFailure(self, sys.exc_info()) - except KeyboardInterrupt: - raise - except _ExpectedFailure as e: - expected_failure(result, e.exc_info) - except _UnexpectedSuccess: - addUnexpectedSuccess = getattr(result, 'addUnexpectedSuccess', None) - if addUnexpectedSuccess is not None: - addUnexpectedSuccess(self) - else: - warnings.warn("TestResult has no addUnexpectedSuccess method, " - "reporting as failures", - RuntimeWarning) - result.addFailure(self, sys.exc_info()) - except SkipTest as e: - self._addSkip(result, str(e)) - except: - self._enter_pm() - result.addError(self, sys.exc_info()) - else: - success = True - try: - if self.expected == "fail": - try: - self.tearDown() - except: - raise _ExpectedFailure(sys.exc_info()) - else: - self.tearDown() - except KeyboardInterrupt: - raise - except _ExpectedFailure as e: - expected_failure(result, e.exc_info) - except: - self._enter_pm() - result.addError(self, sys.exc_info()) - success = False - # Here we could handle doCleanups() instead of calling cleanTest directly - self.cleanTest() - - if success: - result.addSuccess(self) - - finally: - result.stopTest(self) - if orig_result is None: - stopTestRun = getattr(result, 'stopTestRun', None) - if stopTestRun is not None: - stopTestRun() - - @classmethod - def match(cls, filename): - """Determine if the specified filename should be handled by this test class. - - This is done by looking for a match for the filename using cls.match_re. - """ - if not cls.match_re: - return False - m = cls.match_re.match(filename) - return m is not None - - @classmethod - def add_tests_to_suite(cls, mod_name, filepath, suite, testloader, marionette, - fixtures, testvars, **kwargs): - """Add all the tests in the specified file to the specified suite.""" - raise NotImplementedError - - @property - def test_name(self): - if hasattr(self, 'jsFile'): - return os.path.basename(self.jsFile) - else: - return '{0}.py {1}.{2}'.format(self.__class__.__module__, - self.__class__.__name__, - self._testMethodName) - - def id(self): - # TBPL starring requires that the "test name" field of a failure message - # not differ over time. The test name to be used is passed to - # mozlog via the test id, so this is overriden to maintain - # consistency. - return self.test_name - - def setUp(self): - # Convert the marionette weakref to an object, just for the - # duration of the test; this is deleted in tearDown() to prevent - # a persistent circular reference which in turn would prevent - # proper garbage collection. - self.start_time = time.time() - self.marionette = self._marionette_weakref() - if self.marionette.session is None: - self.marionette.start_session() - self.marionette.timeout.reset() - - super(CommonTestCase, self).setUp() - - def cleanTest(self): - self._deleteSession() - - def _deleteSession(self): - if hasattr(self, 'start_time'): - self.duration = time.time() - self.start_time - if hasattr(self.marionette, 'session'): - if self.marionette.session is not None: - try: - self.loglines.extend(self.marionette.get_logs()) - except Exception, inst: - self.loglines = [['Error getting log: {}'.format(inst)]] - try: - self.marionette.delete_session() - except IOError: - # Gecko has crashed? - pass - self.marionette = None - - def setup_SpecialPowers_observer(self): - self.marionette.set_context("chrome") - self.marionette.execute_script(""" -let SECURITY_PREF = "security.turn_off_all_security_so_that_viruses_can_take_over_this_computer"; -Components.utils.import("resource://gre/modules/Preferences.jsm"); -Preferences.set(SECURITY_PREF, true); - -if (!testUtils.hasOwnProperty("specialPowersObserver")) { - let loader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"] - .getService(Components.interfaces.mozIJSSubScriptLoader); - loader.loadSubScript("chrome://specialpowers/content/SpecialPowersObserver.jsm", - testUtils); - testUtils.specialPowersObserver = new testUtils.SpecialPowersObserver(); - testUtils.specialPowersObserver.init(); -} -""") - - def run_js_test(self, filename, marionette=None): - """Run a JavaScript test file. - - It collects its set of assertions into the current test's results. - - :param filename: The path to the JavaScript test file to execute. - May be relative to the current script. - :param marionette: The Marionette object in which to execute the test. - Defaults to self.marionette. - """ - marionette = marionette or self.marionette - if not os.path.isabs(filename): - # Find the caller's filename and make the path relative to that. - caller_file = sys._getframe(1).f_globals.get('__file__', '') - caller_file = os.path.abspath(caller_file) - filename = os.path.join(os.path.dirname(caller_file), filename) - self.assert_(os.path.exists(filename), - 'Script "{}" must exist' .format(filename)) - original_test_name = self.marionette.test_name - self.marionette.test_name = os.path.basename(filename) - f = open(filename, 'r') - js = f.read() - args = [] - - head_js = JSTest.head_js_re.search(js) - if head_js: - head_js = head_js.group(3) - head = open(os.path.join(os.path.dirname(filename), head_js), 'r') - js = head.read() + js - - context = JSTest.context_re.search(js) - if context: - context = context.group(3) - else: - context = 'content' - - if 'SpecialPowers' in js: - self.setup_SpecialPowers_observer() - - if context == 'content': - js = "var SpecialPowers = window.wrappedJSObject.SpecialPowers;\n" + js - else: - marionette.execute_script(""" - if (typeof(SpecialPowers) == 'undefined') { - let loader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"] - .getService(Components.interfaces.mozIJSSubScriptLoader); - loader.loadSubScript("chrome://specialpowers/content/specialpowersAPI.js"); - loader.loadSubScript("chrome://specialpowers/content/SpecialPowersObserverAPI.js"); - loader.loadSubScript("chrome://specialpowers/content/ChromePowers.js"); - } - """) - - marionette.set_context(context) - - if context != 'chrome': - marionette.navigate('data:text/html,<html>test page</html>') - - timeout = JSTest.timeout_re.search(js) - if timeout: - ms = timeout.group(3) - marionette.timeout.script = int(ms) / 1000.0 - - inactivity_timeout = JSTest.inactivity_timeout_re.search(js) - if inactivity_timeout: - inactivity_timeout = int(inactivity_timeout.group(3)) - - try: - results = marionette.execute_js_script( - js, - args, - inactivity_timeout=inactivity_timeout, - filename=os.path.basename(filename) - ) - - self.assertTrue('timeout' not in filename, - 'expected timeout not triggered') - - if 'fail' in filename: - self.assertTrue(len(results['failures']) > 0, - "expected test failures didn't occur") - else: - for failure in results['failures']: - diag = "" if failure.get('diag') is None else failure['diag'] - name = ("got false, expected true" if failure.get('name') is None else - failure['name']) - self.logger.test_status(self.test_name, name, 'FAIL', - message=diag) - for failure in results['expectedFailures']: - diag = "" if failure.get('diag') is None else failure['diag'] - name = ("got false, expected false" if failure.get('name') is None else - failure['name']) - self.logger.test_status(self.test_name, name, 'FAIL', - expected='FAIL', message=diag) - for failure in results['unexpectedSuccesses']: - diag = "" if failure.get('diag') is None else failure['diag'] - name = ("got true, expected false" if failure.get('name') is None else - failure['name']) - self.logger.test_status(self.test_name, name, 'PASS', - expected='FAIL', message=diag) - self.assertEqual(0, len(results['failures']), - '{} tests failed' .format(len(results['failures']))) - if len(results['unexpectedSuccesses']) > 0: - raise _UnexpectedSuccess('') - if len(results['expectedFailures']) > 0: - raise _ExpectedFailure((AssertionError, AssertionError(''), None)) - - self.assertTrue(results['passed'] + - len(results['failures']) + - len(results['expectedFailures']) + - len(results['unexpectedSuccesses']) > 0, - 'no tests run') - - except ScriptTimeoutException: - if 'timeout' in filename: - # expected exception - pass - else: - self.loglines = marionette.get_logs() - raise - self.marionette.test_name = original_test_name - - -class MarionetteTestCase(CommonTestCase): - - match_re = re.compile(r"test_(.*)\.py$") - - def __init__(self, marionette_weakref, fixtures, methodName='runTest', - filepath='', **kwargs): - self.filepath = filepath - self.testvars = kwargs.pop('testvars', None) - - super(MarionetteTestCase, self).__init__( - methodName, marionette_weakref=marionette_weakref, fixtures=fixtures, **kwargs) - - @classmethod - def add_tests_to_suite(cls, mod_name, filepath, suite, testloader, marionette, - fixtures, testvars, **kwargs): - # since we use imp.load_source to load test modules, if a module - # is loaded with the same name as another one the module would just be - # reloaded. - # - # We may end up by finding too many test in a module then since - # reload() only update the module dict (so old keys are still there!) - # see https://docs.python.org/2/library/functions.html#reload - # - # we get rid of that by removing the module from sys.modules, - # so we ensure that it will be fully loaded by the - # imp.load_source call. - if mod_name in sys.modules: - del sys.modules[mod_name] - - test_mod = imp.load_source(mod_name, filepath) - - for name in dir(test_mod): - obj = getattr(test_mod, name) - if (isinstance(obj, (type, types.ClassType)) and - issubclass(obj, unittest.TestCase)): - testnames = testloader.getTestCaseNames(obj) - for testname in testnames: - suite.addTest(obj(weakref.ref(marionette), - fixtures, - methodName=testname, - filepath=filepath, - testvars=testvars, - **kwargs)) - - def setUp(self): - super(MarionetteTestCase, self).setUp() - self.marionette.test_name = self.test_name - self.marionette.execute_script("log('TEST-START: {0}:{1}')" - .format(self.filepath.replace('\\', '\\\\'), - self.methodName), - sandbox="simpletest") - - def tearDown(self): - # In the case no session is active (eg. the application was quit), start - # a new session for clean-up steps. - if not self.marionette.session: - self.marionette.start_session() - - if not self.marionette.crashed: - try: - self.marionette.clear_imported_scripts() - self.marionette.execute_script("log('TEST-END: {0}:{1}')" - .format(self.filepath.replace('\\', '\\\\'), - self.methodName), - sandbox="simpletest") - self.marionette.test_name = None - except (MarionetteException, IOError): - # We have tried to log the test end when there is no listener - # object that we can access - pass - - super(MarionetteTestCase, self).tearDown() - - def wait_for_condition(self, method, timeout=30): - timeout = float(timeout) + time.time() - while time.time() < timeout: - value = method(self.marionette) - if value: - return value - time.sleep(0.5) - else: - raise TimeoutException("wait_for_condition timed out") diff --git a/testing/marionette/harness/marionette_harness/runner/__init__.py b/testing/marionette/harness/marionette_harness/runner/__init__.py deleted file mode 100644 index e8a0575bb..000000000 --- a/testing/marionette/harness/marionette_harness/runner/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -# 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 .base import ( - BaseMarionetteArguments, - BaseMarionetteTestRunner, - Marionette, - MarionetteTest, - MarionetteTestResult, - MarionetteTextTestRunner, - TestManifest, - TestResult, - TestResultCollection, -) - -from .mixins import ( - BrowserMobProxyTestCaseMixin, - BrowserMobProxyArguments, - BrowserMobTestCase, - WindowManagerMixin, -) diff --git a/testing/marionette/harness/marionette_harness/runner/base.py b/testing/marionette/harness/marionette_harness/runner/base.py deleted file mode 100644 index 72f23524b..000000000 --- a/testing/marionette/harness/marionette_harness/runner/base.py +++ /dev/null @@ -1,1076 +0,0 @@ -# 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/. - -import json -import os -import random -import re -import socket -import sys -import time -import traceback -import unittest - -from argparse import ArgumentParser -from copy import deepcopy - -import mozinfo -import moznetwork -import mozprofile -import mozversion -import serve - -from manifestparser import TestManifest -from manifestparser.filters import tags -from marionette_driver.marionette import Marionette -from moztest.adapters.unit import StructuredTestResult, StructuredTestRunner -from moztest.results import TestResult, TestResultCollection, relevant_line -from serve import iter_proc, iter_url - -here = os.path.abspath(os.path.dirname(__file__)) - - -def update_mozinfo(path=None): - """Walk up directories to find mozinfo.json and update the info.""" - path = path or here - dirs = set() - while path != os.path.expanduser('~'): - if path in dirs: - break - dirs.add(path) - path = os.path.split(path)[0] - - return mozinfo.find_and_update_from_json(*dirs) - - -class MarionetteTest(TestResult): - - @property - def test_name(self): - if self.test_class is not None: - return '{0}.py {1}.{2}'.format(self.test_class.split('.')[0], - self.test_class, - self.name) - else: - return self.name - - -class MarionetteTestResult(StructuredTestResult, TestResultCollection): - - resultClass = MarionetteTest - - def __init__(self, *args, **kwargs): - self.marionette = kwargs.pop('marionette') - TestResultCollection.__init__(self, 'MarionetteTest') - self.passed = 0 - self.testsRun = 0 - self.result_modifiers = [] # used by mixins to modify the result - StructuredTestResult.__init__(self, *args, **kwargs) - - @property - def skipped(self): - return [t for t in self if t.result == 'SKIPPED'] - - @skipped.setter - def skipped(self, value): - pass - - @property - def expectedFailures(self): - return [t for t in self if t.result == 'KNOWN-FAIL'] - - @expectedFailures.setter - def expectedFailures(self, value): - pass - - @property - def unexpectedSuccesses(self): - return [t for t in self if t.result == 'UNEXPECTED-PASS'] - - @unexpectedSuccesses.setter - def unexpectedSuccesses(self, value): - pass - - @property - def tests_passed(self): - return [t for t in self if t.result == 'PASS'] - - @property - def errors(self): - return [t for t in self if t.result == 'ERROR'] - - @errors.setter - def errors(self, value): - pass - - @property - def failures(self): - return [t for t in self if t.result == 'UNEXPECTED-FAIL'] - - @failures.setter - def failures(self, value): - pass - - @property - def duration(self): - if self.stop_time: - return self.stop_time - self.start_time - else: - return 0 - - def add_test_result(self, test, result_expected='PASS', - result_actual='PASS', output='', context=None, **kwargs): - def get_class(test): - return test.__class__.__module__ + '.' + test.__class__.__name__ - - name = str(test).split()[0] - test_class = get_class(test) - if hasattr(test, 'jsFile'): - name = os.path.basename(test.jsFile) - test_class = None - - t = self.resultClass(name=name, test_class=test_class, - time_start=test.start_time, result_expected=result_expected, - context=context, **kwargs) - # call any registered result modifiers - for modifier in self.result_modifiers: - result_expected, result_actual, output, context = modifier( - t, result_expected, result_actual, output, context) - t.finish(result_actual, - time_end=time.time() if test.start_time else 0, - reason=relevant_line(output), - output=output) - self.append(t) - - def addError(self, test, err): - self.add_test_result(test, output=self._exc_info_to_string(err, test), - result_actual='ERROR') - super(MarionetteTestResult, self).addError(test, err) - - def addFailure(self, test, err): - self.add_test_result(test, output=self._exc_info_to_string(err, test), - result_actual='UNEXPECTED-FAIL') - super(MarionetteTestResult, self).addFailure(test, err) - - def addSuccess(self, test): - self.passed += 1 - self.add_test_result(test, result_actual='PASS') - super(MarionetteTestResult, self).addSuccess(test) - - def addExpectedFailure(self, test, err): - """Called when an expected failure/error occured.""" - self.add_test_result(test, output=self._exc_info_to_string(err, test), - result_actual='KNOWN-FAIL') - super(MarionetteTestResult, self).addExpectedFailure(test, err) - - def addUnexpectedSuccess(self, test): - """Called when a test was expected to fail, but succeed.""" - self.add_test_result(test, result_actual='UNEXPECTED-PASS') - super(MarionetteTestResult, self).addUnexpectedSuccess(test) - - def addSkip(self, test, reason): - self.add_test_result(test, output=reason, result_actual='SKIPPED') - super(MarionetteTestResult, self).addSkip(test, reason) - - def getInfo(self, test): - return test.test_name - - def getDescription(self, test): - doc_first_line = test.shortDescription() - if self.descriptions and doc_first_line: - return '\n'.join((str(test), doc_first_line)) - else: - desc = str(test) - if hasattr(test, 'jsFile'): - desc = "{0}, {1}".format(test.jsFile, desc) - return desc - - def printLogs(self, test): - for testcase in test._tests: - if hasattr(testcase, 'loglines') and testcase.loglines: - # Don't dump loglines to the console if they only contain - # TEST-START and TEST-END. - skip_log = True - for line in testcase.loglines: - str_line = ' '.join(line) - if 'TEST-END' not in str_line and 'TEST-START' not in str_line: - skip_log = False - break - if skip_log: - return - self.logger.info('START LOG:') - for line in testcase.loglines: - self.logger.info(' '.join(line).encode('ascii', 'replace')) - self.logger.info('END LOG:') - - def stopTest(self, *args, **kwargs): - unittest._TextTestResult.stopTest(self, *args, **kwargs) - if self.marionette.check_for_crash(): - # this tells unittest.TestSuite not to continue running tests - self.shouldStop = True - test = next((a for a in args if isinstance(a, unittest.TestCase)), - None) - if test: - self.addError(test, sys.exc_info()) - - -class MarionetteTextTestRunner(StructuredTestRunner): - - resultclass = MarionetteTestResult - - def __init__(self, **kwargs): - self.marionette = kwargs.pop('marionette') - self.capabilities = kwargs.pop('capabilities') - - StructuredTestRunner.__init__(self, **kwargs) - - def _makeResult(self): - return self.resultclass(self.stream, - self.descriptions, - self.verbosity, - marionette=self.marionette, - logger=self.logger, - result_callbacks=self.result_callbacks) - - def run(self, test): - result = super(MarionetteTextTestRunner, self).run(test) - result.printLogs(test) - return result - - -class BaseMarionetteArguments(ArgumentParser): - # Bug 1336953 - Until we can remove the socket timeout parameter it has to be - # set a default value which is larger than the longest timeout as defined by the - # WebDriver spec. In that case its 300s for page load. Also add another minute - # so that slow builds have enough time to send the timeout error to the client. - socket_timeout_default = 360.0 - - def __init__(self, **kwargs): - ArgumentParser.__init__(self, **kwargs) - - def dir_path(path): - path = os.path.abspath(os.path.expanduser(path)) - if not os.access(path, os.F_OK): - os.makedirs(path) - return path - - self.argument_containers = [] - self.add_argument('tests', - nargs='*', - default=[], - help='Tests to run. ' - 'One or more paths to test files (Python or JS), ' - 'manifest files (.ini) or directories. ' - 'When a directory is specified, ' - 'all test files in the directory will be run.') - self.add_argument('--binary', - help='path to gecko executable to launch before running the test') - self.add_argument('--address', - help='host:port of running Gecko instance to connect to') - self.add_argument('--emulator', - action='store_true', - help='If no --address is given, then the harness will launch an ' - 'emulator. (See Remote options group.) ' - 'If --address is given, then the harness assumes you are ' - 'running an emulator already, and will launch gecko app ' - 'on that emulator.') - self.add_argument('--app', - help='application to use. see marionette_driver.geckoinstance') - self.add_argument('--app-arg', - dest='app_args', - action='append', - default=[], - help='specify a command line argument to be passed onto the application') - self.add_argument('--profile', - help='profile to use when launching the gecko process. If not passed, ' - 'then a profile will be constructed and used', - type=dir_path) - self.add_argument('--pref', - action='append', - dest='prefs_args', - help="A preference to set. Must be a key-value pair separated by a ':'.") - self.add_argument('--preferences', - action='append', - dest='prefs_files', - help="read preferences from a JSON or INI file. For INI, use " - "'file.ini:section' to specify a particular section.") - self.add_argument('--addon', - action='append', - dest='addons', - help="addon to install; repeat for multiple addons.") - self.add_argument('--repeat', - type=int, - default=0, - help='number of times to repeat the test(s)') - self.add_argument('--testvars', - action='append', - help='path to a json file with any test data required') - self.add_argument('--symbols-path', - help='absolute path to directory containing breakpad symbols, or the ' - 'url of a zip file containing symbols') - self.add_argument('--startup-timeout', - type=int, - default=60, - help='the max number of seconds to wait for a Marionette connection ' - 'after launching a binary') - self.add_argument('--shuffle', - action='store_true', - default=False, - help='run tests in a random order') - self.add_argument('--shuffle-seed', - type=int, - default=random.randint(0, sys.maxint), - help='Use given seed to shuffle tests') - self.add_argument('--total-chunks', - type=int, - help='how many chunks to split the tests up into') - self.add_argument('--this-chunk', - type=int, - help='which chunk to run') - self.add_argument('--server-root', - help='url to a webserver or path to a document root from which content ' - 'resources are served (default: {}).'.format(os.path.join( - os.path.dirname(here), 'www'))) - self.add_argument('--gecko-log', - help="Define the path to store log file. If the path is" - " a directory, the real log file will be created" - " given the format gecko-(timestamp).log. If it is" - " a file, if will be used directly. '-' may be passed" - " to write to stdout. Default: './gecko.log'") - self.add_argument('--logger-name', - default='Marionette-based Tests', - help='Define the name to associate with the logger used') - self.add_argument('--jsdebugger', - action='store_true', - default=False, - help='Enable the jsdebugger for marionette javascript.') - self.add_argument('--pydebugger', - help='Enable python post-mortem debugger when a test fails.' - ' Pass in the debugger you want to use, eg pdb or ipdb.') - self.add_argument('--socket-timeout', - type=float, - default=self.socket_timeout_default, - help='Set the global timeout for marionette socket operations.' - ' Default: %(default)ss.') - self.add_argument('--disable-e10s', - action='store_false', - dest='e10s', - default=True, - help='Disable e10s when running marionette tests.') - self.add_argument('--tag', - action='append', dest='test_tags', - default=None, - help="Filter out tests that don't have the given tag. Can be " - "used multiple times in which case the test must contain " - "at least one of the given tags.") - self.add_argument('--workspace', - action='store', - default=None, - help="Path to directory for Marionette output. " - "(Default: .) (Default profile dest: TMP)", - type=dir_path) - self.add_argument('-v', '--verbose', - action='count', - help='Increase verbosity to include debug messages with -v, ' - 'and trace messages with -vv.') - self.register_argument_container(RemoteMarionetteArguments()) - - def register_argument_container(self, container): - group = self.add_argument_group(container.name) - - for cli, kwargs in container.args: - group.add_argument(*cli, **kwargs) - - self.argument_containers.append(container) - - def parse_known_args(self, args=None, namespace=None): - args, remainder = ArgumentParser.parse_known_args(self, args, namespace) - for container in self.argument_containers: - if hasattr(container, 'parse_args_handler'): - container.parse_args_handler(args) - return (args, remainder) - - def _get_preferences(self, prefs_files, prefs_args): - """Return user defined profile preferences as a dict.""" - # object that will hold the preferences - prefs = mozprofile.prefs.Preferences() - - # add preferences files - if prefs_files: - for prefs_file in prefs_files: - prefs.add_file(prefs_file) - - separator = ':' - cli_prefs = [] - if prefs_args: - misformatted = [] - for pref in prefs_args: - if separator not in pref: - misformatted.append(pref) - else: - cli_prefs.append(pref.split(separator, 1)) - if misformatted: - self._print_message("Warning: Ignoring preferences not in key{}value format: {}\n" - .format(separator, ", ".join(misformatted))) - # string preferences - prefs.add(cli_prefs, cast=True) - - return dict(prefs()) - - def verify_usage(self, args): - if not args.tests: - self.error('You must specify one or more test files, manifests, or directories.') - - missing_tests = [path for path in args.tests if not os.path.exists(path)] - if missing_tests: - self.error("Test file(s) not found: " + " ".join([path for path in missing_tests])) - - if not args.address and not args.binary and not args.emulator: - self.error('You must specify --binary, or --address, or --emulator') - - if args.total_chunks is not None and args.this_chunk is None: - self.error('You must specify which chunk to run.') - - if args.this_chunk is not None and args.total_chunks is None: - self.error('You must specify how many chunks to split the tests into.') - - if args.total_chunks is not None: - if not 1 < args.total_chunks: - self.error('Total chunks must be greater than 1.') - if not 1 <= args.this_chunk <= args.total_chunks: - self.error('Chunk to run must be between 1 and {}.'.format(args.total_chunks)) - - if args.jsdebugger: - args.app_args.append('-jsdebugger') - args.socket_timeout = None - - args.prefs = self._get_preferences(args.prefs_files, args.prefs_args) - - for container in self.argument_containers: - if hasattr(container, 'verify_usage_handler'): - container.verify_usage_handler(args) - - return args - - -class RemoteMarionetteArguments(object): - name = 'Remote (Emulator/Device)' - args = [ - [['--emulator-binary'], - {'help': 'Path to emulator binary. By default mozrunner uses `which emulator`', - 'dest': 'emulator_bin', - }], - [['--adb'], - {'help': 'Path to the adb. By default mozrunner uses `which adb`', - 'dest': 'adb_path' - }], - [['--avd'], - {'help': ('Name of an AVD available in your environment.' - 'See mozrunner.FennecEmulatorRunner'), - }], - [['--avd-home'], - {'help': 'Path to avd parent directory', - }], - [['--device'], - {'help': ('Serial ID to connect to as seen in `adb devices`,' - 'e.g emulator-5444'), - 'dest': 'device_serial', - }], - [['--package'], - {'help': 'Name of Android package, e.g. org.mozilla.fennec', - 'dest': 'package_name', - }], - - ] - - -class Fixtures(object): - def where_is(self, uri, on="http"): - return serve.where_is(uri, on) - - -class BaseMarionetteTestRunner(object): - - textrunnerclass = MarionetteTextTestRunner - driverclass = Marionette - - def __init__(self, address=None, - app=None, app_args=None, binary=None, profile=None, - logger=None, logdir=None, - repeat=0, testvars=None, - symbols_path=None, - shuffle=False, shuffle_seed=random.randint(0, sys.maxint), this_chunk=1, - total_chunks=1, - server_root=None, gecko_log=None, result_callbacks=None, - prefs=None, test_tags=None, - socket_timeout=BaseMarionetteArguments.socket_timeout_default, - startup_timeout=None, addons=None, workspace=None, - verbose=0, e10s=True, emulator=False, **kwargs): - self._appinfo = None - self._appName = None - self._capabilities = None - self._filename_pattern = None - self._version_info = {} - - self.fixture_servers = {} - self.fixtures = Fixtures() - self.extra_kwargs = kwargs - self.test_kwargs = deepcopy(kwargs) - self.address = address - self.app = app - self.app_args = app_args or [] - self.bin = binary - self.emulator = emulator - self.profile = profile - self.addons = addons - self.logger = logger - self.marionette = None - self.logdir = logdir - self.repeat = repeat - self.symbols_path = symbols_path - self.socket_timeout = socket_timeout - self.shuffle = shuffle - self.shuffle_seed = shuffle_seed - self.server_root = server_root - self.this_chunk = this_chunk - self.total_chunks = total_chunks - self.mixin_run_tests = [] - self.manifest_skipped_tests = [] - self.tests = [] - self.result_callbacks = result_callbacks or [] - self.prefs = prefs or {} - self.test_tags = test_tags - self.startup_timeout = startup_timeout - self.workspace = workspace - # If no workspace is set, default location for gecko.log is . - # and default location for profile is TMP - self.workspace_path = workspace or os.getcwd() - self.verbose = verbose - self.e10s = e10s - if self.e10s: - self.prefs.update({ - 'browser.tabs.remote.autostart': True, - 'browser.tabs.remote.force-enable': True, - 'extensions.e10sBlocksEnabling': False - }) - - def gather_debug(test, status): - # No screenshots and page source for skipped tests - if status == "SKIP": - return - - rv = {} - marionette = test._marionette_weakref() - - # In the event we're gathering debug without starting a session, - # skip marionette commands - if marionette.session is not None: - try: - with marionette.using_context(marionette.CONTEXT_CHROME): - rv['screenshot'] = marionette.screenshot() - with marionette.using_context(marionette.CONTEXT_CONTENT): - rv['source'] = marionette.page_source - except Exception as exc: - self.logger.warning('Failed to gather test failure debug: {}'.format(exc)) - return rv - - self.result_callbacks.append(gather_debug) - - # testvars are set up in self.testvars property - self._testvars = None - self.testvars_paths = testvars - - self.test_handlers = [] - - self.reset_test_stats() - - self.logger.info('Using workspace for temporary data: ' - '"{}"'.format(self.workspace_path)) - - if not gecko_log: - self.gecko_log = os.path.join(self.workspace_path or '', 'gecko.log') - else: - self.gecko_log = gecko_log - - self.results = [] - - @property - def filename_pattern(self): - if self._filename_pattern is None: - self._filename_pattern = re.compile( - "^test(((_.+?)+?\.((py)|(js)))|(([A-Z].*?)+?\.js))$") - - return self._filename_pattern - - @property - def testvars(self): - if self._testvars is not None: - return self._testvars - - self._testvars = {} - - def update(d, u): - """Update a dictionary that may contain nested dictionaries.""" - for k, v in u.iteritems(): - o = d.get(k, {}) - if isinstance(v, dict) and isinstance(o, dict): - d[k] = update(d.get(k, {}), v) - else: - d[k] = u[k] - return d - - json_testvars = self._load_testvars() - for j in json_testvars: - self._testvars = update(self._testvars, j) - return self._testvars - - def _load_testvars(self): - data = [] - if self.testvars_paths is not None: - for path in list(self.testvars_paths): - path = os.path.abspath(os.path.expanduser(path)) - if not os.path.exists(path): - raise IOError('--testvars file {} does not exist'.format(path)) - try: - with open(path) as f: - data.append(json.loads(f.read())) - except ValueError as e: - exc, val, tb = sys.exc_info() - msg = "JSON file ({0}) is not properly formatted: {1}" - raise exc, msg.format(os.path.abspath(path), e.message), tb - return data - - @property - def capabilities(self): - if self._capabilities: - return self._capabilities - - self.marionette.start_session() - self._capabilities = self.marionette.session_capabilities - self.marionette.delete_session() - return self._capabilities - - @property - def appinfo(self): - if self._appinfo: - return self._appinfo - - self.marionette.start_session() - with self.marionette.using_context('chrome'): - self._appinfo = self.marionette.execute_script(""" - try { - return Services.appinfo; - } catch (e) { - return null; - }""") - self.marionette.delete_session() - self._appinfo = self._appinfo or {} - return self._appinfo - - @property - def appName(self): - if self._appName: - return self._appName - - self._appName = self.capabilities.get('browserName') - return self._appName - - @property - def bin(self): - return self._bin - - @bin.setter - def bin(self, path): - """Set binary and reset parts of runner accordingly. - Intended use: to change binary between calls to run_tests - """ - self._bin = path - self.tests = [] - self.cleanup() - - @property - def version_info(self): - if not self._version_info: - try: - # TODO: Get version_info in Fennec case - self._version_info = mozversion.get_version(binary=self.bin) - except Exception: - self.logger.warning("Failed to retrieve version information for {}".format( - self.bin)) - return self._version_info - - def reset_test_stats(self): - self.passed = 0 - self.failed = 0 - self.crashed = 0 - self.unexpected_successes = 0 - self.todo = 0 - self.skipped = 0 - self.failures = [] - - def _build_kwargs(self): - if self.logdir and not os.access(self.logdir, os.F_OK): - os.mkdir(self.logdir) - - kwargs = { - 'socket_timeout': self.socket_timeout, - 'prefs': self.prefs, - 'startup_timeout': self.startup_timeout, - 'verbose': self.verbose, - 'symbols_path': self.symbols_path, - } - if self.bin or self.emulator: - kwargs.update({ - 'host': 'localhost', - 'port': 2828, - 'app': self.app, - 'app_args': self.app_args, - 'profile': self.profile, - 'addons': self.addons, - 'gecko_log': self.gecko_log, - # ensure Marionette class takes care of starting gecko instance - 'bin': True, - }) - - if self.bin: - kwargs.update({ - 'bin': self.bin, - }) - - if self.emulator: - kwargs.update({ - 'avd_home': self.extra_kwargs.get('avd_home'), - 'adb_path': self.extra_kwargs.get('adb_path'), - 'emulator_binary': self.extra_kwargs.get('emulator_bin'), - 'avd': self.extra_kwargs.get('avd'), - 'package_name': self.extra_kwargs.get('package_name'), - }) - - if self.address: - host, port = self.address.split(':') - kwargs.update({ - 'host': host, - 'port': int(port), - }) - if self.emulator: - kwargs.update({ - 'connect_to_running_emulator': True, - }) - if not self.bin and not self.emulator: - try: - # Establish a socket connection so we can vertify the data come back - connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - connection.connect((host, int(port))) - connection.close() - except Exception as e: - exc, val, tb = sys.exc_info() - msg = "Connection attempt to {0}:{1} failed with error: {2}" - raise exc, msg.format(host, port, e), tb - if self.workspace: - kwargs['workspace'] = self.workspace_path - return kwargs - - def record_crash(self): - crash = True - try: - crash = self.marionette.check_for_crash() - self.crashed += int(crash) - except Exception: - traceback.print_exc() - return crash - - def _initialize_test_run(self, tests): - assert len(tests) > 0 - assert len(self.test_handlers) > 0 - self.reset_test_stats() - - def _add_tests(self, tests): - for test in tests: - self.add_test(test) - - invalid_tests = [t['filepath'] for t in self.tests - if not self._is_filename_valid(t['filepath'])] - if invalid_tests: - raise Exception("Test file names must be of the form " - "'test_something.py', 'test_something.js', or 'testSomething.js'." - " Invalid test names:\n {}".format('\n '.join(invalid_tests))) - - def _is_filename_valid(self, filename): - filename = os.path.basename(filename) - return self.filename_pattern.match(filename) - - def _log_skipped_tests(self): - for test in self.manifest_skipped_tests: - name = os.path.basename(test['path']) - self.logger.test_start(name) - self.logger.test_end(name, - 'SKIP', - message=test['disabled']) - self.todo += 1 - - def run_tests(self, tests): - start_time = time.time() - self._initialize_test_run(tests) - - if self.marionette is None: - self.marionette = self.driverclass(**self._build_kwargs()) - self.logger.info("Profile path is %s" % self.marionette.profile_path) - - if len(self.fixture_servers) == 0 or \ - any(not server.is_alive for _, server in self.fixture_servers): - self.logger.info("Starting fixture servers") - self.fixture_servers = self.start_fixture_servers() - for url in iter_url(self.fixture_servers): - self.logger.info("Fixture server listening on %s" % url) - - # backwards compatibility - self.marionette.baseurl = serve.where_is("/") - - self._add_tests(tests) - - device_info = None - if self.marionette.instance and self.emulator: - try: - device_info = self.marionette.instance.runner.device.dm.getInfo() - except Exception: - self.logger.warning('Could not get device info', exc_info=True) - - appinfo_e10s = self.appinfo.get('browserTabsRemoteAutostart', False) - self.logger.info("e10s is {}".format("enabled" if appinfo_e10s else "disabled")) - if self.e10s != appinfo_e10s: - message_e10s = ("BaseMarionetteTestRunner configuration (self.e10s) does " - "not match browser appinfo") - self.cleanup() - raise AssertionError(message_e10s) - - self.logger.suite_start(self.tests, - version_info=self.version_info, - device_info=device_info) - - self._log_skipped_tests() - - interrupted = None - try: - counter = self.repeat - while counter >= 0: - round_num = self.repeat - counter - if round_num > 0: - self.logger.info('\nREPEAT {}\n-------'.format(round_num)) - self.run_test_sets() - counter -= 1 - except KeyboardInterrupt: - # in case of KeyboardInterrupt during the test execution - # we want to display current test results. - # so we keep the exception to raise it later. - interrupted = sys.exc_info() - except: - # For any other exception we return immediately and have to - # cleanup running processes - self.cleanup() - raise - - try: - self._print_summary(tests) - self.record_crash() - self.elapsedtime = time.time() - start_time - - for run_tests in self.mixin_run_tests: - run_tests(tests) - if self.shuffle: - self.logger.info("Using shuffle seed: %d" % self.shuffle_seed) - - self.logger.suite_end() - except: - # raise only the exception if we were not interrupted - if not interrupted: - raise - finally: - self.cleanup() - - # reraise previous interruption now - if interrupted: - raise interrupted[0], interrupted[1], interrupted[2] - - def _print_summary(self, tests): - self.logger.info('\nSUMMARY\n-------') - self.logger.info('passed: {}'.format(self.passed)) - if self.unexpected_successes == 0: - self.logger.info('failed: {}'.format(self.failed)) - else: - self.logger.info( - 'failed: {0} (unexpected sucesses: {1})'.format(self.failed, - self.unexpected_successes)) - if self.skipped == 0: - self.logger.info('todo: {}'.format(self.todo)) - else: - self.logger.info('todo: {0} (skipped: {1})'.format(self.todo, self.skipped)) - - if self.failed > 0: - self.logger.info('\nFAILED TESTS\n-------') - for failed_test in self.failures: - self.logger.info('{}'.format(failed_test[0])) - - def start_fixture_servers(self): - root = self.server_root or os.path.join(os.path.dirname(here), "www") - if self.appName == "fennec": - return serve.start(root, host=moznetwork.get_ip()) - else: - return serve.start(root) - - def add_test(self, test, expected='pass'): - filepath = os.path.abspath(test) - - if os.path.isdir(filepath): - for root, dirs, files in os.walk(filepath): - for filename in files: - if filename.endswith('.ini'): - msg_tmpl = ("Ignoring manifest '{0}'; running all tests in '{1}'." - " See --help for details.") - relpath = os.path.relpath(os.path.join(root, filename), filepath) - self.logger.warning(msg_tmpl.format(relpath, filepath)) - elif self._is_filename_valid(filename): - test_file = os.path.join(root, filename) - self.add_test(test_file) - return - - file_ext = os.path.splitext(os.path.split(filepath)[-1])[1] - - if file_ext == '.ini': - manifest = TestManifest() - manifest.read(filepath) - - json_path = update_mozinfo(filepath) - self.logger.info("mozinfo updated from: {}".format(json_path)) - self.logger.info("mozinfo is: {}".format(mozinfo.info)) - - filters = [] - if self.test_tags: - filters.append(tags(self.test_tags)) - - values = { - "appname": self.appName, - "e10s": self.e10s, - "manage_instance": self.marionette.instance is not None, - } - values.update(mozinfo.info) - - manifest_tests = manifest.active_tests(exists=False, - disabled=True, - filters=filters, - **values) - if len(manifest_tests) == 0: - self.logger.error("No tests to run using specified " - "combination of filters: {}".format( - manifest.fmt_filters())) - - target_tests = [] - for test in manifest_tests: - if test.get('disabled'): - self.manifest_skipped_tests.append(test) - else: - target_tests.append(test) - - for i in target_tests: - if not os.path.exists(i["path"]): - raise IOError("test file: {} does not exist".format(i["path"])) - - file_ext = os.path.splitext(os.path.split(i['path'])[-1])[-1] - - self.add_test(i["path"], i["expected"]) - return - - self.tests.append({'filepath': filepath, 'expected': expected}) - - def run_test(self, filepath, expected): - testloader = unittest.TestLoader() - suite = unittest.TestSuite() - self.test_kwargs['expected'] = expected - mod_name = os.path.splitext(os.path.split(filepath)[-1])[0] - for handler in self.test_handlers: - if handler.match(os.path.basename(filepath)): - handler.add_tests_to_suite(mod_name, - filepath, - suite, - testloader, - self.marionette, - self.fixtures, - self.testvars, - **self.test_kwargs) - break - - if suite.countTestCases(): - runner = self.textrunnerclass(logger=self.logger, - marionette=self.marionette, - capabilities=self.capabilities, - result_callbacks=self.result_callbacks) - - results = runner.run(suite) - self.results.append(results) - - self.failed += len(results.failures) + len(results.errors) - if hasattr(results, 'skipped'): - self.skipped += len(results.skipped) - self.todo += len(results.skipped) - self.passed += results.passed - for failure in results.failures + results.errors: - self.failures.append((results.getInfo(failure), failure.output, - 'TEST-UNEXPECTED-FAIL')) - if hasattr(results, 'unexpectedSuccesses'): - self.failed += len(results.unexpectedSuccesses) - self.unexpected_successes += len(results.unexpectedSuccesses) - for failure in results.unexpectedSuccesses: - self.failures.append((results.getInfo(failure), failure.output, - 'TEST-UNEXPECTED-PASS')) - if hasattr(results, 'expectedFailures'): - self.todo += len(results.expectedFailures) - - self.mixin_run_tests = [] - for result in self.results: - result.result_modifiers = [] - - def run_test_set(self, tests): - if self.shuffle: - random.seed(self.shuffle_seed) - random.shuffle(tests) - - for test in tests: - self.run_test(test['filepath'], test['expected']) - if self.record_crash(): - break - - def run_test_sets(self): - if len(self.tests) < 1: - raise Exception('There are no tests to run.') - elif self.total_chunks > len(self.tests): - raise ValueError('Total number of chunks must be between 1 and {}.' - .format(len(self.tests))) - if self.total_chunks > 1: - chunks = [[] for i in range(self.total_chunks)] - for i, test in enumerate(self.tests): - target_chunk = i % self.total_chunks - chunks[target_chunk].append(test) - - self.logger.info('Running chunk {0} of {1} ({2} tests selected from a ' - 'total of {3})'.format(self.this_chunk, self.total_chunks, - len(chunks[self.this_chunk - 1]), - len(self.tests))) - self.tests = chunks[self.this_chunk - 1] - - self.run_test_set(self.tests) - - def cleanup(self): - for proc in iter_proc(self.fixture_servers): - proc.stop() - proc.kill() - self.fixture_servers = {} - - if hasattr(self, 'marionette') and self.marionette: - if self.marionette.instance is not None: - self.marionette.instance.close() - self.marionette.instance = None - - self.marionette.cleanup() - self.marionette = None - - __del__ = cleanup diff --git a/testing/marionette/harness/marionette_harness/runner/httpd.py b/testing/marionette/harness/marionette_harness/runner/httpd.py deleted file mode 100644 index 2136e2bcc..000000000 --- a/testing/marionette/harness/marionette_harness/runner/httpd.py +++ /dev/null @@ -1,142 +0,0 @@ -#!/usr/bin/env python - -# 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/. - -"""Specialisation of wptserver.server.WebTestHttpd for testing -Marionette. - -""" - -import argparse -import os -import select -import sys -import time -import urlparse - -from wptserve import server, handlers, routes as default_routes - - -here = os.path.abspath(os.path.dirname(__file__)) -default_doc_root = os.path.join(os.path.dirname(here), "www") -default_ssl_cert = os.path.join(here, "test.cert") -default_ssl_key = os.path.join(here, "test.key") - - -@handlers.handler -def upload_handler(request, response): - return 200, [], [request.headers.get("Content-Type")] or [] - - -@handlers.handler -def slow_loading_document(request, response): - time.sleep(5) - return """<!doctype html> -<title>ok</title> -<p>ok""" - - -class NotAliveError(Exception): - """Occurs when attempting to run a function that requires the HTTPD - to have been started, and it has not. - - """ - pass - - -class FixtureServer(object): - - def __init__(self, doc_root, url="http://127.0.0.1:0", use_ssl=False, - ssl_cert=None, ssl_key=None): - if not os.path.isdir(doc_root): - raise ValueError("Server root is not a directory: %s" % doc_root) - - url = urlparse.urlparse(url) - if url.scheme is None: - raise ValueError("Server scheme not provided") - - scheme, host, port = url.scheme, url.hostname, url.port - if host is None: - host = "127.0.0.1" - if port is None: - port = 0 - - routes = [("POST", "/file_upload", upload_handler), - ("GET", "/slow", slow_loading_document)] - routes.extend(default_routes.routes) - - self._httpd = server.WebTestHttpd(host=host, - port=port, - bind_hostname=True, - doc_root=doc_root, - routes=routes, - use_ssl=True if scheme == "https" else False, - certificate=ssl_cert, - key_file=ssl_key) - - def start(self, block=False): - if self.is_alive: - return - self._httpd.start(block=block) - - def wait(self): - if not self.is_alive: - return - try: - select.select([], [], []) - except KeyboardInterrupt: - self.stop() - - def stop(self): - if not self.is_alive: - return - self._httpd.stop() - - def get_url(self, path): - if not self.is_alive: - raise NotAliveError() - return self._httpd.get_url(path) - - @property - def doc_root(self): - return self._httpd.router.doc_root - - @property - def router(self): - return self._httpd.router - - @property - def routes(self): - return self._httpd.router.routes - - @property - def is_alive(self): - return self._httpd.started - - -if __name__ == "__main__": - parser = argparse.ArgumentParser( - description="Specialised HTTP server for testing Marionette.") - parser.add_argument("url", help=""" -service address including scheme, hostname, port, and prefix for document root, -e.g. \"https://0.0.0.0:0/base/\"""") - parser.add_argument( - "-r", dest="doc_root", default=default_doc_root, - help="path to document root (default %(default)s)") - parser.add_argument( - "-c", dest="ssl_cert", default=default_ssl_cert, - help="path to SSL certificate (default %(default)s)") - parser.add_argument( - "-k", dest="ssl_key", default=default_ssl_key, - help="path to SSL certificate key (default %(default)s)") - args = parser.parse_args() - - httpd = FixtureServer(args.doc_root, args.url, - ssl_cert=args.ssl_cert, - ssl_key=args.ssl_key) - httpd.start() - print >>sys.stderr, "%s: started fixture server on %s" % \ - (sys.argv[0], httpd.get_url("/")) - httpd.wait() diff --git a/testing/marionette/harness/marionette_harness/runner/mixins/__init__.py b/testing/marionette/harness/marionette_harness/runner/mixins/__init__.py deleted file mode 100644 index d4abaacb4..000000000 --- a/testing/marionette/harness/marionette_harness/runner/mixins/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# 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 .browsermob import ( - BrowserMobProxyTestCaseMixin, - BrowserMobProxyArguments, - BrowserMobTestCase, -) - -from .window_manager import ( - WindowManagerMixin, -) diff --git a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/History.md b/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/History.md deleted file mode 100644 index 65c692bfc..000000000 --- a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/History.md +++ /dev/null @@ -1,57 +0,0 @@ - -0.6.0 / 2014-01-21 -================== - - * Added support for parameters in har creation - * Bug fixes for tests that are out of date - * Setup server constructor to look on path for location of browsermob-proxy executable. As well as looking for a file. Also added example code for using browsermob-proxy with chrome - * Fix project name - * adding docs - -0.5.0 / 2013-05-23 -================== -* Allow proxying of ssl requests with selenium. -* Updating case for proxy type - - -0.4.0 / 2013-03-06 -================== - - * Allow setting basic authentication - * Adding the ability to remap hosts which is available from BrowserMob Proxy Beta 7 - * Merge pull request #6 from lukeis/patch-2 - * Update readme.md - * initial commit of event listener to auto do record - * server.create_proxy is a function, should be called :) - * forgot to add the port - -0.2.0 / 2012-06-18 -================== - - * pep8 --ignore=E501 - * DELETE /proxy/:port/ - * /proxy/:port/limits - * /proxy/:port/blacklist - * /proxy/:port/whitelist - * fixing /proxy/:port/har/pageRef - * fixing /proxy/:port/har/pageRef - * fixing passing in a page ref as the name for the page in /proxy/:port/har - * tests around /proxy/:port/har and some cleanup of the implementation - * make /proxy/:port/headers work - * wrapping selenium_proxy with webdriver_proxy since the project is more than just webdriver - * extending the client to play nice with remote webdriver instances - * create_proxy sounds and feels like a method to me, let's make it so - * ensure the self.process exist, to reduce possibilities of AttributeError - * check the path before attempting to start the server - * wait longer than 3 seconds for the server to come up - -0.1.0 / 2012-03-22 -================== - -* Removed httplib2 in preference for requests -* Added support for setting headers - -0.0.1 / 2012-01-16 -================== - -* Initial version diff --git a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/browsermobproxy/__init__.py b/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/browsermobproxy/__init__.py deleted file mode 100644 index 5c6d63004..000000000 --- a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/browsermobproxy/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -__version__ = '0.5.0' - -from .server import Server -from .client import Client - -__all__ = ['Server', 'Client', 'browsermobproxy'] diff --git a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/browsermobproxy/client.py b/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/browsermobproxy/client.py deleted file mode 100644 index 00b98ca5e..000000000 --- a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/browsermobproxy/client.py +++ /dev/null @@ -1,326 +0,0 @@ -import requests - -try: - from urllib.parse import urlencode, unquote -except ImportError: - from urllib import urlencode, unquote -import json - - -class Client(object): - def __init__(self, url, params={}): - """ - Initialises a new Client object - - - :param url: This is where the BrowserMob Proxy lives - :param params: URL query (for example httpProxy and httpsProxy vars) - """ - self.host = "http://" + url - if params: - urlparams = "?" + unquote(urlencode(params)) - else: - urlparams = "" - resp = requests.post('{}/proxy'.format(self.host + urlparams)) - jcontent = json.loads(resp.content.decode('utf-8')) - self.port = jcontent['port'] - url_parts = self.host.split(":") - self.proxy = url_parts[1][2:] + ":" + str(self.port) - - def close(self): - """ - shuts down the proxy and closes the port - """ - r = requests.delete('{0}/proxy/{1}'.format(self.host, self.port)) - return r.status_code - - # webdriver integration - # ...as a proxy object - def selenium_proxy(self): - """ - Returns a Selenium WebDriver Proxy class with details of the HTTP Proxy - """ - from selenium import webdriver - return webdriver.Proxy({ - "httpProxy": self.proxy, - "sslProxy": self.proxy, - }) - - def webdriver_proxy(self): - """ - Returns a Selenium WebDriver Proxy class with details of the HTTP Proxy - """ - return self.selenium_proxy() - - # ...as a capability - def add_to_capabilities(self, capabilities): - """ - Adds an 'proxy' entry to a desired capabilities dictionary with the - BrowserMob proxy information - - - :param capabilities: The Desired capabilities object from Selenium WebDriver - """ - capabilities['proxy'] = { - 'proxyType': "MANUAL", - 'httpProxy': self.proxy, - 'sslProxy': self.proxy - } - - def add_to_webdriver_capabilities(self, capabilities): - self.add_to_capabilities(capabilities) - - # browsermob proxy api - @property - def har(self): - """ - Gets the HAR that has been recorded - """ - r = requests.get('{0}/proxy/{1}/har'.format(self.host, self.port)) - - return r.json() - - def new_har(self, ref=None, options={}): - """ - This sets a new HAR to be recorded - - - :param ref: A reference for the HAR. Defaults to None - :param options: A dictionary that will be passed to BrowserMob Proxy \ - with specific keywords. Keywords are: \ - captureHeaders - Boolean, capture headers \ - captureContent - Boolean, capture content bodies \ - captureBinaryContent - Boolean, capture binary content - """ - if ref: - payload = {"initialPageRef": ref} - else: - payload = {} - if options: - payload.update(options) - - r = requests.put('{0}/proxy/{1}/har'.format(self.host, self.port), payload) - if r.status_code == 200: - return (r.status_code, r.json()) - else: - return (r.status_code, None) - - def new_page(self, ref=None): - """ - This sets a new page to be recorded - - - :param ref: A reference for the new page. Defaults to None - """ - if ref: - payload = {"pageRef": ref} - else: - payload = {} - r = requests.put('{0}/proxy/{1}/har/pageRef'.format(self.host, self.port), - payload) - return r.status_code - - def blacklist(self, regexp, status_code): - """ - Sets a list of URL patterns to blacklist - - - :param regex: a comma separated list of regular expressions - :param status_code: the HTTP status code to return for URLs that do not \ - match the blacklist - - """ - r = requests.put('{0}/proxy/{1}/blacklist'.format(self.host, self.port), - {'regex': regexp, 'status': status_code}) - return r.status_code - - def whitelist(self, regexp, status_code): - """ - Sets a list of URL patterns to whitelist - - - :param regex: a comma separated list of regular expressions - :param status_code: the HTTP status code to return for URLs that do not \ - match the whitelist - """ - r = requests.put('{0}/proxy/{1}/whitelist'.format(self.host, self.port), - {'regex': regexp, 'status': status_code}) - return r.status_code - - def basic_authentication(self, domain, username, password): - """ - This add automatic basic authentication - - - :param domain: domain to set authentication credentials for - :param username: valid username to use when authenticating - :param password: valid password to use when authenticating - """ - r = requests.post(url='{0}/proxy/{1}/auth/basic/{2}'.format(self.host, self.port, domain), - data=json.dumps({'username': username, 'password': password}), - headers={'content-type': 'application/json'}) - return r.status_code - - def headers(self, headers): - """ - This sets the headers that will set by the proxy on all requests - - - :param headers: this is a dictionary of the headers to be set - """ - if not isinstance(headers, dict): - raise TypeError("headers needs to be dictionary") - - r = requests.post(url='{0}/proxy/{1}/headers'.format(self.host, self.port), - data=json.dumps(headers), - headers={'content-type': 'application/json'}) - return r.status_code - - def response_interceptor(self, js): - """ - Executes the javascript against each response - - - :param js: the javascript to execute - """ - r = requests.post(url='{0}/proxy/{1}/interceptor/response'.format(self.host, self.port), - data=js, - headers={'content-type': 'x-www-form-urlencoded'}) - return r.status_code - - def request_interceptor(self, js): - """ - Executes the javascript against each request - - - :param js: the javascript to execute - """ - r = requests.post(url='{0}/proxy/{1}/interceptor/request'.format(self.host, self.port), - data=js, - headers={'content-type': 'x-www-form-urlencoded'}) - return r.status_code - - LIMITS = { - 'upstream_kbps': 'upstreamKbps', - 'downstream_kbps': 'downstreamKbps', - 'latency': 'latency' - } - - def limits(self, options): - """ - Limit the bandwidth through the proxy. - - - :param options: A dictionary with all the details you want to set. \ - downstreamKbps - Sets the downstream kbps \ - upstreamKbps - Sets the upstream kbps \ - latency - Add the given latency to each HTTP request - """ - params = {} - - for (k, v) in list(options.items()): - if k not in self.LIMITS: - raise KeyError('invalid key: {}'.format(k)) - - params[self.LIMITS[k]] = int(v) - - if len(list(params.items())) == 0: - raise KeyError("You need to specify one of the valid Keys") - - r = requests.put('{0}/proxy/{1}/limit'.format(self.host, self.port), - params) - return r.status_code - - TIMEOUTS = { - 'request': 'requestTimeout', - 'read': 'readTimeout', - 'connection': 'connectionTimeout', - 'dns': 'dnsCacheTimeout' - } - - def timeouts(self, options): - """ - Configure various timeouts in the proxy - - - :param options: A dictionary with all the details you want to set. \ - request - request timeout (in seconds) \ - read - read timeout (in seconds) \ - connection - connection timeout (in seconds) \ - dns - dns lookup timeout (in seconds) - """ - params = {} - - for (k, v) in list(options.items()): - if k not in self.TIMEOUTS: - raise KeyError('invalid key: {}'.format(k)) - - params[self.TIMEOUTS[k]] = int(v) - - if len(list(params.items())) == 0: - raise KeyError("You need to specify one of the valid Keys") - - r = requests.put('{0}/proxy/{1}/timeout'.format(self.host, self.port), - params) - return r.status_code - - def remap_hosts(self, address, ip_address): - """ - Remap the hosts for a specific URL - - - :param address: url that you wish to remap - :param ip_address: IP Address that will handle all traffic for the address passed in - """ - assert address is not None and ip_address is not None - r = requests.post('{0}/proxy/{1}/hosts'.format(self.host, self.port), - json.dumps({address: ip_address}), - headers={'content-type': 'application/json'}) - return r.status_code - - def wait_for_traffic_to_stop(self, quiet_period, timeout): - """ - Waits for the network to be quiet - - - :param quiet_period: number of miliseconds the network needs to be quiet for - :param timeout: max number of miliseconds to wait - """ - r = requests.put('{0}/proxy/{1}/wait'.format(self.host, self.port), - {'quietPeriodInMs': quiet_period, 'timeoutInMs': timeout}) - return r.status_code - - def clear_dns_cache(self): - """ - Clears the DNS cache associated with the proxy instance - """ - r = requests.delete('{0}/proxy/{1}/dns/cache'.format(self.host, self.port)) - return r.status_code - - def rewrite_url(self, match, replace): - """ - Rewrites the requested url. - - - :param match: a regex to match requests with - :param replace: unicode \ - a string to replace the matches with - """ - params = { - "matchRegex": match, - "replace": replace - } - r = requests.put('{0}/proxy/{1}/rewrite'.format(self.host, self.port), - params) - return r.status_code - - def retry(self, retry_count): - """ - Retries. No idea what its used for, but its in the API... - - - :param retry_count: the number of retries - """ - r = requests.put('{0}/proxy/{1}/retry'.format(self.host, self.port), - {'retrycount': retry_count}) - return r.status_code diff --git a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/browsermobproxy/server.py b/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/browsermobproxy/server.py deleted file mode 100644 index ff6db4efe..000000000 --- a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/browsermobproxy/server.py +++ /dev/null @@ -1,106 +0,0 @@ -import os -import platform -import socket -import subprocess -import time - -from .client import Client - - -class Server(object): - - def __init__(self, path='browsermob-proxy', options={}): - """ - Initialises a Server object - - :param path: Path to the browsermob proxy batch file - :param options: Dictionary that can hold the port. \ - More items will be added in the future. \ - This defaults to an empty dictionary - """ - path_var_sep = ':' - if platform.system() == 'Windows': - path_var_sep = ';' - if not path.endswith('.bat'): - path += '.bat' - - exec_not_on_path = True - for directory in os.environ['PATH'].split(path_var_sep): - if(os.path.isfile(os.path.join(directory, path))): - exec_not_on_path = False - break - - if not os.path.isfile(path) and exec_not_on_path: - raise IOError("Browsermob-Proxy binary couldn't be found in path" - " provided: {}".format(path)) - - self.path = path - self.port = options.get('port', 8080) - self.process = None - - if platform.system() == 'Darwin': - self.command = ['sh'] - else: - self.command = [] - self.command += [path, '--port={}'.format(self.port)] - - def start(self): - """ - This will start the browsermob proxy and then wait until it can - interact with it - """ - self.log_file = open(os.path.abspath('server.log'), 'w') - self.process = subprocess.Popen(self.command, - stdout=self.log_file, - stderr=subprocess.STDOUT) - count = 0 - while not self._is_listening(): - time.sleep(0.5) - count += 1 - if count == 60: - self.stop() - raise Exception("Can't connect to Browsermob-Proxy") - - def stop(self): - """ - This will stop the process running the proxy - """ - if self.process.poll() is not None: - return - - try: - self.process.kill() - self.process.wait() - except AttributeError: - # kill may not be available under windows environment - pass - - self.log_file.close() - - @property - def url(self): - """ - Gets the url that the proxy is running on. This is not the URL clients - should connect to. - """ - return "http://localhost:{}".format(self.port) - - def create_proxy(self, params={}): - """ - Gets a client class that allow to set all the proxy details that you - may need to. - :param params: Dictionary where you can specify params \ - like httpProxy and httpsProxy - """ - client = Client(self.url[7:], params) - return client - - def _is_listening(self): - try: - socket_ = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - socket_.settimeout(1) - socket_.connect(("localhost", self.port)) - socket_.close() - return True - except socket.error: - return False diff --git a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/browsermobproxy/webdriver_event_listener.py b/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/browsermobproxy/webdriver_event_listener.py deleted file mode 100644 index 533542b09..000000000 --- a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/browsermobproxy/webdriver_event_listener.py +++ /dev/null @@ -1,34 +0,0 @@ -from selenium.webdriver.support.abstract_event_listener import AbstractEventListener - -class WebDriverEventListener(AbstractEventListener): - - def __init__(self, client, refs={}): - self.client = client - self.hars = [] - self.refs = refs - - def before_navigate_to(self, url, driver): - if len(self.hars) != 0: - self.hars.append(self.client.har) - self.client.new_har("navigate-to-{}".format(url), self.refs) - - def before_navigate_back(self, driver=None): - if driver: - name = "-from-{}".format(driver.current_url) - else: - name = "navigate-back" - self.client.new_page(name) - - def before_navigate_forward(self, driver=None): - if driver: - name = "-from-{}".format(driver.current_url) - else: - name = "navigate-forward" - self.client.new_page(name) - - def before_click(self, element, driver): - name = "click-element-{}".format(element.id) - self.client.new_page(name) - - def before_quit(self, driver): - self.hars.append(self.client.har) diff --git a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/Makefile b/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/Makefile deleted file mode 100644 index 554b2a7cf..000000000 --- a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/Makefile +++ /dev/null @@ -1,153 +0,0 @@ -# Makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -PAPER = -BUILDDIR = _build - -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . -# the i18n builder cannot share the environment and doctrees with the others -I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . - -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext - -help: - @echo "Please use \`make <target>' where <target> is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " singlehtml to make a single large HTML file" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " devhelp to make HTML files and a Devhelp project" - @echo " epub to make an epub" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run them through pdflatex" - @echo " text to make text files" - @echo " man to make manual pages" - @echo " texinfo to make Texinfo files" - @echo " info to make Texinfo files and run them through makeinfo" - @echo " gettext to make PO message catalogs" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - -clean: - -rm -rf $(BUILDDIR)/* - -html: - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -singlehtml: - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml - @echo - @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/BrowserMobProxy.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/BrowserMobProxy.qhc" - -devhelp: - $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp - @echo - @echo "Build finished." - @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/BrowserMobProxy" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/BrowserMobProxy" - @echo "# devhelp" - -epub: - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub - @echo - @echo "Build finished. The epub file is in $(BUILDDIR)/epub." - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make' in that directory to run these through (pdf)latex" \ - "(use \`make latexpdf' here to do that automatically)." - -latexpdf: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through pdflatex..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -text: - $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text - @echo - @echo "Build finished. The text files are in $(BUILDDIR)/text." - -man: - $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man - @echo - @echo "Build finished. The manual pages are in $(BUILDDIR)/man." - -texinfo: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo - @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." - @echo "Run \`make' in that directory to run these through makeinfo" \ - "(use \`make info' here to do that automatically)." - -info: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo "Running Texinfo files through makeinfo..." - make -C $(BUILDDIR)/texinfo info - @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." - -gettext: - $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale - @echo - @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." - -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." - -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." diff --git a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/.buildinfo b/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/.buildinfo deleted file mode 100644 index 9274f43c2..000000000 --- a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/.buildinfo +++ /dev/null @@ -1,4 +0,0 @@ -# Sphinx build info version 1 -# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. -config: 22346719ac3713bfe792fb5638c0a301 -tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/_modules/browsermobproxy.html b/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/_modules/browsermobproxy.html deleted file mode 100644 index d3b83c20f..000000000 --- a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/_modules/browsermobproxy.html +++ /dev/null @@ -1,98 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" - "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> - - -<html xmlns="http://www.w3.org/1999/xhtml"> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> - - <title>browsermobproxy — BrowserMob Proxy 0.6.0 documentation</title> - - <link rel="stylesheet" href="../_static/default.css" type="text/css" /> - <link rel="stylesheet" href="../_static/pygments.css" type="text/css" /> - - <script type="text/javascript"> - var DOCUMENTATION_OPTIONS = { - URL_ROOT: '../', - VERSION: '0.6.0', - COLLAPSE_INDEX: false, - FILE_SUFFIX: '.html', - HAS_SOURCE: true - }; - </script> - <script type="text/javascript" src="../_static/jquery.js"></script> - <script type="text/javascript" src="../_static/underscore.js"></script> - <script type="text/javascript" src="../_static/doctools.js"></script> - <link rel="top" title="BrowserMob Proxy 0.6.0 documentation" href="../index.html" /> - <link rel="up" title="Module code" href="index.html" /> - </head> - <body> - <div class="related"> - <h3>Navigation</h3> - <ul> - <li class="right" style="margin-right: 10px"> - <a href="../genindex.html" title="General Index" - accesskey="I">index</a></li> - <li class="right" > - <a href="../py-modindex.html" title="Python Module Index" - >modules</a> |</li> - <li><a href="../index.html">BrowserMob Proxy 0.6.0 documentation</a> »</li> - <li><a href="index.html" accesskey="U">Module code</a> »</li> - </ul> - </div> - - <div class="document"> - <div class="documentwrapper"> - <div class="bodywrapper"> - <div class="body"> - - <h1>Source code for browsermobproxy</h1><div class="highlight"><pre> -<span class="n">__version__</span> <span class="o">=</span> <span class="s">'0.5.0'</span> - -<span class="kn">from</span> <span class="nn">server</span> <span class="kn">import</span> <span class="n">Server</span> -<span class="kn">from</span> <span class="nn">client</span> <span class="kn">import</span> <span class="n">Client</span> - -<span class="n">__all__</span> <span class="o">=</span> <span class="p">[</span><span class="s">'Server'</span><span class="p">,</span> <span class="s">'Client'</span><span class="p">,</span> <span class="s">'browsermobproxy'</span><span class="p">]</span> -</pre></div> - - </div> - </div> - </div> - <div class="sphinxsidebar"> - <div class="sphinxsidebarwrapper"> -<div id="searchbox" style="display: none"> - <h3>Quick search</h3> - <form class="search" action="../search.html" method="get"> - <input type="text" name="q" /> - <input type="submit" value="Go" /> - <input type="hidden" name="check_keywords" value="yes" /> - <input type="hidden" name="area" value="default" /> - </form> - <p class="searchtip" style="font-size: 90%"> - Enter search terms or a module, class or function name. - </p> -</div> -<script type="text/javascript">$('#searchbox').show(0);</script> - </div> - </div> - <div class="clearer"></div> - </div> - <div class="related"> - <h3>Navigation</h3> - <ul> - <li class="right" style="margin-right: 10px"> - <a href="../genindex.html" title="General Index" - >index</a></li> - <li class="right" > - <a href="../py-modindex.html" title="Python Module Index" - >modules</a> |</li> - <li><a href="../index.html">BrowserMob Proxy 0.6.0 documentation</a> »</li> - <li><a href="index.html" >Module code</a> »</li> - </ul> - </div> - <div class="footer"> - © Copyright 2014, David Burns. - Created using <a href="http://sphinx-doc.org/">Sphinx</a> 1.2.1. - </div> - </body> -</html>
\ No newline at end of file diff --git a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/_modules/index.html b/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/_modules/index.html deleted file mode 100644 index 5650bd363..000000000 --- a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/_modules/index.html +++ /dev/null @@ -1,90 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" - "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> - - -<html xmlns="http://www.w3.org/1999/xhtml"> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> - - <title>Overview: module code — BrowserMob Proxy 0.6.0 documentation</title> - - <link rel="stylesheet" href="../_static/default.css" type="text/css" /> - <link rel="stylesheet" href="../_static/pygments.css" type="text/css" /> - - <script type="text/javascript"> - var DOCUMENTATION_OPTIONS = { - URL_ROOT: '../', - VERSION: '0.6.0', - COLLAPSE_INDEX: false, - FILE_SUFFIX: '.html', - HAS_SOURCE: true - }; - </script> - <script type="text/javascript" src="../_static/jquery.js"></script> - <script type="text/javascript" src="../_static/underscore.js"></script> - <script type="text/javascript" src="../_static/doctools.js"></script> - <link rel="top" title="BrowserMob Proxy 0.6.0 documentation" href="../index.html" /> - </head> - <body> - <div class="related"> - <h3>Navigation</h3> - <ul> - <li class="right" style="margin-right: 10px"> - <a href="../genindex.html" title="General Index" - accesskey="I">index</a></li> - <li class="right" > - <a href="../py-modindex.html" title="Python Module Index" - >modules</a> |</li> - <li><a href="../index.html">BrowserMob Proxy 0.6.0 documentation</a> »</li> - </ul> - </div> - - <div class="document"> - <div class="documentwrapper"> - <div class="bodywrapper"> - <div class="body"> - - <h1>All modules for which code is available</h1> -<ul><li><a href="browsermobproxy.html">browsermobproxy</a></li> -</ul> - - </div> - </div> - </div> - <div class="sphinxsidebar"> - <div class="sphinxsidebarwrapper"> -<div id="searchbox" style="display: none"> - <h3>Quick search</h3> - <form class="search" action="../search.html" method="get"> - <input type="text" name="q" /> - <input type="submit" value="Go" /> - <input type="hidden" name="check_keywords" value="yes" /> - <input type="hidden" name="area" value="default" /> - </form> - <p class="searchtip" style="font-size: 90%"> - Enter search terms or a module, class or function name. - </p> -</div> -<script type="text/javascript">$('#searchbox').show(0);</script> - </div> - </div> - <div class="clearer"></div> - </div> - <div class="related"> - <h3>Navigation</h3> - <ul> - <li class="right" style="margin-right: 10px"> - <a href="../genindex.html" title="General Index" - >index</a></li> - <li class="right" > - <a href="../py-modindex.html" title="Python Module Index" - >modules</a> |</li> - <li><a href="../index.html">BrowserMob Proxy 0.6.0 documentation</a> »</li> - </ul> - </div> - <div class="footer"> - © Copyright 2014, David Burns. - Created using <a href="http://sphinx-doc.org/">Sphinx</a> 1.2.1. - </div> - </body> -</html>
\ No newline at end of file diff --git a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/_sources/client.txt b/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/_sources/client.txt deleted file mode 100644 index 95c74fa9f..000000000 --- a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/_sources/client.txt +++ /dev/null @@ -1,8 +0,0 @@ -.. toctree:: - :maxdepth: 2 - -:mod:`client` Package ---------------------- -.. automodule:: browsermobproxy -.. autoclass:: Client - :members: diff --git a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/_sources/index.txt b/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/_sources/index.txt deleted file mode 100644 index 0a568cf64..000000000 --- a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/_sources/index.txt +++ /dev/null @@ -1,72 +0,0 @@ -.. BrowserMob Proxy documentation master file, created by - sphinx-quickstart on Fri May 24 12:37:12 2013. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. -.. highlightlang:: python - - - -Welcome to BrowserMob Proxy's documentation! -============================================ - -Python client for the BrowserMob Proxy 2.0 REST API. - -How to install --------------- - -BrowserMob Proxy is available on PyPI_, so you can install it with ``pip``:: - - $ pip install browsermob-proxy - -Or with `easy_install`:: - - $ easy_install browsermob-proxy - -Or by cloning the repo from GitHub_:: - - $ git clone git://github.com/AutomatedTester/browsermob-proxy-py.git - -Then install it by running:: - - $ python setup.py install - -How to use with selenium-webdriver ----------------------------------- - -Manually:: - - from browsermobproxy import Server - server = Server("path/to/browsermob-proxy") - server.start() - proxy = server.create_proxy() - - from selenium import webdriver - profile = webdriver.FirefoxProfile() - profile.set_proxy(proxy.selenium_proxy()) - driver = webdriver.Firefox(firefox_profile=profile) - - - proxy.new_har("google") - driver.get("http://www.google.co.uk") - proxy.har # returns a HAR JSON blob - - server.stop() - driver.quit() - -Contents: - -.. toctree:: - :maxdepth: 2 - - client.rst - server.rst - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` - -.. _GitHub: https://github.com/AutomatedTester/browsermob-proxy-py -.. _PyPI: http://pypi.python.org/pypi/browsermob-proxy diff --git a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/_sources/server.txt b/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/_sources/server.txt deleted file mode 100644 index 8af94b70a..000000000 --- a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/_sources/server.txt +++ /dev/null @@ -1,8 +0,0 @@ -.. toctree:: - :maxdepth: 2 - -:mod:`server` Package ---------------------- -.. automodule:: browsermobproxy -.. autoclass:: Server - :members:
\ No newline at end of file diff --git a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/_static/basic.css b/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/_static/basic.css deleted file mode 100644 index c959cf0db..000000000 --- a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/_static/basic.css +++ /dev/null @@ -1,537 +0,0 @@ -/* - * basic.css - * ~~~~~~~~~ - * - * Sphinx stylesheet -- basic theme. - * - * :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - -/* -- main layout ----------------------------------------------------------- */ - -div.clearer { - clear: both; -} - -/* -- relbar ---------------------------------------------------------------- */ - -div.related { - width: 100%; - font-size: 90%; -} - -div.related h3 { - display: none; -} - -div.related ul { - margin: 0; - padding: 0 0 0 10px; - list-style: none; -} - -div.related li { - display: inline; -} - -div.related li.right { - float: right; - margin-right: 5px; -} - -/* -- sidebar --------------------------------------------------------------- */ - -div.sphinxsidebarwrapper { - padding: 10px 5px 0 10px; -} - -div.sphinxsidebar { - float: left; - width: 230px; - margin-left: -100%; - font-size: 90%; -} - -div.sphinxsidebar ul { - list-style: none; -} - -div.sphinxsidebar ul ul, -div.sphinxsidebar ul.want-points { - margin-left: 20px; - list-style: square; -} - -div.sphinxsidebar ul ul { - margin-top: 0; - margin-bottom: 0; -} - -div.sphinxsidebar form { - margin-top: 10px; -} - -div.sphinxsidebar input { - border: 1px solid #98dbcc; - font-family: sans-serif; - font-size: 1em; -} - -div.sphinxsidebar #searchbox input[type="text"] { - width: 170px; -} - -div.sphinxsidebar #searchbox input[type="submit"] { - width: 30px; -} - -img { - border: 0; - max-width: 100%; -} - -/* -- search page ----------------------------------------------------------- */ - -ul.search { - margin: 10px 0 0 20px; - padding: 0; -} - -ul.search li { - padding: 5px 0 5px 20px; - background-image: url(file.png); - background-repeat: no-repeat; - background-position: 0 7px; -} - -ul.search li a { - font-weight: bold; -} - -ul.search li div.context { - color: #888; - margin: 2px 0 0 30px; - text-align: left; -} - -ul.keywordmatches li.goodmatch a { - font-weight: bold; -} - -/* -- index page ------------------------------------------------------------ */ - -table.contentstable { - width: 90%; -} - -table.contentstable p.biglink { - line-height: 150%; -} - -a.biglink { - font-size: 1.3em; -} - -span.linkdescr { - font-style: italic; - padding-top: 5px; - font-size: 90%; -} - -/* -- general index --------------------------------------------------------- */ - -table.indextable { - width: 100%; -} - -table.indextable td { - text-align: left; - vertical-align: top; -} - -table.indextable dl, table.indextable dd { - margin-top: 0; - margin-bottom: 0; -} - -table.indextable tr.pcap { - height: 10px; -} - -table.indextable tr.cap { - margin-top: 10px; - background-color: #f2f2f2; -} - -img.toggler { - margin-right: 3px; - margin-top: 3px; - cursor: pointer; -} - -div.modindex-jumpbox { - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; - margin: 1em 0 1em 0; - padding: 0.4em; -} - -div.genindex-jumpbox { - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; - margin: 1em 0 1em 0; - padding: 0.4em; -} - -/* -- general body styles --------------------------------------------------- */ - -a.headerlink { - visibility: hidden; -} - -h1:hover > a.headerlink, -h2:hover > a.headerlink, -h3:hover > a.headerlink, -h4:hover > a.headerlink, -h5:hover > a.headerlink, -h6:hover > a.headerlink, -dt:hover > a.headerlink { - visibility: visible; -} - -div.body p.caption { - text-align: inherit; -} - -div.body td { - text-align: left; -} - -.field-list ul { - padding-left: 1em; -} - -.first { - margin-top: 0 !important; -} - -p.rubric { - margin-top: 30px; - font-weight: bold; -} - -img.align-left, .figure.align-left, object.align-left { - clear: left; - float: left; - margin-right: 1em; -} - -img.align-right, .figure.align-right, object.align-right { - clear: right; - float: right; - margin-left: 1em; -} - -img.align-center, .figure.align-center, object.align-center { - display: block; - margin-left: auto; - margin-right: auto; -} - -.align-left { - text-align: left; -} - -.align-center { - text-align: center; -} - -.align-right { - text-align: right; -} - -/* -- sidebars -------------------------------------------------------------- */ - -div.sidebar { - margin: 0 0 0.5em 1em; - border: 1px solid #ddb; - padding: 7px 7px 0 7px; - background-color: #ffe; - width: 40%; - float: right; -} - -p.sidebar-title { - font-weight: bold; -} - -/* -- topics ---------------------------------------------------------------- */ - -div.topic { - border: 1px solid #ccc; - padding: 7px 7px 0 7px; - margin: 10px 0 10px 0; -} - -p.topic-title { - font-size: 1.1em; - font-weight: bold; - margin-top: 10px; -} - -/* -- admonitions ----------------------------------------------------------- */ - -div.admonition { - margin-top: 10px; - margin-bottom: 10px; - padding: 7px; -} - -div.admonition dt { - font-weight: bold; -} - -div.admonition dl { - margin-bottom: 0; -} - -p.admonition-title { - margin: 0px 10px 5px 0px; - font-weight: bold; -} - -div.body p.centered { - text-align: center; - margin-top: 25px; -} - -/* -- tables ---------------------------------------------------------------- */ - -table.docutils { - border: 0; - border-collapse: collapse; -} - -table.docutils td, table.docutils th { - padding: 1px 8px 1px 5px; - border-top: 0; - border-left: 0; - border-right: 0; - border-bottom: 1px solid #aaa; -} - -table.field-list td, table.field-list th { - border: 0 !important; -} - -table.footnote td, table.footnote th { - border: 0 !important; -} - -th { - text-align: left; - padding-right: 5px; -} - -table.citation { - border-left: solid 1px gray; - margin-left: 1px; -} - -table.citation td { - border-bottom: none; -} - -/* -- other body styles ----------------------------------------------------- */ - -ol.arabic { - list-style: decimal; -} - -ol.loweralpha { - list-style: lower-alpha; -} - -ol.upperalpha { - list-style: upper-alpha; -} - -ol.lowerroman { - list-style: lower-roman; -} - -ol.upperroman { - list-style: upper-roman; -} - -dl { - margin-bottom: 15px; -} - -dd p { - margin-top: 0px; -} - -dd ul, dd table { - margin-bottom: 10px; -} - -dd { - margin-top: 3px; - margin-bottom: 10px; - margin-left: 30px; -} - -dt:target, .highlighted { - background-color: #fbe54e; -} - -dl.glossary dt { - font-weight: bold; - font-size: 1.1em; -} - -.field-list ul { - margin: 0; - padding-left: 1em; -} - -.field-list p { - margin: 0; -} - -.optional { - font-size: 1.3em; -} - -.versionmodified { - font-style: italic; -} - -.system-message { - background-color: #fda; - padding: 5px; - border: 3px solid red; -} - -.footnote:target { - background-color: #ffa; -} - -.line-block { - display: block; - margin-top: 1em; - margin-bottom: 1em; -} - -.line-block .line-block { - margin-top: 0; - margin-bottom: 0; - margin-left: 1.5em; -} - -.guilabel, .menuselection { - font-family: sans-serif; -} - -.accelerator { - text-decoration: underline; -} - -.classifier { - font-style: oblique; -} - -abbr, acronym { - border-bottom: dotted 1px; - cursor: help; -} - -/* -- code displays --------------------------------------------------------- */ - -pre { - overflow: auto; - overflow-y: hidden; /* fixes display issues on Chrome browsers */ -} - -td.linenos pre { - padding: 5px 0px; - border: 0; - background-color: transparent; - color: #aaa; -} - -table.highlighttable { - margin-left: 0.5em; -} - -table.highlighttable td { - padding: 0 0.5em 0 0.5em; -} - -tt.descname { - background-color: transparent; - font-weight: bold; - font-size: 1.2em; -} - -tt.descclassname { - background-color: transparent; -} - -tt.xref, a tt { - background-color: transparent; - font-weight: bold; -} - -h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt { - background-color: transparent; -} - -.viewcode-link { - float: right; -} - -.viewcode-back { - float: right; - font-family: sans-serif; -} - -div.viewcode-block:target { - margin: -1px -10px; - padding: 0 10px; -} - -/* -- math display ---------------------------------------------------------- */ - -img.math { - vertical-align: middle; -} - -div.body div.math p { - text-align: center; -} - -span.eqno { - float: right; -} - -/* -- printout stylesheet --------------------------------------------------- */ - -@media print { - div.document, - div.documentwrapper, - div.bodywrapper { - margin: 0 !important; - width: 100%; - } - - div.sphinxsidebar, - div.related, - div.footer, - #top-link { - display: none; - } -}
\ No newline at end of file diff --git a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/_static/default.css b/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/_static/default.css deleted file mode 100644 index e534a0780..000000000 --- a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/_static/default.css +++ /dev/null @@ -1,256 +0,0 @@ -/* - * default.css_t - * ~~~~~~~~~~~~~ - * - * Sphinx stylesheet -- default theme. - * - * :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - -@import url("basic.css"); - -/* -- page layout ----------------------------------------------------------- */ - -body { - font-family: sans-serif; - font-size: 100%; - background-color: #11303d; - color: #000; - margin: 0; - padding: 0; -} - -div.document { - background-color: #1c4e63; -} - -div.documentwrapper { - float: left; - width: 100%; -} - -div.bodywrapper { - margin: 0 0 0 230px; -} - -div.body { - background-color: #ffffff; - color: #000000; - padding: 0 20px 30px 20px; -} - -div.footer { - color: #ffffff; - width: 100%; - padding: 9px 0 9px 0; - text-align: center; - font-size: 75%; -} - -div.footer a { - color: #ffffff; - text-decoration: underline; -} - -div.related { - background-color: #133f52; - line-height: 30px; - color: #ffffff; -} - -div.related a { - color: #ffffff; -} - -div.sphinxsidebar { -} - -div.sphinxsidebar h3 { - font-family: 'Trebuchet MS', sans-serif; - color: #ffffff; - font-size: 1.4em; - font-weight: normal; - margin: 0; - padding: 0; -} - -div.sphinxsidebar h3 a { - color: #ffffff; -} - -div.sphinxsidebar h4 { - font-family: 'Trebuchet MS', sans-serif; - color: #ffffff; - font-size: 1.3em; - font-weight: normal; - margin: 5px 0 0 0; - padding: 0; -} - -div.sphinxsidebar p { - color: #ffffff; -} - -div.sphinxsidebar p.topless { - margin: 5px 10px 10px 10px; -} - -div.sphinxsidebar ul { - margin: 10px; - padding: 0; - color: #ffffff; -} - -div.sphinxsidebar a { - color: #98dbcc; -} - -div.sphinxsidebar input { - border: 1px solid #98dbcc; - font-family: sans-serif; - font-size: 1em; -} - - - -/* -- hyperlink styles ------------------------------------------------------ */ - -a { - color: #355f7c; - text-decoration: none; -} - -a:visited { - color: #355f7c; - text-decoration: none; -} - -a:hover { - text-decoration: underline; -} - - - -/* -- body styles ----------------------------------------------------------- */ - -div.body h1, -div.body h2, -div.body h3, -div.body h4, -div.body h5, -div.body h6 { - font-family: 'Trebuchet MS', sans-serif; - background-color: #f2f2f2; - font-weight: normal; - color: #20435c; - border-bottom: 1px solid #ccc; - margin: 20px -20px 10px -20px; - padding: 3px 0 3px 10px; -} - -div.body h1 { margin-top: 0; font-size: 200%; } -div.body h2 { font-size: 160%; } -div.body h3 { font-size: 140%; } -div.body h4 { font-size: 120%; } -div.body h5 { font-size: 110%; } -div.body h6 { font-size: 100%; } - -a.headerlink { - color: #c60f0f; - font-size: 0.8em; - padding: 0 4px 0 4px; - text-decoration: none; -} - -a.headerlink:hover { - background-color: #c60f0f; - color: white; -} - -div.body p, div.body dd, div.body li { - text-align: justify; - line-height: 130%; -} - -div.admonition p.admonition-title + p { - display: inline; -} - -div.admonition p { - margin-bottom: 5px; -} - -div.admonition pre { - margin-bottom: 5px; -} - -div.admonition ul, div.admonition ol { - margin-bottom: 5px; -} - -div.note { - background-color: #eee; - border: 1px solid #ccc; -} - -div.seealso { - background-color: #ffc; - border: 1px solid #ff6; -} - -div.topic { - background-color: #eee; -} - -div.warning { - background-color: #ffe4e4; - border: 1px solid #f66; -} - -p.admonition-title { - display: inline; -} - -p.admonition-title:after { - content: ":"; -} - -pre { - padding: 5px; - background-color: #eeffcc; - color: #333333; - line-height: 120%; - border: 1px solid #ac9; - border-left: none; - border-right: none; -} - -tt { - background-color: #ecf0f3; - padding: 0 1px 0 1px; - font-size: 0.95em; -} - -th { - background-color: #ede; -} - -.warning tt { - background: #efc2c2; -} - -.note tt { - background: #d6d6d6; -} - -.viewcode-back { - font-family: sans-serif; -} - -div.viewcode-block:target { - background-color: #f4debf; - border-top: 1px solid #ac9; - border-bottom: 1px solid #ac9; -}
\ No newline at end of file diff --git a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/_static/doctools.js b/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/_static/doctools.js deleted file mode 100644 index 2036e5f5f..000000000 --- a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/_static/doctools.js +++ /dev/null @@ -1,238 +0,0 @@ -/* - * doctools.js - * ~~~~~~~~~~~ - * - * Sphinx JavaScript utilities for all documentation. - * - * :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - -/** - * select a different prefix for underscore - */ -$u = _.noConflict(); - -/** - * make the code below compatible with browsers without - * an installed firebug like debugger -if (!window.console || !console.firebug) { - var names = ["log", "debug", "info", "warn", "error", "assert", "dir", - "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", - "profile", "profileEnd"]; - window.console = {}; - for (var i = 0; i < names.length; ++i) - window.console[names[i]] = function() {}; -} - */ - -/** - * small helper function to urldecode strings - */ -jQuery.urldecode = function(x) { - return decodeURIComponent(x).replace(/\+/g, ' '); -}; - -/** - * small helper function to urlencode strings - */ -jQuery.urlencode = encodeURIComponent; - -/** - * This function returns the parsed url parameters of the - * current request. Multiple values per key are supported, - * it will always return arrays of strings for the value parts. - */ -jQuery.getQueryParameters = function(s) { - if (typeof s == 'undefined') - s = document.location.search; - var parts = s.substr(s.indexOf('?') + 1).split('&'); - var result = {}; - for (var i = 0; i < parts.length; i++) { - var tmp = parts[i].split('=', 2); - var key = jQuery.urldecode(tmp[0]); - var value = jQuery.urldecode(tmp[1]); - if (key in result) - result[key].push(value); - else - result[key] = [value]; - } - return result; -}; - -/** - * highlight a given string on a jquery object by wrapping it in - * span elements with the given class name. - */ -jQuery.fn.highlightText = function(text, className) { - function highlight(node) { - if (node.nodeType == 3) { - var val = node.nodeValue; - var pos = val.toLowerCase().indexOf(text); - if (pos >= 0 && !jQuery(node.parentNode).hasClass(className)) { - var span = document.createElement("span"); - span.className = className; - span.appendChild(document.createTextNode(val.substr(pos, text.length))); - node.parentNode.insertBefore(span, node.parentNode.insertBefore( - document.createTextNode(val.substr(pos + text.length)), - node.nextSibling)); - node.nodeValue = val.substr(0, pos); - } - } - else if (!jQuery(node).is("button, select, textarea")) { - jQuery.each(node.childNodes, function() { - highlight(this); - }); - } - } - return this.each(function() { - highlight(this); - }); -}; - -/** - * Small JavaScript module for the documentation. - */ -var Documentation = { - - init : function() { - this.fixFirefoxAnchorBug(); - this.highlightSearchWords(); - this.initIndexTable(); - }, - - /** - * i18n support - */ - TRANSLATIONS : {}, - PLURAL_EXPR : function(n) { return n == 1 ? 0 : 1; }, - LOCALE : 'unknown', - - // gettext and ngettext don't access this so that the functions - // can safely bound to a different name (_ = Documentation.gettext) - gettext : function(string) { - var translated = Documentation.TRANSLATIONS[string]; - if (typeof translated == 'undefined') - return string; - return (typeof translated == 'string') ? translated : translated[0]; - }, - - ngettext : function(singular, plural, n) { - var translated = Documentation.TRANSLATIONS[singular]; - if (typeof translated == 'undefined') - return (n == 1) ? singular : plural; - return translated[Documentation.PLURALEXPR(n)]; - }, - - addTranslations : function(catalog) { - for (var key in catalog.messages) - this.TRANSLATIONS[key] = catalog.messages[key]; - this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); - this.LOCALE = catalog.locale; - }, - - /** - * add context elements like header anchor links - */ - addContextElements : function() { - $('div[id] > :header:first').each(function() { - $('<a class="headerlink">\u00B6</a>'). - attr('href', '#' + this.id). - attr('title', _('Permalink to this headline')). - appendTo(this); - }); - $('dt[id]').each(function() { - $('<a class="headerlink">\u00B6</a>'). - attr('href', '#' + this.id). - attr('title', _('Permalink to this definition')). - appendTo(this); - }); - }, - - /** - * workaround a firefox stupidity - */ - fixFirefoxAnchorBug : function() { - if (document.location.hash && $.browser.mozilla) - window.setTimeout(function() { - document.location.href += ''; - }, 10); - }, - - /** - * highlight the search words provided in the url in the text - */ - highlightSearchWords : function() { - var params = $.getQueryParameters(); - var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; - if (terms.length) { - var body = $('div.body'); - if (!body.length) { - body = $('body'); - } - window.setTimeout(function() { - $.each(terms, function() { - body.highlightText(this.toLowerCase(), 'highlighted'); - }); - }, 10); - $('<p class="highlight-link"><a href="javascript:Documentation.' + - 'hideSearchWords()">' + _('Hide Search Matches') + '</a></p>') - .appendTo($('#searchbox')); - } - }, - - /** - * init the domain index toggle buttons - */ - initIndexTable : function() { - var togglers = $('img.toggler').click(function() { - var src = $(this).attr('src'); - var idnum = $(this).attr('id').substr(7); - $('tr.cg-' + idnum).toggle(); - if (src.substr(-9) == 'minus.png') - $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); - else - $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); - }).css('display', ''); - if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) { - togglers.click(); - } - }, - - /** - * helper function to hide the search marks again - */ - hideSearchWords : function() { - $('#searchbox .highlight-link').fadeOut(300); - $('span.highlighted').removeClass('highlighted'); - }, - - /** - * make the url absolute - */ - makeURL : function(relativeURL) { - return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; - }, - - /** - * get the current relative url - */ - getCurrentURL : function() { - var path = document.location.pathname; - var parts = path.split(/\//); - $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { - if (this == '..') - parts.pop(); - }); - var url = parts.join('/'); - return path.substring(url.lastIndexOf('/') + 1, path.length - 1); - } -}; - -// quick alias for translations -_ = Documentation.gettext; - -$(document).ready(function() { - Documentation.init(); -}); diff --git a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/_static/jquery.js b/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/_static/jquery.js deleted file mode 100644 index 388377952..000000000 --- a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/_static/jquery.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! jQuery v1.8.3 jquery.com | jquery.org/license */ -(function(e,t){function _(e){var t=M[e]={};return v.each(e.split(y),function(e,n){t[n]=!0}),t}function H(e,n,r){if(r===t&&e.nodeType===1){var i="data-"+n.replace(P,"-$1").toLowerCase();r=e.getAttribute(i);if(typeof r=="string"){try{r=r==="true"?!0:r==="false"?!1:r==="null"?null:+r+""===r?+r:D.test(r)?v.parseJSON(r):r}catch(s){}v.data(e,n,r)}else r=t}return r}function B(e){var t;for(t in e){if(t==="data"&&v.isEmptyObject(e[t]))continue;if(t!=="toJSON")return!1}return!0}function et(){return!1}function tt(){return!0}function ut(e){return!e||!e.parentNode||e.parentNode.nodeType===11}function at(e,t){do e=e[t];while(e&&e.nodeType!==1);return e}function ft(e,t,n){t=t||0;if(v.isFunction(t))return v.grep(e,function(e,r){var i=!!t.call(e,r,e);return i===n});if(t.nodeType)return v.grep(e,function(e,r){return e===t===n});if(typeof t=="string"){var r=v.grep(e,function(e){return e.nodeType===1});if(it.test(t))return v.filter(t,r,!n);t=v.filter(t,r)}return v.grep(e,function(e,r){return v.inArray(e,t)>=0===n})}function lt(e){var t=ct.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}function Lt(e,t){return e.getElementsByTagName(t)[0]||e.appendChild(e.ownerDocument.createElement(t))}function At(e,t){if(t.nodeType!==1||!v.hasData(e))return;var n,r,i,s=v._data(e),o=v._data(t,s),u=s.events;if(u){delete o.handle,o.events={};for(n in u)for(r=0,i=u[n].length;r<i;r++)v.event.add(t,n,u[n][r])}o.data&&(o.data=v.extend({},o.data))}function Ot(e,t){var n;if(t.nodeType!==1)return;t.clearAttributes&&t.clearAttributes(),t.mergeAttributes&&t.mergeAttributes(e),n=t.nodeName.toLowerCase(),n==="object"?(t.parentNode&&(t.outerHTML=e.outerHTML),v.support.html5Clone&&e.innerHTML&&!v.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):n==="input"&&Et.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):n==="option"?t.selected=e.defaultSelected:n==="input"||n==="textarea"?t.defaultValue=e.defaultValue:n==="script"&&t.text!==e.text&&(t.text=e.text),t.removeAttribute(v.expando)}function Mt(e){return typeof e.getElementsByTagName!="undefined"?e.getElementsByTagName("*"):typeof e.querySelectorAll!="undefined"?e.querySelectorAll("*"):[]}function _t(e){Et.test(e.type)&&(e.defaultChecked=e.checked)}function Qt(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=Jt.length;while(i--){t=Jt[i]+n;if(t in e)return t}return r}function Gt(e,t){return e=t||e,v.css(e,"display")==="none"||!v.contains(e.ownerDocument,e)}function Yt(e,t){var n,r,i=[],s=0,o=e.length;for(;s<o;s++){n=e[s];if(!n.style)continue;i[s]=v._data(n,"olddisplay"),t?(!i[s]&&n.style.display==="none"&&(n.style.display=""),n.style.display===""&&Gt(n)&&(i[s]=v._data(n,"olddisplay",nn(n.nodeName)))):(r=Dt(n,"display"),!i[s]&&r!=="none"&&v._data(n,"olddisplay",r))}for(s=0;s<o;s++){n=e[s];if(!n.style)continue;if(!t||n.style.display==="none"||n.style.display==="")n.style.display=t?i[s]||"":"none"}return e}function Zt(e,t,n){var r=Rt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function en(e,t,n,r){var i=n===(r?"border":"content")?4:t==="width"?1:0,s=0;for(;i<4;i+=2)n==="margin"&&(s+=v.css(e,n+$t[i],!0)),r?(n==="content"&&(s-=parseFloat(Dt(e,"padding"+$t[i]))||0),n!=="margin"&&(s-=parseFloat(Dt(e,"border"+$t[i]+"Width"))||0)):(s+=parseFloat(Dt(e,"padding"+$t[i]))||0,n!=="padding"&&(s+=parseFloat(Dt(e,"border"+$t[i]+"Width"))||0));return s}function tn(e,t,n){var r=t==="width"?e.offsetWidth:e.offsetHeight,i=!0,s=v.support.boxSizing&&v.css(e,"boxSizing")==="border-box";if(r<=0||r==null){r=Dt(e,t);if(r<0||r==null)r=e.style[t];if(Ut.test(r))return r;i=s&&(v.support.boxSizingReliable||r===e.style[t]),r=parseFloat(r)||0}return r+en(e,t,n||(s?"border":"content"),i)+"px"}function nn(e){if(Wt[e])return Wt[e];var t=v("<"+e+">").appendTo(i.body),n=t.css("display");t.remove();if(n==="none"||n===""){Pt=i.body.appendChild(Pt||v.extend(i.createElement("iframe"),{frameBorder:0,width:0,height:0}));if(!Ht||!Pt.createElement)Ht=(Pt.contentWindow||Pt.contentDocument).document,Ht.write("<!doctype html><html><body>"),Ht.close();t=Ht.body.appendChild(Ht.createElement(e)),n=Dt(t,"display"),i.body.removeChild(Pt)}return Wt[e]=n,n}function fn(e,t,n,r){var i;if(v.isArray(t))v.each(t,function(t,i){n||sn.test(e)?r(e,i):fn(e+"["+(typeof i=="object"?t:"")+"]",i,n,r)});else if(!n&&v.type(t)==="object")for(i in t)fn(e+"["+i+"]",t[i],n,r);else r(e,t)}function Cn(e){return function(t,n){typeof t!="string"&&(n=t,t="*");var r,i,s,o=t.toLowerCase().split(y),u=0,a=o.length;if(v.isFunction(n))for(;u<a;u++)r=o[u],s=/^\+/.test(r),s&&(r=r.substr(1)||"*"),i=e[r]=e[r]||[],i[s?"unshift":"push"](n)}}function kn(e,n,r,i,s,o){s=s||n.dataTypes[0],o=o||{},o[s]=!0;var u,a=e[s],f=0,l=a?a.length:0,c=e===Sn;for(;f<l&&(c||!u);f++)u=a[f](n,r,i),typeof u=="string"&&(!c||o[u]?u=t:(n.dataTypes.unshift(u),u=kn(e,n,r,i,u,o)));return(c||!u)&&!o["*"]&&(u=kn(e,n,r,i,"*",o)),u}function Ln(e,n){var r,i,s=v.ajaxSettings.flatOptions||{};for(r in n)n[r]!==t&&((s[r]?e:i||(i={}))[r]=n[r]);i&&v.extend(!0,e,i)}function An(e,n,r){var i,s,o,u,a=e.contents,f=e.dataTypes,l=e.responseFields;for(s in l)s in r&&(n[l[s]]=r[s]);while(f[0]==="*")f.shift(),i===t&&(i=e.mimeType||n.getResponseHeader("content-type"));if(i)for(s in a)if(a[s]&&a[s].test(i)){f.unshift(s);break}if(f[0]in r)o=f[0];else{for(s in r){if(!f[0]||e.converters[s+" "+f[0]]){o=s;break}u||(u=s)}o=o||u}if(o)return o!==f[0]&&f.unshift(o),r[o]}function On(e,t){var n,r,i,s,o=e.dataTypes.slice(),u=o[0],a={},f=0;e.dataFilter&&(t=e.dataFilter(t,e.dataType));if(o[1])for(n in e.converters)a[n.toLowerCase()]=e.converters[n];for(;i=o[++f];)if(i!=="*"){if(u!=="*"&&u!==i){n=a[u+" "+i]||a["* "+i];if(!n)for(r in a){s=r.split(" ");if(s[1]===i){n=a[u+" "+s[0]]||a["* "+s[0]];if(n){n===!0?n=a[r]:a[r]!==!0&&(i=s[0],o.splice(f--,0,i));break}}}if(n!==!0)if(n&&e["throws"])t=n(t);else try{t=n(t)}catch(l){return{state:"parsererror",error:n?l:"No conversion from "+u+" to "+i}}}u=i}return{state:"success",data:t}}function Fn(){try{return new e.XMLHttpRequest}catch(t){}}function In(){try{return new e.ActiveXObject("Microsoft.XMLHTTP")}catch(t){}}function $n(){return setTimeout(function(){qn=t},0),qn=v.now()}function Jn(e,t){v.each(t,function(t,n){var r=(Vn[t]||[]).concat(Vn["*"]),i=0,s=r.length;for(;i<s;i++)if(r[i].call(e,t,n))return})}function Kn(e,t,n){var r,i=0,s=0,o=Xn.length,u=v.Deferred().always(function(){delete a.elem}),a=function(){var t=qn||$n(),n=Math.max(0,f.startTime+f.duration-t),r=n/f.duration||0,i=1-r,s=0,o=f.tweens.length;for(;s<o;s++)f.tweens[s].run(i);return u.notifyWith(e,[f,i,n]),i<1&&o?n:(u.resolveWith(e,[f]),!1)},f=u.promise({elem:e,props:v.extend({},t),opts:v.extend(!0,{specialEasing:{}},n),originalProperties:t,originalOptions:n,startTime:qn||$n(),duration:n.duration,tweens:[],createTween:function(t,n,r){var i=v.Tween(e,f.opts,t,n,f.opts.specialEasing[t]||f.opts.easing);return f.tweens.push(i),i},stop:function(t){var n=0,r=t?f.tweens.length:0;for(;n<r;n++)f.tweens[n].run(1);return t?u.resolveWith(e,[f,t]):u.rejectWith(e,[f,t]),this}}),l=f.props;Qn(l,f.opts.specialEasing);for(;i<o;i++){r=Xn[i].call(f,e,l,f.opts);if(r)return r}return Jn(f,l),v.isFunction(f.opts.start)&&f.opts.start.call(e,f),v.fx.timer(v.extend(a,{anim:f,queue:f.opts.queue,elem:e})),f.progress(f.opts.progress).done(f.opts.done,f.opts.complete).fail(f.opts.fail).always(f.opts.always)}function Qn(e,t){var n,r,i,s,o;for(n in e){r=v.camelCase(n),i=t[r],s=e[n],v.isArray(s)&&(i=s[1],s=e[n]=s[0]),n!==r&&(e[r]=s,delete e[n]),o=v.cssHooks[r];if(o&&"expand"in o){s=o.expand(s),delete e[r];for(n in s)n in e||(e[n]=s[n],t[n]=i)}else t[r]=i}}function Gn(e,t,n){var r,i,s,o,u,a,f,l,c,h=this,p=e.style,d={},m=[],g=e.nodeType&&Gt(e);n.queue||(l=v._queueHooks(e,"fx"),l.unqueued==null&&(l.unqueued=0,c=l.empty.fire,l.empty.fire=function(){l.unqueued||c()}),l.unqueued++,h.always(function(){h.always(function(){l.unqueued--,v.queue(e,"fx").length||l.empty.fire()})})),e.nodeType===1&&("height"in t||"width"in t)&&(n.overflow=[p.overflow,p.overflowX,p.overflowY],v.css(e,"display")==="inline"&&v.css(e,"float")==="none"&&(!v.support.inlineBlockNeedsLayout||nn(e.nodeName)==="inline"?p.display="inline-block":p.zoom=1)),n.overflow&&(p.overflow="hidden",v.support.shrinkWrapBlocks||h.done(function(){p.overflow=n.overflow[0],p.overflowX=n.overflow[1],p.overflowY=n.overflow[2]}));for(r in t){s=t[r];if(Un.exec(s)){delete t[r],a=a||s==="toggle";if(s===(g?"hide":"show"))continue;m.push(r)}}o=m.length;if(o){u=v._data(e,"fxshow")||v._data(e,"fxshow",{}),"hidden"in u&&(g=u.hidden),a&&(u.hidden=!g),g?v(e).show():h.done(function(){v(e).hide()}),h.done(function(){var t;v.removeData(e,"fxshow",!0);for(t in d)v.style(e,t,d[t])});for(r=0;r<o;r++)i=m[r],f=h.createTween(i,g?u[i]:0),d[i]=u[i]||v.style(e,i),i in u||(u[i]=f.start,g&&(f.end=f.start,f.start=i==="width"||i==="height"?1:0))}}function Yn(e,t,n,r,i){return new Yn.prototype.init(e,t,n,r,i)}function Zn(e,t){var n,r={height:e},i=0;t=t?1:0;for(;i<4;i+=2-t)n=$t[i],r["margin"+n]=r["padding"+n]=e;return t&&(r.opacity=r.width=e),r}function tr(e){return v.isWindow(e)?e:e.nodeType===9?e.defaultView||e.parentWindow:!1}var n,r,i=e.document,s=e.location,o=e.navigator,u=e.jQuery,a=e.$,f=Array.prototype.push,l=Array.prototype.slice,c=Array.prototype.indexOf,h=Object.prototype.toString,p=Object.prototype.hasOwnProperty,d=String.prototype.trim,v=function(e,t){return new v.fn.init(e,t,n)},m=/[\-+]?(?:\d*\.|)\d+(?:[eE][\-+]?\d+|)/.source,g=/\S/,y=/\s+/,b=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,w=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,E=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,S=/^[\],:{}\s]*$/,x=/(?:^|:|,)(?:\s*\[)+/g,T=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,N=/"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,C=/^-ms-/,k=/-([\da-z])/gi,L=function(e,t){return(t+"").toUpperCase()},A=function(){i.addEventListener?(i.removeEventListener("DOMContentLoaded",A,!1),v.ready()):i.readyState==="complete"&&(i.detachEvent("onreadystatechange",A),v.ready())},O={};v.fn=v.prototype={constructor:v,init:function(e,n,r){var s,o,u,a;if(!e)return this;if(e.nodeType)return this.context=this[0]=e,this.length=1,this;if(typeof e=="string"){e.charAt(0)==="<"&&e.charAt(e.length-1)===">"&&e.length>=3?s=[null,e,null]:s=w.exec(e);if(s&&(s[1]||!n)){if(s[1])return n=n instanceof v?n[0]:n,a=n&&n.nodeType?n.ownerDocument||n:i,e=v.parseHTML(s[1],a,!0),E.test(s[1])&&v.isPlainObject(n)&&this.attr.call(e,n,!0),v.merge(this,e);o=i.getElementById(s[2]);if(o&&o.parentNode){if(o.id!==s[2])return r.find(e);this.length=1,this[0]=o}return this.context=i,this.selector=e,this}return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e)}return v.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),v.makeArray(e,this))},selector:"",jquery:"1.8.3",length:0,size:function(){return this.length},toArray:function(){return l.call(this)},get:function(e){return e==null?this.toArray():e<0?this[this.length+e]:this[e]},pushStack:function(e,t,n){var r=v.merge(this.constructor(),e);return r.prevObject=this,r.context=this.context,t==="find"?r.selector=this.selector+(this.selector?" ":"")+n:t&&(r.selector=this.selector+"."+t+"("+n+")"),r},each:function(e,t){return v.each(this,e,t)},ready:function(e){return v.ready.promise().done(e),this},eq:function(e){return e=+e,e===-1?this.slice(e):this.slice(e,e+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(l.apply(this,arguments),"slice",l.call(arguments).join(","))},map:function(e){return this.pushStack(v.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:[].sort,splice:[].splice},v.fn.init.prototype=v.fn,v.extend=v.fn.extend=function(){var e,n,r,i,s,o,u=arguments[0]||{},a=1,f=arguments.length,l=!1;typeof u=="boolean"&&(l=u,u=arguments[1]||{},a=2),typeof u!="object"&&!v.isFunction(u)&&(u={}),f===a&&(u=this,--a);for(;a<f;a++)if((e=arguments[a])!=null)for(n in e){r=u[n],i=e[n];if(u===i)continue;l&&i&&(v.isPlainObject(i)||(s=v.isArray(i)))?(s?(s=!1,o=r&&v.isArray(r)?r:[]):o=r&&v.isPlainObject(r)?r:{},u[n]=v.extend(l,o,i)):i!==t&&(u[n]=i)}return u},v.extend({noConflict:function(t){return e.$===v&&(e.$=a),t&&e.jQuery===v&&(e.jQuery=u),v},isReady:!1,readyWait:1,holdReady:function(e){e?v.readyWait++:v.ready(!0)},ready:function(e){if(e===!0?--v.readyWait:v.isReady)return;if(!i.body)return setTimeout(v.ready,1);v.isReady=!0;if(e!==!0&&--v.readyWait>0)return;r.resolveWith(i,[v]),v.fn.trigger&&v(i).trigger("ready").off("ready")},isFunction:function(e){return v.type(e)==="function"},isArray:Array.isArray||function(e){return v.type(e)==="array"},isWindow:function(e){return e!=null&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return e==null?String(e):O[h.call(e)]||"object"},isPlainObject:function(e){if(!e||v.type(e)!=="object"||e.nodeType||v.isWindow(e))return!1;try{if(e.constructor&&!p.call(e,"constructor")&&!p.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}var r;for(r in e);return r===t||p.call(e,r)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw new Error(e)},parseHTML:function(e,t,n){var r;return!e||typeof e!="string"?null:(typeof t=="boolean"&&(n=t,t=0),t=t||i,(r=E.exec(e))?[t.createElement(r[1])]:(r=v.buildFragment([e],t,n?null:[]),v.merge([],(r.cacheable?v.clone(r.fragment):r.fragment).childNodes)))},parseJSON:function(t){if(!t||typeof t!="string")return null;t=v.trim(t);if(e.JSON&&e.JSON.parse)return e.JSON.parse(t);if(S.test(t.replace(T,"@").replace(N,"]").replace(x,"")))return(new Function("return "+t))();v.error("Invalid JSON: "+t)},parseXML:function(n){var r,i;if(!n||typeof n!="string")return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(s){r=t}return(!r||!r.documentElement||r.getElementsByTagName("parsererror").length)&&v.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&g.test(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(C,"ms-").replace(k,L)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,n,r){var i,s=0,o=e.length,u=o===t||v.isFunction(e);if(r){if(u){for(i in e)if(n.apply(e[i],r)===!1)break}else for(;s<o;)if(n.apply(e[s++],r)===!1)break}else if(u){for(i in e)if(n.call(e[i],i,e[i])===!1)break}else for(;s<o;)if(n.call(e[s],s,e[s++])===!1)break;return e},trim:d&&!d.call("\ufeff\u00a0")?function(e){return e==null?"":d.call(e)}:function(e){return e==null?"":(e+"").replace(b,"")},makeArray:function(e,t){var n,r=t||[];return e!=null&&(n=v.type(e),e.length==null||n==="string"||n==="function"||n==="regexp"||v.isWindow(e)?f.call(r,e):v.merge(r,e)),r},inArray:function(e,t,n){var r;if(t){if(c)return c.call(t,e,n);r=t.length,n=n?n<0?Math.max(0,r+n):n:0;for(;n<r;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,s=0;if(typeof r=="number")for(;s<r;s++)e[i++]=n[s];else while(n[s]!==t)e[i++]=n[s++];return e.length=i,e},grep:function(e,t,n){var r,i=[],s=0,o=e.length;n=!!n;for(;s<o;s++)r=!!t(e[s],s),n!==r&&i.push(e[s]);return i},map:function(e,n,r){var i,s,o=[],u=0,a=e.length,f=e instanceof v||a!==t&&typeof a=="number"&&(a>0&&e[0]&&e[a-1]||a===0||v.isArray(e));if(f)for(;u<a;u++)i=n(e[u],u,r),i!=null&&(o[o.length]=i);else for(s in e)i=n(e[s],s,r),i!=null&&(o[o.length]=i);return o.concat.apply([],o)},guid:1,proxy:function(e,n){var r,i,s;return typeof n=="string"&&(r=e[n],n=e,e=r),v.isFunction(e)?(i=l.call(arguments,2),s=function(){return e.apply(n,i.concat(l.call(arguments)))},s.guid=e.guid=e.guid||v.guid++,s):t},access:function(e,n,r,i,s,o,u){var a,f=r==null,l=0,c=e.length;if(r&&typeof r=="object"){for(l in r)v.access(e,n,l,r[l],1,o,i);s=1}else if(i!==t){a=u===t&&v.isFunction(i),f&&(a?(a=n,n=function(e,t,n){return a.call(v(e),n)}):(n.call(e,i),n=null));if(n)for(;l<c;l++)n(e[l],r,a?i.call(e[l],l,n(e[l],r)):i,u);s=1}return s?e:f?n.call(e):c?n(e[0],r):o},now:function(){return(new Date).getTime()}}),v.ready.promise=function(t){if(!r){r=v.Deferred();if(i.readyState==="complete")setTimeout(v.ready,1);else if(i.addEventListener)i.addEventListener("DOMContentLoaded",A,!1),e.addEventListener("load",v.ready,!1);else{i.attachEvent("onreadystatechange",A),e.attachEvent("onload",v.ready);var n=!1;try{n=e.frameElement==null&&i.documentElement}catch(s){}n&&n.doScroll&&function o(){if(!v.isReady){try{n.doScroll("left")}catch(e){return setTimeout(o,50)}v.ready()}}()}}return r.promise(t)},v.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(e,t){O["[object "+t+"]"]=t.toLowerCase()}),n=v(i);var M={};v.Callbacks=function(e){e=typeof e=="string"?M[e]||_(e):v.extend({},e);var n,r,i,s,o,u,a=[],f=!e.once&&[],l=function(t){n=e.memory&&t,r=!0,u=s||0,s=0,o=a.length,i=!0;for(;a&&u<o;u++)if(a[u].apply(t[0],t[1])===!1&&e.stopOnFalse){n=!1;break}i=!1,a&&(f?f.length&&l(f.shift()):n?a=[]:c.disable())},c={add:function(){if(a){var t=a.length;(function r(t){v.each(t,function(t,n){var i=v.type(n);i==="function"?(!e.unique||!c.has(n))&&a.push(n):n&&n.length&&i!=="string"&&r(n)})})(arguments),i?o=a.length:n&&(s=t,l(n))}return this},remove:function(){return a&&v.each(arguments,function(e,t){var n;while((n=v.inArray(t,a,n))>-1)a.splice(n,1),i&&(n<=o&&o--,n<=u&&u--)}),this},has:function(e){return v.inArray(e,a)>-1},empty:function(){return a=[],this},disable:function(){return a=f=n=t,this},disabled:function(){return!a},lock:function(){return f=t,n||c.disable(),this},locked:function(){return!f},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],a&&(!r||f)&&(i?f.push(t):l(t)),this},fire:function(){return c.fireWith(this,arguments),this},fired:function(){return!!r}};return c},v.extend({Deferred:function(e){var t=[["resolve","done",v.Callbacks("once memory"),"resolved"],["reject","fail",v.Callbacks("once memory"),"rejected"],["notify","progress",v.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return v.Deferred(function(n){v.each(t,function(t,r){var s=r[0],o=e[t];i[r[1]](v.isFunction(o)?function(){var e=o.apply(this,arguments);e&&v.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[s+"With"](this===i?n:this,[e])}:n[s])}),e=null}).promise()},promise:function(e){return e!=null?v.extend(e,r):r}},i={};return r.pipe=r.then,v.each(t,function(e,s){var o=s[2],u=s[3];r[s[1]]=o.add,u&&o.add(function(){n=u},t[e^1][2].disable,t[2][2].lock),i[s[0]]=o.fire,i[s[0]+"With"]=o.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=l.call(arguments),r=n.length,i=r!==1||e&&v.isFunction(e.promise)?r:0,s=i===1?e:v.Deferred(),o=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?l.call(arguments):r,n===u?s.notifyWith(t,n):--i||s.resolveWith(t,n)}},u,a,f;if(r>1){u=new Array(r),a=new Array(r),f=new Array(r);for(;t<r;t++)n[t]&&v.isFunction(n[t].promise)?n[t].promise().done(o(t,f,n)).fail(s.reject).progress(o(t,a,u)):--i}return i||s.resolveWith(f,n),s.promise()}}),v.support=function(){var t,n,r,s,o,u,a,f,l,c,h,p=i.createElement("div");p.setAttribute("className","t"),p.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",n=p.getElementsByTagName("*"),r=p.getElementsByTagName("a")[0];if(!n||!r||!n.length)return{};s=i.createElement("select"),o=s.appendChild(i.createElement("option")),u=p.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(r.getAttribute("style")),hrefNormalized:r.getAttribute("href")==="/a",opacity:/^0.5/.test(r.style.opacity),cssFloat:!!r.style.cssFloat,checkOn:u.value==="on",optSelected:o.selected,getSetAttribute:p.className!=="t",enctype:!!i.createElement("form").enctype,html5Clone:i.createElement("nav").cloneNode(!0).outerHTML!=="<:nav></:nav>",boxModel:i.compatMode==="CSS1Compat",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},u.checked=!0,t.noCloneChecked=u.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!o.disabled;try{delete p.test}catch(d){t.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",h=function(){t.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick"),p.detachEvent("onclick",h)),u=i.createElement("input"),u.value="t",u.setAttribute("type","radio"),t.radioValue=u.value==="t",u.setAttribute("checked","checked"),u.setAttribute("name","t"),p.appendChild(u),a=i.createDocumentFragment(),a.appendChild(p.lastChild),t.checkClone=a.cloneNode(!0).cloneNode(!0).lastChild.checked,t.appendChecked=u.checked,a.removeChild(u),a.appendChild(p);if(p.attachEvent)for(l in{submit:!0,change:!0,focusin:!0})f="on"+l,c=f in p,c||(p.setAttribute(f,"return;"),c=typeof p[f]=="function"),t[l+"Bubbles"]=c;return v(function(){var n,r,s,o,u="padding:0;margin:0;border:0;display:block;overflow:hidden;",a=i.getElementsByTagName("body")[0];if(!a)return;n=i.createElement("div"),n.style.cssText="visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px",a.insertBefore(n,a.firstChild),r=i.createElement("div"),n.appendChild(r),r.innerHTML="<table><tr><td></td><td>t</td></tr></table>",s=r.getElementsByTagName("td"),s[0].style.cssText="padding:0;margin:0;border:0;display:none",c=s[0].offsetHeight===0,s[0].style.display="",s[1].style.display="none",t.reliableHiddenOffsets=c&&s[0].offsetHeight===0,r.innerHTML="",r.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",t.boxSizing=r.offsetWidth===4,t.doesNotIncludeMarginInBodyOffset=a.offsetTop!==1,e.getComputedStyle&&(t.pixelPosition=(e.getComputedStyle(r,null)||{}).top!=="1%",t.boxSizingReliable=(e.getComputedStyle(r,null)||{width:"4px"}).width==="4px",o=i.createElement("div"),o.style.cssText=r.style.cssText=u,o.style.marginRight=o.style.width="0",r.style.width="1px",r.appendChild(o),t.reliableMarginRight=!parseFloat((e.getComputedStyle(o,null)||{}).marginRight)),typeof r.style.zoom!="undefined"&&(r.innerHTML="",r.style.cssText=u+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=r.offsetWidth===3,r.style.display="block",r.style.overflow="visible",r.innerHTML="<div></div>",r.firstChild.style.width="5px",t.shrinkWrapBlocks=r.offsetWidth!==3,n.style.zoom=1),a.removeChild(n),n=r=s=o=null}),a.removeChild(p),n=r=s=o=u=a=p=null,t}();var D=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;v.extend({cache:{},deletedIds:[],uuid:0,expando:"jQuery"+(v.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(e){return e=e.nodeType?v.cache[e[v.expando]]:e[v.expando],!!e&&!B(e)},data:function(e,n,r,i){if(!v.acceptData(e))return;var s,o,u=v.expando,a=typeof n=="string",f=e.nodeType,l=f?v.cache:e,c=f?e[u]:e[u]&&u;if((!c||!l[c]||!i&&!l[c].data)&&a&&r===t)return;c||(f?e[u]=c=v.deletedIds.pop()||v.guid++:c=u),l[c]||(l[c]={},f||(l[c].toJSON=v.noop));if(typeof n=="object"||typeof n=="function")i?l[c]=v.extend(l[c],n):l[c].data=v.extend(l[c].data,n);return s=l[c],i||(s.data||(s.data={}),s=s.data),r!==t&&(s[v.camelCase(n)]=r),a?(o=s[n],o==null&&(o=s[v.camelCase(n)])):o=s,o},removeData:function(e,t,n){if(!v.acceptData(e))return;var r,i,s,o=e.nodeType,u=o?v.cache:e,a=o?e[v.expando]:v.expando;if(!u[a])return;if(t){r=n?u[a]:u[a].data;if(r){v.isArray(t)||(t in r?t=[t]:(t=v.camelCase(t),t in r?t=[t]:t=t.split(" ")));for(i=0,s=t.length;i<s;i++)delete r[t[i]];if(!(n?B:v.isEmptyObject)(r))return}}if(!n){delete u[a].data;if(!B(u[a]))return}o?v.cleanData([e],!0):v.support.deleteExpando||u!=u.window?delete u[a]:u[a]=null},_data:function(e,t,n){return v.data(e,t,n,!0)},acceptData:function(e){var t=e.nodeName&&v.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),v.fn.extend({data:function(e,n){var r,i,s,o,u,a=this[0],f=0,l=null;if(e===t){if(this.length){l=v.data(a);if(a.nodeType===1&&!v._data(a,"parsedAttrs")){s=a.attributes;for(u=s.length;f<u;f++)o=s[f].name,o.indexOf("data-")||(o=v.camelCase(o.substring(5)),H(a,o,l[o]));v._data(a,"parsedAttrs",!0)}}return l}return typeof e=="object"?this.each(function(){v.data(this,e)}):(r=e.split(".",2),r[1]=r[1]?"."+r[1]:"",i=r[1]+"!",v.access(this,function(n){if(n===t)return l=this.triggerHandler("getData"+i,[r[0]]),l===t&&a&&(l=v.data(a,e),l=H(a,e,l)),l===t&&r[1]?this.data(r[0]):l;r[1]=n,this.each(function(){var t=v(this);t.triggerHandler("setData"+i,r),v.data(this,e,n),t.triggerHandler("changeData"+i,r)})},null,n,arguments.length>1,null,!1))},removeData:function(e){return this.each(function(){v.removeData(this,e)})}}),v.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=v._data(e,t),n&&(!r||v.isArray(n)?r=v._data(e,t,v.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=v.queue(e,t),r=n.length,i=n.shift(),s=v._queueHooks(e,t),o=function(){v.dequeue(e,t)};i==="inprogress"&&(i=n.shift(),r--),i&&(t==="fx"&&n.unshift("inprogress"),delete s.stop,i.call(e,o,s)),!r&&s&&s.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return v._data(e,n)||v._data(e,n,{empty:v.Callbacks("once memory").add(function(){v.removeData(e,t+"queue",!0),v.removeData(e,n,!0)})})}}),v.fn.extend({queue:function(e,n){var r=2;return typeof e!="string"&&(n=e,e="fx",r--),arguments.length<r?v.queue(this[0],e):n===t?this:this.each(function(){var t=v.queue(this,e,n);v._queueHooks(this,e),e==="fx"&&t[0]!=="inprogress"&&v.dequeue(this,e)})},dequeue:function(e){return this.each(function(){v.dequeue(this,e)})},delay:function(e,t){return e=v.fx?v.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,s=v.Deferred(),o=this,u=this.length,a=function(){--i||s.resolveWith(o,[o])};typeof e!="string"&&(n=e,e=t),e=e||"fx";while(u--)r=v._data(o[u],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(a));return a(),s.promise(n)}});var j,F,I,q=/[\t\r\n]/g,R=/\r/g,U=/^(?:button|input)$/i,z=/^(?:button|input|object|select|textarea)$/i,W=/^a(?:rea|)$/i,X=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,V=v.support.getSetAttribute;v.fn.extend({attr:function(e,t){return v.access(this,v.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){v.removeAttr(this,e)})},prop:function(e,t){return v.access(this,v.prop,e,t,arguments.length>1)},removeProp:function(e){return e=v.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,s,o,u;if(v.isFunction(e))return this.each(function(t){v(this).addClass(e.call(this,t,this.className))});if(e&&typeof e=="string"){t=e.split(y);for(n=0,r=this.length;n<r;n++){i=this[n];if(i.nodeType===1)if(!i.className&&t.length===1)i.className=e;else{s=" "+i.className+" ";for(o=0,u=t.length;o<u;o++)s.indexOf(" "+t[o]+" ")<0&&(s+=t[o]+" ");i.className=v.trim(s)}}}return this},removeClass:function(e){var n,r,i,s,o,u,a;if(v.isFunction(e))return this.each(function(t){v(this).removeClass(e.call(this,t,this.className))});if(e&&typeof e=="string"||e===t){n=(e||"").split(y);for(u=0,a=this.length;u<a;u++){i=this[u];if(i.nodeType===1&&i.className){r=(" "+i.className+" ").replace(q," ");for(s=0,o=n.length;s<o;s++)while(r.indexOf(" "+n[s]+" ")>=0)r=r.replace(" "+n[s]+" "," ");i.className=e?v.trim(r):""}}}return this},toggleClass:function(e,t){var n=typeof e,r=typeof t=="boolean";return v.isFunction(e)?this.each(function(n){v(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if(n==="string"){var i,s=0,o=v(this),u=t,a=e.split(y);while(i=a[s++])u=r?u:!o.hasClass(i),o[u?"addClass":"removeClass"](i)}else if(n==="undefined"||n==="boolean")this.className&&v._data(this,"__className__",this.className),this.className=this.className||e===!1?"":v._data(this,"__className__")||""})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;n<r;n++)if(this[n].nodeType===1&&(" "+this[n].className+" ").replace(q," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,s=this[0];if(!arguments.length){if(s)return n=v.valHooks[s.type]||v.valHooks[s.nodeName.toLowerCase()],n&&"get"in n&&(r=n.get(s,"value"))!==t?r:(r=s.value,typeof r=="string"?r.replace(R,""):r==null?"":r);return}return i=v.isFunction(e),this.each(function(r){var s,o=v(this);if(this.nodeType!==1)return;i?s=e.call(this,r,o.val()):s=e,s==null?s="":typeof s=="number"?s+="":v.isArray(s)&&(s=v.map(s,function(e){return e==null?"":e+""})),n=v.valHooks[this.type]||v.valHooks[this.nodeName.toLowerCase()];if(!n||!("set"in n)||n.set(this,s,"value")===t)this.value=s})}}),v.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,s=e.type==="select-one"||i<0,o=s?null:[],u=s?i+1:r.length,a=i<0?u:s?i:0;for(;a<u;a++){n=r[a];if((n.selected||a===i)&&(v.support.optDisabled?!n.disabled:n.getAttribute("disabled")===null)&&(!n.parentNode.disabled||!v.nodeName(n.parentNode,"optgroup"))){t=v(n).val();if(s)return t;o.push(t)}}return o},set:function(e,t){var n=v.makeArray(t);return v(e).find("option").each(function(){this.selected=v.inArray(v(this).val(),n)>=0}),n.length||(e.selectedIndex=-1),n}}},attrFn:{},attr:function(e,n,r,i){var s,o,u,a=e.nodeType;if(!e||a===3||a===8||a===2)return;if(i&&v.isFunction(v.fn[n]))return v(e)[n](r);if(typeof e.getAttribute=="undefined")return v.prop(e,n,r);u=a!==1||!v.isXMLDoc(e),u&&(n=n.toLowerCase(),o=v.attrHooks[n]||(X.test(n)?F:j));if(r!==t){if(r===null){v.removeAttr(e,n);return}return o&&"set"in o&&u&&(s=o.set(e,r,n))!==t?s:(e.setAttribute(n,r+""),r)}return o&&"get"in o&&u&&(s=o.get(e,n))!==null?s:(s=e.getAttribute(n),s===null?t:s)},removeAttr:function(e,t){var n,r,i,s,o=0;if(t&&e.nodeType===1){r=t.split(y);for(;o<r.length;o++)i=r[o],i&&(n=v.propFix[i]||i,s=X.test(i),s||v.attr(e,i,""),e.removeAttribute(V?i:n),s&&n in e&&(e[n]=!1))}},attrHooks:{type:{set:function(e,t){if(U.test(e.nodeName)&&e.parentNode)v.error("type property can't be changed");else if(!v.support.radioValue&&t==="radio"&&v.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}},value:{get:function(e,t){return j&&v.nodeName(e,"button")?j.get(e,t):t in e?e.value:null},set:function(e,t,n){if(j&&v.nodeName(e,"button"))return j.set(e,t,n);e.value=t}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(e,n,r){var i,s,o,u=e.nodeType;if(!e||u===3||u===8||u===2)return;return o=u!==1||!v.isXMLDoc(e),o&&(n=v.propFix[n]||n,s=v.propHooks[n]),r!==t?s&&"set"in s&&(i=s.set(e,r,n))!==t?i:e[n]=r:s&&"get"in s&&(i=s.get(e,n))!==null?i:e[n]},propHooks:{tabIndex:{get:function(e){var n=e.getAttributeNode("tabindex");return n&&n.specified?parseInt(n.value,10):z.test(e.nodeName)||W.test(e.nodeName)&&e.href?0:t}}}}),F={get:function(e,n){var r,i=v.prop(e,n);return i===!0||typeof i!="boolean"&&(r=e.getAttributeNode(n))&&r.nodeValue!==!1?n.toLowerCase():t},set:function(e,t,n){var r;return t===!1?v.removeAttr(e,n):(r=v.propFix[n]||n,r in e&&(e[r]=!0),e.setAttribute(n,n.toLowerCase())),n}},V||(I={name:!0,id:!0,coords:!0},j=v.valHooks.button={get:function(e,n){var r;return r=e.getAttributeNode(n),r&&(I[n]?r.value!=="":r.specified)?r.value:t},set:function(e,t,n){var r=e.getAttributeNode(n);return r||(r=i.createAttribute(n),e.setAttributeNode(r)),r.value=t+""}},v.each(["width","height"],function(e,t){v.attrHooks[t]=v.extend(v.attrHooks[t],{set:function(e,n){if(n==="")return e.setAttribute(t,"auto"),n}})}),v.attrHooks.contenteditable={get:j.get,set:function(e,t,n){t===""&&(t="false"),j.set(e,t,n)}}),v.support.hrefNormalized||v.each(["href","src","width","height"],function(e,n){v.attrHooks[n]=v.extend(v.attrHooks[n],{get:function(e){var r=e.getAttribute(n,2);return r===null?t:r}})}),v.support.style||(v.attrHooks.style={get:function(e){return e.style.cssText.toLowerCase()||t},set:function(e,t){return e.style.cssText=t+""}}),v.support.optSelected||(v.propHooks.selected=v.extend(v.propHooks.selected,{get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}})),v.support.enctype||(v.propFix.enctype="encoding"),v.support.checkOn||v.each(["radio","checkbox"],function(){v.valHooks[this]={get:function(e){return e.getAttribute("value")===null?"on":e.value}}}),v.each(["radio","checkbox"],function(){v.valHooks[this]=v.extend(v.valHooks[this],{set:function(e,t){if(v.isArray(t))return e.checked=v.inArray(v(e).val(),t)>=0}})});var $=/^(?:textarea|input|select)$/i,J=/^([^\.]*|)(?:\.(.+)|)$/,K=/(?:^|\s)hover(\.\S+|)\b/,Q=/^key/,G=/^(?:mouse|contextmenu)|click/,Y=/^(?:focusinfocus|focusoutblur)$/,Z=function(e){return v.event.special.hover?e:e.replace(K,"mouseenter$1 mouseleave$1")};v.event={add:function(e,n,r,i,s){var o,u,a,f,l,c,h,p,d,m,g;if(e.nodeType===3||e.nodeType===8||!n||!r||!(o=v._data(e)))return;r.handler&&(d=r,r=d.handler,s=d.selector),r.guid||(r.guid=v.guid++),a=o.events,a||(o.events=a={}),u=o.handle,u||(o.handle=u=function(e){return typeof v=="undefined"||!!e&&v.event.triggered===e.type?t:v.event.dispatch.apply(u.elem,arguments)},u.elem=e),n=v.trim(Z(n)).split(" ");for(f=0;f<n.length;f++){l=J.exec(n[f])||[],c=l[1],h=(l[2]||"").split(".").sort(),g=v.event.special[c]||{},c=(s?g.delegateType:g.bindType)||c,g=v.event.special[c]||{},p=v.extend({type:c,origType:l[1],data:i,handler:r,guid:r.guid,selector:s,needsContext:s&&v.expr.match.needsContext.test(s),namespace:h.join(".")},d),m=a[c];if(!m){m=a[c]=[],m.delegateCount=0;if(!g.setup||g.setup.call(e,i,h,u)===!1)e.addEventListener?e.addEventListener(c,u,!1):e.attachEvent&&e.attachEvent("on"+c,u)}g.add&&(g.add.call(e,p),p.handler.guid||(p.handler.guid=r.guid)),s?m.splice(m.delegateCount++,0,p):m.push(p),v.event.global[c]=!0}e=null},global:{},remove:function(e,t,n,r,i){var s,o,u,a,f,l,c,h,p,d,m,g=v.hasData(e)&&v._data(e);if(!g||!(h=g.events))return;t=v.trim(Z(t||"")).split(" ");for(s=0;s<t.length;s++){o=J.exec(t[s])||[],u=a=o[1],f=o[2];if(!u){for(u in h)v.event.remove(e,u+t[s],n,r,!0);continue}p=v.event.special[u]||{},u=(r?p.delegateType:p.bindType)||u,d=h[u]||[],l=d.length,f=f?new RegExp("(^|\\.)"+f.split(".").sort().join("\\.(?:.*\\.|)")+"(\\.|$)"):null;for(c=0;c<d.length;c++)m=d[c],(i||a===m.origType)&&(!n||n.guid===m.guid)&&(!f||f.test(m.namespace))&&(!r||r===m.selector||r==="**"&&m.selector)&&(d.splice(c--,1),m.selector&&d.delegateCount--,p.remove&&p.remove.call(e,m));d.length===0&&l!==d.length&&((!p.teardown||p.teardown.call(e,f,g.handle)===!1)&&v.removeEvent(e,u,g.handle),delete h[u])}v.isEmptyObject(h)&&(delete g.handle,v.removeData(e,"events",!0))},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(n,r,s,o){if(!s||s.nodeType!==3&&s.nodeType!==8){var u,a,f,l,c,h,p,d,m,g,y=n.type||n,b=[];if(Y.test(y+v.event.triggered))return;y.indexOf("!")>=0&&(y=y.slice(0,-1),a=!0),y.indexOf(".")>=0&&(b=y.split("."),y=b.shift(),b.sort());if((!s||v.event.customEvent[y])&&!v.event.global[y])return;n=typeof n=="object"?n[v.expando]?n:new v.Event(y,n):new v.Event(y),n.type=y,n.isTrigger=!0,n.exclusive=a,n.namespace=b.join("."),n.namespace_re=n.namespace?new RegExp("(^|\\.)"+b.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,h=y.indexOf(":")<0?"on"+y:"";if(!s){u=v.cache;for(f in u)u[f].events&&u[f].events[y]&&v.event.trigger(n,r,u[f].handle.elem,!0);return}n.result=t,n.target||(n.target=s),r=r!=null?v.makeArray(r):[],r.unshift(n),p=v.event.special[y]||{};if(p.trigger&&p.trigger.apply(s,r)===!1)return;m=[[s,p.bindType||y]];if(!o&&!p.noBubble&&!v.isWindow(s)){g=p.delegateType||y,l=Y.test(g+y)?s:s.parentNode;for(c=s;l;l=l.parentNode)m.push([l,g]),c=l;c===(s.ownerDocument||i)&&m.push([c.defaultView||c.parentWindow||e,g])}for(f=0;f<m.length&&!n.isPropagationStopped();f++)l=m[f][0],n.type=m[f][1],d=(v._data(l,"events")||{})[n.type]&&v._data(l,"handle"),d&&d.apply(l,r),d=h&&l[h],d&&v.acceptData(l)&&d.apply&&d.apply(l,r)===!1&&n.preventDefault();return n.type=y,!o&&!n.isDefaultPrevented()&&(!p._default||p._default.apply(s.ownerDocument,r)===!1)&&(y!=="click"||!v.nodeName(s,"a"))&&v.acceptData(s)&&h&&s[y]&&(y!=="focus"&&y!=="blur"||n.target.offsetWidth!==0)&&!v.isWindow(s)&&(c=s[h],c&&(s[h]=null),v.event.triggered=y,s[y](),v.event.triggered=t,c&&(s[h]=c)),n.result}return},dispatch:function(n){n=v.event.fix(n||e.event);var r,i,s,o,u,a,f,c,h,p,d=(v._data(this,"events")||{})[n.type]||[],m=d.delegateCount,g=l.call(arguments),y=!n.exclusive&&!n.namespace,b=v.event.special[n.type]||{},w=[];g[0]=n,n.delegateTarget=this;if(b.preDispatch&&b.preDispatch.call(this,n)===!1)return;if(m&&(!n.button||n.type!=="click"))for(s=n.target;s!=this;s=s.parentNode||this)if(s.disabled!==!0||n.type!=="click"){u={},f=[];for(r=0;r<m;r++)c=d[r],h=c.selector,u[h]===t&&(u[h]=c.needsContext?v(h,this).index(s)>=0:v.find(h,this,null,[s]).length),u[h]&&f.push(c);f.length&&w.push({elem:s,matches:f})}d.length>m&&w.push({elem:this,matches:d.slice(m)});for(r=0;r<w.length&&!n.isPropagationStopped();r++){a=w[r],n.currentTarget=a.elem;for(i=0;i<a.matches.length&&!n.isImmediatePropagationStopped();i++){c=a.matches[i];if(y||!n.namespace&&!c.namespace||n.namespace_re&&n.namespace_re.test(c.namespace))n.data=c.data,n.handleObj=c,o=((v.event.special[c.origType]||{}).handle||c.handler).apply(a.elem,g),o!==t&&(n.result=o,o===!1&&(n.preventDefault(),n.stopPropagation()))}}return b.postDispatch&&b.postDispatch.call(this,n),n.result},props:"attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return e.which==null&&(e.which=t.charCode!=null?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,s,o,u=n.button,a=n.fromElement;return e.pageX==null&&n.clientX!=null&&(r=e.target.ownerDocument||i,s=r.documentElement,o=r.body,e.pageX=n.clientX+(s&&s.scrollLeft||o&&o.scrollLeft||0)-(s&&s.clientLeft||o&&o.clientLeft||0),e.pageY=n.clientY+(s&&s.scrollTop||o&&o.scrollTop||0)-(s&&s.clientTop||o&&o.clientTop||0)),!e.relatedTarget&&a&&(e.relatedTarget=a===e.target?n.toElement:a),!e.which&&u!==t&&(e.which=u&1?1:u&2?3:u&4?2:0),e}},fix:function(e){if(e[v.expando])return e;var t,n,r=e,s=v.event.fixHooks[e.type]||{},o=s.props?this.props.concat(s.props):this.props;e=v.Event(r);for(t=o.length;t;)n=o[--t],e[n]=r[n];return e.target||(e.target=r.srcElement||i),e.target.nodeType===3&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,r):e},special:{load:{noBubble:!0},focus:{delegateType:"focusin"},blur:{delegateType:"focusout"},beforeunload:{setup:function(e,t,n){v.isWindow(this)&&(this.onbeforeunload=n)},teardown:function(e,t){this.onbeforeunload===t&&(this.onbeforeunload=null)}}},simulate:function(e,t,n,r){var i=v.extend(new v.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?v.event.trigger(i,null,t):v.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},v.event.handle=v.event.dispatch,v.removeEvent=i.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]=="undefined"&&(e[r]=null),e.detachEvent(r,n))},v.Event=function(e,t){if(!(this instanceof v.Event))return new v.Event(e,t);e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?tt:et):this.type=e,t&&v.extend(this,t),this.timeStamp=e&&e.timeStamp||v.now(),this[v.expando]=!0},v.Event.prototype={preventDefault:function(){this.isDefaultPrevented=tt;var e=this.originalEvent;if(!e)return;e.preventDefault?e.preventDefault():e.returnValue=!1},stopPropagation:function(){this.isPropagationStopped=tt;var e=this.originalEvent;if(!e)return;e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=tt,this.stopPropagation()},isDefaultPrevented:et,isPropagationStopped:et,isImmediatePropagationStopped:et},v.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){v.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,s=e.handleObj,o=s.selector;if(!i||i!==r&&!v.contains(r,i))e.type=s.origType,n=s.handler.apply(this,arguments),e.type=t;return n}}}),v.support.submitBubbles||(v.event.special.submit={setup:function(){if(v.nodeName(this,"form"))return!1;v.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=v.nodeName(n,"input")||v.nodeName(n,"button")?n.form:t;r&&!v._data(r,"_submit_attached")&&(v.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),v._data(r,"_submit_attached",!0))})},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&v.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){if(v.nodeName(this,"form"))return!1;v.event.remove(this,"._submit")}}),v.support.changeBubbles||(v.event.special.change={setup:function(){if($.test(this.nodeName)){if(this.type==="checkbox"||this.type==="radio")v.event.add(this,"propertychange._change",function(e){e.originalEvent.propertyName==="checked"&&(this._just_changed=!0)}),v.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),v.event.simulate("change",this,e,!0)});return!1}v.event.add(this,"beforeactivate._change",function(e){var t=e.target;$.test(t.nodeName)&&!v._data(t,"_change_attached")&&(v.event.add(t,"change._change",function(e){this.parentNode&&!e.isSimulated&&!e.isTrigger&&v.event.simulate("change",this.parentNode,e,!0)}),v._data(t,"_change_attached",!0))})},handle:function(e){var t=e.target;if(this!==t||e.isSimulated||e.isTrigger||t.type!=="radio"&&t.type!=="checkbox")return e.handleObj.handler.apply(this,arguments)},teardown:function(){return v.event.remove(this,"._change"),!$.test(this.nodeName)}}),v.support.focusinBubbles||v.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){v.event.simulate(t,e.target,v.event.fix(e),!0)};v.event.special[t]={setup:function(){n++===0&&i.addEventListener(e,r,!0)},teardown:function(){--n===0&&i.removeEventListener(e,r,!0)}}}),v.fn.extend({on:function(e,n,r,i,s){var o,u;if(typeof e=="object"){typeof n!="string"&&(r=r||n,n=t);for(u in e)this.on(u,n,r,e[u],s);return this}r==null&&i==null?(i=n,r=n=t):i==null&&(typeof n=="string"?(i=r,r=t):(i=r,r=n,n=t));if(i===!1)i=et;else if(!i)return this;return s===1&&(o=i,i=function(e){return v().off(e),o.apply(this,arguments)},i.guid=o.guid||(o.guid=v.guid++)),this.each(function(){v.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,s;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,v(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if(typeof e=="object"){for(s in e)this.off(s,n,e[s]);return this}if(n===!1||typeof n=="function")r=n,n=t;return r===!1&&(r=et),this.each(function(){v.event.remove(this,e,r,n)})},bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},live:function(e,t,n){return v(this.context).on(e,this.selector,t,n),this},die:function(e,t){return v(this.context).off(e,this.selector||"**",t),this},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return arguments.length===1?this.off(e,"**"):this.off(t,e||"**",n)},trigger:function(e,t){return this.each(function(){v.event.trigger(e,t,this)})},triggerHandler:function(e,t){if(this[0])return v.event.trigger(e,t,this[0],!0)},toggle:function(e){var t=arguments,n=e.guid||v.guid++,r=0,i=function(n){var i=(v._data(this,"lastToggle"+e.guid)||0)%r;return v._data(this,"lastToggle"+e.guid,i+1),n.preventDefault(),t[i].apply(this,arguments)||!1};i.guid=n;while(r<t.length)t[r++].guid=n;return this.click(i)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),v.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(e,t){v.fn[t]=function(e,n){return n==null&&(n=e,e=null),arguments.length>0?this.on(t,null,e,n):this.trigger(t)},Q.test(t)&&(v.event.fixHooks[t]=v.event.keyHooks),G.test(t)&&(v.event.fixHooks[t]=v.event.mouseHooks)}),function(e,t){function nt(e,t,n,r){n=n||[],t=t||g;var i,s,a,f,l=t.nodeType;if(!e||typeof e!="string")return n;if(l!==1&&l!==9)return[];a=o(t);if(!a&&!r)if(i=R.exec(e))if(f=i[1]){if(l===9){s=t.getElementById(f);if(!s||!s.parentNode)return n;if(s.id===f)return n.push(s),n}else if(t.ownerDocument&&(s=t.ownerDocument.getElementById(f))&&u(t,s)&&s.id===f)return n.push(s),n}else{if(i[2])return S.apply(n,x.call(t.getElementsByTagName(e),0)),n;if((f=i[3])&&Z&&t.getElementsByClassName)return S.apply(n,x.call(t.getElementsByClassName(f),0)),n}return vt(e.replace(j,"$1"),t,n,r,a)}function rt(e){return function(t){var n=t.nodeName.toLowerCase();return n==="input"&&t.type===e}}function it(e){return function(t){var n=t.nodeName.toLowerCase();return(n==="input"||n==="button")&&t.type===e}}function st(e){return N(function(t){return t=+t,N(function(n,r){var i,s=e([],n.length,t),o=s.length;while(o--)n[i=s[o]]&&(n[i]=!(r[i]=n[i]))})})}function ot(e,t,n){if(e===t)return n;var r=e.nextSibling;while(r){if(r===t)return-1;r=r.nextSibling}return 1}function ut(e,t){var n,r,s,o,u,a,f,l=L[d][e+" "];if(l)return t?0:l.slice(0);u=e,a=[],f=i.preFilter;while(u){if(!n||(r=F.exec(u)))r&&(u=u.slice(r[0].length)||u),a.push(s=[]);n=!1;if(r=I.exec(u))s.push(n=new m(r.shift())),u=u.slice(n.length),n.type=r[0].replace(j," ");for(o in i.filter)(r=J[o].exec(u))&&(!f[o]||(r=f[o](r)))&&(s.push(n=new m(r.shift())),u=u.slice(n.length),n.type=o,n.matches=r);if(!n)break}return t?u.length:u?nt.error(e):L(e,a).slice(0)}function at(e,t,r){var i=t.dir,s=r&&t.dir==="parentNode",o=w++;return t.first?function(t,n,r){while(t=t[i])if(s||t.nodeType===1)return e(t,n,r)}:function(t,r,u){if(!u){var a,f=b+" "+o+" ",l=f+n;while(t=t[i])if(s||t.nodeType===1){if((a=t[d])===l)return t.sizset;if(typeof a=="string"&&a.indexOf(f)===0){if(t.sizset)return t}else{t[d]=l;if(e(t,r,u))return t.sizset=!0,t;t.sizset=!1}}}else while(t=t[i])if(s||t.nodeType===1)if(e(t,r,u))return t}}function ft(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function lt(e,t,n,r,i){var s,o=[],u=0,a=e.length,f=t!=null;for(;u<a;u++)if(s=e[u])if(!n||n(s,r,i))o.push(s),f&&t.push(u);return o}function ct(e,t,n,r,i,s){return r&&!r[d]&&(r=ct(r)),i&&!i[d]&&(i=ct(i,s)),N(function(s,o,u,a){var f,l,c,h=[],p=[],d=o.length,v=s||dt(t||"*",u.nodeType?[u]:u,[]),m=e&&(s||!t)?lt(v,h,e,u,a):v,g=n?i||(s?e:d||r)?[]:o:m;n&&n(m,g,u,a);if(r){f=lt(g,p),r(f,[],u,a),l=f.length;while(l--)if(c=f[l])g[p[l]]=!(m[p[l]]=c)}if(s){if(i||e){if(i){f=[],l=g.length;while(l--)(c=g[l])&&f.push(m[l]=c);i(null,g=[],f,a)}l=g.length;while(l--)(c=g[l])&&(f=i?T.call(s,c):h[l])>-1&&(s[f]=!(o[f]=c))}}else g=lt(g===o?g.splice(d,g.length):g),i?i(null,o,g,a):S.apply(o,g)})}function ht(e){var t,n,r,s=e.length,o=i.relative[e[0].type],u=o||i.relative[" "],a=o?1:0,f=at(function(e){return e===t},u,!0),l=at(function(e){return T.call(t,e)>-1},u,!0),h=[function(e,n,r){return!o&&(r||n!==c)||((t=n).nodeType?f(e,n,r):l(e,n,r))}];for(;a<s;a++)if(n=i.relative[e[a].type])h=[at(ft(h),n)];else{n=i.filter[e[a].type].apply(null,e[a].matches);if(n[d]){r=++a;for(;r<s;r++)if(i.relative[e[r].type])break;return ct(a>1&&ft(h),a>1&&e.slice(0,a-1).join("").replace(j,"$1"),n,a<r&&ht(e.slice(a,r)),r<s&&ht(e=e.slice(r)),r<s&&e.join(""))}h.push(n)}return ft(h)}function pt(e,t){var r=t.length>0,s=e.length>0,o=function(u,a,f,l,h){var p,d,v,m=[],y=0,w="0",x=u&&[],T=h!=null,N=c,C=u||s&&i.find.TAG("*",h&&a.parentNode||a),k=b+=N==null?1:Math.E;T&&(c=a!==g&&a,n=o.el);for(;(p=C[w])!=null;w++){if(s&&p){for(d=0;v=e[d];d++)if(v(p,a,f)){l.push(p);break}T&&(b=k,n=++o.el)}r&&((p=!v&&p)&&y--,u&&x.push(p))}y+=w;if(r&&w!==y){for(d=0;v=t[d];d++)v(x,m,a,f);if(u){if(y>0)while(w--)!x[w]&&!m[w]&&(m[w]=E.call(l));m=lt(m)}S.apply(l,m),T&&!u&&m.length>0&&y+t.length>1&&nt.uniqueSort(l)}return T&&(b=k,c=N),x};return o.el=0,r?N(o):o}function dt(e,t,n){var r=0,i=t.length;for(;r<i;r++)nt(e,t[r],n);return n}function vt(e,t,n,r,s){var o,u,f,l,c,h=ut(e),p=h.length;if(!r&&h.length===1){u=h[0]=h[0].slice(0);if(u.length>2&&(f=u[0]).type==="ID"&&t.nodeType===9&&!s&&i.relative[u[1].type]){t=i.find.ID(f.matches[0].replace($,""),t,s)[0];if(!t)return n;e=e.slice(u.shift().length)}for(o=J.POS.test(e)?-1:u.length-1;o>=0;o--){f=u[o];if(i.relative[l=f.type])break;if(c=i.find[l])if(r=c(f.matches[0].replace($,""),z.test(u[0].type)&&t.parentNode||t,s)){u.splice(o,1),e=r.length&&u.join("");if(!e)return S.apply(n,x.call(r,0)),n;break}}}return a(e,h)(r,t,s,n,z.test(e)),n}function mt(){}var n,r,i,s,o,u,a,f,l,c,h=!0,p="undefined",d=("sizcache"+Math.random()).replace(".",""),m=String,g=e.document,y=g.documentElement,b=0,w=0,E=[].pop,S=[].push,x=[].slice,T=[].indexOf||function(e){var t=0,n=this.length;for(;t<n;t++)if(this[t]===e)return t;return-1},N=function(e,t){return e[d]=t==null||t,e},C=function(){var e={},t=[];return N(function(n,r){return t.push(n)>i.cacheLength&&delete e[t.shift()],e[n+" "]=r},e)},k=C(),L=C(),A=C(),O="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",_=M.replace("w","w#"),D="([*^$|!~]?=)",P="\\["+O+"*("+M+")"+O+"*(?:"+D+O+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+_+")|)|)"+O+"*\\]",H=":("+M+")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:"+P+")|[^:]|\\\\.)*|.*))\\)|)",B=":(even|odd|eq|gt|lt|nth|first|last)(?:\\("+O+"*((?:-\\d)?\\d*)"+O+"*\\)|)(?=[^-]|$)",j=new RegExp("^"+O+"+|((?:^|[^\\\\])(?:\\\\.)*)"+O+"+$","g"),F=new RegExp("^"+O+"*,"+O+"*"),I=new RegExp("^"+O+"*([\\x20\\t\\r\\n\\f>+~])"+O+"*"),q=new RegExp(H),R=/^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,U=/^:not/,z=/[\x20\t\r\n\f]*[+~]/,W=/:not\($/,X=/h\d/i,V=/input|select|textarea|button/i,$=/\\(?!\\)/g,J={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),NAME:new RegExp("^\\[name=['\"]?("+M+")['\"]?\\]"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+P),PSEUDO:new RegExp("^"+H),POS:new RegExp(B,"i"),CHILD:new RegExp("^:(only|nth|first|last)-child(?:\\("+O+"*(even|odd|(([+-]|)(\\d*)n|)"+O+"*(?:([+-]|)"+O+"*(\\d+)|))"+O+"*\\)|)","i"),needsContext:new RegExp("^"+O+"*[>+~]|"+B,"i")},K=function(e){var t=g.createElement("div");try{return e(t)}catch(n){return!1}finally{t=null}},Q=K(function(e){return e.appendChild(g.createComment("")),!e.getElementsByTagName("*").length}),G=K(function(e){return e.innerHTML="<a href='#'></a>",e.firstChild&&typeof e.firstChild.getAttribute!==p&&e.firstChild.getAttribute("href")==="#"}),Y=K(function(e){e.innerHTML="<select></select>";var t=typeof e.lastChild.getAttribute("multiple");return t!=="boolean"&&t!=="string"}),Z=K(function(e){return e.innerHTML="<div class='hidden e'></div><div class='hidden'></div>",!e.getElementsByClassName||!e.getElementsByClassName("e").length?!1:(e.lastChild.className="e",e.getElementsByClassName("e").length===2)}),et=K(function(e){e.id=d+0,e.innerHTML="<a name='"+d+"'></a><div name='"+d+"'></div>",y.insertBefore(e,y.firstChild);var t=g.getElementsByName&&g.getElementsByName(d).length===2+g.getElementsByName(d+0).length;return r=!g.getElementById(d),y.removeChild(e),t});try{x.call(y.childNodes,0)[0].nodeType}catch(tt){x=function(e){var t,n=[];for(;t=this[e];e++)n.push(t);return n}}nt.matches=function(e,t){return nt(e,null,null,t)},nt.matchesSelector=function(e,t){return nt(t,null,null,[e]).length>0},s=nt.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(i===1||i===9||i===11){if(typeof e.textContent=="string")return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=s(e)}else if(i===3||i===4)return e.nodeValue}else for(;t=e[r];r++)n+=s(t);return n},o=nt.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?t.nodeName!=="HTML":!1},u=nt.contains=y.contains?function(e,t){var n=e.nodeType===9?e.documentElement:e,r=t&&t.parentNode;return e===r||!!(r&&r.nodeType===1&&n.contains&&n.contains(r))}:y.compareDocumentPosition?function(e,t){return t&&!!(e.compareDocumentPosition(t)&16)}:function(e,t){while(t=t.parentNode)if(t===e)return!0;return!1},nt.attr=function(e,t){var n,r=o(e);return r||(t=t.toLowerCase()),(n=i.attrHandle[t])?n(e):r||Y?e.getAttribute(t):(n=e.getAttributeNode(t),n?typeof e[t]=="boolean"?e[t]?t:null:n.specified?n.value:null:null)},i=nt.selectors={cacheLength:50,createPseudo:N,match:J,attrHandle:G?{}:{href:function(e){return e.getAttribute("href",2)},type:function(e){return e.getAttribute("type")}},find:{ID:r?function(e,t,n){if(typeof t.getElementById!==p&&!n){var r=t.getElementById(e);return r&&r.parentNode?[r]:[]}}:function(e,n,r){if(typeof n.getElementById!==p&&!r){var i=n.getElementById(e);return i?i.id===e||typeof i.getAttributeNode!==p&&i.getAttributeNode("id").value===e?[i]:t:[]}},TAG:Q?function(e,t){if(typeof t.getElementsByTagName!==p)return t.getElementsByTagName(e)}:function(e,t){var n=t.getElementsByTagName(e);if(e==="*"){var r,i=[],s=0;for(;r=n[s];s++)r.nodeType===1&&i.push(r);return i}return n},NAME:et&&function(e,t){if(typeof t.getElementsByName!==p)return t.getElementsByName(name)},CLASS:Z&&function(e,t,n){if(typeof t.getElementsByClassName!==p&&!n)return t.getElementsByClassName(e)}},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace($,""),e[3]=(e[4]||e[5]||"").replace($,""),e[2]==="~="&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),e[1]==="nth"?(e[2]||nt.error(e[0]),e[3]=+(e[3]?e[4]+(e[5]||1):2*(e[2]==="even"||e[2]==="odd")),e[4]=+(e[6]+e[7]||e[2]==="odd")):e[2]&&nt.error(e[0]),e},PSEUDO:function(e){var t,n;if(J.CHILD.test(e[0]))return null;if(e[3])e[2]=e[3];else if(t=e[4])q.test(t)&&(n=ut(t,!0))&&(n=t.indexOf(")",t.length-n)-t.length)&&(t=t.slice(0,n),e[0]=e[0].slice(0,n)),e[2]=t;return e.slice(0,3)}},filter:{ID:r?function(e){return e=e.replace($,""),function(t){return t.getAttribute("id")===e}}:function(e){return e=e.replace($,""),function(t){var n=typeof t.getAttributeNode!==p&&t.getAttributeNode("id");return n&&n.value===e}},TAG:function(e){return e==="*"?function(){return!0}:(e=e.replace($,"").toLowerCase(),function(t){return t.nodeName&&t.nodeName.toLowerCase()===e})},CLASS:function(e){var t=k[d][e+" "];return t||(t=new RegExp("(^|"+O+")"+e+"("+O+"|$)"))&&k(e,function(e){return t.test(e.className||typeof e.getAttribute!==p&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r,i){var s=nt.attr(r,e);return s==null?t==="!=":t?(s+="",t==="="?s===n:t==="!="?s!==n:t==="^="?n&&s.indexOf(n)===0:t==="*="?n&&s.indexOf(n)>-1:t==="$="?n&&s.substr(s.length-n.length)===n:t==="~="?(" "+s+" ").indexOf(n)>-1:t==="|="?s===n||s.substr(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r){return e==="nth"?function(e){var t,i,s=e.parentNode;if(n===1&&r===0)return!0;if(s){i=0;for(t=s.firstChild;t;t=t.nextSibling)if(t.nodeType===1){i++;if(e===t)break}}return i-=r,i===n||i%n===0&&i/n>=0}:function(t){var n=t;switch(e){case"only":case"first":while(n=n.previousSibling)if(n.nodeType===1)return!1;if(e==="first")return!0;n=t;case"last":while(n=n.nextSibling)if(n.nodeType===1)return!1;return!0}}},PSEUDO:function(e,t){var n,r=i.pseudos[e]||i.setFilters[e.toLowerCase()]||nt.error("unsupported pseudo: "+e);return r[d]?r(t):r.length>1?(n=[e,e,"",t],i.setFilters.hasOwnProperty(e.toLowerCase())?N(function(e,n){var i,s=r(e,t),o=s.length;while(o--)i=T.call(e,s[o]),e[i]=!(n[i]=s[o])}):function(e){return r(e,0,n)}):r}},pseudos:{not:N(function(e){var t=[],n=[],r=a(e.replace(j,"$1"));return r[d]?N(function(e,t,n,i){var s,o=r(e,null,i,[]),u=e.length;while(u--)if(s=o[u])e[u]=!(t[u]=s)}):function(e,i,s){return t[0]=e,r(t,null,s,n),!n.pop()}}),has:N(function(e){return function(t){return nt(e,t).length>0}}),contains:N(function(e){return function(t){return(t.textContent||t.innerText||s(t)).indexOf(e)>-1}}),enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return t==="input"&&!!e.checked||t==="option"&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},parent:function(e){return!i.pseudos.empty(e)},empty:function(e){var t;e=e.firstChild;while(e){if(e.nodeName>"@"||(t=e.nodeType)===3||t===4)return!1;e=e.nextSibling}return!0},header:function(e){return X.test(e.nodeName)},text:function(e){var t,n;return e.nodeName.toLowerCase()==="input"&&(t=e.type)==="text"&&((n=e.getAttribute("type"))==null||n.toLowerCase()===t)},radio:rt("radio"),checkbox:rt("checkbox"),file:rt("file"),password:rt("password"),image:rt("image"),submit:it("submit"),reset:it("reset"),button:function(e){var t=e.nodeName.toLowerCase();return t==="input"&&e.type==="button"||t==="button"},input:function(e){return V.test(e.nodeName)},focus:function(e){var t=e.ownerDocument;return e===t.activeElement&&(!t.hasFocus||t.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},active:function(e){return e===e.ownerDocument.activeElement},first:st(function(){return[0]}),last:st(function(e,t){return[t-1]}),eq:st(function(e,t,n){return[n<0?n+t:n]}),even:st(function(e,t){for(var n=0;n<t;n+=2)e.push(n);return e}),odd:st(function(e,t){for(var n=1;n<t;n+=2)e.push(n);return e}),lt:st(function(e,t,n){for(var r=n<0?n+t:n;--r>=0;)e.push(r);return e}),gt:st(function(e,t,n){for(var r=n<0?n+t:n;++r<t;)e.push(r);return e})}},f=y.compareDocumentPosition?function(e,t){return e===t?(l=!0,0):(!e.compareDocumentPosition||!t.compareDocumentPosition?e.compareDocumentPosition:e.compareDocumentPosition(t)&4)?-1:1}:function(e,t){if(e===t)return l=!0,0;if(e.sourceIndex&&t.sourceIndex)return e.sourceIndex-t.sourceIndex;var n,r,i=[],s=[],o=e.parentNode,u=t.parentNode,a=o;if(o===u)return ot(e,t);if(!o)return-1;if(!u)return 1;while(a)i.unshift(a),a=a.parentNode;a=u;while(a)s.unshift(a),a=a.parentNode;n=i.length,r=s.length;for(var f=0;f<n&&f<r;f++)if(i[f]!==s[f])return ot(i[f],s[f]);return f===n?ot(e,s[f],-1):ot(i[f],t,1)},[0,0].sort(f),h=!l,nt.uniqueSort=function(e){var t,n=[],r=1,i=0;l=h,e.sort(f);if(l){for(;t=e[r];r++)t===e[r-1]&&(i=n.push(r));while(i--)e.splice(n[i],1)}return e},nt.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},a=nt.compile=function(e,t){var n,r=[],i=[],s=A[d][e+" "];if(!s){t||(t=ut(e)),n=t.length;while(n--)s=ht(t[n]),s[d]?r.push(s):i.push(s);s=A(e,pt(i,r))}return s},g.querySelectorAll&&function(){var e,t=vt,n=/'|\\/g,r=/\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,i=[":focus"],s=[":active"],u=y.matchesSelector||y.mozMatchesSelector||y.webkitMatchesSelector||y.oMatchesSelector||y.msMatchesSelector;K(function(e){e.innerHTML="<select><option selected=''></option></select>",e.querySelectorAll("[selected]").length||i.push("\\["+O+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),e.querySelectorAll(":checked").length||i.push(":checked")}),K(function(e){e.innerHTML="<p test=''></p>",e.querySelectorAll("[test^='']").length&&i.push("[*^$]="+O+"*(?:\"\"|'')"),e.innerHTML="<input type='hidden'/>",e.querySelectorAll(":enabled").length||i.push(":enabled",":disabled")}),i=new RegExp(i.join("|")),vt=function(e,r,s,o,u){if(!o&&!u&&!i.test(e)){var a,f,l=!0,c=d,h=r,p=r.nodeType===9&&e;if(r.nodeType===1&&r.nodeName.toLowerCase()!=="object"){a=ut(e),(l=r.getAttribute("id"))?c=l.replace(n,"\\$&"):r.setAttribute("id",c),c="[id='"+c+"'] ",f=a.length;while(f--)a[f]=c+a[f].join("");h=z.test(e)&&r.parentNode||r,p=a.join(",")}if(p)try{return S.apply(s,x.call(h.querySelectorAll(p),0)),s}catch(v){}finally{l||r.removeAttribute("id")}}return t(e,r,s,o,u)},u&&(K(function(t){e=u.call(t,"div");try{u.call(t,"[test!='']:sizzle"),s.push("!=",H)}catch(n){}}),s=new RegExp(s.join("|")),nt.matchesSelector=function(t,n){n=n.replace(r,"='$1']");if(!o(t)&&!s.test(n)&&!i.test(n))try{var a=u.call(t,n);if(a||e||t.document&&t.document.nodeType!==11)return a}catch(f){}return nt(n,null,null,[t]).length>0})}(),i.pseudos.nth=i.pseudos.eq,i.filters=mt.prototype=i.pseudos,i.setFilters=new mt,nt.attr=v.attr,v.find=nt,v.expr=nt.selectors,v.expr[":"]=v.expr.pseudos,v.unique=nt.uniqueSort,v.text=nt.getText,v.isXMLDoc=nt.isXML,v.contains=nt.contains}(e);var nt=/Until$/,rt=/^(?:parents|prev(?:Until|All))/,it=/^.[^:#\[\.,]*$/,st=v.expr.match.needsContext,ot={children:!0,contents:!0,next:!0,prev:!0};v.fn.extend({find:function(e){var t,n,r,i,s,o,u=this;if(typeof e!="string")return v(e).filter(function(){for(t=0,n=u.length;t<n;t++)if(v.contains(u[t],this))return!0});o=this.pushStack("","find",e);for(t=0,n=this.length;t<n;t++){r=o.length,v.find(e,this[t],o);if(t>0)for(i=r;i<o.length;i++)for(s=0;s<r;s++)if(o[s]===o[i]){o.splice(i--,1);break}}return o},has:function(e){var t,n=v(e,this),r=n.length;return this.filter(function(){for(t=0;t<r;t++)if(v.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e,!1),"not",e)},filter:function(e){return this.pushStack(ft(this,e,!0),"filter",e)},is:function(e){return!!e&&(typeof e=="string"?st.test(e)?v(e,this.context).index(this[0])>=0:v.filter(e,this).length>0:this.filter(e).length>0)},closest:function(e,t){var n,r=0,i=this.length,s=[],o=st.test(e)||typeof e!="string"?v(e,t||this.context):0;for(;r<i;r++){n=this[r];while(n&&n.ownerDocument&&n!==t&&n.nodeType!==11){if(o?o.index(n)>-1:v.find.matchesSelector(n,e)){s.push(n);break}n=n.parentNode}}return s=s.length>1?v.unique(s):s,this.pushStack(s,"closest",e)},index:function(e){return e?typeof e=="string"?v.inArray(this[0],v(e)):v.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.prevAll().length:-1},add:function(e,t){var n=typeof e=="string"?v(e,t):v.makeArray(e&&e.nodeType?[e]:e),r=v.merge(this.get(),n);return this.pushStack(ut(n[0])||ut(r[0])?r:v.unique(r))},addBack:function(e){return this.add(e==null?this.prevObject:this.prevObject.filter(e))}}),v.fn.andSelf=v.fn.addBack,v.each({parent:function(e){var t=e.parentNode;return t&&t.nodeType!==11?t:null},parents:function(e){return v.dir(e,"parentNode")},parentsUntil:function(e,t,n){return v.dir(e,"parentNode",n)},next:function(e){return at(e,"nextSibling")},prev:function(e){return at(e,"previousSibling")},nextAll:function(e){return v.dir(e,"nextSibling")},prevAll:function(e){return v.dir(e,"previousSibling")},nextUntil:function(e,t,n){return v.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return v.dir(e,"previousSibling",n)},siblings:function(e){return v.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return v.sibling(e.firstChild)},contents:function(e){return v.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:v.merge([],e.childNodes)}},function(e,t){v.fn[e]=function(n,r){var i=v.map(this,t,n);return nt.test(e)||(r=n),r&&typeof r=="string"&&(i=v.filter(r,i)),i=this.length>1&&!ot[e]?v.unique(i):i,this.length>1&&rt.test(e)&&(i=i.reverse()),this.pushStack(i,e,l.call(arguments).join(","))}}),v.extend({filter:function(e,t,n){return n&&(e=":not("+e+")"),t.length===1?v.find.matchesSelector(t[0],e)?[t[0]]:[]:v.find.matches(e,t)},dir:function(e,n,r){var i=[],s=e[n];while(s&&s.nodeType!==9&&(r===t||s.nodeType!==1||!v(s).is(r)))s.nodeType===1&&i.push(s),s=s[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)e.nodeType===1&&e!==t&&n.push(e);return n}});var ct="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",ht=/ jQuery\d+="(?:null|\d+)"/g,pt=/^\s+/,dt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,vt=/<([\w:]+)/,mt=/<tbody/i,gt=/<|&#?\w+;/,yt=/<(?:script|style|link)/i,bt=/<(?:script|object|embed|option|style)/i,wt=new RegExp("<(?:"+ct+")[\\s/>]","i"),Et=/^(?:checkbox|radio)$/,St=/checked\s*(?:[^=]|=\s*.checked.)/i,xt=/\/(java|ecma)script/i,Tt=/^\s*<!(?:\[CDATA\[|\-\-)|[\]\-]{2}>\s*$/g,Nt={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]},Ct=lt(i),kt=Ct.appendChild(i.createElement("div"));Nt.optgroup=Nt.option,Nt.tbody=Nt.tfoot=Nt.colgroup=Nt.caption=Nt.thead,Nt.th=Nt.td,v.support.htmlSerialize||(Nt._default=[1,"X<div>","</div>"]),v.fn.extend({text:function(e){return v.access(this,function(e){return e===t?v.text(this):this.empty().append((this[0]&&this[0].ownerDocument||i).createTextNode(e))},null,e,arguments.length)},wrapAll:function(e){if(v.isFunction(e))return this.each(function(t){v(this).wrapAll(e.call(this,t))});if(this[0]){var t=v(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&e.firstChild.nodeType===1)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return v.isFunction(e)?this.each(function(t){v(this).wrapInner(e.call(this,t))}):this.each(function(){var t=v(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=v.isFunction(e);return this.each(function(n){v(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){v.nodeName(this,"body")||v(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(e){(this.nodeType===1||this.nodeType===11)&&this.appendChild(e)})},prepend:function(){return this.domManip(arguments,!0,function(e){(this.nodeType===1||this.nodeType===11)&&this.insertBefore(e,this.firstChild)})},before:function(){if(!ut(this[0]))return this.domManip(arguments,!1,function(e){this.parentNode.insertBefore(e,this)});if(arguments.length){var e=v.clean(arguments);return this.pushStack(v.merge(e,this),"before",this.selector)}},after:function(){if(!ut(this[0]))return this.domManip(arguments,!1,function(e){this.parentNode.insertBefore(e,this.nextSibling)});if(arguments.length){var e=v.clean(arguments);return this.pushStack(v.merge(this,e),"after",this.selector)}},remove:function(e,t){var n,r=0;for(;(n=this[r])!=null;r++)if(!e||v.filter(e,[n]).length)!t&&n.nodeType===1&&(v.cleanData(n.getElementsByTagName("*")),v.cleanData([n])),n.parentNode&&n.parentNode.removeChild(n);return this},empty:function(){var e,t=0;for(;(e=this[t])!=null;t++){e.nodeType===1&&v.cleanData(e.getElementsByTagName("*"));while(e.firstChild)e.removeChild(e.firstChild)}return this},clone:function(e,t){return e=e==null?!1:e,t=t==null?e:t,this.map(function(){return v.clone(this,e,t)})},html:function(e){return v.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return n.nodeType===1?n.innerHTML.replace(ht,""):t;if(typeof e=="string"&&!yt.test(e)&&(v.support.htmlSerialize||!wt.test(e))&&(v.support.leadingWhitespace||!pt.test(e))&&!Nt[(vt.exec(e)||["",""])[1].toLowerCase()]){e=e.replace(dt,"<$1></$2>");try{for(;r<i;r++)n=this[r]||{},n.nodeType===1&&(v.cleanData(n.getElementsByTagName("*")),n.innerHTML=e);n=0}catch(s){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(e){return ut(this[0])?this.length?this.pushStack(v(v.isFunction(e)?e():e),"replaceWith",e):this:v.isFunction(e)?this.each(function(t){var n=v(this),r=n.html();n.replaceWith(e.call(this,t,r))}):(typeof e!="string"&&(e=v(e).detach()),this.each(function(){var t=this.nextSibling,n=this.parentNode;v(this).remove(),t?v(t).before(e):v(n).append(e)}))},detach:function(e){return this.remove(e,!0)},domManip:function(e,n,r){e=[].concat.apply([],e);var i,s,o,u,a=0,f=e[0],l=[],c=this.length;if(!v.support.checkClone&&c>1&&typeof f=="string"&&St.test(f))return this.each(function(){v(this).domManip(e,n,r)});if(v.isFunction(f))return this.each(function(i){var s=v(this);e[0]=f.call(this,i,n?s.html():t),s.domManip(e,n,r)});if(this[0]){i=v.buildFragment(e,this,l),o=i.fragment,s=o.firstChild,o.childNodes.length===1&&(o=s);if(s){n=n&&v.nodeName(s,"tr");for(u=i.cacheable||c-1;a<c;a++)r.call(n&&v.nodeName(this[a],"table")?Lt(this[a],"tbody"):this[a],a===u?o:v.clone(o,!0,!0))}o=s=null,l.length&&v.each(l,function(e,t){t.src?v.ajax?v.ajax({url:t.src,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0}):v.error("no ajax"):v.globalEval((t.text||t.textContent||t.innerHTML||"").replace(Tt,"")),t.parentNode&&t.parentNode.removeChild(t)})}return this}}),v.buildFragment=function(e,n,r){var s,o,u,a=e[0];return n=n||i,n=!n.nodeType&&n[0]||n,n=n.ownerDocument||n,e.length===1&&typeof a=="string"&&a.length<512&&n===i&&a.charAt(0)==="<"&&!bt.test(a)&&(v.support.checkClone||!St.test(a))&&(v.support.html5Clone||!wt.test(a))&&(o=!0,s=v.fragments[a],u=s!==t),s||(s=n.createDocumentFragment(),v.clean(e,n,s,r),o&&(v.fragments[a]=u&&s)),{fragment:s,cacheable:o}},v.fragments={},v.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){v.fn[e]=function(n){var r,i=0,s=[],o=v(n),u=o.length,a=this.length===1&&this[0].parentNode;if((a==null||a&&a.nodeType===11&&a.childNodes.length===1)&&u===1)return o[t](this[0]),this;for(;i<u;i++)r=(i>0?this.clone(!0):this).get(),v(o[i])[t](r),s=s.concat(r);return this.pushStack(s,e,o.selector)}}),v.extend({clone:function(e,t,n){var r,i,s,o;v.support.html5Clone||v.isXMLDoc(e)||!wt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(kt.innerHTML=e.outerHTML,kt.removeChild(o=kt.firstChild));if((!v.support.noCloneEvent||!v.support.noCloneChecked)&&(e.nodeType===1||e.nodeType===11)&&!v.isXMLDoc(e)){Ot(e,o),r=Mt(e),i=Mt(o);for(s=0;r[s];++s)i[s]&&Ot(r[s],i[s])}if(t){At(e,o);if(n){r=Mt(e),i=Mt(o);for(s=0;r[s];++s)At(r[s],i[s])}}return r=i=null,o},clean:function(e,t,n,r){var s,o,u,a,f,l,c,h,p,d,m,g,y=t===i&&Ct,b=[];if(!t||typeof t.createDocumentFragment=="undefined")t=i;for(s=0;(u=e[s])!=null;s++){typeof u=="number"&&(u+="");if(!u)continue;if(typeof u=="string")if(!gt.test(u))u=t.createTextNode(u);else{y=y||lt(t),c=t.createElement("div"),y.appendChild(c),u=u.replace(dt,"<$1></$2>"),a=(vt.exec(u)||["",""])[1].toLowerCase(),f=Nt[a]||Nt._default,l=f[0],c.innerHTML=f[1]+u+f[2];while(l--)c=c.lastChild;if(!v.support.tbody){h=mt.test(u),p=a==="table"&&!h?c.firstChild&&c.firstChild.childNodes:f[1]==="<table>"&&!h?c.childNodes:[];for(o=p.length-1;o>=0;--o)v.nodeName(p[o],"tbody")&&!p[o].childNodes.length&&p[o].parentNode.removeChild(p[o])}!v.support.leadingWhitespace&&pt.test(u)&&c.insertBefore(t.createTextNode(pt.exec(u)[0]),c.firstChild),u=c.childNodes,c.parentNode.removeChild(c)}u.nodeType?b.push(u):v.merge(b,u)}c&&(u=c=y=null);if(!v.support.appendChecked)for(s=0;(u=b[s])!=null;s++)v.nodeName(u,"input")?_t(u):typeof u.getElementsByTagName!="undefined"&&v.grep(u.getElementsByTagName("input"),_t);if(n){m=function(e){if(!e.type||xt.test(e.type))return r?r.push(e.parentNode?e.parentNode.removeChild(e):e):n.appendChild(e)};for(s=0;(u=b[s])!=null;s++)if(!v.nodeName(u,"script")||!m(u))n.appendChild(u),typeof u.getElementsByTagName!="undefined"&&(g=v.grep(v.merge([],u.getElementsByTagName("script")),m),b.splice.apply(b,[s+1,0].concat(g)),s+=g.length)}return b},cleanData:function(e,t){var n,r,i,s,o=0,u=v.expando,a=v.cache,f=v.support.deleteExpando,l=v.event.special;for(;(i=e[o])!=null;o++)if(t||v.acceptData(i)){r=i[u],n=r&&a[r];if(n){if(n.events)for(s in n.events)l[s]?v.event.remove(i,s):v.removeEvent(i,s,n.handle);a[r]&&(delete a[r],f?delete i[u]:i.removeAttribute?i.removeAttribute(u):i[u]=null,v.deletedIds.push(r))}}}}),function(){var e,t;v.uaMatch=function(e){e=e.toLowerCase();var t=/(chrome)[ \/]([\w.]+)/.exec(e)||/(webkit)[ \/]([\w.]+)/.exec(e)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(e)||/(msie) ([\w.]+)/.exec(e)||e.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(e)||[];return{browser:t[1]||"",version:t[2]||"0"}},e=v.uaMatch(o.userAgent),t={},e.browser&&(t[e.browser]=!0,t.version=e.version),t.chrome?t.webkit=!0:t.webkit&&(t.safari=!0),v.browser=t,v.sub=function(){function e(t,n){return new e.fn.init(t,n)}v.extend(!0,e,this),e.superclass=this,e.fn=e.prototype=this(),e.fn.constructor=e,e.sub=this.sub,e.fn.init=function(r,i){return i&&i instanceof v&&!(i instanceof e)&&(i=e(i)),v.fn.init.call(this,r,i,t)},e.fn.init.prototype=e.fn;var t=e(i);return e}}();var Dt,Pt,Ht,Bt=/alpha\([^)]*\)/i,jt=/opacity=([^)]*)/,Ft=/^(top|right|bottom|left)$/,It=/^(none|table(?!-c[ea]).+)/,qt=/^margin/,Rt=new RegExp("^("+m+")(.*)$","i"),Ut=new RegExp("^("+m+")(?!px)[a-z%]+$","i"),zt=new RegExp("^([-+])=("+m+")","i"),Wt={BODY:"block"},Xt={position:"absolute",visibility:"hidden",display:"block"},Vt={letterSpacing:0,fontWeight:400},$t=["Top","Right","Bottom","Left"],Jt=["Webkit","O","Moz","ms"],Kt=v.fn.toggle;v.fn.extend({css:function(e,n){return v.access(this,function(e,n,r){return r!==t?v.style(e,n,r):v.css(e,n)},e,n,arguments.length>1)},show:function(){return Yt(this,!0)},hide:function(){return Yt(this)},toggle:function(e,t){var n=typeof e=="boolean";return v.isFunction(e)&&v.isFunction(t)?Kt.apply(this,arguments):this.each(function(){(n?e:Gt(this))?v(this).show():v(this).hide()})}}),v.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Dt(e,"opacity");return n===""?"1":n}}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":v.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(!e||e.nodeType===3||e.nodeType===8||!e.style)return;var s,o,u,a=v.camelCase(n),f=e.style;n=v.cssProps[a]||(v.cssProps[a]=Qt(f,a)),u=v.cssHooks[n]||v.cssHooks[a];if(r===t)return u&&"get"in u&&(s=u.get(e,!1,i))!==t?s:f[n];o=typeof r,o==="string"&&(s=zt.exec(r))&&(r=(s[1]+1)*s[2]+parseFloat(v.css(e,n)),o="number");if(r==null||o==="number"&&isNaN(r))return;o==="number"&&!v.cssNumber[a]&&(r+="px");if(!u||!("set"in u)||(r=u.set(e,r,i))!==t)try{f[n]=r}catch(l){}},css:function(e,n,r,i){var s,o,u,a=v.camelCase(n);return n=v.cssProps[a]||(v.cssProps[a]=Qt(e.style,a)),u=v.cssHooks[n]||v.cssHooks[a],u&&"get"in u&&(s=u.get(e,!0,i)),s===t&&(s=Dt(e,n)),s==="normal"&&n in Vt&&(s=Vt[n]),r||i!==t?(o=parseFloat(s),r||v.isNumeric(o)?o||0:s):s},swap:function(e,t,n){var r,i,s={};for(i in t)s[i]=e.style[i],e.style[i]=t[i];r=n.call(e);for(i in t)e.style[i]=s[i];return r}}),e.getComputedStyle?Dt=function(t,n){var r,i,s,o,u=e.getComputedStyle(t,null),a=t.style;return u&&(r=u.getPropertyValue(n)||u[n],r===""&&!v.contains(t.ownerDocument,t)&&(r=v.style(t,n)),Ut.test(r)&&qt.test(n)&&(i=a.width,s=a.minWidth,o=a.maxWidth,a.minWidth=a.maxWidth=a.width=r,r=u.width,a.width=i,a.minWidth=s,a.maxWidth=o)),r}:i.documentElement.currentStyle&&(Dt=function(e,t){var n,r,i=e.currentStyle&&e.currentStyle[t],s=e.style;return i==null&&s&&s[t]&&(i=s[t]),Ut.test(i)&&!Ft.test(t)&&(n=s.left,r=e.runtimeStyle&&e.runtimeStyle.left,r&&(e.runtimeStyle.left=e.currentStyle.left),s.left=t==="fontSize"?"1em":i,i=s.pixelLeft+"px",s.left=n,r&&(e.runtimeStyle.left=r)),i===""?"auto":i}),v.each(["height","width"],function(e,t){v.cssHooks[t]={get:function(e,n,r){if(n)return e.offsetWidth===0&&It.test(Dt(e,"display"))?v.swap(e,Xt,function(){return tn(e,t,r)}):tn(e,t,r)},set:function(e,n,r){return Zt(e,n,r?en(e,t,r,v.support.boxSizing&&v.css(e,"boxSizing")==="border-box"):0)}}}),v.support.opacity||(v.cssHooks.opacity={get:function(e,t){return jt.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=v.isNumeric(t)?"alpha(opacity="+t*100+")":"",s=r&&r.filter||n.filter||"";n.zoom=1;if(t>=1&&v.trim(s.replace(Bt,""))===""&&n.removeAttribute){n.removeAttribute("filter");if(r&&!r.filter)return}n.filter=Bt.test(s)?s.replace(Bt,i):s+" "+i}}),v(function(){v.support.reliableMarginRight||(v.cssHooks.marginRight={get:function(e,t){return v.swap(e,{display:"inline-block"},function(){if(t)return Dt(e,"marginRight")})}}),!v.support.pixelPosition&&v.fn.position&&v.each(["top","left"],function(e,t){v.cssHooks[t]={get:function(e,n){if(n){var r=Dt(e,t);return Ut.test(r)?v(e).position()[t]+"px":r}}}})}),v.expr&&v.expr.filters&&(v.expr.filters.hidden=function(e){return e.offsetWidth===0&&e.offsetHeight===0||!v.support.reliableHiddenOffsets&&(e.style&&e.style.display||Dt(e,"display"))==="none"},v.expr.filters.visible=function(e){return!v.expr.filters.hidden(e)}),v.each({margin:"",padding:"",border:"Width"},function(e,t){v.cssHooks[e+t]={expand:function(n){var r,i=typeof n=="string"?n.split(" "):[n],s={};for(r=0;r<4;r++)s[e+$t[r]+t]=i[r]||i[r-2]||i[0];return s}},qt.test(e)||(v.cssHooks[e+t].set=Zt)});var rn=/%20/g,sn=/\[\]$/,on=/\r?\n/g,un=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,an=/^(?:select|textarea)/i;v.fn.extend({serialize:function(){return v.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?v.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||an.test(this.nodeName)||un.test(this.type))}).map(function(e,t){var n=v(this).val();return n==null?null:v.isArray(n)?v.map(n,function(e,n){return{name:t.name,value:e.replace(on,"\r\n")}}):{name:t.name,value:n.replace(on,"\r\n")}}).get()}}),v.param=function(e,n){var r,i=[],s=function(e,t){t=v.isFunction(t)?t():t==null?"":t,i[i.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};n===t&&(n=v.ajaxSettings&&v.ajaxSettings.traditional);if(v.isArray(e)||e.jquery&&!v.isPlainObject(e))v.each(e,function(){s(this.name,this.value)});else for(r in e)fn(r,e[r],n,s);return i.join("&").replace(rn,"+")};var ln,cn,hn=/#.*$/,pn=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,dn=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,vn=/^(?:GET|HEAD)$/,mn=/^\/\//,gn=/\?/,yn=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,bn=/([?&])_=[^&]*/,wn=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,En=v.fn.load,Sn={},xn={},Tn=["*/"]+["*"];try{cn=s.href}catch(Nn){cn=i.createElement("a"),cn.href="",cn=cn.href}ln=wn.exec(cn.toLowerCase())||[],v.fn.load=function(e,n,r){if(typeof e!="string"&&En)return En.apply(this,arguments);if(!this.length)return this;var i,s,o,u=this,a=e.indexOf(" ");return a>=0&&(i=e.slice(a,e.length),e=e.slice(0,a)),v.isFunction(n)?(r=n,n=t):n&&typeof n=="object"&&(s="POST"),v.ajax({url:e,type:s,dataType:"html",data:n,complete:function(e,t){r&&u.each(r,o||[e.responseText,t,e])}}).done(function(e){o=arguments,u.html(i?v("<div>").append(e.replace(yn,"")).find(i):e)}),this},v.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(e,t){v.fn[t]=function(e){return this.on(t,e)}}),v.each(["get","post"],function(e,n){v[n]=function(e,r,i,s){return v.isFunction(r)&&(s=s||i,i=r,r=t),v.ajax({type:n,url:e,data:r,success:i,dataType:s})}}),v.extend({getScript:function(e,n){return v.get(e,t,n,"script")},getJSON:function(e,t,n){return v.get(e,t,n,"json")},ajaxSetup:function(e,t){return t?Ln(e,v.ajaxSettings):(t=e,e=v.ajaxSettings),Ln(e,t),e},ajaxSettings:{url:cn,isLocal:dn.test(ln[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":Tn},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":e.String,"text html":!0,"text json":v.parseJSON,"text xml":v.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:Cn(Sn),ajaxTransport:Cn(xn),ajax:function(e,n){function T(e,n,s,a){var l,y,b,w,S,T=n;if(E===2)return;E=2,u&&clearTimeout(u),o=t,i=a||"",x.readyState=e>0?4:0,s&&(w=An(c,x,s));if(e>=200&&e<300||e===304)c.ifModified&&(S=x.getResponseHeader("Last-Modified"),S&&(v.lastModified[r]=S),S=x.getResponseHeader("Etag"),S&&(v.etag[r]=S)),e===304?(T="notmodified",l=!0):(l=On(c,w),T=l.state,y=l.data,b=l.error,l=!b);else{b=T;if(!T||e)T="error",e<0&&(e=0)}x.status=e,x.statusText=(n||T)+"",l?d.resolveWith(h,[y,T,x]):d.rejectWith(h,[x,T,b]),x.statusCode(g),g=t,f&&p.trigger("ajax"+(l?"Success":"Error"),[x,c,l?y:b]),m.fireWith(h,[x,T]),f&&(p.trigger("ajaxComplete",[x,c]),--v.active||v.event.trigger("ajaxStop"))}typeof e=="object"&&(n=e,e=t),n=n||{};var r,i,s,o,u,a,f,l,c=v.ajaxSetup({},n),h=c.context||c,p=h!==c&&(h.nodeType||h instanceof v)?v(h):v.event,d=v.Deferred(),m=v.Callbacks("once memory"),g=c.statusCode||{},b={},w={},E=0,S="canceled",x={readyState:0,setRequestHeader:function(e,t){if(!E){var n=e.toLowerCase();e=w[n]=w[n]||e,b[e]=t}return this},getAllResponseHeaders:function(){return E===2?i:null},getResponseHeader:function(e){var n;if(E===2){if(!s){s={};while(n=pn.exec(i))s[n[1].toLowerCase()]=n[2]}n=s[e.toLowerCase()]}return n===t?null:n},overrideMimeType:function(e){return E||(c.mimeType=e),this},abort:function(e){return e=e||S,o&&o.abort(e),T(0,e),this}};d.promise(x),x.success=x.done,x.error=x.fail,x.complete=m.add,x.statusCode=function(e){if(e){var t;if(E<2)for(t in e)g[t]=[g[t],e[t]];else t=e[x.status],x.always(t)}return this},c.url=((e||c.url)+"").replace(hn,"").replace(mn,ln[1]+"//"),c.dataTypes=v.trim(c.dataType||"*").toLowerCase().split(y),c.crossDomain==null&&(a=wn.exec(c.url.toLowerCase()),c.crossDomain=!(!a||a[1]===ln[1]&&a[2]===ln[2]&&(a[3]||(a[1]==="http:"?80:443))==(ln[3]||(ln[1]==="http:"?80:443)))),c.data&&c.processData&&typeof c.data!="string"&&(c.data=v.param(c.data,c.traditional)),kn(Sn,c,n,x);if(E===2)return x;f=c.global,c.type=c.type.toUpperCase(),c.hasContent=!vn.test(c.type),f&&v.active++===0&&v.event.trigger("ajaxStart");if(!c.hasContent){c.data&&(c.url+=(gn.test(c.url)?"&":"?")+c.data,delete c.data),r=c.url;if(c.cache===!1){var N=v.now(),C=c.url.replace(bn,"$1_="+N);c.url=C+(C===c.url?(gn.test(c.url)?"&":"?")+"_="+N:"")}}(c.data&&c.hasContent&&c.contentType!==!1||n.contentType)&&x.setRequestHeader("Content-Type",c.contentType),c.ifModified&&(r=r||c.url,v.lastModified[r]&&x.setRequestHeader("If-Modified-Since",v.lastModified[r]),v.etag[r]&&x.setRequestHeader("If-None-Match",v.etag[r])),x.setRequestHeader("Accept",c.dataTypes[0]&&c.accepts[c.dataTypes[0]]?c.accepts[c.dataTypes[0]]+(c.dataTypes[0]!=="*"?", "+Tn+"; q=0.01":""):c.accepts["*"]);for(l in c.headers)x.setRequestHeader(l,c.headers[l]);if(!c.beforeSend||c.beforeSend.call(h,x,c)!==!1&&E!==2){S="abort";for(l in{success:1,error:1,complete:1})x[l](c[l]);o=kn(xn,c,n,x);if(!o)T(-1,"No Transport");else{x.readyState=1,f&&p.trigger("ajaxSend",[x,c]),c.async&&c.timeout>0&&(u=setTimeout(function(){x.abort("timeout")},c.timeout));try{E=1,o.send(b,T)}catch(k){if(!(E<2))throw k;T(-1,k)}}return x}return x.abort()},active:0,lastModified:{},etag:{}});var Mn=[],_n=/\?/,Dn=/(=)\?(?=&|$)|\?\?/,Pn=v.now();v.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Mn.pop()||v.expando+"_"+Pn++;return this[e]=!0,e}}),v.ajaxPrefilter("json jsonp",function(n,r,i){var s,o,u,a=n.data,f=n.url,l=n.jsonp!==!1,c=l&&Dn.test(f),h=l&&!c&&typeof a=="string"&&!(n.contentType||"").indexOf("application/x-www-form-urlencoded")&&Dn.test(a);if(n.dataTypes[0]==="jsonp"||c||h)return s=n.jsonpCallback=v.isFunction(n.jsonpCallback)?n.jsonpCallback():n.jsonpCallback,o=e[s],c?n.url=f.replace(Dn,"$1"+s):h?n.data=a.replace(Dn,"$1"+s):l&&(n.url+=(_n.test(f)?"&":"?")+n.jsonp+"="+s),n.converters["script json"]=function(){return u||v.error(s+" was not called"),u[0]},n.dataTypes[0]="json",e[s]=function(){u=arguments},i.always(function(){e[s]=o,n[s]&&(n.jsonpCallback=r.jsonpCallback,Mn.push(s)),u&&v.isFunction(o)&&o(u[0]),u=o=t}),"script"}),v.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(e){return v.globalEval(e),e}}}),v.ajaxPrefilter("script",function(e){e.cache===t&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),v.ajaxTransport("script",function(e){if(e.crossDomain){var n,r=i.head||i.getElementsByTagName("head")[0]||i.documentElement;return{send:function(s,o){n=i.createElement("script"),n.async="async",e.scriptCharset&&(n.charset=e.scriptCharset),n.src=e.url,n.onload=n.onreadystatechange=function(e,i){if(i||!n.readyState||/loaded|complete/.test(n.readyState))n.onload=n.onreadystatechange=null,r&&n.parentNode&&r.removeChild(n),n=t,i||o(200,"success")},r.insertBefore(n,r.firstChild)},abort:function(){n&&n.onload(0,1)}}}});var Hn,Bn=e.ActiveXObject?function(){for(var e in Hn)Hn[e](0,1)}:!1,jn=0;v.ajaxSettings.xhr=e.ActiveXObject?function(){return!this.isLocal&&Fn()||In()}:Fn,function(e){v.extend(v.support,{ajax:!!e,cors:!!e&&"withCredentials"in e})}(v.ajaxSettings.xhr()),v.support.ajax&&v.ajaxTransport(function(n){if(!n.crossDomain||v.support.cors){var r;return{send:function(i,s){var o,u,a=n.xhr();n.username?a.open(n.type,n.url,n.async,n.username,n.password):a.open(n.type,n.url,n.async);if(n.xhrFields)for(u in n.xhrFields)a[u]=n.xhrFields[u];n.mimeType&&a.overrideMimeType&&a.overrideMimeType(n.mimeType),!n.crossDomain&&!i["X-Requested-With"]&&(i["X-Requested-With"]="XMLHttpRequest");try{for(u in i)a.setRequestHeader(u,i[u])}catch(f){}a.send(n.hasContent&&n.data||null),r=function(e,i){var u,f,l,c,h;try{if(r&&(i||a.readyState===4)){r=t,o&&(a.onreadystatechange=v.noop,Bn&&delete Hn[o]);if(i)a.readyState!==4&&a.abort();else{u=a.status,l=a.getAllResponseHeaders(),c={},h=a.responseXML,h&&h.documentElement&&(c.xml=h);try{c.text=a.responseText}catch(p){}try{f=a.statusText}catch(p){f=""}!u&&n.isLocal&&!n.crossDomain?u=c.text?200:404:u===1223&&(u=204)}}}catch(d){i||s(-1,d)}c&&s(u,f,c,l)},n.async?a.readyState===4?setTimeout(r,0):(o=++jn,Bn&&(Hn||(Hn={},v(e).unload(Bn)),Hn[o]=r),a.onreadystatechange=r):r()},abort:function(){r&&r(0,1)}}}});var qn,Rn,Un=/^(?:toggle|show|hide)$/,zn=new RegExp("^(?:([-+])=|)("+m+")([a-z%]*)$","i"),Wn=/queueHooks$/,Xn=[Gn],Vn={"*":[function(e,t){var n,r,i=this.createTween(e,t),s=zn.exec(t),o=i.cur(),u=+o||0,a=1,f=20;if(s){n=+s[2],r=s[3]||(v.cssNumber[e]?"":"px");if(r!=="px"&&u){u=v.css(i.elem,e,!0)||n||1;do a=a||".5",u/=a,v.style(i.elem,e,u+r);while(a!==(a=i.cur()/o)&&a!==1&&--f)}i.unit=r,i.start=u,i.end=s[1]?u+(s[1]+1)*n:n}return i}]};v.Animation=v.extend(Kn,{tweener:function(e,t){v.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");var n,r=0,i=e.length;for(;r<i;r++)n=e[r],Vn[n]=Vn[n]||[],Vn[n].unshift(t)},prefilter:function(e,t){t?Xn.unshift(e):Xn.push(e)}}),v.Tween=Yn,Yn.prototype={constructor:Yn,init:function(e,t,n,r,i,s){this.elem=e,this.prop=n,this.easing=i||"swing",this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=s||(v.cssNumber[n]?"":"px")},cur:function(){var e=Yn.propHooks[this.prop];return e&&e.get?e.get(this):Yn.propHooks._default.get(this)},run:function(e){var t,n=Yn.propHooks[this.prop];return this.options.duration?this.pos=t=v.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):Yn.propHooks._default.set(this),this}},Yn.prototype.init.prototype=Yn.prototype,Yn.propHooks={_default:{get:function(e){var t;return e.elem[e.prop]==null||!!e.elem.style&&e.elem.style[e.prop]!=null?(t=v.css(e.elem,e.prop,!1,""),!t||t==="auto"?0:t):e.elem[e.prop]},set:function(e){v.fx.step[e.prop]?v.fx.step[e.prop](e):e.elem.style&&(e.elem.style[v.cssProps[e.prop]]!=null||v.cssHooks[e.prop])?v.style(e.elem,e.prop,e.now+e.unit):e.elem[e.prop]=e.now}}},Yn.propHooks.scrollTop=Yn.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},v.each(["toggle","show","hide"],function(e,t){var n=v.fn[t];v.fn[t]=function(r,i,s){return r==null||typeof r=="boolean"||!e&&v.isFunction(r)&&v.isFunction(i)?n.apply(this,arguments):this.animate(Zn(t,!0),r,i,s)}}),v.fn.extend({fadeTo:function(e,t,n,r){return this.filter(Gt).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=v.isEmptyObject(e),s=v.speed(t,n,r),o=function(){var t=Kn(this,v.extend({},e),s);i&&t.stop(!0)};return i||s.queue===!1?this.each(o):this.queue(s.queue,o)},stop:function(e,n,r){var i=function(e){var t=e.stop;delete e.stop,t(r)};return typeof e!="string"&&(r=n,n=e,e=t),n&&e!==!1&&this.queue(e||"fx",[]),this.each(function(){var t=!0,n=e!=null&&e+"queueHooks",s=v.timers,o=v._data(this);if(n)o[n]&&o[n].stop&&i(o[n]);else for(n in o)o[n]&&o[n].stop&&Wn.test(n)&&i(o[n]);for(n=s.length;n--;)s[n].elem===this&&(e==null||s[n].queue===e)&&(s[n].anim.stop(r),t=!1,s.splice(n,1));(t||!r)&&v.dequeue(this,e)})}}),v.each({slideDown:Zn("show"),slideUp:Zn("hide"),slideToggle:Zn("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,t){v.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),v.speed=function(e,t,n){var r=e&&typeof e=="object"?v.extend({},e):{complete:n||!n&&t||v.isFunction(e)&&e,duration:e,easing:n&&t||t&&!v.isFunction(t)&&t};r.duration=v.fx.off?0:typeof r.duration=="number"?r.duration:r.duration in v.fx.speeds?v.fx.speeds[r.duration]:v.fx.speeds._default;if(r.queue==null||r.queue===!0)r.queue="fx";return r.old=r.complete,r.complete=function(){v.isFunction(r.old)&&r.old.call(this),r.queue&&v.dequeue(this,r.queue)},r},v.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2}},v.timers=[],v.fx=Yn.prototype.init,v.fx.tick=function(){var e,n=v.timers,r=0;qn=v.now();for(;r<n.length;r++)e=n[r],!e()&&n[r]===e&&n.splice(r--,1);n.length||v.fx.stop(),qn=t},v.fx.timer=function(e){e()&&v.timers.push(e)&&!Rn&&(Rn=setInterval(v.fx.tick,v.fx.interval))},v.fx.interval=13,v.fx.stop=function(){clearInterval(Rn),Rn=null},v.fx.speeds={slow:600,fast:200,_default:400},v.fx.step={},v.expr&&v.expr.filters&&(v.expr.filters.animated=function(e){return v.grep(v.timers,function(t){return e===t.elem}).length});var er=/^(?:body|html)$/i;v.fn.offset=function(e){if(arguments.length)return e===t?this:this.each(function(t){v.offset.setOffset(this,e,t)});var n,r,i,s,o,u,a,f={top:0,left:0},l=this[0],c=l&&l.ownerDocument;if(!c)return;return(r=c.body)===l?v.offset.bodyOffset(l):(n=c.documentElement,v.contains(n,l)?(typeof l.getBoundingClientRect!="undefined"&&(f=l.getBoundingClientRect()),i=tr(c),s=n.clientTop||r.clientTop||0,o=n.clientLeft||r.clientLeft||0,u=i.pageYOffset||n.scrollTop,a=i.pageXOffset||n.scrollLeft,{top:f.top+u-s,left:f.left+a-o}):f)},v.offset={bodyOffset:function(e){var t=e.offsetTop,n=e.offsetLeft;return v.support.doesNotIncludeMarginInBodyOffset&&(t+=parseFloat(v.css(e,"marginTop"))||0,n+=parseFloat(v.css(e,"marginLeft"))||0),{top:t,left:n}},setOffset:function(e,t,n){var r=v.css(e,"position");r==="static"&&(e.style.position="relative");var i=v(e),s=i.offset(),o=v.css(e,"top"),u=v.css(e,"left"),a=(r==="absolute"||r==="fixed")&&v.inArray("auto",[o,u])>-1,f={},l={},c,h;a?(l=i.position(),c=l.top,h=l.left):(c=parseFloat(o)||0,h=parseFloat(u)||0),v.isFunction(t)&&(t=t.call(e,n,s)),t.top!=null&&(f.top=t.top-s.top+c),t.left!=null&&(f.left=t.left-s.left+h),"using"in t?t.using.call(e,f):i.css(f)}},v.fn.extend({position:function(){if(!this[0])return;var e=this[0],t=this.offsetParent(),n=this.offset(),r=er.test(t[0].nodeName)?{top:0,left:0}:t.offset();return n.top-=parseFloat(v.css(e,"marginTop"))||0,n.left-=parseFloat(v.css(e,"marginLeft"))||0,r.top+=parseFloat(v.css(t[0],"borderTopWidth"))||0,r.left+=parseFloat(v.css(t[0],"borderLeftWidth"))||0,{top:n.top-r.top,left:n.left-r.left}},offsetParent:function(){return this.map(function(){var e=this.offsetParent||i.body;while(e&&!er.test(e.nodeName)&&v.css(e,"position")==="static")e=e.offsetParent;return e||i.body})}}),v.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,n){var r=/Y/.test(n);v.fn[e]=function(i){return v.access(this,function(e,i,s){var o=tr(e);if(s===t)return o?n in o?o[n]:o.document.documentElement[i]:e[i];o?o.scrollTo(r?v(o).scrollLeft():s,r?s:v(o).scrollTop()):e[i]=s},e,i,arguments.length,null)}}),v.each({Height:"height",Width:"width"},function(e,n){v.each({padding:"inner"+e,content:n,"":"outer"+e},function(r,i){v.fn[i]=function(i,s){var o=arguments.length&&(r||typeof i!="boolean"),u=r||(i===!0||s===!0?"margin":"border");return v.access(this,function(n,r,i){var s;return v.isWindow(n)?n.document.documentElement["client"+e]:n.nodeType===9?(s=n.documentElement,Math.max(n.body["scroll"+e],s["scroll"+e],n.body["offset"+e],s["offset"+e],s["client"+e])):i===t?v.css(n,r,i,u):v.style(n,r,i,u)},n,o?i:t,o,null)}})}),e.jQuery=e.$=v,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return v})})(window);
\ No newline at end of file diff --git a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/_static/pygments.css b/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/_static/pygments.css deleted file mode 100644 index d79caa151..000000000 --- a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/_static/pygments.css +++ /dev/null @@ -1,62 +0,0 @@ -.highlight .hll { background-color: #ffffcc } -.highlight { background: #eeffcc; } -.highlight .c { color: #408090; font-style: italic } /* Comment */ -.highlight .err { border: 1px solid #FF0000 } /* Error */ -.highlight .k { color: #007020; font-weight: bold } /* Keyword */ -.highlight .o { color: #666666 } /* Operator */ -.highlight .cm { color: #408090; font-style: italic } /* Comment.Multiline */ -.highlight .cp { color: #007020 } /* Comment.Preproc */ -.highlight .c1 { color: #408090; font-style: italic } /* Comment.Single */ -.highlight .cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */ -.highlight .gd { color: #A00000 } /* Generic.Deleted */ -.highlight .ge { font-style: italic } /* Generic.Emph */ -.highlight .gr { color: #FF0000 } /* Generic.Error */ -.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ -.highlight .gi { color: #00A000 } /* Generic.Inserted */ -.highlight .go { color: #333333 } /* Generic.Output */ -.highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ -.highlight .gs { font-weight: bold } /* Generic.Strong */ -.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ -.highlight .gt { color: #0044DD } /* Generic.Traceback */ -.highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */ -.highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */ -.highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */ -.highlight .kp { color: #007020 } /* Keyword.Pseudo */ -.highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */ -.highlight .kt { color: #902000 } /* Keyword.Type */ -.highlight .m { color: #208050 } /* Literal.Number */ -.highlight .s { color: #4070a0 } /* Literal.String */ -.highlight .na { color: #4070a0 } /* Name.Attribute */ -.highlight .nb { color: #007020 } /* Name.Builtin */ -.highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */ -.highlight .no { color: #60add5 } /* Name.Constant */ -.highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */ -.highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */ -.highlight .ne { color: #007020 } /* Name.Exception */ -.highlight .nf { color: #06287e } /* Name.Function */ -.highlight .nl { color: #002070; font-weight: bold } /* Name.Label */ -.highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ -.highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */ -.highlight .nv { color: #bb60d5 } /* Name.Variable */ -.highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */ -.highlight .w { color: #bbbbbb } /* Text.Whitespace */ -.highlight .mf { color: #208050 } /* Literal.Number.Float */ -.highlight .mh { color: #208050 } /* Literal.Number.Hex */ -.highlight .mi { color: #208050 } /* Literal.Number.Integer */ -.highlight .mo { color: #208050 } /* Literal.Number.Oct */ -.highlight .sb { color: #4070a0 } /* Literal.String.Backtick */ -.highlight .sc { color: #4070a0 } /* Literal.String.Char */ -.highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */ -.highlight .s2 { color: #4070a0 } /* Literal.String.Double */ -.highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */ -.highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */ -.highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */ -.highlight .sx { color: #c65d09 } /* Literal.String.Other */ -.highlight .sr { color: #235388 } /* Literal.String.Regex */ -.highlight .s1 { color: #4070a0 } /* Literal.String.Single */ -.highlight .ss { color: #517918 } /* Literal.String.Symbol */ -.highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */ -.highlight .vc { color: #bb60d5 } /* Name.Variable.Class */ -.highlight .vg { color: #bb60d5 } /* Name.Variable.Global */ -.highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */ -.highlight .il { color: #208050 } /* Literal.Number.Integer.Long */
\ No newline at end of file diff --git a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/_static/searchtools.js b/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/_static/searchtools.js deleted file mode 100644 index f5c7e5fee..000000000 --- a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/_static/searchtools.js +++ /dev/null @@ -1,622 +0,0 @@ -/* - * searchtools.js_t - * ~~~~~~~~~~~~~~~~ - * - * Sphinx JavaScript utilties for the full-text search. - * - * :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - - -/** - * Porter Stemmer - */ -var Stemmer = function() { - - var step2list = { - ational: 'ate', - tional: 'tion', - enci: 'ence', - anci: 'ance', - izer: 'ize', - bli: 'ble', - alli: 'al', - entli: 'ent', - eli: 'e', - ousli: 'ous', - ization: 'ize', - ation: 'ate', - ator: 'ate', - alism: 'al', - iveness: 'ive', - fulness: 'ful', - ousness: 'ous', - aliti: 'al', - iviti: 'ive', - biliti: 'ble', - logi: 'log' - }; - - var step3list = { - icate: 'ic', - ative: '', - alize: 'al', - iciti: 'ic', - ical: 'ic', - ful: '', - ness: '' - }; - - var c = "[^aeiou]"; // consonant - var v = "[aeiouy]"; // vowel - var C = c + "[^aeiouy]*"; // consonant sequence - var V = v + "[aeiou]*"; // vowel sequence - - var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 - var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 - var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 - var s_v = "^(" + C + ")?" + v; // vowel in stem - - this.stemWord = function (w) { - var stem; - var suffix; - var firstch; - var origword = w; - - if (w.length < 3) - return w; - - var re; - var re2; - var re3; - var re4; - - firstch = w.substr(0,1); - if (firstch == "y") - w = firstch.toUpperCase() + w.substr(1); - - // Step 1a - re = /^(.+?)(ss|i)es$/; - re2 = /^(.+?)([^s])s$/; - - if (re.test(w)) - w = w.replace(re,"$1$2"); - else if (re2.test(w)) - w = w.replace(re2,"$1$2"); - - // Step 1b - re = /^(.+?)eed$/; - re2 = /^(.+?)(ed|ing)$/; - if (re.test(w)) { - var fp = re.exec(w); - re = new RegExp(mgr0); - if (re.test(fp[1])) { - re = /.$/; - w = w.replace(re,""); - } - } - else if (re2.test(w)) { - var fp = re2.exec(w); - stem = fp[1]; - re2 = new RegExp(s_v); - if (re2.test(stem)) { - w = stem; - re2 = /(at|bl|iz)$/; - re3 = new RegExp("([^aeiouylsz])\\1$"); - re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); - if (re2.test(w)) - w = w + "e"; - else if (re3.test(w)) { - re = /.$/; - w = w.replace(re,""); - } - else if (re4.test(w)) - w = w + "e"; - } - } - - // Step 1c - re = /^(.+?)y$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - re = new RegExp(s_v); - if (re.test(stem)) - w = stem + "i"; - } - - // Step 2 - re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - suffix = fp[2]; - re = new RegExp(mgr0); - if (re.test(stem)) - w = stem + step2list[suffix]; - } - - // Step 3 - re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - suffix = fp[2]; - re = new RegExp(mgr0); - if (re.test(stem)) - w = stem + step3list[suffix]; - } - - // Step 4 - re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; - re2 = /^(.+?)(s|t)(ion)$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - re = new RegExp(mgr1); - if (re.test(stem)) - w = stem; - } - else if (re2.test(w)) { - var fp = re2.exec(w); - stem = fp[1] + fp[2]; - re2 = new RegExp(mgr1); - if (re2.test(stem)) - w = stem; - } - - // Step 5 - re = /^(.+?)e$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - re = new RegExp(mgr1); - re2 = new RegExp(meq1); - re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); - if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) - w = stem; - } - re = /ll$/; - re2 = new RegExp(mgr1); - if (re.test(w) && re2.test(w)) { - re = /.$/; - w = w.replace(re,""); - } - - // and turn initial Y back to y - if (firstch == "y") - w = firstch.toLowerCase() + w.substr(1); - return w; - } -} - - - -/** - * Simple result scoring code. - */ -var Scorer = { - // Implement the following function to further tweak the score for each result - // The function takes a result array [filename, title, anchor, descr, score] - // and returns the new score. - /* - score: function(result) { - return result[4]; - }, - */ - - // query matches the full name of an object - objNameMatch: 11, - // or matches in the last dotted part of the object name - objPartialMatch: 6, - // Additive scores depending on the priority of the object - objPrio: {0: 15, // used to be importantResults - 1: 5, // used to be objectResults - 2: -5}, // used to be unimportantResults - // Used when the priority is not in the mapping. - objPrioDefault: 0, - - // query found in title - title: 15, - // query found in terms - term: 5 -}; - - -/** - * Search Module - */ -var Search = { - - _index : null, - _queued_query : null, - _pulse_status : -1, - - init : function() { - var params = $.getQueryParameters(); - if (params.q) { - var query = params.q[0]; - $('input[name="q"]')[0].value = query; - this.performSearch(query); - } - }, - - loadIndex : function(url) { - $.ajax({type: "GET", url: url, data: null, - dataType: "script", cache: true, - complete: function(jqxhr, textstatus) { - if (textstatus != "success") { - document.getElementById("searchindexloader").src = url; - } - }}); - }, - - setIndex : function(index) { - var q; - this._index = index; - if ((q = this._queued_query) !== null) { - this._queued_query = null; - Search.query(q); - } - }, - - hasIndex : function() { - return this._index !== null; - }, - - deferQuery : function(query) { - this._queued_query = query; - }, - - stopPulse : function() { - this._pulse_status = 0; - }, - - startPulse : function() { - if (this._pulse_status >= 0) - return; - function pulse() { - var i; - Search._pulse_status = (Search._pulse_status + 1) % 4; - var dotString = ''; - for (i = 0; i < Search._pulse_status; i++) - dotString += '.'; - Search.dots.text(dotString); - if (Search._pulse_status > -1) - window.setTimeout(pulse, 500); - } - pulse(); - }, - - /** - * perform a search for something (or wait until index is loaded) - */ - performSearch : function(query) { - // create the required interface elements - this.out = $('#search-results'); - this.title = $('<h2>' + _('Searching') + '</h2>').appendTo(this.out); - this.dots = $('<span></span>').appendTo(this.title); - this.status = $('<p style="display: none"></p>').appendTo(this.out); - this.output = $('<ul class="search"/>').appendTo(this.out); - - $('#search-progress').text(_('Preparing search...')); - this.startPulse(); - - // index already loaded, the browser was quick! - if (this.hasIndex()) - this.query(query); - else - this.deferQuery(query); - }, - - /** - * execute search (requires search index to be loaded) - */ - query : function(query) { - var i; - var stopwords = ["a","and","are","as","at","be","but","by","for","if","in","into","is","it","near","no","not","of","on","or","such","that","the","their","then","there","these","they","this","to","was","will","with"]; - - // stem the searchterms and add them to the correct list - var stemmer = new Stemmer(); - var searchterms = []; - var excluded = []; - var hlterms = []; - var tmp = query.split(/\s+/); - var objectterms = []; - for (i = 0; i < tmp.length; i++) { - if (tmp[i] !== "") { - objectterms.push(tmp[i].toLowerCase()); - } - - if ($u.indexOf(stopwords, tmp[i].toLowerCase()) != -1 || tmp[i].match(/^\d+$/) || - tmp[i] === "") { - // skip this "word" - continue; - } - // stem the word - var word = stemmer.stemWord(tmp[i].toLowerCase()); - var toAppend; - // select the correct list - if (word[0] == '-') { - toAppend = excluded; - word = word.substr(1); - } - else { - toAppend = searchterms; - hlterms.push(tmp[i].toLowerCase()); - } - // only add if not already in the list - if (!$u.contains(toAppend, word)) - toAppend.push(word); - } - var highlightstring = '?highlight=' + $.urlencode(hlterms.join(" ")); - - // console.debug('SEARCH: searching for:'); - // console.info('required: ', searchterms); - // console.info('excluded: ', excluded); - - // prepare search - var terms = this._index.terms; - var titleterms = this._index.titleterms; - - // array of [filename, title, anchor, descr, score] - var results = []; - $('#search-progress').empty(); - - // lookup as object - for (i = 0; i < objectterms.length; i++) { - var others = [].concat(objectterms.slice(0, i), - objectterms.slice(i+1, objectterms.length)); - results = results.concat(this.performObjectSearch(objectterms[i], others)); - } - - // lookup as search terms in fulltext - results = results.concat(this.performTermsSearch(searchterms, excluded, terms, Scorer.term)) - .concat(this.performTermsSearch(searchterms, excluded, titleterms, Scorer.title)); - - // let the scorer override scores with a custom scoring function - if (Scorer.score) { - for (i = 0; i < results.length; i++) - results[i][4] = Scorer.score(results[i]); - } - - // now sort the results by score (in opposite order of appearance, since the - // display function below uses pop() to retrieve items) and then - // alphabetically - results.sort(function(a, b) { - var left = a[4]; - var right = b[4]; - if (left > right) { - return 1; - } else if (left < right) { - return -1; - } else { - // same score: sort alphabetically - left = a[1].toLowerCase(); - right = b[1].toLowerCase(); - return (left > right) ? -1 : ((left < right) ? 1 : 0); - } - }); - - // for debugging - //Search.lastresults = results.slice(); // a copy - //console.info('search results:', Search.lastresults); - - // print the results - var resultCount = results.length; - function displayNextItem() { - // results left, load the summary and display it - if (results.length) { - var item = results.pop(); - var listItem = $('<li style="display:none"></li>'); - if (DOCUMENTATION_OPTIONS.FILE_SUFFIX === '') { - // dirhtml builder - var dirname = item[0] + '/'; - if (dirname.match(/\/index\/$/)) { - dirname = dirname.substring(0, dirname.length-6); - } else if (dirname == 'index/') { - dirname = ''; - } - listItem.append($('<a/>').attr('href', - DOCUMENTATION_OPTIONS.URL_ROOT + dirname + - highlightstring + item[2]).html(item[1])); - } else { - // normal html builders - listItem.append($('<a/>').attr('href', - item[0] + DOCUMENTATION_OPTIONS.FILE_SUFFIX + - highlightstring + item[2]).html(item[1])); - } - if (item[3]) { - listItem.append($('<span> (' + item[3] + ')</span>')); - Search.output.append(listItem); - listItem.slideDown(5, function() { - displayNextItem(); - }); - } else if (DOCUMENTATION_OPTIONS.HAS_SOURCE) { - $.ajax({url: DOCUMENTATION_OPTIONS.URL_ROOT + '_sources/' + item[0] + '.txt', - dataType: "text", - complete: function(jqxhr, textstatus) { - var data = jqxhr.responseText; - if (data !== '') { - listItem.append(Search.makeSearchSummary(data, searchterms, hlterms)); - } - Search.output.append(listItem); - listItem.slideDown(5, function() { - displayNextItem(); - }); - }}); - } else { - // no source available, just display title - Search.output.append(listItem); - listItem.slideDown(5, function() { - displayNextItem(); - }); - } - } - // search finished, update title and status message - else { - Search.stopPulse(); - Search.title.text(_('Search Results')); - if (!resultCount) - Search.status.text(_('Your search did not match any documents. Please make sure that all words are spelled correctly and that you\'ve selected enough categories.')); - else - Search.status.text(_('Search finished, found %s page(s) matching the search query.').replace('%s', resultCount)); - Search.status.fadeIn(500); - } - } - displayNextItem(); - }, - - /** - * search for object names - */ - performObjectSearch : function(object, otherterms) { - var filenames = this._index.filenames; - var objects = this._index.objects; - var objnames = this._index.objnames; - var titles = this._index.titles; - - var i; - var results = []; - - for (var prefix in objects) { - for (var name in objects[prefix]) { - var fullname = (prefix ? prefix + '.' : '') + name; - if (fullname.toLowerCase().indexOf(object) > -1) { - var score = 0; - var parts = fullname.split('.'); - // check for different match types: exact matches of full name or - // "last name" (i.e. last dotted part) - if (fullname == object || parts[parts.length - 1] == object) { - score += Scorer.objNameMatch; - // matches in last name - } else if (parts[parts.length - 1].indexOf(object) > -1) { - score += Scorer.objPartialMatch; - } - var match = objects[prefix][name]; - var objname = objnames[match[1]][2]; - var title = titles[match[0]]; - // If more than one term searched for, we require other words to be - // found in the name/title/description - if (otherterms.length > 0) { - var haystack = (prefix + ' ' + name + ' ' + - objname + ' ' + title).toLowerCase(); - var allfound = true; - for (i = 0; i < otherterms.length; i++) { - if (haystack.indexOf(otherterms[i]) == -1) { - allfound = false; - break; - } - } - if (!allfound) { - continue; - } - } - var descr = objname + _(', in ') + title; - - var anchor = match[3]; - if (anchor === '') - anchor = fullname; - else if (anchor == '-') - anchor = objnames[match[1]][1] + '-' + fullname; - // add custom score for some objects according to scorer - if (Scorer.objPrio.hasOwnProperty(match[2])) { - score += Scorer.objPrio[match[2]]; - } else { - score += Scorer.objPrioDefault; - } - results.push([filenames[match[0]], fullname, '#'+anchor, descr, score]); - } - } - } - - return results; - }, - - /** - * search for full-text terms in the index - */ - performTermsSearch : function(searchterms, excluded, terms, score) { - var filenames = this._index.filenames; - var titles = this._index.titles; - - var i, j, file, files; - var fileMap = {}; - var results = []; - - // perform the search on the required terms - for (i = 0; i < searchterms.length; i++) { - var word = searchterms[i]; - // no match but word was a required one - if ((files = terms[word]) === undefined) - break; - if (files.length === undefined) { - files = [files]; - } - // create the mapping - for (j = 0; j < files.length; j++) { - file = files[j]; - if (file in fileMap) - fileMap[file].push(word); - else - fileMap[file] = [word]; - } - } - - // now check if the files don't contain excluded terms - for (file in fileMap) { - var valid = true; - - // check if all requirements are matched - if (fileMap[file].length != searchterms.length) - continue; - - // ensure that none of the excluded terms is in the search result - for (i = 0; i < excluded.length; i++) { - if (terms[excluded[i]] == file || - $u.contains(terms[excluded[i]] || [], file)) { - valid = false; - break; - } - } - - // if we have still a valid result we can add it to the result list - if (valid) { - results.push([filenames[file], titles[file], '', null, score]); - } - } - return results; - }, - - /** - * helper function to return a node containing the - * search summary for a given text. keywords is a list - * of stemmed words, hlwords is the list of normal, unstemmed - * words. the first one is used to find the occurance, the - * latter for highlighting it. - */ - makeSearchSummary : function(text, keywords, hlwords) { - var textLower = text.toLowerCase(); - var start = 0; - $.each(keywords, function() { - var i = textLower.indexOf(this.toLowerCase()); - if (i > -1) - start = i; - }); - start = Math.max(start - 120, 0); - var excerpt = ((start > 0) ? '...' : '') + - $.trim(text.substr(start, 240)) + - ((start + 240 - text.length) ? '...' : ''); - var rv = $('<div class="context"></div>').text(excerpt); - $.each(hlwords, function() { - rv = rv.highlightText(this, 'highlighted'); - }); - return rv; - } -}; - -$(document).ready(function() { - Search.init(); -});
\ No newline at end of file diff --git a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/_static/sidebar.js b/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/_static/sidebar.js deleted file mode 100644 index 874a890a3..000000000 --- a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/_static/sidebar.js +++ /dev/null @@ -1,159 +0,0 @@ -/* - * sidebar.js - * ~~~~~~~~~~ - * - * This script makes the Sphinx sidebar collapsible. - * - * .sphinxsidebar contains .sphinxsidebarwrapper. This script adds - * in .sphixsidebar, after .sphinxsidebarwrapper, the #sidebarbutton - * used to collapse and expand the sidebar. - * - * When the sidebar is collapsed the .sphinxsidebarwrapper is hidden - * and the width of the sidebar and the margin-left of the document - * are decreased. When the sidebar is expanded the opposite happens. - * This script saves a per-browser/per-session cookie used to - * remember the position of the sidebar among the pages. - * Once the browser is closed the cookie is deleted and the position - * reset to the default (expanded). - * - * :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - -$(function() { - - - - - - - - - // global elements used by the functions. - // the 'sidebarbutton' element is defined as global after its - // creation, in the add_sidebar_button function - var bodywrapper = $('.bodywrapper'); - var sidebar = $('.sphinxsidebar'); - var sidebarwrapper = $('.sphinxsidebarwrapper'); - - // for some reason, the document has no sidebar; do not run into errors - if (!sidebar.length) return; - - // original margin-left of the bodywrapper and width of the sidebar - // with the sidebar expanded - var bw_margin_expanded = bodywrapper.css('margin-left'); - var ssb_width_expanded = sidebar.width(); - - // margin-left of the bodywrapper and width of the sidebar - // with the sidebar collapsed - var bw_margin_collapsed = '.8em'; - var ssb_width_collapsed = '.8em'; - - // colors used by the current theme - var dark_color = $('.related').css('background-color'); - var light_color = $('.document').css('background-color'); - - function sidebar_is_collapsed() { - return sidebarwrapper.is(':not(:visible)'); - } - - function toggle_sidebar() { - if (sidebar_is_collapsed()) - expand_sidebar(); - else - collapse_sidebar(); - } - - function collapse_sidebar() { - sidebarwrapper.hide(); - sidebar.css('width', ssb_width_collapsed); - bodywrapper.css('margin-left', bw_margin_collapsed); - sidebarbutton.css({ - 'margin-left': '0', - 'height': bodywrapper.height() - }); - sidebarbutton.find('span').text('»'); - sidebarbutton.attr('title', _('Expand sidebar')); - document.cookie = 'sidebar=collapsed'; - } - - function expand_sidebar() { - bodywrapper.css('margin-left', bw_margin_expanded); - sidebar.css('width', ssb_width_expanded); - sidebarwrapper.show(); - sidebarbutton.css({ - 'margin-left': ssb_width_expanded-12, - 'height': bodywrapper.height() - }); - sidebarbutton.find('span').text('«'); - sidebarbutton.attr('title', _('Collapse sidebar')); - document.cookie = 'sidebar=expanded'; - } - - function add_sidebar_button() { - sidebarwrapper.css({ - 'float': 'left', - 'margin-right': '0', - 'width': ssb_width_expanded - 28 - }); - // create the button - sidebar.append( - '<div id="sidebarbutton"><span>«</span></div>' - ); - var sidebarbutton = $('#sidebarbutton'); - light_color = sidebarbutton.css('background-color'); - // find the height of the viewport to center the '<<' in the page - var viewport_height; - if (window.innerHeight) - viewport_height = window.innerHeight; - else - viewport_height = $(window).height(); - sidebarbutton.find('span').css({ - 'display': 'block', - 'margin-top': (viewport_height - sidebar.position().top - 20) / 2 - }); - - sidebarbutton.click(toggle_sidebar); - sidebarbutton.attr('title', _('Collapse sidebar')); - sidebarbutton.css({ - 'color': '#FFFFFF', - 'border-left': '1px solid ' + dark_color, - 'font-size': '1.2em', - 'cursor': 'pointer', - 'height': bodywrapper.height(), - 'padding-top': '1px', - 'margin-left': ssb_width_expanded - 12 - }); - - sidebarbutton.hover( - function () { - $(this).css('background-color', dark_color); - }, - function () { - $(this).css('background-color', light_color); - } - ); - } - - function set_position_from_cookie() { - if (!document.cookie) - return; - var items = document.cookie.split(';'); - for(var k=0; k<items.length; k++) { - var key_val = items[k].split('='); - var key = key_val[0].replace(/ /, ""); // strip leading spaces - if (key == 'sidebar') { - var value = key_val[1]; - if ((value == 'collapsed') && (!sidebar_is_collapsed())) - collapse_sidebar(); - else if ((value == 'expanded') && (sidebar_is_collapsed())) - expand_sidebar(); - } - } - } - - add_sidebar_button(); - var sidebarbutton = $('#sidebarbutton'); - set_position_from_cookie(); -});
\ No newline at end of file diff --git a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/_static/underscore.js b/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/_static/underscore.js deleted file mode 100644 index 5b55f32be..000000000 --- a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/_static/underscore.js +++ /dev/null @@ -1,31 +0,0 @@ -// Underscore.js 1.3.1 -// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. -// Underscore is freely distributable under the MIT license. -// Portions of Underscore are inspired or borrowed from Prototype, -// Oliver Steele's Functional, and John Resig's Micro-Templating. -// For all details and documentation: -// http://documentcloud.github.com/underscore -(function(){function q(a,c,d){if(a===c)return a!==0||1/a==1/c;if(a==null||c==null)return a===c;if(a._chain)a=a._wrapped;if(c._chain)c=c._wrapped;if(a.isEqual&&b.isFunction(a.isEqual))return a.isEqual(c);if(c.isEqual&&b.isFunction(c.isEqual))return c.isEqual(a);var e=l.call(a);if(e!=l.call(c))return false;switch(e){case "[object String]":return a==String(c);case "[object Number]":return a!=+a?c!=+c:a==0?1/a==1/c:a==+c;case "[object Date]":case "[object Boolean]":return+a==+c;case "[object RegExp]":return a.source== -c.source&&a.global==c.global&&a.multiline==c.multiline&&a.ignoreCase==c.ignoreCase}if(typeof a!="object"||typeof c!="object")return false;for(var f=d.length;f--;)if(d[f]==a)return true;d.push(a);var f=0,g=true;if(e=="[object Array]"){if(f=a.length,g=f==c.length)for(;f--;)if(!(g=f in a==f in c&&q(a[f],c[f],d)))break}else{if("constructor"in a!="constructor"in c||a.constructor!=c.constructor)return false;for(var h in a)if(b.has(a,h)&&(f++,!(g=b.has(c,h)&&q(a[h],c[h],d))))break;if(g){for(h in c)if(b.has(c, -h)&&!f--)break;g=!f}}d.pop();return g}var r=this,G=r._,n={},k=Array.prototype,o=Object.prototype,i=k.slice,H=k.unshift,l=o.toString,I=o.hasOwnProperty,w=k.forEach,x=k.map,y=k.reduce,z=k.reduceRight,A=k.filter,B=k.every,C=k.some,p=k.indexOf,D=k.lastIndexOf,o=Array.isArray,J=Object.keys,s=Function.prototype.bind,b=function(a){return new m(a)};if(typeof exports!=="undefined"){if(typeof module!=="undefined"&&module.exports)exports=module.exports=b;exports._=b}else r._=b;b.VERSION="1.3.1";var j=b.each= -b.forEach=function(a,c,d){if(a!=null)if(w&&a.forEach===w)a.forEach(c,d);else if(a.length===+a.length)for(var e=0,f=a.length;e<f;e++){if(e in a&&c.call(d,a[e],e,a)===n)break}else for(e in a)if(b.has(a,e)&&c.call(d,a[e],e,a)===n)break};b.map=b.collect=function(a,c,b){var e=[];if(a==null)return e;if(x&&a.map===x)return a.map(c,b);j(a,function(a,g,h){e[e.length]=c.call(b,a,g,h)});if(a.length===+a.length)e.length=a.length;return e};b.reduce=b.foldl=b.inject=function(a,c,d,e){var f=arguments.length>2;a== -null&&(a=[]);if(y&&a.reduce===y)return e&&(c=b.bind(c,e)),f?a.reduce(c,d):a.reduce(c);j(a,function(a,b,i){f?d=c.call(e,d,a,b,i):(d=a,f=true)});if(!f)throw new TypeError("Reduce of empty array with no initial value");return d};b.reduceRight=b.foldr=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(z&&a.reduceRight===z)return e&&(c=b.bind(c,e)),f?a.reduceRight(c,d):a.reduceRight(c);var g=b.toArray(a).reverse();e&&!f&&(c=b.bind(c,e));return f?b.reduce(g,c,d,e):b.reduce(g,c)};b.find=b.detect= -function(a,c,b){var e;E(a,function(a,g,h){if(c.call(b,a,g,h))return e=a,true});return e};b.filter=b.select=function(a,c,b){var e=[];if(a==null)return e;if(A&&a.filter===A)return a.filter(c,b);j(a,function(a,g,h){c.call(b,a,g,h)&&(e[e.length]=a)});return e};b.reject=function(a,c,b){var e=[];if(a==null)return e;j(a,function(a,g,h){c.call(b,a,g,h)||(e[e.length]=a)});return e};b.every=b.all=function(a,c,b){var e=true;if(a==null)return e;if(B&&a.every===B)return a.every(c,b);j(a,function(a,g,h){if(!(e= -e&&c.call(b,a,g,h)))return n});return e};var E=b.some=b.any=function(a,c,d){c||(c=b.identity);var e=false;if(a==null)return e;if(C&&a.some===C)return a.some(c,d);j(a,function(a,b,h){if(e||(e=c.call(d,a,b,h)))return n});return!!e};b.include=b.contains=function(a,c){var b=false;if(a==null)return b;return p&&a.indexOf===p?a.indexOf(c)!=-1:b=E(a,function(a){return a===c})};b.invoke=function(a,c){var d=i.call(arguments,2);return b.map(a,function(a){return(b.isFunction(c)?c||a:a[c]).apply(a,d)})};b.pluck= -function(a,c){return b.map(a,function(a){return a[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a))return Math.max.apply(Math,a);if(!c&&b.isEmpty(a))return-Infinity;var e={computed:-Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b>=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);if(!c&&b.isEmpty(a))return Infinity;var e={computed:Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b<e.computed&&(e={value:a,computed:b})}); -return e.value};b.shuffle=function(a){var b=[],d;j(a,function(a,f){f==0?b[0]=a:(d=Math.floor(Math.random()*(f+1)),b[f]=b[d],b[d]=a)});return b};b.sortBy=function(a,c,d){return b.pluck(b.map(a,function(a,b,g){return{value:a,criteria:c.call(d,a,b,g)}}).sort(function(a,b){var c=a.criteria,d=b.criteria;return c<d?-1:c>d?1:0}),"value")};b.groupBy=function(a,c){var d={},e=b.isFunction(c)?c:function(a){return a[c]};j(a,function(a,b){var c=e(a,b);(d[c]||(d[c]=[])).push(a)});return d};b.sortedIndex=function(a, -c,d){d||(d=b.identity);for(var e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<d(c)?e=g+1:f=g}return e};b.toArray=function(a){return!a?[]:a.toArray?a.toArray():b.isArray(a)?i.call(a):b.isArguments(a)?i.call(a):b.values(a)};b.size=function(a){return b.toArray(a).length};b.first=b.head=function(a,b,d){return b!=null&&!d?i.call(a,0,b):a[0]};b.initial=function(a,b,d){return i.call(a,0,a.length-(b==null||d?1:b))};b.last=function(a,b,d){return b!=null&&!d?i.call(a,Math.max(a.length-b,0)):a[a.length-1]};b.rest= -b.tail=function(a,b,d){return i.call(a,b==null||d?1:b)};b.compact=function(a){return b.filter(a,function(a){return!!a})};b.flatten=function(a,c){return b.reduce(a,function(a,e){if(b.isArray(e))return a.concat(c?e:b.flatten(e));a[a.length]=e;return a},[])};b.without=function(a){return b.difference(a,i.call(arguments,1))};b.uniq=b.unique=function(a,c,d){var d=d?b.map(a,d):a,e=[];b.reduce(d,function(d,g,h){if(0==h||(c===true?b.last(d)!=g:!b.include(d,g)))d[d.length]=g,e[e.length]=a[h];return d},[]); -return e};b.union=function(){return b.uniq(b.flatten(arguments,true))};b.intersection=b.intersect=function(a){var c=i.call(arguments,1);return b.filter(b.uniq(a),function(a){return b.every(c,function(c){return b.indexOf(c,a)>=0})})};b.difference=function(a){var c=b.flatten(i.call(arguments,1));return b.filter(a,function(a){return!b.include(c,a)})};b.zip=function(){for(var a=i.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e<c;e++)d[e]=b.pluck(a,""+e);return d};b.indexOf=function(a,c, -d){if(a==null)return-1;var e;if(d)return d=b.sortedIndex(a,c),a[d]===c?d:-1;if(p&&a.indexOf===p)return a.indexOf(c);for(d=0,e=a.length;d<e;d++)if(d in a&&a[d]===c)return d;return-1};b.lastIndexOf=function(a,b){if(a==null)return-1;if(D&&a.lastIndexOf===D)return a.lastIndexOf(b);for(var d=a.length;d--;)if(d in a&&a[d]===b)return d;return-1};b.range=function(a,b,d){arguments.length<=1&&(b=a||0,a=0);for(var d=arguments[2]||1,e=Math.max(Math.ceil((b-a)/d),0),f=0,g=Array(e);f<e;)g[f++]=a,a+=d;return g}; -var F=function(){};b.bind=function(a,c){var d,e;if(a.bind===s&&s)return s.apply(a,i.call(arguments,1));if(!b.isFunction(a))throw new TypeError;e=i.call(arguments,2);return d=function(){if(!(this instanceof d))return a.apply(c,e.concat(i.call(arguments)));F.prototype=a.prototype;var b=new F,g=a.apply(b,e.concat(i.call(arguments)));return Object(g)===g?g:b}};b.bindAll=function(a){var c=i.call(arguments,1);c.length==0&&(c=b.functions(a));j(c,function(c){a[c]=b.bind(a[c],a)});return a};b.memoize=function(a, -c){var d={};c||(c=b.identity);return function(){var e=c.apply(this,arguments);return b.has(d,e)?d[e]:d[e]=a.apply(this,arguments)}};b.delay=function(a,b){var d=i.call(arguments,2);return setTimeout(function(){return a.apply(a,d)},b)};b.defer=function(a){return b.delay.apply(b,[a,1].concat(i.call(arguments,1)))};b.throttle=function(a,c){var d,e,f,g,h,i=b.debounce(function(){h=g=false},c);return function(){d=this;e=arguments;var b;f||(f=setTimeout(function(){f=null;h&&a.apply(d,e);i()},c));g?h=true: -a.apply(d,e);i();g=true}};b.debounce=function(a,b){var d;return function(){var e=this,f=arguments;clearTimeout(d);d=setTimeout(function(){d=null;a.apply(e,f)},b)}};b.once=function(a){var b=false,d;return function(){if(b)return d;b=true;return d=a.apply(this,arguments)}};b.wrap=function(a,b){return function(){var d=[a].concat(i.call(arguments,0));return b.apply(this,d)}};b.compose=function(){var a=arguments;return function(){for(var b=arguments,d=a.length-1;d>=0;d--)b=[a[d].apply(this,b)];return b[0]}}; -b.after=function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};b.keys=J||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var c=[],d;for(d in a)b.has(a,d)&&(c[c.length]=d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=b.methods=function(a){var c=[],d;for(d in a)b.isFunction(a[d])&&c.push(d);return c.sort()};b.extend=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]=b[d]});return a};b.defaults=function(a){j(i.call(arguments, -1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return!b.isObject(a)?a:b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,b){return q(a,b,[])};b.isEmpty=function(a){if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(b.has(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=o||function(a){return l.call(a)=="[object Array]"};b.isObject=function(a){return a===Object(a)}; -b.isArguments=function(a){return l.call(a)=="[object Arguments]"};if(!b.isArguments(arguments))b.isArguments=function(a){return!(!a||!b.has(a,"callee"))};b.isFunction=function(a){return l.call(a)=="[object Function]"};b.isString=function(a){return l.call(a)=="[object String]"};b.isNumber=function(a){return l.call(a)=="[object Number]"};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===true||a===false||l.call(a)=="[object Boolean]"};b.isDate=function(a){return l.call(a)=="[object Date]"}; -b.isRegExp=function(a){return l.call(a)=="[object RegExp]"};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.has=function(a,b){return I.call(a,b)};b.noConflict=function(){r._=G;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e<a;e++)b.call(d,e)};b.escape=function(a){return(""+a).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/")};b.mixin=function(a){j(b.functions(a), -function(c){K(c,b[c]=a[c])})};var L=0;b.uniqueId=function(a){var b=L++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var t=/.^/,u=function(a){return a.replace(/\\\\/g,"\\").replace(/\\'/g,"'")};b.template=function(a,c){var d=b.templateSettings,d="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+a.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(d.escape||t,function(a,b){return"',_.escape("+ -u(b)+"),'"}).replace(d.interpolate||t,function(a,b){return"',"+u(b)+",'"}).replace(d.evaluate||t,function(a,b){return"');"+u(b).replace(/[\r\n\t]/g," ")+";__p.push('"}).replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');",e=new Function("obj","_",d);return c?e(c,b):function(a){return e.call(this,a,b)}};b.chain=function(a){return b(a).chain()};var m=function(a){this._wrapped=a};b.prototype=m.prototype;var v=function(a,c){return c?b(a).chain():a},K=function(a,c){m.prototype[a]= -function(){var a=i.call(arguments);H.call(a,this._wrapped);return v(c.apply(b,a),this._chain)}};b.mixin(b);j("pop,push,reverse,shift,sort,splice,unshift".split(","),function(a){var b=k[a];m.prototype[a]=function(){var d=this._wrapped;b.apply(d,arguments);var e=d.length;(a=="shift"||a=="splice")&&e===0&&delete d[0];return v(d,this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];m.prototype[a]=function(){return v(b.apply(this._wrapped,arguments),this._chain)}});m.prototype.chain=function(){this._chain= -true;return this};m.prototype.value=function(){return this._wrapped}}).call(this); diff --git a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/_static/websupport.js b/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/_static/websupport.js deleted file mode 100644 index 19fcda564..000000000 --- a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/_static/websupport.js +++ /dev/null @@ -1,808 +0,0 @@ -/* - * websupport.js - * ~~~~~~~~~~~~~ - * - * sphinx.websupport utilties for all documentation. - * - * :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - -(function($) { - $.fn.autogrow = function() { - return this.each(function() { - var textarea = this; - - $.fn.autogrow.resize(textarea); - - $(textarea) - .focus(function() { - textarea.interval = setInterval(function() { - $.fn.autogrow.resize(textarea); - }, 500); - }) - .blur(function() { - clearInterval(textarea.interval); - }); - }); - }; - - $.fn.autogrow.resize = function(textarea) { - var lineHeight = parseInt($(textarea).css('line-height'), 10); - var lines = textarea.value.split('\n'); - var columns = textarea.cols; - var lineCount = 0; - $.each(lines, function() { - lineCount += Math.ceil(this.length / columns) || 1; - }); - var height = lineHeight * (lineCount + 1); - $(textarea).css('height', height); - }; -})(jQuery); - -(function($) { - var comp, by; - - function init() { - initEvents(); - initComparator(); - } - - function initEvents() { - $('a.comment-close').live("click", function(event) { - event.preventDefault(); - hide($(this).attr('id').substring(2)); - }); - $('a.vote').live("click", function(event) { - event.preventDefault(); - handleVote($(this)); - }); - $('a.reply').live("click", function(event) { - event.preventDefault(); - openReply($(this).attr('id').substring(2)); - }); - $('a.close-reply').live("click", function(event) { - event.preventDefault(); - closeReply($(this).attr('id').substring(2)); - }); - $('a.sort-option').live("click", function(event) { - event.preventDefault(); - handleReSort($(this)); - }); - $('a.show-proposal').live("click", function(event) { - event.preventDefault(); - showProposal($(this).attr('id').substring(2)); - }); - $('a.hide-proposal').live("click", function(event) { - event.preventDefault(); - hideProposal($(this).attr('id').substring(2)); - }); - $('a.show-propose-change').live("click", function(event) { - event.preventDefault(); - showProposeChange($(this).attr('id').substring(2)); - }); - $('a.hide-propose-change').live("click", function(event) { - event.preventDefault(); - hideProposeChange($(this).attr('id').substring(2)); - }); - $('a.accept-comment').live("click", function(event) { - event.preventDefault(); - acceptComment($(this).attr('id').substring(2)); - }); - $('a.delete-comment').live("click", function(event) { - event.preventDefault(); - deleteComment($(this).attr('id').substring(2)); - }); - $('a.comment-markup').live("click", function(event) { - event.preventDefault(); - toggleCommentMarkupBox($(this).attr('id').substring(2)); - }); - } - - /** - * Set comp, which is a comparator function used for sorting and - * inserting comments into the list. - */ - function setComparator() { - // If the first three letters are "asc", sort in ascending order - // and remove the prefix. - if (by.substring(0,3) == 'asc') { - var i = by.substring(3); - comp = function(a, b) { return a[i] - b[i]; }; - } else { - // Otherwise sort in descending order. - comp = function(a, b) { return b[by] - a[by]; }; - } - - // Reset link styles and format the selected sort option. - $('a.sel').attr('href', '#').removeClass('sel'); - $('a.by' + by).removeAttr('href').addClass('sel'); - } - - /** - * Create a comp function. If the user has preferences stored in - * the sortBy cookie, use those, otherwise use the default. - */ - function initComparator() { - by = 'rating'; // Default to sort by rating. - // If the sortBy cookie is set, use that instead. - if (document.cookie.length > 0) { - var start = document.cookie.indexOf('sortBy='); - if (start != -1) { - start = start + 7; - var end = document.cookie.indexOf(";", start); - if (end == -1) { - end = document.cookie.length; - by = unescape(document.cookie.substring(start, end)); - } - } - } - setComparator(); - } - - /** - * Show a comment div. - */ - function show(id) { - $('#ao' + id).hide(); - $('#ah' + id).show(); - var context = $.extend({id: id}, opts); - var popup = $(renderTemplate(popupTemplate, context)).hide(); - popup.find('textarea[name="proposal"]').hide(); - popup.find('a.by' + by).addClass('sel'); - var form = popup.find('#cf' + id); - form.submit(function(event) { - event.preventDefault(); - addComment(form); - }); - $('#s' + id).after(popup); - popup.slideDown('fast', function() { - getComments(id); - }); - } - - /** - * Hide a comment div. - */ - function hide(id) { - $('#ah' + id).hide(); - $('#ao' + id).show(); - var div = $('#sc' + id); - div.slideUp('fast', function() { - div.remove(); - }); - } - - /** - * Perform an ajax request to get comments for a node - * and insert the comments into the comments tree. - */ - function getComments(id) { - $.ajax({ - type: 'GET', - url: opts.getCommentsURL, - data: {node: id}, - success: function(data, textStatus, request) { - var ul = $('#cl' + id); - var speed = 100; - $('#cf' + id) - .find('textarea[name="proposal"]') - .data('source', data.source); - - if (data.comments.length === 0) { - ul.html('<li>No comments yet.</li>'); - ul.data('empty', true); - } else { - // If there are comments, sort them and put them in the list. - var comments = sortComments(data.comments); - speed = data.comments.length * 100; - appendComments(comments, ul); - ul.data('empty', false); - } - $('#cn' + id).slideUp(speed + 200); - ul.slideDown(speed); - }, - error: function(request, textStatus, error) { - showError('Oops, there was a problem retrieving the comments.'); - }, - dataType: 'json' - }); - } - - /** - * Add a comment via ajax and insert the comment into the comment tree. - */ - function addComment(form) { - var node_id = form.find('input[name="node"]').val(); - var parent_id = form.find('input[name="parent"]').val(); - var text = form.find('textarea[name="comment"]').val(); - var proposal = form.find('textarea[name="proposal"]').val(); - - if (text == '') { - showError('Please enter a comment.'); - return; - } - - // Disable the form that is being submitted. - form.find('textarea,input').attr('disabled', 'disabled'); - - // Send the comment to the server. - $.ajax({ - type: "POST", - url: opts.addCommentURL, - dataType: 'json', - data: { - node: node_id, - parent: parent_id, - text: text, - proposal: proposal - }, - success: function(data, textStatus, error) { - // Reset the form. - if (node_id) { - hideProposeChange(node_id); - } - form.find('textarea') - .val('') - .add(form.find('input')) - .removeAttr('disabled'); - var ul = $('#cl' + (node_id || parent_id)); - if (ul.data('empty')) { - $(ul).empty(); - ul.data('empty', false); - } - insertComment(data.comment); - var ao = $('#ao' + node_id); - ao.find('img').attr({'src': opts.commentBrightImage}); - if (node_id) { - // if this was a "root" comment, remove the commenting box - // (the user can get it back by reopening the comment popup) - $('#ca' + node_id).slideUp(); - } - }, - error: function(request, textStatus, error) { - form.find('textarea,input').removeAttr('disabled'); - showError('Oops, there was a problem adding the comment.'); - } - }); - } - - /** - * Recursively append comments to the main comment list and children - * lists, creating the comment tree. - */ - function appendComments(comments, ul) { - $.each(comments, function() { - var div = createCommentDiv(this); - ul.append($(document.createElement('li')).html(div)); - appendComments(this.children, div.find('ul.comment-children')); - // To avoid stagnating data, don't store the comments children in data. - this.children = null; - div.data('comment', this); - }); - } - - /** - * After adding a new comment, it must be inserted in the correct - * location in the comment tree. - */ - function insertComment(comment) { - var div = createCommentDiv(comment); - - // To avoid stagnating data, don't store the comments children in data. - comment.children = null; - div.data('comment', comment); - - var ul = $('#cl' + (comment.node || comment.parent)); - var siblings = getChildren(ul); - - var li = $(document.createElement('li')); - li.hide(); - - // Determine where in the parents children list to insert this comment. - for(i=0; i < siblings.length; i++) { - if (comp(comment, siblings[i]) <= 0) { - $('#cd' + siblings[i].id) - .parent() - .before(li.html(div)); - li.slideDown('fast'); - return; - } - } - - // If we get here, this comment rates lower than all the others, - // or it is the only comment in the list. - ul.append(li.html(div)); - li.slideDown('fast'); - } - - function acceptComment(id) { - $.ajax({ - type: 'POST', - url: opts.acceptCommentURL, - data: {id: id}, - success: function(data, textStatus, request) { - $('#cm' + id).fadeOut('fast'); - $('#cd' + id).removeClass('moderate'); - }, - error: function(request, textStatus, error) { - showError('Oops, there was a problem accepting the comment.'); - } - }); - } - - function deleteComment(id) { - $.ajax({ - type: 'POST', - url: opts.deleteCommentURL, - data: {id: id}, - success: function(data, textStatus, request) { - var div = $('#cd' + id); - if (data == 'delete') { - // Moderator mode: remove the comment and all children immediately - div.slideUp('fast', function() { - div.remove(); - }); - return; - } - // User mode: only mark the comment as deleted - div - .find('span.user-id:first') - .text('[deleted]').end() - .find('div.comment-text:first') - .text('[deleted]').end() - .find('#cm' + id + ', #dc' + id + ', #ac' + id + ', #rc' + id + - ', #sp' + id + ', #hp' + id + ', #cr' + id + ', #rl' + id) - .remove(); - var comment = div.data('comment'); - comment.username = '[deleted]'; - comment.text = '[deleted]'; - div.data('comment', comment); - }, - error: function(request, textStatus, error) { - showError('Oops, there was a problem deleting the comment.'); - } - }); - } - - function showProposal(id) { - $('#sp' + id).hide(); - $('#hp' + id).show(); - $('#pr' + id).slideDown('fast'); - } - - function hideProposal(id) { - $('#hp' + id).hide(); - $('#sp' + id).show(); - $('#pr' + id).slideUp('fast'); - } - - function showProposeChange(id) { - $('#pc' + id).hide(); - $('#hc' + id).show(); - var textarea = $('#pt' + id); - textarea.val(textarea.data('source')); - $.fn.autogrow.resize(textarea[0]); - textarea.slideDown('fast'); - } - - function hideProposeChange(id) { - $('#hc' + id).hide(); - $('#pc' + id).show(); - var textarea = $('#pt' + id); - textarea.val('').removeAttr('disabled'); - textarea.slideUp('fast'); - } - - function toggleCommentMarkupBox(id) { - $('#mb' + id).toggle(); - } - - /** Handle when the user clicks on a sort by link. */ - function handleReSort(link) { - var classes = link.attr('class').split(/\s+/); - for (var i=0; i<classes.length; i++) { - if (classes[i] != 'sort-option') { - by = classes[i].substring(2); - } - } - setComparator(); - // Save/update the sortBy cookie. - var expiration = new Date(); - expiration.setDate(expiration.getDate() + 365); - document.cookie= 'sortBy=' + escape(by) + - ';expires=' + expiration.toUTCString(); - $('ul.comment-ul').each(function(index, ul) { - var comments = getChildren($(ul), true); - comments = sortComments(comments); - appendComments(comments, $(ul).empty()); - }); - } - - /** - * Function to process a vote when a user clicks an arrow. - */ - function handleVote(link) { - if (!opts.voting) { - showError("You'll need to login to vote."); - return; - } - - var id = link.attr('id'); - if (!id) { - // Didn't click on one of the voting arrows. - return; - } - // If it is an unvote, the new vote value is 0, - // Otherwise it's 1 for an upvote, or -1 for a downvote. - var value = 0; - if (id.charAt(1) != 'u') { - value = id.charAt(0) == 'u' ? 1 : -1; - } - // The data to be sent to the server. - var d = { - comment_id: id.substring(2), - value: value - }; - - // Swap the vote and unvote links. - link.hide(); - $('#' + id.charAt(0) + (id.charAt(1) == 'u' ? 'v' : 'u') + d.comment_id) - .show(); - - // The div the comment is displayed in. - var div = $('div#cd' + d.comment_id); - var data = div.data('comment'); - - // If this is not an unvote, and the other vote arrow has - // already been pressed, unpress it. - if ((d.value !== 0) && (data.vote === d.value * -1)) { - $('#' + (d.value == 1 ? 'd' : 'u') + 'u' + d.comment_id).hide(); - $('#' + (d.value == 1 ? 'd' : 'u') + 'v' + d.comment_id).show(); - } - - // Update the comments rating in the local data. - data.rating += (data.vote === 0) ? d.value : (d.value - data.vote); - data.vote = d.value; - div.data('comment', data); - - // Change the rating text. - div.find('.rating:first') - .text(data.rating + ' point' + (data.rating == 1 ? '' : 's')); - - // Send the vote information to the server. - $.ajax({ - type: "POST", - url: opts.processVoteURL, - data: d, - error: function(request, textStatus, error) { - showError('Oops, there was a problem casting that vote.'); - } - }); - } - - /** - * Open a reply form used to reply to an existing comment. - */ - function openReply(id) { - // Swap out the reply link for the hide link - $('#rl' + id).hide(); - $('#cr' + id).show(); - - // Add the reply li to the children ul. - var div = $(renderTemplate(replyTemplate, {id: id})).hide(); - $('#cl' + id) - .prepend(div) - // Setup the submit handler for the reply form. - .find('#rf' + id) - .submit(function(event) { - event.preventDefault(); - addComment($('#rf' + id)); - closeReply(id); - }) - .find('input[type=button]') - .click(function() { - closeReply(id); - }); - div.slideDown('fast', function() { - $('#rf' + id).find('textarea').focus(); - }); - } - - /** - * Close the reply form opened with openReply. - */ - function closeReply(id) { - // Remove the reply div from the DOM. - $('#rd' + id).slideUp('fast', function() { - $(this).remove(); - }); - - // Swap out the hide link for the reply link - $('#cr' + id).hide(); - $('#rl' + id).show(); - } - - /** - * Recursively sort a tree of comments using the comp comparator. - */ - function sortComments(comments) { - comments.sort(comp); - $.each(comments, function() { - this.children = sortComments(this.children); - }); - return comments; - } - - /** - * Get the children comments from a ul. If recursive is true, - * recursively include childrens' children. - */ - function getChildren(ul, recursive) { - var children = []; - ul.children().children("[id^='cd']") - .each(function() { - var comment = $(this).data('comment'); - if (recursive) - comment.children = getChildren($(this).find('#cl' + comment.id), true); - children.push(comment); - }); - return children; - } - - /** Create a div to display a comment in. */ - function createCommentDiv(comment) { - if (!comment.displayed && !opts.moderator) { - return $('<div class="moderate">Thank you! Your comment will show up ' - + 'once it is has been approved by a moderator.</div>'); - } - // Prettify the comment rating. - comment.pretty_rating = comment.rating + ' point' + - (comment.rating == 1 ? '' : 's'); - // Make a class (for displaying not yet moderated comments differently) - comment.css_class = comment.displayed ? '' : ' moderate'; - // Create a div for this comment. - var context = $.extend({}, opts, comment); - var div = $(renderTemplate(commentTemplate, context)); - - // If the user has voted on this comment, highlight the correct arrow. - if (comment.vote) { - var direction = (comment.vote == 1) ? 'u' : 'd'; - div.find('#' + direction + 'v' + comment.id).hide(); - div.find('#' + direction + 'u' + comment.id).show(); - } - - if (opts.moderator || comment.text != '[deleted]') { - div.find('a.reply').show(); - if (comment.proposal_diff) - div.find('#sp' + comment.id).show(); - if (opts.moderator && !comment.displayed) - div.find('#cm' + comment.id).show(); - if (opts.moderator || (opts.username == comment.username)) - div.find('#dc' + comment.id).show(); - } - return div; - } - - /** - * A simple template renderer. Placeholders such as <%id%> are replaced - * by context['id'] with items being escaped. Placeholders such as <#id#> - * are not escaped. - */ - function renderTemplate(template, context) { - var esc = $(document.createElement('div')); - - function handle(ph, escape) { - var cur = context; - $.each(ph.split('.'), function() { - cur = cur[this]; - }); - return escape ? esc.text(cur || "").html() : cur; - } - - return template.replace(/<([%#])([\w\.]*)\1>/g, function() { - return handle(arguments[2], arguments[1] == '%' ? true : false); - }); - } - - /** Flash an error message briefly. */ - function showError(message) { - $(document.createElement('div')).attr({'class': 'popup-error'}) - .append($(document.createElement('div')) - .attr({'class': 'error-message'}).text(message)) - .appendTo('body') - .fadeIn("slow") - .delay(2000) - .fadeOut("slow"); - } - - /** Add a link the user uses to open the comments popup. */ - $.fn.comment = function() { - return this.each(function() { - var id = $(this).attr('id').substring(1); - var count = COMMENT_METADATA[id]; - var title = count + ' comment' + (count == 1 ? '' : 's'); - var image = count > 0 ? opts.commentBrightImage : opts.commentImage; - var addcls = count == 0 ? ' nocomment' : ''; - $(this) - .append( - $(document.createElement('a')).attr({ - href: '#', - 'class': 'sphinx-comment-open' + addcls, - id: 'ao' + id - }) - .append($(document.createElement('img')).attr({ - src: image, - alt: 'comment', - title: title - })) - .click(function(event) { - event.preventDefault(); - show($(this).attr('id').substring(2)); - }) - ) - .append( - $(document.createElement('a')).attr({ - href: '#', - 'class': 'sphinx-comment-close hidden', - id: 'ah' + id - }) - .append($(document.createElement('img')).attr({ - src: opts.closeCommentImage, - alt: 'close', - title: 'close' - })) - .click(function(event) { - event.preventDefault(); - hide($(this).attr('id').substring(2)); - }) - ); - }); - }; - - var opts = { - processVoteURL: '/_process_vote', - addCommentURL: '/_add_comment', - getCommentsURL: '/_get_comments', - acceptCommentURL: '/_accept_comment', - deleteCommentURL: '/_delete_comment', - commentImage: '/static/_static/comment.png', - closeCommentImage: '/static/_static/comment-close.png', - loadingImage: '/static/_static/ajax-loader.gif', - commentBrightImage: '/static/_static/comment-bright.png', - upArrow: '/static/_static/up.png', - downArrow: '/static/_static/down.png', - upArrowPressed: '/static/_static/up-pressed.png', - downArrowPressed: '/static/_static/down-pressed.png', - voting: false, - moderator: false - }; - - if (typeof COMMENT_OPTIONS != "undefined") { - opts = jQuery.extend(opts, COMMENT_OPTIONS); - } - - var popupTemplate = '\ - <div class="sphinx-comments" id="sc<%id%>">\ - <p class="sort-options">\ - Sort by:\ - <a href="#" class="sort-option byrating">best rated</a>\ - <a href="#" class="sort-option byascage">newest</a>\ - <a href="#" class="sort-option byage">oldest</a>\ - </p>\ - <div class="comment-header">Comments</div>\ - <div class="comment-loading" id="cn<%id%>">\ - loading comments... <img src="<%loadingImage%>" alt="" /></div>\ - <ul id="cl<%id%>" class="comment-ul"></ul>\ - <div id="ca<%id%>">\ - <p class="add-a-comment">Add a comment\ - (<a href="#" class="comment-markup" id="ab<%id%>">markup</a>):</p>\ - <div class="comment-markup-box" id="mb<%id%>">\ - reStructured text markup: <i>*emph*</i>, <b>**strong**</b>, \ - <tt>``code``</tt>, \ - code blocks: <tt>::</tt> and an indented block after blank line</div>\ - <form method="post" id="cf<%id%>" class="comment-form" action="">\ - <textarea name="comment" cols="80"></textarea>\ - <p class="propose-button">\ - <a href="#" id="pc<%id%>" class="show-propose-change">\ - Propose a change ▹\ - </a>\ - <a href="#" id="hc<%id%>" class="hide-propose-change">\ - Propose a change ▿\ - </a>\ - </p>\ - <textarea name="proposal" id="pt<%id%>" cols="80"\ - spellcheck="false"></textarea>\ - <input type="submit" value="Add comment" />\ - <input type="hidden" name="node" value="<%id%>" />\ - <input type="hidden" name="parent" value="" />\ - </form>\ - </div>\ - </div>'; - - var commentTemplate = '\ - <div id="cd<%id%>" class="sphinx-comment<%css_class%>">\ - <div class="vote">\ - <div class="arrow">\ - <a href="#" id="uv<%id%>" class="vote" title="vote up">\ - <img src="<%upArrow%>" />\ - </a>\ - <a href="#" id="uu<%id%>" class="un vote" title="vote up">\ - <img src="<%upArrowPressed%>" />\ - </a>\ - </div>\ - <div class="arrow">\ - <a href="#" id="dv<%id%>" class="vote" title="vote down">\ - <img src="<%downArrow%>" id="da<%id%>" />\ - </a>\ - <a href="#" id="du<%id%>" class="un vote" title="vote down">\ - <img src="<%downArrowPressed%>" />\ - </a>\ - </div>\ - </div>\ - <div class="comment-content">\ - <p class="tagline comment">\ - <span class="user-id"><%username%></span>\ - <span class="rating"><%pretty_rating%></span>\ - <span class="delta"><%time.delta%></span>\ - </p>\ - <div class="comment-text comment"><#text#></div>\ - <p class="comment-opts comment">\ - <a href="#" class="reply hidden" id="rl<%id%>">reply ▹</a>\ - <a href="#" class="close-reply" id="cr<%id%>">reply ▿</a>\ - <a href="#" id="sp<%id%>" class="show-proposal">proposal ▹</a>\ - <a href="#" id="hp<%id%>" class="hide-proposal">proposal ▿</a>\ - <a href="#" id="dc<%id%>" class="delete-comment hidden">delete</a>\ - <span id="cm<%id%>" class="moderation hidden">\ - <a href="#" id="ac<%id%>" class="accept-comment">accept</a>\ - </span>\ - </p>\ - <pre class="proposal" id="pr<%id%>">\ -<#proposal_diff#>\ - </pre>\ - <ul class="comment-children" id="cl<%id%>"></ul>\ - </div>\ - <div class="clearleft"></div>\ - </div>\ - </div>'; - - var replyTemplate = '\ - <li>\ - <div class="reply-div" id="rd<%id%>">\ - <form id="rf<%id%>">\ - <textarea name="comment" cols="80"></textarea>\ - <input type="submit" value="Add reply" />\ - <input type="button" value="Cancel" />\ - <input type="hidden" name="parent" value="<%id%>" />\ - <input type="hidden" name="node" value="" />\ - </form>\ - </div>\ - </li>'; - - $(document).ready(function() { - init(); - }); -})(jQuery); - -$(document).ready(function() { - // add comment anchors for all paragraphs that are commentable - $('.sphinx-has-comment').comment(); - - // highlight search words in search results - $("div.context").each(function() { - var params = $.getQueryParameters(); - var terms = (params.q) ? params.q[0].split(/\s+/) : []; - var result = $(this); - $.each(terms, function() { - result.highlightText(this.toLowerCase(), 'highlighted'); - }); - }); - - // directly open comment window if requested - var anchor = document.location.hash; - if (anchor.substring(0, 9) == '#comment-') { - $('#ao' + anchor.substring(9)).click(); - document.location.hash = '#s' + anchor.substring(9); - } -}); diff --git a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/client.html b/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/client.html deleted file mode 100644 index 8b5de14b9..000000000 --- a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/client.html +++ /dev/null @@ -1,404 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" - "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> - - -<html xmlns="http://www.w3.org/1999/xhtml"> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> - - <title>client Package — BrowserMob Proxy 0.6.0 documentation</title> - - <link rel="stylesheet" href="_static/default.css" type="text/css" /> - <link rel="stylesheet" href="_static/pygments.css" type="text/css" /> - - <script type="text/javascript"> - var DOCUMENTATION_OPTIONS = { - URL_ROOT: './', - VERSION: '0.6.0', - COLLAPSE_INDEX: false, - FILE_SUFFIX: '.html', - HAS_SOURCE: true - }; - </script> - <script type="text/javascript" src="_static/jquery.js"></script> - <script type="text/javascript" src="_static/underscore.js"></script> - <script type="text/javascript" src="_static/doctools.js"></script> - <link rel="top" title="BrowserMob Proxy 0.6.0 documentation" href="index.html" /> - <link rel="next" title="server Package" href="server.html" /> - <link rel="prev" title="Welcome to BrowserMob Proxy’s documentation!" href="index.html" /> - </head> - <body> - <div class="related"> - <h3>Navigation</h3> - <ul> - <li class="right" style="margin-right: 10px"> - <a href="genindex.html" title="General Index" - accesskey="I">index</a></li> - <li class="right" > - <a href="py-modindex.html" title="Python Module Index" - >modules</a> |</li> - <li class="right" > - <a href="server.html" title="server Package" - accesskey="N">next</a> |</li> - <li class="right" > - <a href="index.html" title="Welcome to BrowserMob Proxy’s documentation!" - accesskey="P">previous</a> |</li> - <li><a href="index.html">BrowserMob Proxy 0.6.0 documentation</a> »</li> - </ul> - </div> - - <div class="document"> - <div class="documentwrapper"> - <div class="bodywrapper"> - <div class="body"> - - <div class="toctree-wrapper compound"> -<ul class="simple"> -</ul> -</div> -<div class="section" id="module-browsermobproxy"> -<span id="client-package"></span><h1><tt class="xref py py-mod docutils literal"><span class="pre">client</span></tt> Package<a class="headerlink" href="#module-browsermobproxy" title="Permalink to this headline">¶</a></h1> -<dl class="class"> -<dt id="browsermobproxy.Client"> -<em class="property">class </em><tt class="descclassname">browsermobproxy.</tt><tt class="descname">Client</tt><big>(</big><em>url</em><big>)</big><a class="headerlink" href="#browsermobproxy.Client" title="Permalink to this definition">¶</a></dt> -<dd><p>Initialises a new Client object</p> -<table class="docutils field-list" frame="void" rules="none"> -<col class="field-name" /> -<col class="field-body" /> -<tbody valign="top"> -<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><strong>url</strong> – This is where the BrowserMob Proxy lives</td> -</tr> -</tbody> -</table> -<dl class="method"> -<dt id="browsermobproxy.Client.add_to_capabilities"> -<tt class="descname">add_to_capabilities</tt><big>(</big><em>capabilities</em><big>)</big><a class="headerlink" href="#browsermobproxy.Client.add_to_capabilities" title="Permalink to this definition">¶</a></dt> -<dd><p>Adds an ‘proxy’ entry to a desired capabilities dictionary with the -BrowserMob proxy information</p> -<table class="docutils field-list" frame="void" rules="none"> -<col class="field-name" /> -<col class="field-body" /> -<tbody valign="top"> -<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><strong>capabilities</strong> – The Desired capabilities object from Selenium WebDriver</td> -</tr> -</tbody> -</table> -</dd></dl> - -<dl class="method"> -<dt id="browsermobproxy.Client.basic_authentication"> -<tt class="descname">basic_authentication</tt><big>(</big><em>domain</em>, <em>username</em>, <em>password</em><big>)</big><a class="headerlink" href="#browsermobproxy.Client.basic_authentication" title="Permalink to this definition">¶</a></dt> -<dd><p>This add automatic basic authentication</p> -<table class="docutils field-list" frame="void" rules="none"> -<col class="field-name" /> -<col class="field-body" /> -<tbody valign="top"> -<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first last simple"> -<li><strong>domain</strong> – domain to set authentication credentials for</li> -<li><strong>username</strong> – valid username to use when authenticating</li> -<li><strong>password</strong> – valid password to use when authenticating</li> -</ul> -</td> -</tr> -</tbody> -</table> -</dd></dl> - -<dl class="method"> -<dt id="browsermobproxy.Client.blacklist"> -<tt class="descname">blacklist</tt><big>(</big><em>regexp</em>, <em>status_code</em><big>)</big><a class="headerlink" href="#browsermobproxy.Client.blacklist" title="Permalink to this definition">¶</a></dt> -<dd><p>Sets a list of URL patterns to blacklist</p> -<table class="docutils field-list" frame="void" rules="none"> -<col class="field-name" /> -<col class="field-body" /> -<tbody valign="top"> -<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first last simple"> -<li><strong>regex</strong> – a comma separated list of regular expressions</li> -<li><strong>status_code</strong> – the HTTP status code to return for URLs that do not match the blacklist</li> -</ul> -</td> -</tr> -</tbody> -</table> -</dd></dl> - -<dl class="method"> -<dt id="browsermobproxy.Client.clear_dns_cache"> -<tt class="descname">clear_dns_cache</tt><big>(</big><big>)</big><a class="headerlink" href="#browsermobproxy.Client.clear_dns_cache" title="Permalink to this definition">¶</a></dt> -<dd><p>Clears the DNS cache associated with the proxy instance</p> -</dd></dl> - -<dl class="method"> -<dt id="browsermobproxy.Client.close"> -<tt class="descname">close</tt><big>(</big><big>)</big><a class="headerlink" href="#browsermobproxy.Client.close" title="Permalink to this definition">¶</a></dt> -<dd><p>shuts down the proxy and closes the port</p> -</dd></dl> - -<dl class="attribute"> -<dt id="browsermobproxy.Client.har"> -<tt class="descname">har</tt><a class="headerlink" href="#browsermobproxy.Client.har" title="Permalink to this definition">¶</a></dt> -<dd><p>Gets the HAR that has been recorded</p> -</dd></dl> - -<dl class="method"> -<dt id="browsermobproxy.Client.headers"> -<tt class="descname">headers</tt><big>(</big><em>headers</em><big>)</big><a class="headerlink" href="#browsermobproxy.Client.headers" title="Permalink to this definition">¶</a></dt> -<dd><p>This sets the headers that will set by the proxy on all requests</p> -<table class="docutils field-list" frame="void" rules="none"> -<col class="field-name" /> -<col class="field-body" /> -<tbody valign="top"> -<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><strong>headers</strong> – this is a dictionary of the headers to be set</td> -</tr> -</tbody> -</table> -</dd></dl> - -<dl class="method"> -<dt id="browsermobproxy.Client.limits"> -<tt class="descname">limits</tt><big>(</big><em>options</em><big>)</big><a class="headerlink" href="#browsermobproxy.Client.limits" title="Permalink to this definition">¶</a></dt> -<dd><p>Limit the bandwidth through the proxy.</p> -<table class="docutils field-list" frame="void" rules="none"> -<col class="field-name" /> -<col class="field-body" /> -<tbody valign="top"> -<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><strong>options</strong> – A dictionary with all the details you want to set. downstreamKbps - Sets the downstream kbps upstreamKbps - Sets the upstream kbps latency - Add the given latency to each HTTP request</td> -</tr> -</tbody> -</table> -</dd></dl> - -<dl class="method"> -<dt id="browsermobproxy.Client.new_har"> -<tt class="descname">new_har</tt><big>(</big><em>ref=None</em>, <em>options={}</em><big>)</big><a class="headerlink" href="#browsermobproxy.Client.new_har" title="Permalink to this definition">¶</a></dt> -<dd><p>This sets a new HAR to be recorded</p> -<table class="docutils field-list" frame="void" rules="none"> -<col class="field-name" /> -<col class="field-body" /> -<tbody valign="top"> -<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first last simple"> -<li><strong>ref</strong> – A reference for the HAR. Defaults to None</li> -<li><strong>options</strong> – A dictionary that will be passed to BrowserMob Proxy with specific keywords. Keywords are: captureHeaders - Boolean, capture headers captureContent - Boolean, capture content bodies captureBinaryContent - Boolean, capture binary content</li> -</ul> -</td> -</tr> -</tbody> -</table> -</dd></dl> - -<dl class="method"> -<dt id="browsermobproxy.Client.new_page"> -<tt class="descname">new_page</tt><big>(</big><em>ref=None</em><big>)</big><a class="headerlink" href="#browsermobproxy.Client.new_page" title="Permalink to this definition">¶</a></dt> -<dd><p>This sets a new page to be recorded</p> -<table class="docutils field-list" frame="void" rules="none"> -<col class="field-name" /> -<col class="field-body" /> -<tbody valign="top"> -<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><strong>ref</strong> – A reference for the new page. Defaults to None</td> -</tr> -</tbody> -</table> -</dd></dl> - -<dl class="method"> -<dt id="browsermobproxy.Client.remap_hosts"> -<tt class="descname">remap_hosts</tt><big>(</big><em>address</em>, <em>ip_address</em><big>)</big><a class="headerlink" href="#browsermobproxy.Client.remap_hosts" title="Permalink to this definition">¶</a></dt> -<dd><p>Remap the hosts for a specific URL</p> -<table class="docutils field-list" frame="void" rules="none"> -<col class="field-name" /> -<col class="field-body" /> -<tbody valign="top"> -<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first last simple"> -<li><strong>address</strong> – url that you wish to remap</li> -<li><strong>ip_address</strong> – IP Address that will handle all traffic for the address passed in</li> -</ul> -</td> -</tr> -</tbody> -</table> -</dd></dl> - -<dl class="method"> -<dt id="browsermobproxy.Client.request_interceptor"> -<tt class="descname">request_interceptor</tt><big>(</big><em>js</em><big>)</big><a class="headerlink" href="#browsermobproxy.Client.request_interceptor" title="Permalink to this definition">¶</a></dt> -<dd><p>Executes the javascript against each request</p> -<table class="docutils field-list" frame="void" rules="none"> -<col class="field-name" /> -<col class="field-body" /> -<tbody valign="top"> -<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><strong>js</strong> – the javascript to execute</td> -</tr> -</tbody> -</table> -</dd></dl> - -<dl class="method"> -<dt id="browsermobproxy.Client.response_interceptor"> -<tt class="descname">response_interceptor</tt><big>(</big><em>js</em><big>)</big><a class="headerlink" href="#browsermobproxy.Client.response_interceptor" title="Permalink to this definition">¶</a></dt> -<dd><p>Executes the javascript against each response</p> -<table class="docutils field-list" frame="void" rules="none"> -<col class="field-name" /> -<col class="field-body" /> -<tbody valign="top"> -<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><strong>js</strong> – the javascript to execute</td> -</tr> -</tbody> -</table> -</dd></dl> - -<dl class="method"> -<dt id="browsermobproxy.Client.retry"> -<tt class="descname">retry</tt><big>(</big><em>retry_count</em><big>)</big><a class="headerlink" href="#browsermobproxy.Client.retry" title="Permalink to this definition">¶</a></dt> -<dd><p>Retries. No idea what its used for, but its in the API...</p> -<table class="docutils field-list" frame="void" rules="none"> -<col class="field-name" /> -<col class="field-body" /> -<tbody valign="top"> -<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><strong>retry_count</strong> – the number of retries</td> -</tr> -</tbody> -</table> -</dd></dl> - -<dl class="method"> -<dt id="browsermobproxy.Client.rewrite_url"> -<tt class="descname">rewrite_url</tt><big>(</big><em>match</em>, <em>replace</em><big>)</big><a class="headerlink" href="#browsermobproxy.Client.rewrite_url" title="Permalink to this definition">¶</a></dt> -<dd><p>Rewrites the requested url.</p> -<table class="docutils field-list" frame="void" rules="none"> -<col class="field-name" /> -<col class="field-body" /> -<tbody valign="top"> -<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first last simple"> -<li><strong>match</strong> – a regex to match requests with</li> -<li><strong>replace</strong> – unicode a string to replace the matches with</li> -</ul> -</td> -</tr> -</tbody> -</table> -</dd></dl> - -<dl class="method"> -<dt id="browsermobproxy.Client.selenium_proxy"> -<tt class="descname">selenium_proxy</tt><big>(</big><big>)</big><a class="headerlink" href="#browsermobproxy.Client.selenium_proxy" title="Permalink to this definition">¶</a></dt> -<dd><p>Returns a Selenium WebDriver Proxy class with details of the HTTP Proxy</p> -</dd></dl> - -<dl class="method"> -<dt id="browsermobproxy.Client.timeouts"> -<tt class="descname">timeouts</tt><big>(</big><em>options</em><big>)</big><a class="headerlink" href="#browsermobproxy.Client.timeouts" title="Permalink to this definition">¶</a></dt> -<dd><p>Configure various timeouts in the proxy</p> -<table class="docutils field-list" frame="void" rules="none"> -<col class="field-name" /> -<col class="field-body" /> -<tbody valign="top"> -<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><strong>options</strong> – A dictionary with all the details you want to set. request - request timeout (in seconds) read - read timeout (in seconds) connection - connection timeout (in seconds) dns - dns lookup timeout (in seconds)</td> -</tr> -</tbody> -</table> -</dd></dl> - -<dl class="method"> -<dt id="browsermobproxy.Client.wait_for_traffic_to_stop"> -<tt class="descname">wait_for_traffic_to_stop</tt><big>(</big><em>quiet_period</em>, <em>timeout</em><big>)</big><a class="headerlink" href="#browsermobproxy.Client.wait_for_traffic_to_stop" title="Permalink to this definition">¶</a></dt> -<dd><p>Waits for the network to be quiet</p> -<table class="docutils field-list" frame="void" rules="none"> -<col class="field-name" /> -<col class="field-body" /> -<tbody valign="top"> -<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first last simple"> -<li><strong>quiet_period</strong> – number of seconds the network needs to be quiet for</li> -<li><strong>timeout</strong> – max number of seconds to wait</li> -</ul> -</td> -</tr> -</tbody> -</table> -</dd></dl> - -<dl class="method"> -<dt id="browsermobproxy.Client.webdriver_proxy"> -<tt class="descname">webdriver_proxy</tt><big>(</big><big>)</big><a class="headerlink" href="#browsermobproxy.Client.webdriver_proxy" title="Permalink to this definition">¶</a></dt> -<dd><p>Returns a Selenium WebDriver Proxy class with details of the HTTP Proxy</p> -</dd></dl> - -<dl class="method"> -<dt id="browsermobproxy.Client.whitelist"> -<tt class="descname">whitelist</tt><big>(</big><em>regexp</em>, <em>status_code</em><big>)</big><a class="headerlink" href="#browsermobproxy.Client.whitelist" title="Permalink to this definition">¶</a></dt> -<dd><p>Sets a list of URL patterns to whitelist</p> -<table class="docutils field-list" frame="void" rules="none"> -<col class="field-name" /> -<col class="field-body" /> -<tbody valign="top"> -<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first last simple"> -<li><strong>regex</strong> – a comma separated list of regular expressions</li> -<li><strong>status_code</strong> – the HTTP status code to return for URLs that do not match the whitelist</li> -</ul> -</td> -</tr> -</tbody> -</table> -</dd></dl> - -</dd></dl> - -</div> - - - </div> - </div> - </div> - <div class="sphinxsidebar"> - <div class="sphinxsidebarwrapper"> - <h4>Previous topic</h4> - <p class="topless"><a href="index.html" - title="previous chapter">Welcome to BrowserMob Proxy’s documentation!</a></p> - <h4>Next topic</h4> - <p class="topless"><a href="server.html" - title="next chapter"><tt class="docutils literal"><span class="pre">server</span></tt> Package</a></p> - <h3>This Page</h3> - <ul class="this-page-menu"> - <li><a href="_sources/client.txt" - rel="nofollow">Show Source</a></li> - </ul> -<div id="searchbox" style="display: none"> - <h3>Quick search</h3> - <form class="search" action="search.html" method="get"> - <input type="text" name="q" /> - <input type="submit" value="Go" /> - <input type="hidden" name="check_keywords" value="yes" /> - <input type="hidden" name="area" value="default" /> - </form> - <p class="searchtip" style="font-size: 90%"> - Enter search terms or a module, class or function name. - </p> -</div> -<script type="text/javascript">$('#searchbox').show(0);</script> - </div> - </div> - <div class="clearer"></div> - </div> - <div class="related"> - <h3>Navigation</h3> - <ul> - <li class="right" style="margin-right: 10px"> - <a href="genindex.html" title="General Index" - >index</a></li> - <li class="right" > - <a href="py-modindex.html" title="Python Module Index" - >modules</a> |</li> - <li class="right" > - <a href="server.html" title="server Package" - >next</a> |</li> - <li class="right" > - <a href="index.html" title="Welcome to BrowserMob Proxy’s documentation!" - >previous</a> |</li> - <li><a href="index.html">BrowserMob Proxy 0.6.0 documentation</a> »</li> - </ul> - </div> - <div class="footer"> - © Copyright 2014, David Burns. - Created using <a href="http://sphinx-doc.org/">Sphinx</a> 1.2.1. - </div> - </body> -</html>
\ No newline at end of file diff --git a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/genindex.html b/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/genindex.html deleted file mode 100644 index 6976d22e6..000000000 --- a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/genindex.html +++ /dev/null @@ -1,297 +0,0 @@ - -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" - "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> - - -<html xmlns="http://www.w3.org/1999/xhtml"> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> - - <title>Index — BrowserMob Proxy 0.6.0 documentation</title> - - <link rel="stylesheet" href="_static/default.css" type="text/css" /> - <link rel="stylesheet" href="_static/pygments.css" type="text/css" /> - - <script type="text/javascript"> - var DOCUMENTATION_OPTIONS = { - URL_ROOT: './', - VERSION: '0.6.0', - COLLAPSE_INDEX: false, - FILE_SUFFIX: '.html', - HAS_SOURCE: true - }; - </script> - <script type="text/javascript" src="_static/jquery.js"></script> - <script type="text/javascript" src="_static/underscore.js"></script> - <script type="text/javascript" src="_static/doctools.js"></script> - <link rel="top" title="BrowserMob Proxy 0.6.0 documentation" href="index.html" /> - </head> - <body> - <div class="related"> - <h3>Navigation</h3> - <ul> - <li class="right" style="margin-right: 10px"> - <a href="#" title="General Index" - accesskey="I">index</a></li> - <li class="right" > - <a href="py-modindex.html" title="Python Module Index" - >modules</a> |</li> - <li><a href="index.html">BrowserMob Proxy 0.6.0 documentation</a> »</li> - </ul> - </div> - - <div class="document"> - <div class="documentwrapper"> - <div class="bodywrapper"> - <div class="body"> - - -<h1 id="index">Index</h1> - -<div class="genindex-jumpbox"> - <a href="#A"><strong>A</strong></a> - | <a href="#B"><strong>B</strong></a> - | <a href="#C"><strong>C</strong></a> - | <a href="#H"><strong>H</strong></a> - | <a href="#L"><strong>L</strong></a> - | <a href="#N"><strong>N</strong></a> - | <a href="#R"><strong>R</strong></a> - | <a href="#S"><strong>S</strong></a> - | <a href="#T"><strong>T</strong></a> - | <a href="#U"><strong>U</strong></a> - | <a href="#W"><strong>W</strong></a> - -</div> -<h2 id="A">A</h2> -<table style="width: 100%" class="indextable genindextable"><tr> - <td style="width: 33%" valign="top"><dl> - - <dt><a href="client.html#browsermobproxy.Client.add_to_capabilities">add_to_capabilities() (browsermobproxy.Client method)</a> - </dt> - - </dl></td> -</tr></table> - -<h2 id="B">B</h2> -<table style="width: 100%" class="indextable genindextable"><tr> - <td style="width: 33%" valign="top"><dl> - - <dt><a href="client.html#browsermobproxy.Client.basic_authentication">basic_authentication() (browsermobproxy.Client method)</a> - </dt> - - - <dt><a href="client.html#browsermobproxy.Client.blacklist">blacklist() (browsermobproxy.Client method)</a> - </dt> - - </dl></td> - <td style="width: 33%" valign="top"><dl> - - <dt><a href="client.html#module-browsermobproxy">browsermobproxy (module)</a>, <a href="server.html#module-browsermobproxy">[1]</a> - </dt> - - </dl></td> -</tr></table> - -<h2 id="C">C</h2> -<table style="width: 100%" class="indextable genindextable"><tr> - <td style="width: 33%" valign="top"><dl> - - <dt><a href="client.html#browsermobproxy.Client.clear_dns_cache">clear_dns_cache() (browsermobproxy.Client method)</a> - </dt> - - - <dt><a href="client.html#browsermobproxy.Client">Client (class in browsermobproxy)</a> - </dt> - - </dl></td> - <td style="width: 33%" valign="top"><dl> - - <dt><a href="client.html#browsermobproxy.Client.close">close() (browsermobproxy.Client method)</a> - </dt> - - - <dt><a href="server.html#browsermobproxy.Server.create_proxy">create_proxy() (browsermobproxy.Server method)</a> - </dt> - - </dl></td> -</tr></table> - -<h2 id="H">H</h2> -<table style="width: 100%" class="indextable genindextable"><tr> - <td style="width: 33%" valign="top"><dl> - - <dt><a href="client.html#browsermobproxy.Client.har">har (browsermobproxy.Client attribute)</a> - </dt> - - </dl></td> - <td style="width: 33%" valign="top"><dl> - - <dt><a href="client.html#browsermobproxy.Client.headers">headers() (browsermobproxy.Client method)</a> - </dt> - - </dl></td> -</tr></table> - -<h2 id="L">L</h2> -<table style="width: 100%" class="indextable genindextable"><tr> - <td style="width: 33%" valign="top"><dl> - - <dt><a href="client.html#browsermobproxy.Client.limits">limits() (browsermobproxy.Client method)</a> - </dt> - - </dl></td> -</tr></table> - -<h2 id="N">N</h2> -<table style="width: 100%" class="indextable genindextable"><tr> - <td style="width: 33%" valign="top"><dl> - - <dt><a href="client.html#browsermobproxy.Client.new_har">new_har() (browsermobproxy.Client method)</a> - </dt> - - </dl></td> - <td style="width: 33%" valign="top"><dl> - - <dt><a href="client.html#browsermobproxy.Client.new_page">new_page() (browsermobproxy.Client method)</a> - </dt> - - </dl></td> -</tr></table> - -<h2 id="R">R</h2> -<table style="width: 100%" class="indextable genindextable"><tr> - <td style="width: 33%" valign="top"><dl> - - <dt><a href="client.html#browsermobproxy.Client.remap_hosts">remap_hosts() (browsermobproxy.Client method)</a> - </dt> - - - <dt><a href="client.html#browsermobproxy.Client.request_interceptor">request_interceptor() (browsermobproxy.Client method)</a> - </dt> - - - <dt><a href="client.html#browsermobproxy.Client.response_interceptor">response_interceptor() (browsermobproxy.Client method)</a> - </dt> - - </dl></td> - <td style="width: 33%" valign="top"><dl> - - <dt><a href="client.html#browsermobproxy.Client.retry">retry() (browsermobproxy.Client method)</a> - </dt> - - - <dt><a href="client.html#browsermobproxy.Client.rewrite_url">rewrite_url() (browsermobproxy.Client method)</a> - </dt> - - </dl></td> -</tr></table> - -<h2 id="S">S</h2> -<table style="width: 100%" class="indextable genindextable"><tr> - <td style="width: 33%" valign="top"><dl> - - <dt><a href="client.html#browsermobproxy.Client.selenium_proxy">selenium_proxy() (browsermobproxy.Client method)</a> - </dt> - - - <dt><a href="server.html#browsermobproxy.Server">Server (class in browsermobproxy)</a> - </dt> - - </dl></td> - <td style="width: 33%" valign="top"><dl> - - <dt><a href="server.html#browsermobproxy.Server.start">start() (browsermobproxy.Server method)</a> - </dt> - - - <dt><a href="server.html#browsermobproxy.Server.stop">stop() (browsermobproxy.Server method)</a> - </dt> - - </dl></td> -</tr></table> - -<h2 id="T">T</h2> -<table style="width: 100%" class="indextable genindextable"><tr> - <td style="width: 33%" valign="top"><dl> - - <dt><a href="client.html#browsermobproxy.Client.timeouts">timeouts() (browsermobproxy.Client method)</a> - </dt> - - </dl></td> -</tr></table> - -<h2 id="U">U</h2> -<table style="width: 100%" class="indextable genindextable"><tr> - <td style="width: 33%" valign="top"><dl> - - <dt><a href="server.html#browsermobproxy.Server.url">url (browsermobproxy.Server attribute)</a> - </dt> - - </dl></td> -</tr></table> - -<h2 id="W">W</h2> -<table style="width: 100%" class="indextable genindextable"><tr> - <td style="width: 33%" valign="top"><dl> - - <dt><a href="client.html#browsermobproxy.Client.wait_for_traffic_to_stop">wait_for_traffic_to_stop() (browsermobproxy.Client method)</a> - </dt> - - - <dt><a href="client.html#browsermobproxy.Client.webdriver_proxy">webdriver_proxy() (browsermobproxy.Client method)</a> - </dt> - - </dl></td> - <td style="width: 33%" valign="top"><dl> - - <dt><a href="client.html#browsermobproxy.Client.whitelist">whitelist() (browsermobproxy.Client method)</a> - </dt> - - </dl></td> -</tr></table> - - - - </div> - </div> - </div> - <div class="sphinxsidebar"> - <div class="sphinxsidebarwrapper"> - - - -<div id="searchbox" style="display: none"> - <h3>Quick search</h3> - <form class="search" action="search.html" method="get"> - <input type="text" name="q" /> - <input type="submit" value="Go" /> - <input type="hidden" name="check_keywords" value="yes" /> - <input type="hidden" name="area" value="default" /> - </form> - <p class="searchtip" style="font-size: 90%"> - Enter search terms or a module, class or function name. - </p> -</div> -<script type="text/javascript">$('#searchbox').show(0);</script> - </div> - </div> - <div class="clearer"></div> - </div> - <div class="related"> - <h3>Navigation</h3> - <ul> - <li class="right" style="margin-right: 10px"> - <a href="#" title="General Index" - >index</a></li> - <li class="right" > - <a href="py-modindex.html" title="Python Module Index" - >modules</a> |</li> - <li><a href="index.html">BrowserMob Proxy 0.6.0 documentation</a> »</li> - </ul> - </div> - <div class="footer"> - © Copyright 2014, David Burns. - Created using <a href="http://sphinx-doc.org/">Sphinx</a> 1.2.1. - </div> - </body> -</html>
\ No newline at end of file diff --git a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/index.html b/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/index.html deleted file mode 100644 index 59fa22e29..000000000 --- a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/index.html +++ /dev/null @@ -1,174 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" - "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> - - -<html xmlns="http://www.w3.org/1999/xhtml"> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> - - <title>Welcome to BrowserMob Proxy’s documentation! — BrowserMob Proxy 0.6.0 documentation</title> - - <link rel="stylesheet" href="_static/default.css" type="text/css" /> - <link rel="stylesheet" href="_static/pygments.css" type="text/css" /> - - <script type="text/javascript"> - var DOCUMENTATION_OPTIONS = { - URL_ROOT: './', - VERSION: '0.6.0', - COLLAPSE_INDEX: false, - FILE_SUFFIX: '.html', - HAS_SOURCE: true - }; - </script> - <script type="text/javascript" src="_static/jquery.js"></script> - <script type="text/javascript" src="_static/underscore.js"></script> - <script type="text/javascript" src="_static/doctools.js"></script> - <link rel="top" title="BrowserMob Proxy 0.6.0 documentation" href="#" /> - <link rel="next" title="client Package" href="client.html" /> - </head> - <body> - <div class="related"> - <h3>Navigation</h3> - <ul> - <li class="right" style="margin-right: 10px"> - <a href="genindex.html" title="General Index" - accesskey="I">index</a></li> - <li class="right" > - <a href="py-modindex.html" title="Python Module Index" - >modules</a> |</li> - <li class="right" > - <a href="client.html" title="client Package" - accesskey="N">next</a> |</li> - <li><a href="#">BrowserMob Proxy 0.6.0 documentation</a> »</li> - </ul> - </div> - - <div class="document"> - <div class="documentwrapper"> - <div class="bodywrapper"> - <div class="body"> - - <div class="section" id="welcome-to-browsermob-proxy-s-documentation"> -<h1>Welcome to BrowserMob Proxy’s documentation!<a class="headerlink" href="#welcome-to-browsermob-proxy-s-documentation" title="Permalink to this headline">¶</a></h1> -<p>Python client for the BrowserMob Proxy 2.0 REST API.</p> -<div class="section" id="how-to-install"> -<h2>How to install<a class="headerlink" href="#how-to-install" title="Permalink to this headline">¶</a></h2> -<p>BrowserMob Proxy is available on <a class="reference external" href="http://pypi.python.org/pypi/browsermob-proxy">PyPI</a>, so you can install it with <tt class="docutils literal"><span class="pre">pip</span></tt>:</p> -<div class="highlight-python"><div class="highlight"><pre>$ pip install browsermob-proxy -</pre></div> -</div> -<p>Or with <cite>easy_install</cite>:</p> -<div class="highlight-python"><div class="highlight"><pre>$ easy_install browsermob-proxy -</pre></div> -</div> -<p>Or by cloning the repo from <a class="reference external" href="https://github.com/AutomatedTester/browsermob-proxy-py">GitHub</a>:</p> -<div class="highlight-python"><div class="highlight"><pre>$ git clone git://github.com/AutomatedTester/browsermob-proxy-py.git -</pre></div> -</div> -<p>Then install it by running:</p> -<div class="highlight-python"><div class="highlight"><pre>$ python setup.py install -</pre></div> -</div> -</div> -<div class="section" id="how-to-use-with-selenium-webdriver"> -<h2>How to use with selenium-webdriver<a class="headerlink" href="#how-to-use-with-selenium-webdriver" title="Permalink to this headline">¶</a></h2> -<p>Manually:</p> -<div class="highlight-python"><div class="highlight"><pre><span class="kn">from</span> <span class="nn">browsermobproxy</span> <span class="kn">import</span> <span class="n">Server</span> -<span class="n">server</span> <span class="o">=</span> <span class="n">Server</span><span class="p">(</span><span class="s">"path/to/browsermob-proxy"</span><span class="p">)</span> -<span class="n">server</span><span class="o">.</span><span class="n">start</span><span class="p">()</span> -<span class="n">proxy</span> <span class="o">=</span> <span class="n">server</span><span class="o">.</span><span class="n">create_proxy</span><span class="p">()</span> - -<span class="kn">from</span> <span class="nn">selenium</span> <span class="kn">import</span> <span class="n">webdriver</span> -<span class="n">profile</span> <span class="o">=</span> <span class="n">webdriver</span><span class="o">.</span><span class="n">FirefoxProfile</span><span class="p">()</span> -<span class="n">profile</span><span class="o">.</span><span class="n">set_proxy</span><span class="p">(</span><span class="n">proxy</span><span class="o">.</span><span class="n">selenium_proxy</span><span class="p">())</span> -<span class="n">driver</span> <span class="o">=</span> <span class="n">webdriver</span><span class="o">.</span><span class="n">Firefox</span><span class="p">(</span><span class="n">firefox_profile</span><span class="o">=</span><span class="n">profile</span><span class="p">)</span> - - -<span class="n">proxy</span><span class="o">.</span><span class="n">new_har</span><span class="p">(</span><span class="s">"google"</span><span class="p">)</span> -<span class="n">driver</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">"http://www.google.co.uk"</span><span class="p">)</span> -<span class="n">proxy</span><span class="o">.</span><span class="n">har</span> <span class="c"># returns a HAR JSON blob</span> - -<span class="n">server</span><span class="o">.</span><span class="n">stop</span><span class="p">()</span> -<span class="n">driver</span><span class="o">.</span><span class="n">quit</span><span class="p">()</span> -</pre></div> -</div> -<p>Contents:</p> -<div class="toctree-wrapper compound"> -<ul> -<li class="toctree-l1"><a class="reference internal" href="client.html"><tt class="docutils literal"><span class="pre">client</span></tt> Package</a></li> -<li class="toctree-l1"><a class="reference internal" href="server.html"><tt class="docutils literal"><span class="pre">server</span></tt> Package</a></li> -</ul> -</div> -</div> -</div> -<div class="section" id="indices-and-tables"> -<h1>Indices and tables<a class="headerlink" href="#indices-and-tables" title="Permalink to this headline">¶</a></h1> -<ul class="simple"> -<li><a class="reference internal" href="genindex.html"><em>Index</em></a></li> -<li><a class="reference internal" href="py-modindex.html"><em>Module Index</em></a></li> -<li><a class="reference internal" href="search.html"><em>Search Page</em></a></li> -</ul> -</div> - - - </div> - </div> - </div> - <div class="sphinxsidebar"> - <div class="sphinxsidebarwrapper"> - <h3><a href="#">Table Of Contents</a></h3> - <ul> -<li><a class="reference internal" href="#">Welcome to BrowserMob Proxy’s documentation!</a><ul> -<li><a class="reference internal" href="#how-to-install">How to install</a></li> -<li><a class="reference internal" href="#how-to-use-with-selenium-webdriver">How to use with selenium-webdriver</a></li> -</ul> -</li> -<li><a class="reference internal" href="#indices-and-tables">Indices and tables</a></li> -</ul> - - <h4>Next topic</h4> - <p class="topless"><a href="client.html" - title="next chapter"><tt class="docutils literal"><span class="pre">client</span></tt> Package</a></p> - <h3>This Page</h3> - <ul class="this-page-menu"> - <li><a href="_sources/index.txt" - rel="nofollow">Show Source</a></li> - </ul> -<div id="searchbox" style="display: none"> - <h3>Quick search</h3> - <form class="search" action="search.html" method="get"> - <input type="text" name="q" /> - <input type="submit" value="Go" /> - <input type="hidden" name="check_keywords" value="yes" /> - <input type="hidden" name="area" value="default" /> - </form> - <p class="searchtip" style="font-size: 90%"> - Enter search terms or a module, class or function name. - </p> -</div> -<script type="text/javascript">$('#searchbox').show(0);</script> - </div> - </div> - <div class="clearer"></div> - </div> - <div class="related"> - <h3>Navigation</h3> - <ul> - <li class="right" style="margin-right: 10px"> - <a href="genindex.html" title="General Index" - >index</a></li> - <li class="right" > - <a href="py-modindex.html" title="Python Module Index" - >modules</a> |</li> - <li class="right" > - <a href="client.html" title="client Package" - >next</a> |</li> - <li><a href="#">BrowserMob Proxy 0.6.0 documentation</a> »</li> - </ul> - </div> - <div class="footer"> - © Copyright 2014, David Burns. - Created using <a href="http://sphinx-doc.org/">Sphinx</a> 1.2.1. - </div> - </body> -</html>
\ No newline at end of file diff --git a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/objects.inv b/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/objects.inv deleted file mode 100644 index 92c3c7bbd..000000000 --- a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/objects.inv +++ /dev/null @@ -1,6 +0,0 @@ -# Sphinx inventory version 2 -# Project: BrowserMob Proxy -# Version: 0.6.0 -# The remainder of this file is compressed using zlib. -xÚ¥TÁnÂ0½ó•¶kѸrÝi‡I“ø€ÈMµ–4í®ð÷¤Ó€M¨dǺ~ÏñË{ ÑQçp_‰ºµ‡}U¯ªþP‡éDzÕàª×èÕ˱´Øa÷è»:!r« °m/smjÛä†7Øá¢á8 -rˆMÏqHÇX‡<÷)ø9ƒr±~¬êkÈòÙvºdüPÔP§È{|bCm£«V•Ím'¶4
Yƒ¶é‹,(Å®„§…ÓxPejÅÈMÞWX/Fžqc¤ O÷1ó#å‡çŒFc¡‡†<)¡”ÐXK€cKŠžDKÀŽ¦Ož*ŒcJ"—kǨ|(S+åÁ¸N’î¶Å²Ù#'åÌÀ¾ 7m•2¸ô±üoôF 5ÛÈF¶Û”ÂdEÑØßÅ•Ct„X"3Ò3ÅnÒî<°3sw4Þ÷»_ ‡\¤zãÁ¾ß•iYËÉ/¿Í>se¥€q(³‹§@EÈóKý–^Ùçz¿/z’Ä
\ No newline at end of file diff --git a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/py-modindex.html b/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/py-modindex.html deleted file mode 100644 index 08a48fbfd..000000000 --- a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/py-modindex.html +++ /dev/null @@ -1,112 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" - "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> - - -<html xmlns="http://www.w3.org/1999/xhtml"> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> - - <title>Python Module Index — BrowserMob Proxy 0.6.0 documentation</title> - - <link rel="stylesheet" href="_static/default.css" type="text/css" /> - <link rel="stylesheet" href="_static/pygments.css" type="text/css" /> - - <script type="text/javascript"> - var DOCUMENTATION_OPTIONS = { - URL_ROOT: './', - VERSION: '0.6.0', - COLLAPSE_INDEX: false, - FILE_SUFFIX: '.html', - HAS_SOURCE: true - }; - </script> - <script type="text/javascript" src="_static/jquery.js"></script> - <script type="text/javascript" src="_static/underscore.js"></script> - <script type="text/javascript" src="_static/doctools.js"></script> - <link rel="top" title="BrowserMob Proxy 0.6.0 documentation" href="index.html" /> - - - <script type="text/javascript"> - DOCUMENTATION_OPTIONS.COLLAPSE_INDEX = true; - </script> - - - </head> - <body> - <div class="related"> - <h3>Navigation</h3> - <ul> - <li class="right" style="margin-right: 10px"> - <a href="genindex.html" title="General Index" - accesskey="I">index</a></li> - <li class="right" > - <a href="#" title="Python Module Index" - >modules</a> |</li> - <li><a href="index.html">BrowserMob Proxy 0.6.0 documentation</a> »</li> - </ul> - </div> - - <div class="document"> - <div class="documentwrapper"> - <div class="bodywrapper"> - <div class="body"> - - - <h1>Python Module Index</h1> - - <div class="modindex-jumpbox"> - <a href="#cap-b"><strong>b</strong></a> - </div> - - <table class="indextable modindextable" cellspacing="0" cellpadding="2"> - <tr class="pcap"><td></td><td> </td><td></td></tr> - <tr class="cap" id="cap-b"><td></td><td> - <strong>b</strong></td><td></td></tr> - <tr> - <td></td> - <td> - <a href="server.html#module-browsermobproxy"><tt class="xref">browsermobproxy</tt></a></td><td> - <em></em></td></tr> - </table> - - - </div> - </div> - </div> - <div class="sphinxsidebar"> - <div class="sphinxsidebarwrapper"> -<div id="searchbox" style="display: none"> - <h3>Quick search</h3> - <form class="search" action="search.html" method="get"> - <input type="text" name="q" /> - <input type="submit" value="Go" /> - <input type="hidden" name="check_keywords" value="yes" /> - <input type="hidden" name="area" value="default" /> - </form> - <p class="searchtip" style="font-size: 90%"> - Enter search terms or a module, class or function name. - </p> -</div> -<script type="text/javascript">$('#searchbox').show(0);</script> - </div> - </div> - <div class="clearer"></div> - </div> - <div class="related"> - <h3>Navigation</h3> - <ul> - <li class="right" style="margin-right: 10px"> - <a href="genindex.html" title="General Index" - >index</a></li> - <li class="right" > - <a href="#" title="Python Module Index" - >modules</a> |</li> - <li><a href="index.html">BrowserMob Proxy 0.6.0 documentation</a> »</li> - </ul> - </div> - <div class="footer"> - © Copyright 2014, David Burns. - Created using <a href="http://sphinx-doc.org/">Sphinx</a> 1.2.1. - </div> - </body> -</html>
\ No newline at end of file diff --git a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/search.html b/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/search.html deleted file mode 100644 index 9adb77d91..000000000 --- a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/search.html +++ /dev/null @@ -1,105 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" - "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> - - -<html xmlns="http://www.w3.org/1999/xhtml"> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> - - <title>Search — BrowserMob Proxy 0.6.0 documentation</title> - - <link rel="stylesheet" href="_static/default.css" type="text/css" /> - <link rel="stylesheet" href="_static/pygments.css" type="text/css" /> - - <script type="text/javascript"> - var DOCUMENTATION_OPTIONS = { - URL_ROOT: './', - VERSION: '0.6.0', - COLLAPSE_INDEX: false, - FILE_SUFFIX: '.html', - HAS_SOURCE: true - }; - </script> - <script type="text/javascript" src="_static/jquery.js"></script> - <script type="text/javascript" src="_static/underscore.js"></script> - <script type="text/javascript" src="_static/doctools.js"></script> - <script type="text/javascript" src="_static/searchtools.js"></script> - <link rel="top" title="BrowserMob Proxy 0.6.0 documentation" href="index.html" /> - <script type="text/javascript"> - jQuery(function() { Search.loadIndex("searchindex.js"); }); - </script> - - <script type="text/javascript" id="searchindexloader"></script> - - - </head> - <body> - <div class="related"> - <h3>Navigation</h3> - <ul> - <li class="right" style="margin-right: 10px"> - <a href="genindex.html" title="General Index" - accesskey="I">index</a></li> - <li class="right" > - <a href="py-modindex.html" title="Python Module Index" - >modules</a> |</li> - <li><a href="index.html">BrowserMob Proxy 0.6.0 documentation</a> »</li> - </ul> - </div> - - <div class="document"> - <div class="documentwrapper"> - <div class="bodywrapper"> - <div class="body"> - - <h1 id="search-documentation">Search</h1> - <div id="fallback" class="admonition warning"> - <script type="text/javascript">$('#fallback').hide();</script> - <p> - Please activate JavaScript to enable the search - functionality. - </p> - </div> - <p> - From here you can search these documents. Enter your search - words into the box below and click "search". Note that the search - function will automatically search for all of the words. Pages - containing fewer words won't appear in the result list. - </p> - <form action="" method="get"> - <input type="text" name="q" value="" /> - <input type="submit" value="search" /> - <span id="search-progress" style="padding-left: 10px"></span> - </form> - - <div id="search-results"> - - </div> - - </div> - </div> - </div> - <div class="sphinxsidebar"> - <div class="sphinxsidebarwrapper"> - </div> - </div> - <div class="clearer"></div> - </div> - <div class="related"> - <h3>Navigation</h3> - <ul> - <li class="right" style="margin-right: 10px"> - <a href="genindex.html" title="General Index" - >index</a></li> - <li class="right" > - <a href="py-modindex.html" title="Python Module Index" - >modules</a> |</li> - <li><a href="index.html">BrowserMob Proxy 0.6.0 documentation</a> »</li> - </ul> - </div> - <div class="footer"> - © Copyright 2014, David Burns. - Created using <a href="http://sphinx-doc.org/">Sphinx</a> 1.2.1. - </div> - </body> -</html>
\ No newline at end of file diff --git a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/searchindex.js b/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/searchindex.js deleted file mode 100644 index cf2f7d0f2..000000000 --- a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/searchindex.js +++ /dev/null @@ -1 +0,0 @@ -Search.setIndex({envversion:42,terms:{all:[1,2],basic_authent:1,execut:1,whitelist:1,rest:0,bandwidth:1,code:1,comma:1,kbp:1,paramet:[1,2],profil:0,configur:1,param:[],should:2,quiet_period:1,add:1,latenc:1,dict:[],blob:0,har:[0,1],pass:1,match:1,"return":[0,1],string:1,variou:1,get:[0,1,2],read:1,browsermobproxi:[0,1,2],express:1,stop:[0,2],number:1,repo:0,capturecont:1,traffic:1,upstream:1,password:1,ip_address:1,specif:1,list:1,authent:1,server:[],separ:1,retry_count:1,api:[0,1],timeout:1,each:1,through:1,unicod:1,where:1,page:[0,1],www:0,set:[1,2],captur:1,manual:0,idea:1,second:1,remap:1,connect:[1,2],domain:1,arg:2,close:1,process:2,port:[1,2],index:0,statu:1,network:1,item:2,pattern:1,content:[0,1],rewrit:1,retri:1,max:1,"import":0,ref:1,refer:1,shut:1,run:[0,2],webdriver_proxi:1,javascript:1,bodi:1,host:1,dictionari:[1,2],address:1,path:[0,2],wait:[1,2],search:0,quiet:1,against:1,initialis:[1,2],instanc:1,request_interceptor:1,rewrite_url:1,com:0,firefox:0,status_cod:1,modul:0,easy_instal:0,automat:1,down:1,header:1,"boolean":1,empti:2,wait_for_traffic_to_stop:1,regexp:1,regex:1,quit:0,given:1,git:0,from:[0,1],interact:2,json:0,been:1,avail:0,start:[0,2],live:1,basic:1,upstreamkbp:1,until:2,more:2,automatedtest:0,desir:1,option:[1,2],python:0,blacklist:1,hold:2,capturebinarycont:1,cach:1,clear_dns_cach:1,none:1,keyword:1,"default":[1,2],wish:1,setup:0,batch:2,record:1,limit:1,can:[0,2],str:[],remap_host:1,firefoxprofil:0,new_har:[0,1],"int":[],request:1,selenium_proxi:[0,1],capturehead:1,downstreamkbp:1,need:[1,2],replac:1,file:2,pip:0,create_proxi:[0,2],mai:2,downstream:1,want:1,when:1,detail:[1,2],binari:1,valid:1,lookup:1,futur:2,"new":1,you:[0,1,2],respons:1,add_to_cap:1,http:[0,1],allow:2,usernam:1,pypi:0,clone:0,object:[1,2],driver:0,what:1,firefox_profil:0,capabl:1,regular:1,set_proxi:0,associ:1,"class":[1,2],googl:0,handl:1,github:0,response_interceptor:1,url:[1,2],entri:1,clear:1,credenti:1,inform:1,client:[],thi:[1,2],new_pag:1},objtypes:{"0":"py:module","1":"py:method","2":"py:attribute","3":"py:class"},objnames:{"0":["py","module","Python module"],"1":["py","method","Python method"],"2":["py","attribute","Python attribute"],"3":["py","class","Python class"]},filenames:["index","client","server"],titles:["Welcome to BrowserMob Proxy’s documentation!","<tt class=\"docutils literal\"><span class=\"pre\">client</span></tt> Package","<tt class=\"docutils literal\"><span class=\"pre\">server</span></tt> Package"],objects:{"":{browsermobproxy:[2,0,0,"-"]},browsermobproxy:{Client:[1,3,1,""],Server:[2,3,1,""]},"browsermobproxy.Server":{url:[2,2,1,""],start:[2,1,1,""],stop:[2,1,1,""],create_proxy:[2,1,1,""]},"browsermobproxy.Client":{remap_hosts:[1,1,1,""],blacklist:[1,1,1,""],selenium_proxy:[1,1,1,""],response_interceptor:[1,1,1,""],webdriver_proxy:[1,1,1,""],clear_dns_cache:[1,1,1,""],timeouts:[1,1,1,""],whitelist:[1,1,1,""],new_page:[1,1,1,""],basic_authentication:[1,1,1,""],retry:[1,1,1,""],request_interceptor:[1,1,1,""],limits:[1,1,1,""],wait_for_traffic_to_stop:[1,1,1,""],rewrite_url:[1,1,1,""],close:[1,1,1,""],har:[1,2,1,""],add_to_capabilities:[1,1,1,""],headers:[1,1,1,""],new_har:[1,1,1,""]}},titleterms:{welcom:0,packag:[1,2],browsermob:0,selenium:0,webdriv:0,server:2,how:0,client:1,indic:0,tabl:0,instal:0,document:0,proxi:0}})
\ No newline at end of file diff --git a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/server.html b/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/server.html deleted file mode 100644 index 74146e4ce..000000000 --- a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/_build/html/server.html +++ /dev/null @@ -1,157 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" - "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> - - -<html xmlns="http://www.w3.org/1999/xhtml"> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> - - <title>server Package — BrowserMob Proxy 0.6.0 documentation</title> - - <link rel="stylesheet" href="_static/default.css" type="text/css" /> - <link rel="stylesheet" href="_static/pygments.css" type="text/css" /> - - <script type="text/javascript"> - var DOCUMENTATION_OPTIONS = { - URL_ROOT: './', - VERSION: '0.6.0', - COLLAPSE_INDEX: false, - FILE_SUFFIX: '.html', - HAS_SOURCE: true - }; - </script> - <script type="text/javascript" src="_static/jquery.js"></script> - <script type="text/javascript" src="_static/underscore.js"></script> - <script type="text/javascript" src="_static/doctools.js"></script> - <link rel="top" title="BrowserMob Proxy 0.6.0 documentation" href="index.html" /> - <link rel="prev" title="client Package" href="client.html" /> - </head> - <body> - <div class="related"> - <h3>Navigation</h3> - <ul> - <li class="right" style="margin-right: 10px"> - <a href="genindex.html" title="General Index" - accesskey="I">index</a></li> - <li class="right" > - <a href="py-modindex.html" title="Python Module Index" - >modules</a> |</li> - <li class="right" > - <a href="client.html" title="client Package" - accesskey="P">previous</a> |</li> - <li><a href="index.html">BrowserMob Proxy 0.6.0 documentation</a> »</li> - </ul> - </div> - - <div class="document"> - <div class="documentwrapper"> - <div class="bodywrapper"> - <div class="body"> - - <div class="toctree-wrapper compound"> -<ul class="simple"> -</ul> -</div> -<div class="section" id="module-browsermobproxy"> -<span id="server-package"></span><h1><tt class="xref py py-mod docutils literal"><span class="pre">server</span></tt> Package<a class="headerlink" href="#module-browsermobproxy" title="Permalink to this headline">¶</a></h1> -<dl class="class"> -<dt id="browsermobproxy.Server"> -<em class="property">class </em><tt class="descclassname">browsermobproxy.</tt><tt class="descname">Server</tt><big>(</big><em>path='browsermob-proxy'</em>, <em>options={}</em><big>)</big><a class="headerlink" href="#browsermobproxy.Server" title="Permalink to this definition">¶</a></dt> -<dd><p>Initialises a Server object</p> -<table class="docutils field-list" frame="void" rules="none"> -<col class="field-name" /> -<col class="field-body" /> -<tbody valign="top"> -<tr class="field-odd field"><th class="field-name">Args:</th><td class="field-body"></td> -</tr> -<tr class="field-even field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first last simple"> -<li><strong>path</strong> – Path to the browsermob proxy batch file</li> -<li><strong>options</strong> – Dictionary that can hold the port. More items will be added in the future. This defaults to an empty dictionary</li> -</ul> -</td> -</tr> -</tbody> -</table> -<dl class="method"> -<dt id="browsermobproxy.Server.create_proxy"> -<tt class="descname">create_proxy</tt><big>(</big><big>)</big><a class="headerlink" href="#browsermobproxy.Server.create_proxy" title="Permalink to this definition">¶</a></dt> -<dd><p>Gets a client class that allow to set all the proxy details that you -may need to.</p> -</dd></dl> - -<dl class="method"> -<dt id="browsermobproxy.Server.start"> -<tt class="descname">start</tt><big>(</big><big>)</big><a class="headerlink" href="#browsermobproxy.Server.start" title="Permalink to this definition">¶</a></dt> -<dd><p>This will start the browsermob proxy and then wait until it can -interact with it</p> -</dd></dl> - -<dl class="method"> -<dt id="browsermobproxy.Server.stop"> -<tt class="descname">stop</tt><big>(</big><big>)</big><a class="headerlink" href="#browsermobproxy.Server.stop" title="Permalink to this definition">¶</a></dt> -<dd><p>This will stop the process running the proxy</p> -</dd></dl> - -<dl class="attribute"> -<dt id="browsermobproxy.Server.url"> -<tt class="descname">url</tt><a class="headerlink" href="#browsermobproxy.Server.url" title="Permalink to this definition">¶</a></dt> -<dd><p>Gets the url that the proxy is running on. This is not the URL clients -should connect to.</p> -</dd></dl> - -</dd></dl> - -</div> - - - </div> - </div> - </div> - <div class="sphinxsidebar"> - <div class="sphinxsidebarwrapper"> - <h4>Previous topic</h4> - <p class="topless"><a href="client.html" - title="previous chapter"><tt class="docutils literal"><span class="pre">client</span></tt> Package</a></p> - <h3>This Page</h3> - <ul class="this-page-menu"> - <li><a href="_sources/server.txt" - rel="nofollow">Show Source</a></li> - </ul> -<div id="searchbox" style="display: none"> - <h3>Quick search</h3> - <form class="search" action="search.html" method="get"> - <input type="text" name="q" /> - <input type="submit" value="Go" /> - <input type="hidden" name="check_keywords" value="yes" /> - <input type="hidden" name="area" value="default" /> - </form> - <p class="searchtip" style="font-size: 90%"> - Enter search terms or a module, class or function name. - </p> -</div> -<script type="text/javascript">$('#searchbox').show(0);</script> - </div> - </div> - <div class="clearer"></div> - </div> - <div class="related"> - <h3>Navigation</h3> - <ul> - <li class="right" style="margin-right: 10px"> - <a href="genindex.html" title="General Index" - >index</a></li> - <li class="right" > - <a href="py-modindex.html" title="Python Module Index" - >modules</a> |</li> - <li class="right" > - <a href="client.html" title="client Package" - >previous</a> |</li> - <li><a href="index.html">BrowserMob Proxy 0.6.0 documentation</a> »</li> - </ul> - </div> - <div class="footer"> - © Copyright 2014, David Burns. - Created using <a href="http://sphinx-doc.org/">Sphinx</a> 1.2.1. - </div> - </body> -</html>
\ No newline at end of file diff --git a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/client.rst b/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/client.rst deleted file mode 100644 index 95c74fa9f..000000000 --- a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/client.rst +++ /dev/null @@ -1,8 +0,0 @@ -.. toctree:: - :maxdepth: 2 - -:mod:`client` Package ---------------------- -.. automodule:: browsermobproxy -.. autoclass:: Client - :members: diff --git a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/conf.py b/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/conf.py deleted file mode 100644 index 961c48907..000000000 --- a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/conf.py +++ /dev/null @@ -1,243 +0,0 @@ -# -*- coding: utf-8 -*- -# -# BrowserMob Proxy documentation build configuration file, created by -# sphinx-quickstart on Fri May 24 12:37:12 2013. -# -# This file is execfile()d with the current directory set to its containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -import sys, os - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -sys.path.insert(0, os.path.abspath('../')) - -# -- General configuration ----------------------------------------------------- - -# If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be extensions -# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.coverage', 'sphinx.ext.viewcode', 'sphinx.ext.autodoc'] -autoclass_content = 'both' - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The encoding of source files. -#source_encoding = 'utf-8-sig' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'BrowserMob Proxy' -copyright = u'2014, David Burns' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -version = '0.6.0' -# The full version, including alpha/beta/rc tags. -release = '0.6.0' - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -#language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -#today = '' -# Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = ['_build'] - -# The reST default role (used for this markup: `text`) to use for all documents. -#default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -#add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -#show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] - - -# -- Options for HTML output --------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -html_theme = 'default' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -#html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] - -# The name for this set of Sphinx documents. If None, it defaults to -# "<project> v<release> documentation". -#html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -#html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -#html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -#html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -#html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -#html_additional_pages = {} - -# If false, no module index is generated. -#html_domain_indices = True - -# If false, no index is generated. -#html_use_index = True - -# If true, the index is split into individual pages for each letter. -#html_split_index = False - -# If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a <link> tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -#html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None - -# Output file base name for HTML help builder. -htmlhelp_basename = 'BrowserMobProxydoc' - - -# -- Options for LaTeX output -------------------------------------------------- - -latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -#'papersize': 'letterpaper', - -# The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', - -# Additional stuff for the LaTeX preamble. -#'preamble': '', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, documentclass [howto/manual]). -latex_documents = [ - ('index', 'BrowserMobProxy.tex', u'BrowserMob Proxy Documentation', - u'David Burns', 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -#latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -#latex_use_parts = False - -# If true, show page references after internal links. -#latex_show_pagerefs = False - -# If true, show URL addresses after external links. -#latex_show_urls = False - -# Documents to append as an appendix to all manuals. -#latex_appendices = [] - -# If false, no module index is generated. -#latex_domain_indices = True - - -# -- Options for manual page output -------------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - ('index', 'browsermobproxy', u'BrowserMob Proxy Documentation', - [u'David Burns'], 1) -] - -# If true, show URL addresses after external links. -#man_show_urls = False - - -# -- Options for Texinfo output ------------------------------------------------ - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - ('index', 'BrowserMobProxy', u'BrowserMob Proxy Documentation', - u'David Burns', 'BrowserMobProxy', 'One line description of project.', - 'Miscellaneous'), -] - -# Documents to append as an appendix to all manuals. -#texinfo_appendices = [] - -# If false, no module index is generated. -#texinfo_domain_indices = True - -# How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' diff --git a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/index.rst b/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/index.rst deleted file mode 100644 index 0a568cf64..000000000 --- a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/index.rst +++ /dev/null @@ -1,72 +0,0 @@ -.. BrowserMob Proxy documentation master file, created by - sphinx-quickstart on Fri May 24 12:37:12 2013. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. -.. highlightlang:: python - - - -Welcome to BrowserMob Proxy's documentation! -============================================ - -Python client for the BrowserMob Proxy 2.0 REST API. - -How to install --------------- - -BrowserMob Proxy is available on PyPI_, so you can install it with ``pip``:: - - $ pip install browsermob-proxy - -Or with `easy_install`:: - - $ easy_install browsermob-proxy - -Or by cloning the repo from GitHub_:: - - $ git clone git://github.com/AutomatedTester/browsermob-proxy-py.git - -Then install it by running:: - - $ python setup.py install - -How to use with selenium-webdriver ----------------------------------- - -Manually:: - - from browsermobproxy import Server - server = Server("path/to/browsermob-proxy") - server.start() - proxy = server.create_proxy() - - from selenium import webdriver - profile = webdriver.FirefoxProfile() - profile.set_proxy(proxy.selenium_proxy()) - driver = webdriver.Firefox(firefox_profile=profile) - - - proxy.new_har("google") - driver.get("http://www.google.co.uk") - proxy.har # returns a HAR JSON blob - - server.stop() - driver.quit() - -Contents: - -.. toctree:: - :maxdepth: 2 - - client.rst - server.rst - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` - -.. _GitHub: https://github.com/AutomatedTester/browsermob-proxy-py -.. _PyPI: http://pypi.python.org/pypi/browsermob-proxy diff --git a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/make.bat b/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/make.bat deleted file mode 100644 index 567b0da4a..000000000 --- a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/make.bat +++ /dev/null @@ -1,190 +0,0 @@ -@ECHO OFF - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set BUILDDIR=_build -set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . -set I18NSPHINXOPTS=%SPHINXOPTS% . -if NOT "%PAPER%" == "" ( - set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% - set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% -) - -if "%1" == "" goto help - -if "%1" == "help" ( - :help - echo.Please use `make ^<target^>` where ^<target^> is one of - echo. html to make standalone HTML files - echo. dirhtml to make HTML files named index.html in directories - echo. singlehtml to make a single large HTML file - echo. pickle to make pickle files - echo. json to make JSON files - echo. htmlhelp to make HTML files and a HTML help project - echo. qthelp to make HTML files and a qthelp project - echo. devhelp to make HTML files and a Devhelp project - echo. epub to make an epub - echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter - echo. text to make text files - echo. man to make manual pages - echo. texinfo to make Texinfo files - echo. gettext to make PO message catalogs - echo. changes to make an overview over all changed/added/deprecated items - echo. linkcheck to check all external links for integrity - echo. doctest to run all doctests embedded in the documentation if enabled - goto end -) - -if "%1" == "clean" ( - for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i - del /q /s %BUILDDIR%\* - goto end -) - -if "%1" == "html" ( - %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/html. - goto end -) - -if "%1" == "dirhtml" ( - %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. - goto end -) - -if "%1" == "singlehtml" ( - %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. - goto end -) - -if "%1" == "pickle" ( - %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can process the pickle files. - goto end -) - -if "%1" == "json" ( - %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can process the JSON files. - goto end -) - -if "%1" == "htmlhelp" ( - %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can run HTML Help Workshop with the ^ -.hhp project file in %BUILDDIR%/htmlhelp. - goto end -) - -if "%1" == "qthelp" ( - %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can run "qcollectiongenerator" with the ^ -.qhcp project file in %BUILDDIR%/qthelp, like this: - echo.^> qcollectiongenerator %BUILDDIR%\qthelp\BrowserMobProxy.qhcp - echo.To view the help file: - echo.^> assistant -collectionFile %BUILDDIR%\qthelp\BrowserMobProxy.ghc - goto end -) - -if "%1" == "devhelp" ( - %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. - goto end -) - -if "%1" == "epub" ( - %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The epub file is in %BUILDDIR%/epub. - goto end -) - -if "%1" == "latex" ( - %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. - goto end -) - -if "%1" == "text" ( - %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The text files are in %BUILDDIR%/text. - goto end -) - -if "%1" == "man" ( - %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The manual pages are in %BUILDDIR%/man. - goto end -) - -if "%1" == "texinfo" ( - %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. - goto end -) - -if "%1" == "gettext" ( - %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The message catalogs are in %BUILDDIR%/locale. - goto end -) - -if "%1" == "changes" ( - %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes - if errorlevel 1 exit /b 1 - echo. - echo.The overview file is in %BUILDDIR%/changes. - goto end -) - -if "%1" == "linkcheck" ( - %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck - if errorlevel 1 exit /b 1 - echo. - echo.Link check complete; look for any errors in the above output ^ -or in %BUILDDIR%/linkcheck/output.txt. - goto end -) - -if "%1" == "doctest" ( - %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest - if errorlevel 1 exit /b 1 - echo. - echo.Testing of doctests in the sources finished, look at the ^ -results in %BUILDDIR%/doctest/output.txt. - goto end -) - -:end diff --git a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/server.rst b/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/server.rst deleted file mode 100644 index 8af94b70a..000000000 --- a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/docs/server.rst +++ /dev/null @@ -1,8 +0,0 @@ -.. toctree:: - :maxdepth: 2 - -:mod:`server` Package ---------------------- -.. automodule:: browsermobproxy -.. autoclass:: Server - :members:
\ No newline at end of file diff --git a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/readme.md b/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/readme.md deleted file mode 100644 index 913610f66..000000000 --- a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/readme.md +++ /dev/null @@ -1,88 +0,0 @@ -browsermob-proxy-py -=================== - -Python client for the BrowserMob Proxy 2.0 REST API. - - - -How to use with selenium-webdriver ----------------------------------- - -Manually: - -``` python -from browsermobproxy import Server -server = Server("path/to/browsermob-proxy") -server.start() -proxy = server.create_proxy() - -from selenium import webdriver -profile = webdriver.FirefoxProfile() -profile.set_proxy(proxy.selenium_proxy()) -driver = webdriver.Firefox(firefox_profile=profile) - - -proxy.new_har("google") -driver.get("http://www.google.co.uk") -proxy.har # returns a HAR JSON blob - -server.stop() -driver.quit() - -``` - -for Chrome use - -``` -chrome_options = webdriver.ChromeOptions() -chrome_options.add_argument("--proxy-server={0}".format(proxy.proxy)) -browser = webdriver.Chrome(chrome_options = chrome_options) -``` - -Running Tests -------------- -To run the tests in a CI environment, disable the ones that require human -judgement by using - -```bash -$ py.test -m "not human" test -``` - -If you are going to watch the test, the 'human' ones should display an english -muffin instead of the american flag on the 'pick your version' page. Or at -least it does from Canada. - - -See also --------- - -* http://proxy.browsermob.com/ -* https://github.com/webmetrics/browsermob-proxy - -Note on Patches/Pull Requests ------------------------------ - -* Fork the project. -* Make your feature addition or bug fix. -* Add tests for it. This is important so I don't break it in a - future version unintentionally. -* Send me a pull request. Bonus points for topic branches. - -Copyright ---------- - -Copyright 2011 David Burns - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - - diff --git a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/setup.py b/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/setup.py deleted file mode 100644 index f6581cf08..000000000 --- a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/setup.py +++ /dev/null @@ -1,20 +0,0 @@ -from setuptools import setup, find_packages - -setup(name='browsermob-proxy', - version='0.6.0', - description='A library for interacting with the Browsermob Proxy', - author='David Burns', - author_email='david.burns at theautomatedtester dot co dot uk', - url='http://oss.theautomatedtester.co.uk/browsermob-proxy-py', - classifiers=['Development Status :: 3 - Alpha', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: Apache Software License', - 'Operating System :: POSIX', - 'Operating System :: Microsoft :: Windows', - 'Operating System :: MacOS :: MacOS X', - 'Topic :: Software Development :: Testing', - 'Topic :: Software Development :: Libraries', - 'Programming Language :: Python'], - packages = find_packages(), - install_requires=['requests>=1.1.0'], - ) diff --git a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/test/test_client.py b/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/test/test_client.py deleted file mode 100644 index 8968b103f..000000000 --- a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/test/test_client.py +++ /dev/null @@ -1,247 +0,0 @@ -import os.path -import pytest -import sys - - -def setup_module(module): - sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) - -class TestClient(object): - def setup_method(self, method): - from browsermobproxy.client import Client - self.client = Client("localhost:9090") - - def teardown_method(self, method): - self.client.close() - - def test_headers_type(self): - """ - /proxy/:port/headers needs to take a dictionary - """ - with pytest.raises(TypeError): - self.client.headers(['foo']) - - def test_headers_content(self): - """ - /proxy/:port/headers needs to take a dictionary - and returns 200 when its successful - """ - s = self.client.headers({'User-Agent': 'rubber ducks floating in a row'}) - assert(s == 200) - - def test_new_har(self): - """ - /proxy/:port/har - and returns 204 when creating a har with a particular name the first time - and returns 200 and the previous har when creating one with the same name - """ - status_code, har = self.client.new_har() - assert(status_code == 204) - assert(har is None) - status_code, har = self.client.new_har() - assert(status_code == 200) - assert('log' in har) - - def test_new_har(self): - """ - /proxy/:port/har - and returns 204 when creating a har with a particular name the first time - and returns 200 and the previous har when creating one with the same name - """ - status_code, har = self.client.new_har("elephants") - assert(status_code == 204) - assert(har is None) - status_code, har = self.client.new_har("elephants") - assert(status_code == 200) - assert('elephants' == har["log"]["pages"][0]['id']) - - def test_new_page_defaults(self): - """ - /proxy/:port/pageRef - adds a new page of 'Page N' when no page name is given - """ - self.client.new_har() - self.client.new_page() - har = self.client.har - assert(len(har["log"]["pages"]) == 2) - assert(har["log"]["pages"][1]["id"] == "Page 2") - - def test_new_named_page(self): - """ - /proxy/:port/pageRef - adds a new page of 'buttress' - """ - self.client.new_har() - self.client.new_page('buttress') - har = self.client.har - assert(len(har["log"]["pages"]) == 2) - assert(har["log"]["pages"][1]["id"] == "buttress") - - def test_single_whitelist(self): - """ - /proxy/:port/whitelist - adds a whitelist - """ - status_code = self.client.whitelist("http://www\\.facebook\\.com/.*", 200) - assert(status_code == 200) - - def test_multiple_whitelists(self): - """ - /proxy/:port/whitelist - adds a whitelist - """ - status_code = self.client.whitelist("http://www\\.facebook\\.com/.*,http://cdn\\.twitter\\.com", 200) - assert(status_code == 200) - - def test_blacklist(self): - """ - /proxy/:port/blacklist - adds a blacklist - """ - status_code = self.client.blacklist("http://www\\.facebook\\.com/.*", 200) - assert(status_code == 200) - - def test_basic_authentication(self): - """ - /proxy/:port/auth/basic - adds automatic basic authentication - """ - status_code = self.client.basic_authentication("www.example.com", "myUsername", "myPassword") - assert(status_code == 200) - - def test_limits_invalid_key(self): - """ - /proxy/:port/limits - pre-sending checking that the parameter is correct - """ - with pytest.raises(KeyError): - self.client.limits({"hurray": "explosions"}) - - def test_limits_key_no_value(self): - """ - /proxy/:port/limits - pre-sending checking that a parameter exists - """ - with pytest.raises(KeyError): - self.client.limits({}) - - def test_limits_all_key_values(self): - """ - /proxy/:port/limits - can send all 3 at once based on the proxy implementation - """ - limits = {"upstream_kbps": 320, "downstream_kbps": 560, "latency": 30} - status_code = self.client.limits(limits) - assert(status_code == 200) - - def test_rewrite(self): - """ - /proxy/:port/rewrite - - """ - match = "/foo" - replace = "/bar" - status_code = self.client.rewrite_url(match, replace) - assert(status_code == 200) - - def test_close(self): - """ - /proxy/:port - close the proxy port - """ - status_code = self.client.close() - assert(status_code == 200) - status_code = self.client.close() - assert(status_code == 404) - - def test_response_interceptor_with_parsing_js(self): - """ - /proxy/:port/interceptor/response - """ - js = 'alert("foo")' - status_code = self.client.response_interceptor(js) - assert(status_code == 200) - - def test_response_interceptor_with_invalid_js(self): - """ - /proxy/:port/interceptor/response - """ - js = 'alert("foo"' - status_code = self.client.response_interceptor(js) - assert(status_code == 500) - - def test_request_interceptor_with_parsing_js(self): - """ - /proxy/:port/interceptor/request - """ - js = 'alert("foo")' - status_code = self.client.request_interceptor(js) - assert(status_code == 200) - - def test_request_interceptor_with_invalid_js(self): - """ - /proxy/:port/interceptor/request - """ - js = 'alert("foo"' - status_code = self.client.request_interceptor(js) - assert(status_code == 500) - - def test_timeouts_invalid_timeouts(self): - """ - /proxy/:port/timeout - pre-sending checking that the parameter is correct - """ - with pytest.raises(KeyError): - self.client.timeouts({"hurray": "explosions"}) - - def test_timeouts_key_no_value(self): - """ - /proxy/:port/timeout - pre-sending checking that a parameter exists - """ - with pytest.raises(KeyError): - self.client.timeouts({}) - - def test_timeouts_all_key_values(self): - """ - /proxy/:port/timeout - can send all 3 at once based on the proxy implementation - """ - timeouts = {"request": 2, "read": 2, "connection": 2, "dns": 3} - status_code = self.client.timeouts(timeouts) - assert(status_code == 200) - - def test_remap_hosts(self): - """ - /proxy/:port/hosts - """ - status_code = self.client.remap_hosts("example.com", "1.2.3.4") - assert(status_code == 200) - - def test_wait_for_traffic_to_stop(self): - """ - /proxy/:port/wait - """ - status_code = self.client.wait_for_traffic_to_stop(2000, 10000) - assert(status_code == 200) - - def test_clear_dns_cache(self): - """ - /proxy/:port/dns/cache - """ - status_code = self.client.clear_dns_cache() - assert(status_code == 200) - - def test_rewrite_url(self): - """ - /proxy/:port/rewrite - """ - status_code = self.client.rewrite_url('http://www.facebook\.com', 'http://microsoft.com') - assert(status_code == 200) - - def test_retry(self): - """ - /proxy/:port/retry - """ - status_code = self.client.retry(4) - assert(status_code == 200) diff --git a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/test/test_remote.py b/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/test/test_remote.py deleted file mode 100644 index 9a002f109..000000000 --- a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/test/test_remote.py +++ /dev/null @@ -1,31 +0,0 @@ -from selenium import webdriver -import selenium.webdriver.common.desired_capabilities -import os -import sys -import time -import pytest - -def setup_module(module): - sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) - -class TestRemote(object): - def setup_method(self, method): - from browsermobproxy.client import Client - self.client = Client("localhost:9090") - - def teardown_method(self, method): - self.client.close() - - @pytest.mark.human - def test_i_want_my_remote(self): - driver = webdriver.Remote(desired_capabilities=selenium.webdriver.common.desired_capabilities.DesiredCapabilities.FIREFOX, - proxy=self.client) - - self.client.new_har("mtv") - targetURL = "http://www.mtv.com" - self.client.rewrite_url(".*american_flag-384x450\\.jpg", "http://www.foodsubs.com/Photos/englishmuffin.jpg") - - driver.get(targetURL) - time.sleep(5) - - driver.quit() diff --git a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/test/test_webdriver.py b/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/test/test_webdriver.py deleted file mode 100644 index 4a35e7162..000000000 --- a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py/test/test_webdriver.py +++ /dev/null @@ -1,60 +0,0 @@ -from selenium import webdriver -import selenium.webdriver.common.desired_capabilities -from selenium.webdriver.common.proxy import Proxy -import os -import sys -import copy -import time -import pytest - -def setup_module(module): - sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) - -class TestWebDriver(object): - def setup_method(self, method): - from browsermobproxy.client import Client - self.client = Client("localhost:9090") - - def teardown_method(self, method): - self.client.close() - - @pytest.mark.human - def test_i_want_my_by_capability(self): - capabilities = selenium.webdriver.common.desired_capabilities.DesiredCapabilities.FIREFOX - self.client.add_to_capabilities(capabilities) - driver = webdriver.Firefox(capabilities=capabilities) - - self.client.new_har("mtv") - targetURL = "http://www.mtv.com" - self.client.rewrite_url(".*american_flag-384x450\\.jpg", "http://www.foodsubs.com/Photos/englishmuffin.jpg") - - driver.get(targetURL) - - time.sleep(5) - - driver.quit() - - @pytest.mark.human - def test_i_want_my_by_proxy_object(self): - driver = webdriver.Firefox(proxy=self.client) - - self.client.new_har("mtv") - targetURL = "http://www.mtv.com" - self.client.rewrite_url(".*american_flag-384x450\\.jpg", "http://www.foodsubs.com/Photos/englishmuffin.jpg") - - driver.get(targetURL) - - time.sleep(5) - - driver.quit() - - def test_what_things_look_like(self): - bmp_capabilities = copy.deepcopy(selenium.webdriver.common.desired_capabilities.DesiredCapabilities.FIREFOX) - self.client.add_to_capabilities(bmp_capabilities) - - proxy_capabilities = copy.deepcopy(selenium.webdriver.common.desired_capabilities.DesiredCapabilities.FIREFOX) - proxy_addr = 'localhost:{}'.format(self.client.port) - proxy = Proxy({'httpProxy': proxy_addr,'sslProxy': proxy_addr}) - proxy.add_to_capabilities(proxy_capabilities) - - assert bmp_capabilities == proxy_capabilities diff --git a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob.py b/testing/marionette/harness/marionette_harness/runner/mixins/browsermob.py deleted file mode 100644 index e4c3cb545..000000000 --- a/testing/marionette/harness/marionette_harness/runner/mixins/browsermob.py +++ /dev/null @@ -1,80 +0,0 @@ -# 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/. - -import os - -from browsermobproxy import Server -from marionette_harness import MarionetteTestCase - - -class BrowserMobProxyArguments(object): - name = 'Browsermob Proxy' - args = [ - [['--browsermob-script'], - {'help': 'path to the browsermob-proxy shell script or batch file', - }], - [['--browsermob-port'], - {'type': int, - 'help': 'port to run the browsermob proxy on', - }], - ] - - def verify_usage_handler(self, args): - if args.browsermob_script is not None: - if not os.path.exists(args.browsermob_script): - raise ValueError('{} not found'.format(args.browsermob_script)) - - -class BrowserMobProxyTestCaseMixin(object): - - def __init__(self, *args, **kwargs): - self.browsermob_server = None - self.browsermob_port = kwargs.pop('browsermob_port') - self.browsermob_script = kwargs.pop('browsermob_script') - - def setUp(self): - options = {} - if self.browsermob_port: - options['port'] = self.browsermob_port - if not self.browsermob_script: - raise ValueError('Must specify --browsermob-script in order to ' - 'run browsermobproxy tests') - self.browsermob_server = Server( - self.browsermob_script, options=options) - self.browsermob_server.start() - - def create_browsermob_proxy(self): - client = self.browsermob_server.create_proxy() - with self.marionette.using_context('chrome'): - self.marionette.execute_script(""" - Components.utils.import("resource://gre/modules/Preferences.jsm"); - Preferences.set("network.proxy.type", 1); - Preferences.set("network.proxy.http", "localhost"); - Preferences.set("network.proxy.http_port", {port}); - Preferences.set("network.proxy.ssl", "localhost"); - Preferences.set("network.proxy.ssl_port", {port}); - """.format(port=client.port)) - return client - - def tearDown(self): - if self.browsermob_server: - self.browsermob_server.stop() - self.browsermob_server = None - - __del__ = tearDown - - -class BrowserMobTestCase(MarionetteTestCase, BrowserMobProxyTestCaseMixin): - - def __init__(self, *args, **kwargs): - MarionetteTestCase.__init__(self, *args, **kwargs) - BrowserMobProxyTestCaseMixin.__init__(self, *args, **kwargs) - - def setUp(self): - MarionetteTestCase.setUp(self) - BrowserMobProxyTestCaseMixin.setUp(self) - - def tearDown(self): - BrowserMobProxyTestCaseMixin.tearDown(self) - MarionetteTestCase.tearDown(self) diff --git a/testing/marionette/harness/marionette_harness/runner/mixins/window_manager.py b/testing/marionette/harness/marionette_harness/runner/mixins/window_manager.py deleted file mode 100644 index 4316c75d7..000000000 --- a/testing/marionette/harness/marionette_harness/runner/mixins/window_manager.py +++ /dev/null @@ -1,129 +0,0 @@ -# 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/. - -import sys - -from marionette_driver import By, Wait - - -class WindowManagerMixin(object): - - _menu_item_new_tab = (By.ID, "menu_newNavigatorTab") - - 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() - - # Bug 1311350 - close() doesn't wait for tab to be closed. - Wait(self.marionette).until( - lambda mn: handle not in mn.window_handles, - message="Failed to close tab with handle {}".format(handle) - ) - - 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) - - with self.marionette.using_context("chrome"): - for handle in current_chrome_window_handles: - self.marionette.switch_to_window(handle) - self.marionette.close_chrome_window() - - # Bug 1311350 - close_chrome_window() doesn't wait for window to be closed. - Wait(self.marionette).until( - lambda mn: handle not in mn.chrome_window_handles, - message="Failed to close window with handle {}".format(handle) - ) - - self.marionette.switch_to_window(self.start_window) - - def open_tab(self, trigger="menu"): - current_tabs = self.marionette.window_handles - - try: - if callable(trigger): - trigger() - elif trigger == 'menu': - with self.marionette.using_context("chrome"): - self.marionette.find_element(*self._menu_item_new_tab).click() - except Exception: - exc, val, tb = sys.exc_info() - raise exc, 'Failed to trigger opening a new tab: {}'.format(val), 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, trigger=None): - current_windows = self.marionette.chrome_window_handles - - def loaded(handle): - with self.marionette.using_context("chrome"): - return self.marionette.execute_script(""" - Components.utils.import("resource://gre/modules/Services.jsm"); - - let win = Services.wm.getOuterWindowWithId(Number(arguments[0])); - return win.document.readyState == "complete"; - """, script_args=[handle]) - - try: - if callable(trigger): - trigger() - else: - with self.marionette.using_context("chrome"): - self.marionette.execute_script("window.open();") - except Exception: - exc, val, tb = sys.exc_info() - raise exc, 'Failed to trigger opening a new window: {}'.format(val), 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)) - - return new_window diff --git a/testing/marionette/harness/marionette_harness/runner/serve.py b/testing/marionette/harness/marionette_harness/runner/serve.py deleted file mode 100755 index 1969c9170..000000000 --- a/testing/marionette/harness/marionette_harness/runner/serve.py +++ /dev/null @@ -1,227 +0,0 @@ -#!/usr/bin/env python - -# 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/. - -"""Spawns necessary HTTP servers for testing Marionette in child -processes. - -""" - -import argparse -import multiprocessing -import os -import sys - -from collections import defaultdict - -import httpd - - -__all__ = ["default_doc_root", - "iter_proc", - "iter_url", - "registered_servers", - "servers", - "start", - "where_is"] -here = os.path.abspath(os.path.dirname(__file__)) - - -class BlockingChannel(object): - - def __init__(self, channel): - self.chan = channel - self.lock = multiprocessing.Lock() - - def call(self, func, args=()): - self.send((func, args)) - return self.recv() - - def send(self, *args): - try: - self.lock.acquire() - self.chan.send(args) - finally: - self.lock.release() - - def recv(self): - try: - self.lock.acquire() - payload = self.chan.recv() - if isinstance(payload, tuple) and len(payload) == 1: - return payload[0] - return payload - except KeyboardInterrupt: - return ("stop", ()) - finally: - self.lock.release() - - -class ServerProxy(multiprocessing.Process, BlockingChannel): - - def __init__(self, channel, init_func, *init_args, **init_kwargs): - multiprocessing.Process.__init__(self) - BlockingChannel.__init__(self, channel) - self.init_func = init_func - self.init_args = init_args - self.init_kwargs = init_kwargs - - def run(self): - server = self.init_func(*self.init_args, **self.init_kwargs) - server.start(block=False) - - try: - while True: - # ["func", ("arg", ...)] - # ["prop", ()] - sattr, fargs = self.recv() - attr = getattr(server, sattr) - - # apply fargs to attr if it is a function - if callable(attr): - rv = attr(*fargs) - - # otherwise attr is a property - else: - rv = attr - - self.send(rv) - - if sattr == "stop": - return - - except KeyboardInterrupt: - server.stop() - - -class ServerProc(BlockingChannel): - - def __init__(self, init_func): - self._init_func = init_func - self.proc = None - - parent_chan, self.child_chan = multiprocessing.Pipe() - BlockingChannel.__init__(self, parent_chan) - - def start(self, doc_root, ssl_config, **kwargs): - self.proc = ServerProxy( - self.child_chan, self._init_func, doc_root, ssl_config, **kwargs) - self.proc.daemon = True - self.proc.start() - - def get_url(self, url): - return self.call("get_url", (url,)) - - @property - def doc_root(self): - return self.call("doc_root", ()) - - def stop(self): - self.call("stop") - if not self.is_alive: - return - self.proc.join() - - def kill(self): - if not self.is_alive: - return - self.proc.terminate() - self.proc.join(0) - - @property - def is_alive(self): - if self.proc is not None: - return self.proc.is_alive() - return False - - -def http_server(doc_root, ssl_config, host="127.0.0.1", **kwargs): - return httpd.FixtureServer(doc_root, url="http://{}:0/".format(host), **kwargs) - - -def https_server(doc_root, ssl_config, host="127.0.0.1", **kwargs): - return httpd.FixtureServer(doc_root, - url="https://{}:0/".format(host), - ssl_key=ssl_config["key_path"], - ssl_cert=ssl_config["cert_path"], - **kwargs) - - -def start_servers(doc_root, ssl_config, **kwargs): - servers = defaultdict() - for schema, builder_fn in registered_servers: - proc = ServerProc(builder_fn) - proc.start(doc_root, ssl_config, **kwargs) - servers[schema] = (proc.get_url("/"), proc) - return servers - - -def start(doc_root=None, **kwargs): - """Start all relevant test servers. - - If no `doc_root` is given the default - testing/marionette/harness/marionette_harness/www directory will be used. - - Additional keyword arguments can be given which will be passed on - to the individual ``FixtureServer``'s in httpd.py. - - """ - doc_root = doc_root or default_doc_root - ssl_config = {"cert_path": httpd.default_ssl_cert, - "key_path": httpd.default_ssl_key} - - global servers - servers = start_servers(doc_root, ssl_config, **kwargs) - return servers - - -def where_is(uri, on="http"): - """Returns the full URL, including scheme, hostname, and port, for - a fixture resource from the server associated with the ``on`` key. - It will by default look for the resource in the "http" server. - - """ - return servers.get(on)[1].get_url(uri) - - -def iter_proc(servers): - for _, (_, proc) in servers.iteritems(): - yield proc - - -def iter_url(servers): - for _, (url, _) in servers.iteritems(): - yield url - - -default_doc_root = os.path.join(os.path.dirname(here), "www") -registered_servers = [("http", http_server), - ("https", https_server)] -servers = defaultdict() - - -def main(args): - global servers - - parser = argparse.ArgumentParser() - parser.add_argument("-r", dest="doc_root", - help="Path to document root. Overrides default.") - args = parser.parse_args() - - servers = start(args.doc_root) - for url in iter_url(servers): - print >>sys.stderr, "{}: listening on {}".format(sys.argv[0], url) - - try: - while any(proc.is_alive for proc in iter_proc(servers)): - for proc in iter_proc(servers): - proc.proc.join(1) - except KeyboardInterrupt: - for proc in iter_proc(servers): - proc.kill() - - -if __name__ == "__main__": - main(sys.argv[1:]) diff --git a/testing/marionette/harness/marionette_harness/runner/test.cert b/testing/marionette/harness/marionette_harness/runner/test.cert deleted file mode 100644 index 3fd1cba2b..000000000 --- a/testing/marionette/harness/marionette_harness/runner/test.cert +++ /dev/null @@ -1,86 +0,0 @@ -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 2 (0x2) - Signature Algorithm: sha256WithRSAEncryption - Issuer: CN=web-platform-tests - Validity - Not Before: Dec 22 12:09:16 2014 GMT - Not After : Dec 21 12:09:16 2024 GMT - Subject: CN=web-platform.test - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - Public-Key: (2048 bit) - Modulus: - 00:b3:84:d6:8b:01:59:18:85:d1:dc:32:df:38:f7: - 90:85:1b:3e:a5:5e:81:3e:2f:fc:3a:5f:7f:77:ef: - 23:bb:3a:88:27:0f:be:25:46:cd:63:7d:cb:95:d8: - a5:50:10:d2:a2:d2:b7:97:d1:0d:6c:fb:f9:05:e8: - 6f:a8:4b:bd:95:67:9e:7b:94:58:a9:6d:93:fd:e0: - 12:c5:cd:b4:8a:64:52:31:5f:0e:e3:89:84:71:da: - 98:dd:4b:ec:02:25:a5:7d:35:fe:63:da:b3:ac:ec: - a5:46:0f:0d:64:23:5c:6d:f3:ec:cc:28:63:23:c0: - 4b:9a:ec:8f:c1:ee:b1:a2:3e:72:4d:70:b5:09:c1: - eb:b4:10:55:3c:8b:ea:1b:94:7e:4b:74:e6:f4:9f: - 4f:a6:45:30:b5:f0:b8:b4:d1:59:50:65:0a:86:53: - ea:4c:9f:9e:f4:58:6c:31:f5:17:3a:6f:57:8b:cb: - 5f:f0:28:0b:45:92:8d:30:20:49:ff:52:e6:2c:cb: - 18:9a:d7:e6:ee:3e:4f:34:35:15:13:c5:02:da:c5: - 5f:be:fb:5b:ce:8d:bf:b5:35:76:3c:7c:e6:9c:3b: - 26:87:4d:8d:80:e6:16:c6:27:f2:50:49:b6:72:74: - 43:49:49:44:38:bb:78:43:23:ee:16:3e:d9:62:e6: - a5:d7 - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Basic Constraints: - CA:FALSE - X509v3 Subject Key Identifier: - 2D:98:A3:99:39:1C:FE:E9:9A:6D:17:94:D2:3A:96:EE:C8:9E:04:22 - X509v3 Authority Key Identifier: - keyid:6A:AB:53:64:92:36:87:23:34:B3:1D:6F:85:4B:F5:DF:5A:5C:74:8F - - X509v3 Key Usage: - Digital Signature, Non Repudiation, Key Encipherment - X509v3 Extended Key Usage: - TLS Web Server Authentication - X509v3 Subject Alternative Name: - DNS:web-platform.test, DNS:www.web-platform.test, DNS:xn--n8j6ds53lwwkrqhv28a.web-platform.test, DNS:xn--lve-6lad.web-platform.test, DNS:www2.web-platform.test, DNS:www1.web-platform.test - Signature Algorithm: sha256WithRSAEncryption - 33:db:f7:f0:f6:92:16:4f:2d:42:bc:b8:aa:e6:ab:5e:f9:b9: - b0:48:ae:b5:8d:cc:02:7b:e9:6f:4e:75:f7:17:a0:5e:7b:87: - 06:49:48:83:c5:bb:ca:95:07:37:0e:5d:e3:97:de:9e:0c:a4: - 82:30:11:81:49:5d:50:29:72:92:a5:ca:17:b1:7c:f1:32:11: - 17:57:e6:59:c1:ac:e3:3b:26:d2:94:97:50:6a:b9:54:88:84: - 9b:6f:b1:06:f5:80:04:22:10:14:b1:f5:97:25:fc:66:d6:69: - a3:36:08:85:23:ff:8e:3c:2b:e0:6d:e7:61:f1:00:8f:61:3d: - b0:87:ad:72:21:f6:f0:cc:4f:c9:20:bf:83:11:0f:21:f4:b8: - c0:dd:9c:51:d7:bb:27:32:ec:ab:a4:62:14:28:32:da:f2:87: - 80:68:9c:ea:ac:eb:f5:7f:f5:de:f4:c0:39:91:c8:76:a4:ee: - d0:a8:50:db:c1:4b:f9:c4:3d:d9:e8:8e:b6:3f:c0:96:79:12: - d8:fa:4d:0a:b3:36:76:aa:4e:b2:82:2f:a2:d4:0d:db:fd:64: - 77:6f:6e:e9:94:7f:0f:c8:3a:3c:96:3d:cd:4d:6c:ba:66:95: - f7:b4:9d:a4:94:9f:97:b3:9a:0d:dc:18:8c:11:0b:56:65:8e: - 46:4c:e6:5e ------BEGIN CERTIFICATE----- -MIID2jCCAsKgAwIBAgIBAjANBgkqhkiG9w0BAQsFADAdMRswGQYDVQQDDBJ3ZWIt -cGxhdGZvcm0tdGVzdHMwHhcNMTQxMjIyMTIwOTE2WhcNMjQxMjIxMTIwOTE2WjAc -MRowGAYDVQQDExF3ZWItcGxhdGZvcm0udGVzdDCCASIwDQYJKoZIhvcNAQEBBQAD -ggEPADCCAQoCggEBALOE1osBWRiF0dwy3zj3kIUbPqVegT4v/Dpff3fvI7s6iCcP -viVGzWN9y5XYpVAQ0qLSt5fRDWz7+QXob6hLvZVnnnuUWKltk/3gEsXNtIpkUjFf -DuOJhHHamN1L7AIlpX01/mPas6zspUYPDWQjXG3z7MwoYyPAS5rsj8HusaI+ck1w -tQnB67QQVTyL6huUfkt05vSfT6ZFMLXwuLTRWVBlCoZT6kyfnvRYbDH1FzpvV4vL -X/AoC0WSjTAgSf9S5izLGJrX5u4+TzQ1FRPFAtrFX777W86Nv7U1djx85pw7JodN -jYDmFsYn8lBJtnJ0Q0lJRDi7eEMj7hY+2WLmpdcCAwEAAaOCASQwggEgMAkGA1Ud -EwQCMAAwHQYDVR0OBBYEFC2Yo5k5HP7pmm0XlNI6lu7IngQiMB8GA1UdIwQYMBaA -FGqrU2SSNocjNLMdb4VL9d9aXHSPMAsGA1UdDwQEAwIF4DATBgNVHSUEDDAKBggr -BgEFBQcDATCBsAYDVR0RBIGoMIGlghF3ZWItcGxhdGZvcm0udGVzdIIVd3d3Lndl -Yi1wbGF0Zm9ybS50ZXN0gil4bi0tbjhqNmRzNTNsd3drcnFodjI4YS53ZWItcGxh -dGZvcm0udGVzdIIeeG4tLWx2ZS02bGFkLndlYi1wbGF0Zm9ybS50ZXN0ghZ3d3cy -LndlYi1wbGF0Zm9ybS50ZXN0ghZ3d3cxLndlYi1wbGF0Zm9ybS50ZXN0MA0GCSqG -SIb3DQEBCwUAA4IBAQAz2/fw9pIWTy1CvLiq5qte+bmwSK61jcwCe+lvTnX3F6Be -e4cGSUiDxbvKlQc3Dl3jl96eDKSCMBGBSV1QKXKSpcoXsXzxMhEXV+ZZwazjOybS -lJdQarlUiISbb7EG9YAEIhAUsfWXJfxm1mmjNgiFI/+OPCvgbedh8QCPYT2wh61y -IfbwzE/JIL+DEQ8h9LjA3ZxR17snMuyrpGIUKDLa8oeAaJzqrOv1f/Xe9MA5kch2 -pO7QqFDbwUv5xD3Z6I62P8CWeRLY+k0KszZ2qk6ygi+i1A3b/WR3b27plH8PyDo8 -lj3NTWy6ZpX3tJ2klJ+Xs5oN3BiMEQtWZY5GTOZe ------END CERTIFICATE-----
\ No newline at end of file diff --git a/testing/marionette/harness/marionette_harness/runner/test.key b/testing/marionette/harness/marionette_harness/runner/test.key deleted file mode 100644 index 194a49ec4..000000000 --- a/testing/marionette/harness/marionette_harness/runner/test.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCzhNaLAVkYhdHc -Mt8495CFGz6lXoE+L/w6X3937yO7OognD74lRs1jfcuV2KVQENKi0reX0Q1s+/kF -6G+oS72VZ557lFipbZP94BLFzbSKZFIxXw7jiYRx2pjdS+wCJaV9Nf5j2rOs7KVG -Dw1kI1xt8+zMKGMjwEua7I/B7rGiPnJNcLUJweu0EFU8i+oblH5LdOb0n0+mRTC1 -8Li00VlQZQqGU+pMn570WGwx9Rc6b1eLy1/wKAtFko0wIEn/UuYsyxia1+buPk80 -NRUTxQLaxV+++1vOjb+1NXY8fOacOyaHTY2A5hbGJ/JQSbZydENJSUQ4u3hDI+4W -Ptli5qXXAgMBAAECggEBAIcwDQSnIjo2ZECHytQykpG6X6XXEksLhc1Lp0lhPC49 -uNR5pX6a4AcBb3PLr0opMQZO2tUoKA0ff3t0e8loKD+/xXhY0Z/dlioEOP7elwv0 -2nS1mhe9spCuxpk4GGXRhdtR8t2tj8s0do3YvgPgITXoEDX6YBZHNGhZpzSrFPgQ -/c3eGCVmzWYuLFfdj5OPQ9bwTaY4JSvDLZT0/WTgiica7VySwfz3HP1fFqNykTiK -ACQREvtxfk5Ym2nT6oni7CM2zOEJL9SXicXI5HO4bERH0ZYh//F3g6mwGiFXUJPd -NKgaTM1oT9kRGkUaEYsRWrddwR8d5mXLvBuTJbgIsSECgYEA1+2uJSYRW1OqbhYP -ms59YQHSs3VjpJpnCV2zNa2Wixs57KS2cOH7B6KrQCogJFLtgCDVLtyoErfVkD7E -FivTgYr1pVCRppJddQzXik31uOINOBVffr7/09g3GcRN+ubHPZPq3K+dD6gHa3Aj -0nH1EjEEV0QpSTQFn87OF2mc9wcCgYEA1NVqMbbzd+9Xft5FXuSbX6E+S02dOGat -SgpnkTM80rjqa6eHdQzqk3JqyteHPgdi1vdYRlSPOj/X+6tySY0Ej9sRnYOfddA2 -kpiDiVkmiqVolyJPY69Utj+E3TzJ1vhCQuYknJmB7zP9tDcTxMeq0l/NaWvGshEK -yC4UTQog1rECgYASOFILfGzWgfbNlzr12xqlRtwanHst9oFfPvLSQrWDQ2bd2wAy -Aj+GY2mD3oobxouX1i1m6OOdwLlalJFDNauBMNKNgoDnx03vhIfjebSURy7KXrNS -JJe9rm7n07KoyzRgs8yLlp3wJkOKA0pihY8iW9R78JpzPNqEo5SsURMXnQKBgBlV -gfuC9H4tPjP6zzUZbyk1701VYsaI6k2q6WMOP0ox+q1v1p7nN7DvaKjWeOG4TVqb -PKW6gQYE/XeWk9cPcyCQigs+1KdYbnaKsvWRaBYO1GFREzQhdarv6qfPCZOOH40J -Cgid+Sp4/NULzU2aGspJ3xCSZKdjge4MFhyJfRkxAoGBAJlwqY4nue0MBLGNpqcs -WwDtSasHvegKAcxGBKL5oWPbLBk7hk+hdqc8f6YqCkCNqv/ooBspL15ESItL+6yT -zt0YkK4oH9tmLDb+rvqZ7ZdXbWSwKITMoCyyHUtT6OKt/RtA0Vdy9LPnP27oSO/C -dk8Qf7KgKZLWo0ZNkvw38tEC ------END PRIVATE KEY-----
\ No newline at end of file diff --git a/testing/marionette/harness/marionette_harness/runtests.py b/testing/marionette/harness/marionette_harness/runtests.py deleted file mode 100644 index 3d3d09375..000000000 --- a/testing/marionette/harness/marionette_harness/runtests.py +++ /dev/null @@ -1,98 +0,0 @@ -# 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/. - -import sys - -import mozlog - -from marionette_driver import __version__ as driver_version - -from marionette_harness import ( - __version__, - BaseMarionetteTestRunner, - BaseMarionetteArguments, - BrowserMobProxyArguments, - MarionetteTestCase, -) - - -class MarionetteTestRunner(BaseMarionetteTestRunner): - def __init__(self, **kwargs): - BaseMarionetteTestRunner.__init__(self, **kwargs) - self.test_handlers = [MarionetteTestCase] - - -class MarionetteArguments(BaseMarionetteArguments): - def __init__(self, **kwargs): - BaseMarionetteArguments.__init__(self, **kwargs) - self.register_argument_container(BrowserMobProxyArguments()) - - -class MarionetteHarness(object): - def __init__(self, - runner_class=MarionetteTestRunner, - parser_class=MarionetteArguments, - testcase_class=MarionetteTestCase, - args=None): - self._runner_class = runner_class - self._parser_class = parser_class - self._testcase_class = testcase_class - self.args = args or self.parse_args() - - def parse_args(self, logger_defaults=None): - parser = self._parser_class( - usage='%(prog)s [options] test_file_or_dir <test_file_or_dir> ...') - parser.add_argument('--version', action='version', - help="Show version information.", - version="%(prog)s {version}" - " (using marionette-driver: {driver_version}, ".format( - version=__version__, - driver_version=driver_version - )) - mozlog.commandline.add_logging_group(parser) - args = parser.parse_args() - parser.verify_usage(args) - - logger = mozlog.commandline.setup_logging( - args.logger_name, args, logger_defaults or {"tbpl": sys.stdout}) - - args.logger = logger - return vars(args) - - def process_args(self): - if self.args.get('pydebugger'): - self._testcase_class.pydebugger = __import__(self.args['pydebugger']) - - def run(self): - self.process_args() - tests = self.args.pop('tests') - runner = self._runner_class(**self.args) - runner.run_tests(tests) - return runner.failed + runner.crashed - - -def cli(runner_class=MarionetteTestRunner, parser_class=MarionetteArguments, - harness_class=MarionetteHarness, testcase_class=MarionetteTestCase, args=None): - """ - Call the harness to parse args and run tests. - - The following exit codes are expected: - - Test failures: 10 - - Harness/other failures: 1 - - Success: 0 - """ - logger = mozlog.commandline.setup_logging('Marionette test runner', {}) - try: - harness_instance = harness_class(runner_class, parser_class, testcase_class, - args=args) - failed = harness_instance.run() - if failed > 0: - sys.exit(10) - except Exception: - logger.error('Failure during harness execution', exc_info=True) - sys.exit(1) - sys.exit(0) - -if __name__ == "__main__": - cli() diff --git a/testing/marionette/harness/marionette_harness/tests/harness_unit/conftest.py b/testing/marionette/harness/marionette_harness/tests/harness_unit/conftest.py deleted file mode 100644 index 6dc0a89a1..000000000 --- a/testing/marionette/harness/marionette_harness/tests/harness_unit/conftest.py +++ /dev/null @@ -1,100 +0,0 @@ -# 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/. - -import pytest - -from mock import Mock, MagicMock - -from marionette_driver.marionette import Marionette - -from marionette_harness.runner.httpd import FixtureServer - - -@pytest.fixture(scope='module') -def logger(): - """ - Fake logger to help with mocking out other runner-related classes. - """ - import mozlog - return Mock(spec=mozlog.structuredlog.StructuredLogger) - - -@pytest.fixture -def mach_parsed_kwargs(logger): - """ - Parsed and verified dictionary used during simplest - call to mach marionette-test - """ - return { - 'adb_path': None, - 'addons': None, - 'address': None, - 'app': None, - 'app_args': [], - 'avd': None, - 'avd_home': None, - 'binary': u'/path/to/firefox', - 'browsermob_port' : None, - 'browsermob_script' : None, - 'device_serial': None, - 'e10s': True, - 'emulator': False, - 'emulator_bin': None, - 'gecko_log': None, - 'jsdebugger': False, - 'log_errorsummary': None, - 'log_html': None, - 'log_mach': None, - 'log_mach_buffer': None, - 'log_mach_level': None, - 'log_mach_verbose': None, - 'log_raw': None, - 'log_raw_level': None, - 'log_tbpl': None, - 'log_tbpl_buffer': None, - 'log_tbpl_compact': None, - 'log_tbpl_level': None, - 'log_unittest': None, - 'log_xunit': None, - 'logger_name': 'Marionette-based Tests', - 'prefs': {}, - 'prefs_args': None, - 'prefs_files': None, - 'profile': None, - 'pydebugger': None, - 'repeat': 0, - 'server_root': None, - 'shuffle': False, - 'shuffle_seed': 2276870381009474531, - 'socket_timeout': 60.0, - 'startup_timeout': 60, - 'symbols_path': None, - 'test_tags': None, - 'tests': [u'/path/to/unit-tests.ini'], - 'testvars': None, - 'this_chunk': None, - 'timeout': None, - 'total_chunks': None, - 'verbose': None, - 'workspace': None, - 'logger': logger, - } - - -@pytest.fixture -def mock_httpd(request): - """ Mock httpd instance """ - httpd = MagicMock(spec=FixtureServer) - return httpd - - -@pytest.fixture -def mock_marionette(request): - """ Mock marionette instance """ - marionette_class = MagicMock(spec=Marionette) - if 'has_crashed' in request.funcargnames: - marionette_class.check_for_crash.return_value = request.getfuncargvalue( - 'has_crashed' - ) - return marionette_class diff --git a/testing/marionette/harness/marionette_harness/tests/harness_unit/test_httpd.py b/testing/marionette/harness/marionette_harness/tests/harness_unit/test_httpd.py deleted file mode 100644 index bd86b2fff..000000000 --- a/testing/marionette/harness/marionette_harness/tests/harness_unit/test_httpd.py +++ /dev/null @@ -1,90 +0,0 @@ -# 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/. - -import json -import os -import types -import urllib2 - -import pytest - -from wptserve.handlers import json_handler - -from marionette_harness.runner import httpd - -here = os.path.abspath(os.path.dirname(__file__)) -parent = os.path.dirname(here) -default_doc_root = os.path.join(os.path.dirname(parent), "www") - - -@pytest.yield_fixture -def server(): - server = httpd.FixtureServer(default_doc_root) - yield server - server.stop() - - -def test_ctor(): - with pytest.raises(ValueError): - httpd.FixtureServer("foo") - httpd.FixtureServer(default_doc_root) - - -def test_start_stop(server): - server.start() - server.stop() - - -def test_get_url(server): - server.start() - url = server.get_url("/") - assert isinstance(url, types.StringTypes) - assert "http://" in url - - server.stop() - with pytest.raises(httpd.NotAliveError): - server.get_url("/") - - -def test_doc_root(server): - server.start() - assert isinstance(server.doc_root, types.StringTypes) - server.stop() - assert isinstance(server.doc_root, types.StringTypes) - - -def test_router(server): - assert server.router is not None - - -def test_routes(server): - assert server.routes is not None - - -def test_is_alive(server): - assert server.is_alive == False - server.start() - assert server.is_alive == True - - -def test_handler(server): - counter = 0 - - @json_handler - def handler(request, response): - return {"count": counter} - - route = ("GET", "/httpd/test_handler", handler) - server.router.register(*route) - server.start() - - url = server.get_url("/httpd/test_handler") - body = urllib2.urlopen(url).read() - res = json.loads(body) - assert res["count"] == counter - - -if __name__ == "__main__": - import sys - sys.exit(pytest.main(["--verbose", __file__])) diff --git a/testing/marionette/harness/marionette_harness/tests/harness_unit/test_marionette_arguments.py b/testing/marionette/harness/marionette_harness/tests/harness_unit/test_marionette_arguments.py deleted file mode 100644 index 1a0687028..000000000 --- a/testing/marionette/harness/marionette_harness/tests/harness_unit/test_marionette_arguments.py +++ /dev/null @@ -1,32 +0,0 @@ -# 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/. -import pytest - -from marionette_harness.runtests import MarionetteArguments - - -@pytest.mark.parametrize("socket_timeout", ['A', '10', '1B-', '1C2', '44.35']) -def test_parse_arg_socket_timeout(socket_timeout): - argv = ['marionette', '--socket-timeout', socket_timeout] - parser = MarionetteArguments() - - def _is_float_convertible(value): - try: - float(value) - return True - except: - return False - - if not _is_float_convertible(socket_timeout): - with pytest.raises(SystemExit) as ex: - parser.parse_args(args=argv) - assert ex.value.code == 2 - else: - args = parser.parse_args(args=argv) - assert hasattr(args, 'socket_timeout') and args.socket_timeout == float(socket_timeout) - - -if __name__ == '__main__': - import sys - sys.exit(pytest.main(['--verbose', __file__])) diff --git a/testing/marionette/harness/marionette_harness/tests/harness_unit/test_marionette_harness.py b/testing/marionette/harness/marionette_harness/tests/harness_unit/test_marionette_harness.py deleted file mode 100644 index dfbbfb788..000000000 --- a/testing/marionette/harness/marionette_harness/tests/harness_unit/test_marionette_harness.py +++ /dev/null @@ -1,108 +0,0 @@ -# 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/. - -import pytest - -from mock import Mock, patch, sentinel - -import marionette_harness.marionette_test as marionette_test - -from marionette_harness.runtests import MarionetteTestRunner, MarionetteHarness, cli - - -@pytest.fixture -def harness_class(request): - """ - Mock based on MarionetteHarness whose run method just returns a number of - failures according to the supplied test parameter - """ - if 'num_fails_crashed' in request.funcargnames: - num_fails_crashed = request.getfuncargvalue('num_fails_crashed') - else: - num_fails_crashed = (0, 0) - harness_cls = Mock(spec=MarionetteHarness) - harness = harness_cls.return_value - if num_fails_crashed is None: - harness.run.side_effect = Exception - else: - harness.run.return_value = sum(num_fails_crashed) - return harness_cls - - -@pytest.fixture -def runner_class(request): - """ - Mock based on MarionetteTestRunner, wherein the runner.failed, - runner.crashed attributes are provided by a test parameter - """ - if 'num_fails_crashed' in request.funcargnames: - failures, crashed = request.getfuncargvalue('num_fails_crashed') - else: - failures = 0 - crashed = 0 - mock_runner_class = Mock(spec=MarionetteTestRunner) - runner = mock_runner_class.return_value - runner.failed = failures - runner.crashed = crashed - return mock_runner_class - - -@pytest.mark.parametrize( - "num_fails_crashed,exit_code", - [((0, 0), 0), ((1, 0), 10), ((0, 1), 10), (None, 1)], -) -def test_cli_exit_code(num_fails_crashed, exit_code, harness_class): - with pytest.raises(SystemExit) as err: - cli(harness_class=harness_class) - assert err.value.code == exit_code - - -@pytest.mark.parametrize("num_fails_crashed", [(0, 0), (1, 0), (1, 1)]) -def test_call_harness_with_parsed_args_yields_num_failures(mach_parsed_kwargs, - runner_class, - num_fails_crashed): - with patch( - 'marionette_harness.runtests.MarionetteHarness.parse_args' - ) as parse_args: - failed_or_crashed = MarionetteHarness(runner_class, - args=mach_parsed_kwargs).run() - parse_args.assert_not_called() - assert failed_or_crashed == sum(num_fails_crashed) - - -def test_call_harness_with_no_args_yields_num_failures(runner_class): - with patch( - 'marionette_harness.runtests.MarionetteHarness.parse_args', - return_value={'tests': []} - ) as parse_args: - failed_or_crashed = MarionetteHarness(runner_class).run() - assert parse_args.call_count == 1 - assert failed_or_crashed == 0 - - -def test_args_passed_to_runner_class(mach_parsed_kwargs, runner_class): - arg_list = mach_parsed_kwargs.keys() - arg_list.remove('tests') - mach_parsed_kwargs.update([(a, getattr(sentinel, a)) for a in arg_list]) - harness = MarionetteHarness(runner_class, args=mach_parsed_kwargs) - harness.process_args = Mock() - harness.run() - for arg in arg_list: - assert harness._runner_class.call_args[1][arg] is getattr(sentinel, arg) - - -def test_harness_sets_up_default_test_handlers(mach_parsed_kwargs): - """ - If the necessary TestCase is not in test_handlers, - tests are omitted silently - """ - harness = MarionetteHarness(args=mach_parsed_kwargs) - mach_parsed_kwargs.pop('tests') - runner = harness._runner_class(**mach_parsed_kwargs) - assert marionette_test.MarionetteTestCase in runner.test_handlers - - -if __name__ == '__main__': - import sys - sys.exit(pytest.main(['--verbose', __file__])) diff --git a/testing/marionette/harness/marionette_harness/tests/harness_unit/test_marionette_runner.py b/testing/marionette/harness/marionette_harness/tests/harness_unit/test_marionette_runner.py deleted file mode 100644 index 79bdc824e..000000000 --- a/testing/marionette/harness/marionette_harness/tests/harness_unit/test_marionette_runner.py +++ /dev/null @@ -1,442 +0,0 @@ -# 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/. - -import manifestparser -import pytest - -from mock import Mock, patch, mock_open, sentinel, DEFAULT - -from marionette_harness.runtests import MarionetteTestRunner - - -@pytest.fixture -def runner(mach_parsed_kwargs): - """ - MarionetteTestRunner instance initialized with default options. - """ - return MarionetteTestRunner(**mach_parsed_kwargs) - - -@pytest.fixture -def mock_runner(runner, mock_marionette, monkeypatch): - """ - MarionetteTestRunner instance with mocked-out - self.marionette and other properties, - to enable testing runner.run_tests(). - """ - runner.driverclass = mock_marionette - for attr in ['run_test_set', '_capabilities']: - setattr(runner, attr, Mock()) - runner._appName = 'fake_app' - # simulate that browser runs with e10s by default - runner._appinfo = {'browserTabsRemoteAutostart': True} - monkeypatch.setattr('marionette_harness.runner.base.mozversion', Mock()) - return runner - - -@pytest.fixture -def build_kwargs_using(mach_parsed_kwargs): - '''Helper function for test_build_kwargs_* functions''' - def kwarg_builder(new_items, return_socket=False): - mach_parsed_kwargs.update(new_items) - runner = MarionetteTestRunner(**mach_parsed_kwargs) - with patch('marionette_harness.runner.base.socket') as socket: - built_kwargs = runner._build_kwargs() - if return_socket: - return built_kwargs, socket - return built_kwargs - return kwarg_builder - - -@pytest.fixture -def expected_driver_args(runner): - '''Helper fixture for tests of _build_kwargs - with binary/emulator. - Provides a dictionary of certain arguments - related to binary/emulator settings - which we expect to be passed to the - driverclass constructor. Expected values can - be updated in tests as needed. - Provides convenience methods for comparing the - expected arguments to the argument dictionary - created by _build_kwargs. ''' - - class ExpectedDict(dict): - def assert_matches(self, actual): - for (k, v) in self.items(): - assert actual[k] == v - - def assert_keys_not_in(self, actual): - for k in self.keys(): - assert k not in actual - - expected = ExpectedDict(host=None, port=None, bin=None) - for attr in ['app', 'app_args', 'profile', 'addons', 'gecko_log']: - expected[attr] = getattr(runner, attr) - return expected - - -class ManifestFixture: - def __init__(self, name='mock_manifest', - tests=[{'path': u'test_something.py', 'expected': 'pass'}]): - self.filepath = "/path/to/fake/manifest.ini" - self.n_disabled = len([t for t in tests if 'disabled' in t]) - self.n_enabled = len(tests) - self.n_disabled - mock_manifest = Mock(spec=manifestparser.TestManifest, - active_tests=Mock(return_value=tests)) - self.manifest_class = Mock(return_value=mock_manifest) - self.__repr__ = lambda: "<ManifestFixture {}>".format(name) - -@pytest.fixture -def manifest(): - return ManifestFixture() - -@pytest.fixture(params=['enabled', 'disabled', 'enabled_disabled', 'empty']) -def manifest_with_tests(request): - ''' - Fixture for the contents of mock_manifest, where a manifest - can include enabled tests, disabled tests, both, or neither (empty) - ''' - included = [] - if 'enabled' in request.param: - included += [(u'test_expected_pass.py', 'pass'), - (u'test_expected_fail.py', 'fail')] - if 'disabled' in request.param: - included += [(u'test_pass_disabled.py', 'pass', 'skip-if: true'), - (u'test_fail_disabled.py', 'fail', 'skip-if: true')] - keys = ('path', 'expected', 'disabled') - active_tests = [dict(zip(keys, values)) for values in included] - - return ManifestFixture(request.param, active_tests) - - -def test_args_passed_to_driverclass(mock_runner): - built_kwargs = {'arg1': 'value1', 'arg2': 'value2'} - mock_runner._build_kwargs = Mock(return_value=built_kwargs) - with pytest.raises(IOError): - mock_runner.run_tests(['fake_tests.ini']) - assert mock_runner.driverclass.call_args[1] == built_kwargs - - -def test_build_kwargs_basic_args(build_kwargs_using): - '''Test the functionality of runner._build_kwargs: - make sure that basic arguments (those which should - always be included, irrespective of the runner's settings) - get passed to the call to runner.driverclass''' - - basic_args = ['socket_timeout', 'prefs', - 'startup_timeout', 'verbose', 'symbols_path'] - args_dict = {a: getattr(sentinel, a) for a in basic_args} - # Mock an update method to work with calls to MarionetteTestRunner() - args_dict['prefs'].update = Mock(return_value={}) - built_kwargs = build_kwargs_using([(a, getattr(sentinel, a)) for a in basic_args]) - for arg in basic_args: - assert built_kwargs[arg] is getattr(sentinel, arg) - - -@pytest.mark.parametrize('workspace', ['path/to/workspace', None]) -def test_build_kwargs_with_workspace(build_kwargs_using, workspace): - built_kwargs = build_kwargs_using({'workspace': workspace}) - if workspace: - assert built_kwargs['workspace'] == workspace - else: - assert 'workspace' not in built_kwargs - - -@pytest.mark.parametrize('address', ['host:123', None]) -def test_build_kwargs_with_address(build_kwargs_using, address): - built_kwargs, socket = build_kwargs_using( - {'address': address, 'binary': None, 'emulator': None}, - return_socket=True - ) - assert 'connect_to_running_emulator' not in built_kwargs - if address is not None: - host, port = address.split(":") - assert built_kwargs['host'] == host and built_kwargs['port'] == int(port) - socket.socket().connect.assert_called_with((host, int(port))) - assert socket.socket().close.called - else: - assert not socket.socket.called - - -@pytest.mark.parametrize('address', ['host:123', None]) -@pytest.mark.parametrize('binary', ['path/to/bin', None]) -def test_build_kwargs_with_binary_or_address(expected_driver_args, build_kwargs_using, - binary, address): - built_kwargs = build_kwargs_using({'binary': binary, 'address': address, 'emulator': None}) - if binary: - expected_driver_args['bin'] = binary - if address: - host, port = address.split(":") - expected_driver_args.update({'host': host, 'port': int(port)}) - else: - expected_driver_args.update({'host': 'localhost', 'port': 2828}) - expected_driver_args.assert_matches(built_kwargs) - elif address is None: - expected_driver_args.assert_keys_not_in(built_kwargs) - - -@pytest.mark.parametrize('address', ['host:123', None]) -@pytest.mark.parametrize('emulator', [True, False, None]) -def test_build_kwargs_with_emulator_or_address(expected_driver_args, build_kwargs_using, - emulator, address): - emulator_props = [(a, getattr(sentinel, a)) for a in ['avd_home', 'adb_path', 'emulator_bin']] - built_kwargs = build_kwargs_using( - [('emulator', emulator), ('address', address), ('binary', None)] + emulator_props - ) - if emulator: - expected_driver_args.update(emulator_props) - expected_driver_args['emulator_binary'] = expected_driver_args.pop('emulator_bin') - expected_driver_args['bin'] = True - if address: - expected_driver_args['connect_to_running_emulator'] = True - host, port = address.split(":") - expected_driver_args.update({'host': host, 'port': int(port)}) - else: - expected_driver_args.update({'host': 'localhost', 'port': 2828}) - assert 'connect_to_running_emulator' not in built_kwargs - expected_driver_args.assert_matches(built_kwargs) - elif not address: - expected_driver_args.assert_keys_not_in(built_kwargs) - - -def test_parsing_testvars(mach_parsed_kwargs): - mach_parsed_kwargs.pop('tests') - testvars_json_loads = [ - {"wifi": {"ssid": "blah", "keyManagement": "WPA-PSK", "psk": "foo"}}, - {"wifi": {"PEAP": "bar"}, "device": {"stuff": "buzz"}} - ] - expected_dict = { - "wifi": { - "ssid": "blah", - "keyManagement": "WPA-PSK", - "psk": "foo", - "PEAP": "bar" - }, - "device": {"stuff": "buzz"} - } - with patch( - 'marionette_harness.runtests.MarionetteTestRunner._load_testvars', - return_value=testvars_json_loads - ) as load: - runner = MarionetteTestRunner(**mach_parsed_kwargs) - assert runner.testvars == expected_dict - assert load.call_count == 1 - - -def test_load_testvars_throws_expected_errors(mach_parsed_kwargs): - mach_parsed_kwargs['testvars'] = ['some_bad_path.json'] - runner = MarionetteTestRunner(**mach_parsed_kwargs) - with pytest.raises(IOError) as io_exc: - runner._load_testvars() - assert 'does not exist' in io_exc.value.message - with patch('os.path.exists', return_value=True): - with patch('__builtin__.open', mock_open(read_data='[not {valid JSON]')): - with pytest.raises(Exception) as json_exc: - runner._load_testvars() - assert 'not properly formatted' in json_exc.value.message - - -def _check_crash_counts(has_crashed, runner, mock_marionette): - if has_crashed: - assert mock_marionette.check_for_crash.call_count == 1 - assert runner.crashed == 1 - else: - assert runner.crashed == 0 - - -@pytest.mark.parametrize("has_crashed", [True, False]) -def test_increment_crash_count_in_run_test_set(runner, has_crashed, - mock_marionette): - fake_tests = [{'filepath': i, - 'expected': 'pass'} for i in 'abc'] - - with patch.multiple(runner, run_test=DEFAULT, marionette=mock_marionette): - runner.run_test_set(fake_tests) - if not has_crashed: - assert runner.marionette.check_for_crash.call_count == len(fake_tests) - _check_crash_counts(has_crashed, runner, runner.marionette) - - -@pytest.mark.parametrize("has_crashed", [True, False]) -def test_record_crash(runner, has_crashed, mock_marionette): - with patch.object(runner, 'marionette', mock_marionette): - assert runner.record_crash() == has_crashed - _check_crash_counts(has_crashed, runner, runner.marionette) - - -def test_add_test_module(runner): - tests = ['test_something.py', 'testSomething.js', 'bad_test.py'] - assert len(runner.tests) == 0 - for test in tests: - with patch('os.path.abspath', return_value=test) as abspath: - runner.add_test(test) - assert abspath.called - expected = {'filepath': test, 'expected': 'pass'} - assert expected in runner.tests - # add_test doesn't validate module names; 'bad_test.py' gets through - assert len(runner.tests) == 3 - - -def test_add_test_directory(runner): - test_dir = 'path/to/tests' - dir_contents = [ - (test_dir, ('subdir',), ('test_a.py', 'test_a.js', 'bad_test_a.py', 'bad_test_a.js')), - (test_dir + '/subdir', (), ('test_b.py', 'test_b.js', 'bad_test_b.py', 'bad_test_b.js')), - ] - tests = list(dir_contents[0][2] + dir_contents[1][2]) - assert len(runner.tests) == 0 - # Need to use side effect to make isdir return True for test_dir and False for tests - with patch('os.path.isdir', side_effect=[True] + [False for t in tests]) as isdir: - with patch('os.walk', return_value=dir_contents) as walk: - runner.add_test(test_dir) - assert isdir.called and walk.called - for test in runner.tests: - assert test_dir in test['filepath'] - assert len(runner.tests) == 4 - - -@pytest.mark.parametrize("test_files_exist", [True, False]) -def test_add_test_manifest(mock_runner, manifest_with_tests, monkeypatch, test_files_exist): - monkeypatch.setattr('marionette_harness.runner.base.TestManifest', - manifest_with_tests.manifest_class) - mock_runner.marionette = mock_runner.driverclass() - with patch('marionette_harness.runner.base.os.path.exists', return_value=test_files_exist): - if test_files_exist or manifest_with_tests.n_enabled == 0: - mock_runner.add_test(manifest_with_tests.filepath) - assert len(mock_runner.tests) == manifest_with_tests.n_enabled - assert len(mock_runner.manifest_skipped_tests) == manifest_with_tests.n_disabled - for test in mock_runner.tests: - assert test['filepath'].endswith(test['expected'] + '.py') - else: - pytest.raises(IOError, "mock_runner.add_test(manifest_with_tests.filepath)") - assert manifest_with_tests.manifest_class().read.called - assert manifest_with_tests.manifest_class().active_tests.called - - -def get_kwargs_passed_to_manifest(mock_runner, manifest, monkeypatch, **kwargs): - '''Helper function for test_manifest_* tests. - Returns the kwargs passed to the call to manifest.active_tests.''' - monkeypatch.setattr('marionette_harness.runner.base.TestManifest', manifest.manifest_class) - monkeypatch.setattr('marionette_harness.runner.base.mozinfo.info', - {'mozinfo_key': 'mozinfo_val'}) - for attr in kwargs: - setattr(mock_runner, attr, kwargs[attr]) - mock_runner.marionette = mock_runner.driverclass() - with patch('marionette_harness.runner.base.os.path.exists', return_value=True): - mock_runner.add_test(manifest.filepath) - call_args, call_kwargs = manifest.manifest_class().active_tests.call_args - return call_kwargs - - -def test_manifest_basic_args(mock_runner, manifest, monkeypatch): - kwargs = get_kwargs_passed_to_manifest(mock_runner, manifest, monkeypatch) - assert kwargs['exists'] is False - assert kwargs['disabled'] is True - assert kwargs['appname'] == 'fake_app' - assert 'mozinfo_key' in kwargs and kwargs['mozinfo_key'] == 'mozinfo_val' - - -@pytest.mark.parametrize('e10s', (True, False)) -def test_manifest_with_e10s(mock_runner, manifest, monkeypatch, e10s): - kwargs = get_kwargs_passed_to_manifest(mock_runner, manifest, monkeypatch, e10s=e10s) - assert kwargs['e10s'] == e10s - - -@pytest.mark.parametrize('test_tags', (None, ['tag', 'tag2'])) -def test_manifest_with_test_tags(mock_runner, manifest, monkeypatch, test_tags): - kwargs = get_kwargs_passed_to_manifest(mock_runner, manifest, monkeypatch, test_tags=test_tags) - if test_tags is None: - assert kwargs['filters'] == [] - else: - assert len(kwargs['filters']) == 1 and kwargs['filters'][0].tags == test_tags - - -def test_cleanup_with_manifest(mock_runner, manifest_with_tests, monkeypatch): - monkeypatch.setattr('marionette_harness.runner.base.TestManifest', - manifest_with_tests.manifest_class) - if manifest_with_tests.n_enabled > 0: - context = patch('marionette_harness.runner.base.os.path.exists', return_value=True) - else: - context = pytest.raises(Exception) - with context: - mock_runner.run_tests([manifest_with_tests.filepath]) - assert mock_runner.marionette is None - assert mock_runner.fixture_servers == {} - - -def test_reset_test_stats(mock_runner): - def reset_successful(runner): - stats = ['passed', 'failed', 'unexpected_successes', 'todo', 'skipped', 'failures'] - return all([((s in vars(runner)) and (not vars(runner)[s])) for s in stats]) - assert reset_successful(mock_runner) - mock_runner.passed = 1 - mock_runner.failed = 1 - mock_runner.failures.append(['TEST-UNEXPECTED-FAIL']) - assert not reset_successful(mock_runner) - mock_runner.run_tests([u'test_fake_thing.py']) - assert reset_successful(mock_runner) - - -def test_initialize_test_run(mock_runner): - tests = [u'test_fake_thing.py'] - mock_runner.reset_test_stats = Mock() - mock_runner.run_tests(tests) - assert mock_runner.reset_test_stats.called - with pytest.raises(AssertionError) as test_exc: - mock_runner.run_tests([]) - assert "len(tests)" in str(test_exc.traceback[-1].statement) - with pytest.raises(AssertionError) as hndl_exc: - mock_runner.test_handlers = [] - mock_runner.run_tests(tests) - assert "test_handlers" in str(hndl_exc.traceback[-1].statement) - assert mock_runner.reset_test_stats.call_count == 1 - - -def test_add_tests(mock_runner): - assert len(mock_runner.tests) == 0 - fake_tests = ["test_" + i + ".py" for i in "abc"] - mock_runner.run_tests(fake_tests) - assert len(mock_runner.tests) == 3 - for (test_name, added_test) in zip(fake_tests, mock_runner.tests): - assert added_test['filepath'].endswith(test_name) - - -def test_catch_invalid_test_names(runner): - good_tests = [u'test_ok.py', u'test_is_ok.py', u'test_is_ok.js', u'testIsOk.js'] - bad_tests = [u'bad_test.py', u'testbad.py', u'_test_bad.py', u'testBad.notjs', - u'test_bad.notpy', u'test_bad', u'testbad.js', u'badtest.js', - u'test.py', u'test_.py', u'test.js', u'test_.js'] - with pytest.raises(Exception) as exc: - runner._add_tests(good_tests + bad_tests) - msg = exc.value.message - assert "Test file names must be of the form" in msg - for bad_name in bad_tests: - assert bad_name in msg - for good_name in good_tests: - assert good_name not in msg - -@pytest.mark.parametrize('e10s', (True, False)) -def test_e10s_option_sets_prefs(mach_parsed_kwargs, e10s): - mach_parsed_kwargs['e10s'] = e10s - runner = MarionetteTestRunner(**mach_parsed_kwargs) - e10s_prefs = { - 'browser.tabs.remote.autostart': True, - 'browser.tabs.remote.force-enable': True, - 'extensions.e10sBlocksEnabling': False - } - for k,v in e10s_prefs.iteritems(): - if k == 'extensions.e10sBlocksEnabling' and not e10s: - continue - assert runner.prefs.get(k, False) == (v and e10s) - -def test_e10s_option_clash_raises(mock_runner): - mock_runner.e10s = False - with pytest.raises(AssertionError) as e: - mock_runner.run_tests([u'test_fake_thing.py']) - assert "configuration (self.e10s) does not match browser appinfo" in e.value.message - -if __name__ == '__main__': - import sys - sys.exit(pytest.main(['--verbose', __file__])) diff --git a/testing/marionette/harness/marionette_harness/tests/harness_unit/test_marionette_test_result.py b/testing/marionette/harness/marionette_harness/tests/harness_unit/test_marionette_test_result.py deleted file mode 100644 index a69b072cd..000000000 --- a/testing/marionette/harness/marionette_harness/tests/harness_unit/test_marionette_test_result.py +++ /dev/null @@ -1,54 +0,0 @@ -# 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/. - -import pytest - -from marionette_harness import MarionetteTestResult - - -@pytest.fixture -def empty_marionette_testcase(): - """ Testable MarionetteTestCase class """ - from marionette_harness import MarionetteTestCase - - class EmptyTestCase(MarionetteTestCase): - def test_nothing(self): - pass - - return EmptyTestCase - - -@pytest.fixture -def empty_marionette_test(mock_marionette, empty_marionette_testcase): - return empty_marionette_testcase(lambda: mock_marionette, lambda: mock_httpd, - 'test_nothing') - - -@pytest.mark.parametrize("has_crashed", [True, False]) -def test_crash_is_recorded_as_error(empty_marionette_test, - logger, - has_crashed): - """ Number of errors is incremented by stopTest iff has_crashed is true """ - # collect results from the empty test - result = MarionetteTestResult( - marionette=empty_marionette_test._marionette_weakref(), - logger=logger, verbosity=None, - stream=None, descriptions=None, - ) - result.startTest(empty_marionette_test) - assert len(result.errors) == 0 - assert len(result.failures) == 0 - assert result.testsRun == 1 - assert result.shouldStop is False - result.stopTest(empty_marionette_test) - assert result.shouldStop == has_crashed - if has_crashed: - assert len(result.errors) == 1 - else: - assert len(result.errors) == 0 - - -if __name__ == '__main__': - import sys - sys.exit(pytest.main(['--verbose', __file__])) diff --git a/testing/marionette/harness/marionette_harness/tests/harness_unit/test_serve.py b/testing/marionette/harness/marionette_harness/tests/harness_unit/test_serve.py deleted file mode 100644 index 73684c0d6..000000000 --- a/testing/marionette/harness/marionette_harness/tests/harness_unit/test_serve.py +++ /dev/null @@ -1,67 +0,0 @@ -# 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/. - -import types - -import pytest - -from marionette_harness.runner import serve -from marionette_harness.runner.serve import iter_proc, iter_url - - -def teardown_function(func): - for server in [server for server in iter_proc(serve.servers) if server.is_alive]: - server.stop() - server.kill() - - -def test_registered_servers(): - # [(name, factory), ...] - assert serve.registered_servers[0][0] == "http" - assert serve.registered_servers[1][0] == "https" - - -def test_globals(): - assert serve.default_doc_root is not None - assert serve.registered_servers is not None - assert serve.servers is not None - - -def test_start(): - serve.start() - assert len(serve.servers) == 2 - assert "http" in serve.servers - assert "https" in serve.servers - for url in iter_url(serve.servers): - assert isinstance(url, types.StringTypes) - - -def test_start_with_custom_root(tmpdir_factory): - tdir = tmpdir_factory.mktemp("foo") - serve.start(str(tdir)) - for server in iter_proc(serve.servers): - assert server.doc_root == tdir - - -def test_iter_proc(): - serve.start() - for server in iter_proc(serve.servers): - server.stop() - - -def test_iter_url(): - serve.start() - for url in iter_url(serve.servers): - assert isinstance(url, types.StringTypes) - - -def test_where_is(): - serve.start() - assert serve.where_is("/") == serve.servers["http"][1].get_url("/") - assert serve.where_is("/", on="https") == serve.servers["https"][1].get_url("/") - - -if __name__ == "__main__": - import sys - sys.exit(pytest.main(["-s", "--verbose", __file__])) diff --git a/testing/marionette/harness/marionette_harness/tests/unit-tests.ini b/testing/marionette/harness/marionette_harness/tests/unit-tests.ini deleted file mode 100644 index 8d806afea..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit-tests.ini +++ /dev/null @@ -1,11 +0,0 @@ -; marionette unit tests -[include:unit/unit-tests.ini] - -; layout tests -[include:../../../../../layout/base/tests/marionette/manifest.ini] - -; microformats tests -[include:../../../../../toolkit/components/microformats/manifest.ini] - -; migration tests -[include:../../../../../browser/components/migration/tests/marionette/manifest.ini] diff --git a/testing/marionette/harness/marionette_harness/tests/unit/importanotherscript.js b/testing/marionette/harness/marionette_harness/tests/unit/importanotherscript.js deleted file mode 100644 index fa6a1fa7c..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/importanotherscript.js +++ /dev/null @@ -1 +0,0 @@ -var testAnotherFunc = function() { return "i'm yet another test function!";}; diff --git a/testing/marionette/harness/marionette_harness/tests/unit/importscript.js b/testing/marionette/harness/marionette_harness/tests/unit/importscript.js deleted file mode 100644 index 5a5dd8a18..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/importscript.js +++ /dev/null @@ -1 +0,0 @@ -var testFunc = function() { return "i'm a test function!";}; diff --git a/testing/marionette/harness/marionette_harness/tests/unit/mn-restartless-unsigned.xpi b/testing/marionette/harness/marionette_harness/tests/unit/mn-restartless-unsigned.xpi Binary files differdeleted file mode 100644 index c8877b55d..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/mn-restartless-unsigned.xpi +++ /dev/null diff --git a/testing/marionette/harness/marionette_harness/tests/unit/single_finger_functions.py b/testing/marionette/harness/marionette_harness/tests/unit/single_finger_functions.py deleted file mode 100644 index c2daf9d54..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/single_finger_functions.py +++ /dev/null @@ -1,131 +0,0 @@ -from marionette_driver.marionette import Actions -from marionette_driver.errors import TimeoutException -from marionette_driver.by import By - - -def wait_for_condition_else_raise(marionette, wait_for_condition, expected, script): - try: - wait_for_condition(lambda m: expected in m.execute_script(script)) - except TimeoutException as e: - raise TimeoutException("{0} got {1} instead of {2}".format( - e.message, marionette.execute_script(script), expected)) - -def press_release(marionette, times, wait_for_condition, expected): - testAction = marionette.absolute_url("testAction.html") - marionette.navigate(testAction) - action = Actions(marionette) - button = marionette.find_element(By.ID, "button1") - action.press(button).release() - # Insert wait between each press and release chain. - for _ in range(times-1): - action.wait(0.1) - action.press(button).release() - action.perform() - wait_for_condition_else_raise(marionette, wait_for_condition, expected, "return document.getElementById('button1').innerHTML;") - -def move_element(marionette, wait_for_condition, expected1, expected2): - testAction = marionette.absolute_url("testAction.html") - marionette.navigate(testAction) - ele = marionette.find_element(By.ID, "button1") - drop = marionette.find_element(By.ID, "button2") - action = Actions(marionette) - action.press(ele).move(drop).release() - action.perform() - wait_for_condition_else_raise(marionette, wait_for_condition, expected1, "return document.getElementById('button1').innerHTML;") - wait_for_condition_else_raise(marionette, wait_for_condition, expected2, "return document.getElementById('button2').innerHTML;") - -def move_element_offset(marionette, wait_for_condition, expected1, expected2): - testAction = marionette.absolute_url("testAction.html") - marionette.navigate(testAction) - ele = marionette.find_element(By.ID, "button1") - action = Actions(marionette) - action.press(ele).move_by_offset(0,150).move_by_offset(0, 150).release() - action.perform() - wait_for_condition_else_raise(marionette, wait_for_condition, expected1, "return document.getElementById('button1').innerHTML;") - wait_for_condition_else_raise(marionette, wait_for_condition, expected2, "return document.getElementById('button2').innerHTML;") - -def chain(marionette, wait_for_condition, expected1, expected2): - testAction = marionette.absolute_url("testAction.html") - marionette.navigate(testAction) - marionette.timeout.implicit = 15 - action = Actions(marionette) - button1 = marionette.find_element(By.ID, "button1") - action.press(button1).perform() - button2 = marionette.find_element(By.ID, "delayed") - wait_for_condition_else_raise(marionette, wait_for_condition, expected1, "return document.getElementById('button1').innerHTML;") - action.move(button2).release().perform() - wait_for_condition_else_raise(marionette, wait_for_condition, expected2, "return document.getElementById('delayed').innerHTML;") - -def chain_flick(marionette, wait_for_condition, expected1, expected2): - testAction = marionette.absolute_url("testAction.html") - marionette.navigate(testAction) - button = marionette.find_element(By.ID, "button1") - action = Actions(marionette) - action.flick(button, 0, 0, 0, 200).perform() - wait_for_condition_else_raise(marionette, wait_for_condition, expected1,"return document.getElementById('button1').innerHTML;") - wait_for_condition_else_raise(marionette, wait_for_condition, expected2,"return document.getElementById('buttonFlick').innerHTML;") - - -def wait(marionette, wait_for_condition, expected): - testAction = marionette.absolute_url("testAction.html") - marionette.navigate(testAction) - action = Actions(marionette) - button = marionette.find_element(By.ID, "button1") - action.press(button).wait().release().perform() - wait_for_condition_else_raise(marionette, wait_for_condition, expected, "return document.getElementById('button1').innerHTML;") - -def wait_with_value(marionette, wait_for_condition, expected): - testAction = marionette.absolute_url("testAction.html") - marionette.navigate(testAction) - button = marionette.find_element(By.ID, "button1") - action = Actions(marionette) - action.press(button).wait(0.01).release() - action.perform() - wait_for_condition_else_raise(marionette, wait_for_condition, expected, "return document.getElementById('button1').innerHTML;") - -def context_menu(marionette, wait_for_condition, expected1, expected2): - testAction = marionette.absolute_url("testAction.html") - marionette.navigate(testAction) - button = marionette.find_element(By.ID, "button1") - action = Actions(marionette) - action.press(button).wait(5).perform() - wait_for_condition_else_raise(marionette, wait_for_condition, expected1, "return document.getElementById('button1').innerHTML;") - action.release().perform() - wait_for_condition_else_raise(marionette, wait_for_condition, expected2, "return document.getElementById('button1').innerHTML;") - -def long_press_action(marionette, wait_for_condition, expected): - testAction = marionette.absolute_url("testAction.html") - marionette.navigate(testAction) - button = marionette.find_element(By.ID, "button1") - action = Actions(marionette) - action.long_press(button, 5).perform() - wait_for_condition_else_raise(marionette, wait_for_condition, expected, "return document.getElementById('button1').innerHTML;") - -def long_press_on_xy_action(marionette, wait_for_condition, expected): - testAction = marionette.absolute_url("testAction.html") - marionette.navigate(testAction) - html = marionette.find_element(By.TAG_NAME, "html") - button = marionette.find_element(By.ID, "button1") - action = Actions(marionette) - - # Press the center of the button with respect to html. - x = button.rect['x'] + button.rect['width'] / 2.0 - y = button.rect['y'] + button.rect['height'] / 2.0 - action.long_press(html, 5, x, y).perform() - wait_for_condition_else_raise(marionette, wait_for_condition, expected, "return document.getElementById('button1').innerHTML;") - -def single_tap(marionette, wait_for_condition, expected): - testAction = marionette.absolute_url("testAction.html") - marionette.navigate(testAction) - button = marionette.find_element(By.ID, "button1") - action = Actions(marionette) - action.tap(button).perform() - wait_for_condition_else_raise(marionette, wait_for_condition, expected, "return document.getElementById('button1').innerHTML;") - -def double_tap(marionette, wait_for_condition, expected): - testAction = marionette.absolute_url("testAction.html") - marionette.navigate(testAction) - button = marionette.find_element(By.ID, "button1") - action = Actions(marionette) - action.double_tap(button).perform() - wait_for_condition_else_raise(marionette, wait_for_condition, expected, "return document.getElementById('button1').innerHTML;") diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_about_pages.py b/testing/marionette/harness/marionette_harness/tests/unit/test_about_pages.py deleted file mode 100644 index e9992f8a5..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_about_pages.py +++ /dev/null @@ -1,134 +0,0 @@ -# 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.keys import Keys - -from marionette_harness import MarionetteTestCase, skip, skip_if_mobile, WindowManagerMixin - - -class TestAboutPages(WindowManagerMixin, MarionetteTestCase): - - def setUp(self): - super(TestAboutPages, self).setUp() - - if self.marionette.session_capabilities['platformName'] == 'darwin': - self.mod_key = Keys.META - else: - self.mod_key = Keys.CONTROL - - self.remote_uri = self.marionette.absolute_url("windowHandles.html") - - def tearDown(self): - self.close_all_tabs() - - super(TestAboutPages, self).tearDown() - - def open_tab_with_link(self): - with self.marionette.using_context("content"): - self.marionette.navigate(self.remote_uri) - - link = self.marionette.find_element(By.ID, "new-tab") - link.click() - - @skip_if_mobile("Bug 1333209 - Process killed because of connection loss") - def test_back_forward(self): - # Bug 1311041 - Prevent changing of window handle by forcing the test - # to be run in a new tab. - new_tab = self.open_tab(trigger=self.open_tab_with_link) - self.marionette.switch_to_window(new_tab) - - self.marionette.navigate("about:blank") - self.marionette.navigate(self.remote_uri) - self.marionette.navigate("about:support") - - self.marionette.go_back() - self.assertEqual(self.marionette.get_url(), self.remote_uri) - - self.marionette.go_forward() - self.assertEqual(self.marionette.get_url(), "about:support") - - self.marionette.close() - self.marionette.switch_to_window(self.start_tab) - - @skip_if_mobile("Bug 1333209 - Process killed because of connection loss") - def test_navigate_non_remote_about_pages(self): - # Bug 1311041 - Prevent changing of window handle by forcing the test - # to be run in a new tab. - new_tab = self.open_tab(trigger=self.open_tab_with_link) - self.marionette.switch_to_window(new_tab) - - self.marionette.navigate("about:blank") - self.assertEqual(self.marionette.get_url(), "about:blank") - self.marionette.navigate("about:support") - self.assertEqual(self.marionette.get_url(), "about:support") - - self.marionette.close() - self.marionette.switch_to_window(self.start_tab) - - @skip_if_mobile("On Android no shortcuts are available") - def test_navigate_shortcut_key(self): - def open_with_shortcut(): - self.marionette.navigate(self.remote_uri) - with self.marionette.using_context("chrome"): - main_win = self.marionette.find_element(By.ID, "main-window") - main_win.send_keys(self.mod_key, Keys.SHIFT, 'a') - - new_tab = self.open_tab(trigger=open_with_shortcut) - self.marionette.switch_to_window(new_tab) - - Wait(self.marionette).until(lambda mn: mn.get_url() == "about:addons", - message="'about:addons' hasn't been loaded") - - self.marionette.close() - self.marionette.switch_to_window(self.start_tab) - - @skip("Bug 1334137 - Intermittent: Process killed because of hang in getCurrentUrl()") - @skip_if_mobile("Interacting with chrome elements not available for Fennec") - def test_type_to_non_remote_tab(self): - # Bug 1311041 - Prevent changing of window handle by forcing the test - # to be run in a new tab. - new_tab = self.open_tab(trigger=self.open_tab_with_link) - self.marionette.switch_to_window(new_tab) - - with self.marionette.using_context("chrome"): - urlbar = self.marionette.find_element(By.ID, 'urlbar') - urlbar.send_keys(self.mod_key + 'a') - urlbar.send_keys(self.mod_key + 'x') - urlbar.send_keys('about:support' + Keys.ENTER) - Wait(self.marionette).until(lambda mn: mn.get_url() == "about:support", - message="'about:support' hasn't been loaded") - - self.marionette.close() - self.marionette.switch_to_window(self.start_tab) - - @skip_if_mobile("Interacting with chrome elements not available for Fennec") - def test_type_to_remote_tab(self): - # Bug 1311041 - Prevent changing of window handle by forcing the test - # to be run in a new tab. - new_tab = self.open_tab(trigger=self.open_tab_with_link) - self.marionette.switch_to_window(new_tab) - - # about:blank keeps remoteness from remote_uri - self.marionette.navigate("about:blank") - with self.marionette.using_context("chrome"): - urlbar = self.marionette.find_element(By.ID, 'urlbar') - urlbar.send_keys(self.mod_key + 'a') - urlbar.send_keys(self.mod_key + 'x') - urlbar.send_keys(self.remote_uri + Keys.ENTER) - - Wait(self.marionette).until(lambda mn: mn.get_url() == self.remote_uri, - message="'{}' hasn't been loaded".format(self.remote_uri)) - - @skip_if_mobile("Needs application independent method to open a new tab") - def test_hang(self): - # Bug 1311041 - Prevent changing of window handle by forcing the test - # to be run in a new tab. - new_tab = self.open_tab(trigger=self.open_tab_with_link) - - # Close the start tab - self.marionette.close() - self.marionette.switch_to_window(new_tab) - - self.marionette.navigate(self.remote_uri) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_accessibility.py b/testing/marionette/harness/marionette_harness/tests/unit/test_accessibility.py deleted file mode 100644 index 0d8d9dca5..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_accessibility.py +++ /dev/null @@ -1,210 +0,0 @@ -# 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.by import By -from marionette_driver.errors import ( - ElementNotAccessibleException, - ElementNotInteractableException -) - -from marionette_harness import MarionetteTestCase - - - -class TestAccessibility(MarionetteTestCase): - def setUp(self): - super(TestAccessibility, self).setUp() - with self.marionette.using_context("chrome"): - self.marionette.set_pref("dom.ipc.processCount", 1) - - def tearDown(self): - with self.marionette.using_context("chrome"): - self.marionette.clear_pref("dom.ipc.processCount") - - # Elements that are accessible with and without the accessibliity API - valid_elementIDs = [ - # Button1 is an accessible button with a valid accessible name - # computed from subtree - "button1", - # Button2 is an accessible button with a valid accessible name - # computed from aria-label - "button2", - # Button13 is an accessible button that is implemented via role="button" - # and is explorable using tabindex="0" - "button13", - # button17 is an accessible button that overrides parent's - # pointer-events:none; property with its own pointer-events:all; - "button17" - ] - - # Elements that are not accessible with the accessibility API - invalid_elementIDs = [ - # Button3 does not have an accessible object - "button3", - # Button4 does not support any accessible actions - "button4", - # Button5 does not have a correct accessibility role and may not be - # manipulated via the accessibility API - "button5", - # Button6 is missing an accesible name - "button6", - # Button7 is not currently visible via the accessibility API and may - # not be manipulated by it - "button7", - # Button8 is not currently visible via the accessibility API and may - # not be manipulated by it (in hidden subtree) - "button8", - # Button14 is accessible button but is not explorable because of lack - # of tabindex that would make it focusable. - "button14" - ] - - # Elements that are either accessible to accessibility API or not accessible - # at all - falsy_elements = [ - # Element is only visible to the accessibility API and may be - # manipulated by it - "button9", - # Element is not currently visible - "button10" - ] - - displayed_elementIDs = [ - "button1", "button2", "button3", "button4", "button5", "button6", - "button9", "no_accessible_but_displayed" - ] - - displayed_but_a11y_hidden_elementIDs = ["button7", "button8"] - - disabled_elementIDs = ["button11", "no_accessible_but_disabled"] - - # Elements that are enabled but otherwise disabled or not explorable via the accessibility API - disabled_accessibility_elementIDs = ["button12", "button15", "button16"] - - # Elements that are reporting selected state - valid_option_elementIDs = ["option1", "option2"] - - def run_element_test(self, ids, testFn): - for id in ids: - element = self.marionette.find_element(By.ID, id) - testFn(element) - - def setup_accessibility(self, enable_a11y_checks=True, navigate=True): - self.marionette.delete_session() - self.marionette.start_session( - {"requiredCapabilities": {"moz:accessibilityChecks": enable_a11y_checks}}) - self.assertEqual( - self.marionette.session_capabilities["moz:accessibilityChecks"], - enable_a11y_checks) - - # Navigate to test_accessibility.html - if navigate: - test_accessibility = self.marionette.absolute_url("test_accessibility.html") - self.marionette.navigate(test_accessibility) - - def test_valid_single_tap(self): - self.setup_accessibility() - # No exception should be raised - self.run_element_test(self.valid_elementIDs, lambda button: button.tap()) - - def test_single_tap_raises_element_not_accessible(self): - self.setup_accessibility() - self.run_element_test(self.invalid_elementIDs, - lambda button: self.assertRaises(ElementNotAccessibleException, - button.tap)) - self.run_element_test(self.falsy_elements, - lambda button: self.assertRaises(ElementNotInteractableException, - button.tap)) - - def test_single_tap_raises_no_exceptions(self): - self.setup_accessibility(False, True) - # No exception should be raised - self.run_element_test(self.invalid_elementIDs, lambda button: button.tap()) - # Elements are invisible - self.run_element_test(self.falsy_elements, - lambda button: self.assertRaises(ElementNotInteractableException, - button.tap)) - - def test_valid_click(self): - self.setup_accessibility() - # No exception should be raised - self.run_element_test(self.valid_elementIDs, lambda button: button.click()) - - def test_click_raises_element_not_accessible(self): - self.setup_accessibility() - self.run_element_test(self.invalid_elementIDs, - lambda button: self.assertRaises(ElementNotAccessibleException, - button.click)) - self.run_element_test(self.falsy_elements, - lambda button: self.assertRaises(ElementNotInteractableException, - button.click)) - - def test_click_raises_no_exceptions(self): - self.setup_accessibility(False, True) - # No exception should be raised - self.run_element_test(self.invalid_elementIDs, lambda button: button.click()) - # Elements are invisible - self.run_element_test(self.falsy_elements, - lambda button: self.assertRaises(ElementNotInteractableException, - button.click)) - - def test_element_visible_but_not_visible_to_accessbility(self): - self.setup_accessibility() - # Elements are displayed but hidden from accessibility API - self.run_element_test(self.displayed_but_a11y_hidden_elementIDs, - lambda element: self.assertRaises(ElementNotAccessibleException, - element.is_displayed)) - - def test_element_is_visible_to_accessibility(self): - self.setup_accessibility() - # No exception should be raised - self.run_element_test(self.displayed_elementIDs, lambda element: element.is_displayed()) - - def test_element_is_not_enabled_to_accessbility(self): - self.setup_accessibility() - # Buttons are enabled but disabled/not-explorable via the accessibility API - self.run_element_test(self.disabled_accessibility_elementIDs, - lambda element: self.assertRaises(ElementNotAccessibleException, - element.is_enabled)) - - # Buttons are enabled but disabled/not-explorable via the accessibility API and thus are not - # clickable via the accessibility API - self.run_element_test(self.disabled_accessibility_elementIDs, - lambda element: self.assertRaises(ElementNotAccessibleException, - element.click)) - - self.setup_accessibility(False, False) - self.run_element_test(self.disabled_accessibility_elementIDs, - lambda element: element.is_enabled()) - self.run_element_test(self.disabled_accessibility_elementIDs, - lambda element: element.click()) - - def test_element_is_enabled_to_accessibility(self): - self.setup_accessibility() - # No exception should be raised - self.run_element_test(self.disabled_elementIDs, lambda element: element.is_enabled()) - - def test_send_keys_raises_no_exception(self): - self.setup_accessibility() - # Sending keys to valid input should not raise any exceptions - self.run_element_test(['input1'], lambda element: element.send_keys("a")) - - self.setup_accessibility(False, False) - # Sending keys to invalid element should not raise any exceptions when raising accessibility - # exceptions is disabled - self.run_element_test(['button5'], lambda element: element.send_keys("abc")) - - def test_send_keys_raises_element_not_accessible(self): - self.setup_accessibility() - # Sending keys to invalid element should raise an exception - self.run_element_test(['button5'], - lambda element: self.assertRaises(ElementNotAccessibleException, - element.send_keys)) - - def test_is_selected_raises_no_exception(self): - self.setup_accessibility() - # No exception should be raised for valid options - self.run_element_test(self.valid_option_elementIDs, lambda element: element.is_selected()) - # No exception should be raised for non-selectable elements - self.run_element_test(self.valid_elementIDs, lambda element: element.is_selected()) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_addons.py b/testing/marionette/harness/marionette_harness/tests/unit/test_addons.py deleted file mode 100644 index 25f1c05ab..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_addons.py +++ /dev/null @@ -1,58 +0,0 @@ -# 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/. - -import os - -from marionette_driver.addons import Addons, AddonInstallException -from marionette_harness import MarionetteTestCase, skip - - -here = os.path.abspath(os.path.dirname(__file__)) - - -class TestAddons(MarionetteTestCase): - - def setUp(self): - MarionetteTestCase.setUp(self) - self.addons = Addons(self.marionette) - - @property - def all_addon_ids(self): - with self.marionette.using_context('chrome'): - addons = self.marionette.execute_async_script(""" - Components.utils.import("resource://gre/modules/AddonManager.jsm"); - AddonManager.getAllAddons(function(addons){ - let ids = addons.map(function(x) { - return x.id; - }); - marionetteScriptFinished(ids); - }); - """) - - return addons - - def test_install_and_remove_temporary_unsigned_addon(self): - addon_path = os.path.join(here, 'mn-restartless-unsigned.xpi') - - addon_id = self.addons.install(addon_path, temp=True) - self.assertIn(addon_id, self.all_addon_ids) - - self.addons.uninstall(addon_id) - self.assertNotIn(addon_id, self.all_addon_ids) - - def test_install_unsigned_addon(self): - addon_path = os.path.join(here, 'mn-restartless-unsigned.xpi') - - with self.assertRaises(AddonInstallException): - self.addons.install(addon_path) - - @skip("Need to get the test extension signed") - def test_install_and_remove_signed_addon(self): - addon_path = os.path.join(here, 'mn-restartless-signed.xpi') - - addon_id = self.addons.install(addon_path) - self.assertIn(addon_id, self.all_addon_ids) - - self.addons.uninstall(addon_id) - self.assertNotIn(addon_id, self.all_addon_ids) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_anonymous_content.py b/testing/marionette/harness/marionette_harness/tests/unit/test_anonymous_content.py deleted file mode 100644 index 1e7779661..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_anonymous_content.py +++ /dev/null @@ -1,90 +0,0 @@ -# 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.by import By -from marionette_driver.errors import NoSuchElementException -from marionette_driver.marionette import HTMLElement - -from marionette_harness import MarionetteTestCase, WindowManagerMixin - - -class TestAnonymousNodes(WindowManagerMixin, MarionetteTestCase): - - def setUp(self): - super(TestAnonymousNodes, self).setUp() - self.marionette.set_context("chrome") - - def open_window_with_js(): - self.marionette.execute_script(""" - window.open('chrome://marionette/content/test_anonymous_content.xul', - 'foo', 'chrome,centerscreen'); - """) - - new_window = self.open_window(trigger=open_window_with_js) - self.marionette.switch_to_window(new_window) - - def tearDown(self): - self.close_all_windows() - - super(TestAnonymousNodes, self).tearDown() - - def test_switch_to_anonymous_frame(self): - self.marionette.find_element(By.ID, "testAnonymousContentBox") - anon_browser_el = self.marionette.find_element(By.ID, "browser") - self.assertTrue("test_anonymous_content.xul" in self.marionette.get_url()) - self.marionette.switch_to_frame(anon_browser_el) - self.assertTrue("test.xul" in self.marionette.get_url()) - self.marionette.find_element(By.ID, "testXulBox") - self.assertRaises(NoSuchElementException, - self.marionette.find_element, By.ID, "testAnonymousContentBox") - - def test_switch_to_anonymous_iframe(self): - self.marionette.find_element(By.ID, "testAnonymousContentBox") - el = self.marionette.find_element(By.ID, "container2") - anon_iframe_el = el.find_element(By.ANON_ATTRIBUTE, {"anonid": "iframe"}) - self.marionette.switch_to_frame(anon_iframe_el) - self.assertTrue("test.xul" in self.marionette.get_url()) - self.marionette.find_element(By.ID, "testXulBox") - self.assertRaises(NoSuchElementException, self.marionette.find_element, By.ID, - "testAnonymousContentBox") - - def test_find_anonymous_element_by_attribute(self): - accept_button = (By.ANON_ATTRIBUTE, {"dlgtype": "accept"},) - not_existent = (By.ANON_ATTRIBUTE, {"anonid": "notexistent"},) - - # By using the window document element - start_node = self.marionette.find_element(By.CSS_SELECTOR, ":root") - button = start_node.find_element(*accept_button) - self.assertEquals(HTMLElement, type(button)) - with self.assertRaises(NoSuchElementException): - start_node.find_element(*not_existent) - - # By using the default start node - self.assertEquals(button, self.marionette.find_element(*accept_button)) - with self.assertRaises(NoSuchElementException): - self.marionette.find_element(*not_existent) - - def test_find_anonymous_elements_by_attribute(self): - dialog_buttons = (By.ANON_ATTRIBUTE, {"anonid": "buttons"},) - not_existent = (By.ANON_ATTRIBUTE, {"anonid": "notexistent"},) - - # By using the window document element - start_node = self.marionette.find_element(By.CSS_SELECTOR, ":root") - buttons = start_node.find_elements(*dialog_buttons) - self.assertEquals(1, len(buttons)) - self.assertEquals(HTMLElement, type(buttons[0])) - self.assertListEqual([], start_node.find_elements(*not_existent)) - - # By using the default start node - self.assertListEqual(buttons, self.marionette.find_elements(*dialog_buttons)) - self.assertListEqual([], self.marionette.find_elements(*not_existent)) - - def test_find_anonymous_children(self): - self.assertEquals(HTMLElement, type(self.marionette.find_element(By.ANON, None))) - self.assertEquals(2, len(self.marionette.find_elements(By.ANON, None))) - - frame = self.marionette.find_element(By.ID, "framebox") - with self.assertRaises(NoSuchElementException): - frame.find_element(By.ANON, None) - self.assertListEqual([], frame.find_elements(By.ANON, None)) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_browsermobproxy.py b/testing/marionette/harness/marionette_harness/tests/unit/test_browsermobproxy.py deleted file mode 100644 index 64b3d1a77..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_browsermobproxy.py +++ /dev/null @@ -1,34 +0,0 @@ -import datetime - -from marionette_harness.runner import BrowserMobTestCase - - -class TestBrowserMobProxy(BrowserMobTestCase): - """To run this test, you'll need to download the browsermob-proxy from - http://bmp.lightbody.net/, and then pass the path to the startup - script (typically /path/to/browsermob-proxy-2.0.0/bin/browsermob-proxy) - as the --browsermob-script argument when running runtests.py. - - You can additionally pass --browsermob-port to specify the port that - the proxy will run on; it defaults to 8080. - - This test is NOT run in CI, as bmp and dependencies aren't available - there. - """ - - def test_browsermob_proxy_limits(self): - """This illustrates the use of download limits in the proxy, - and verifies that it's slower to load a page @100kbps - than it is to download the same page @1000kbps. - """ - proxy = self.create_browsermob_proxy() - proxy.limits({'downstream_kbps': 1000}) - time1 = datetime.datetime.now() - self.marionette.navigate('http://forecast.weather.gov') - time2 = datetime.datetime.now() - proxy.limits({'downstream_kbps': 100}) - time3 = datetime.datetime.now() - self.marionette.refresh() - time4 = datetime.datetime.now() - self.assertTrue(time4 - time3 > time2 - time1, - "page load @ 100kbps not slower than page load @ 1000kbps") diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_capabilities.py b/testing/marionette/harness/marionette_harness/tests/unit/test_capabilities.py deleted file mode 100644 index d3386316d..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_capabilities.py +++ /dev/null @@ -1,253 +0,0 @@ -# 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.errors import SessionNotCreatedException - -from marionette_harness import MarionetteTestCase - - -class TestCapabilities(MarionetteTestCase): - - def setUp(self): - super(TestCapabilities, self).setUp() - self.caps = self.marionette.session_capabilities - with self.marionette.using_context("chrome"): - self.appinfo = self.marionette.execute_script( - "return Services.appinfo") - self.os_name = self.marionette.execute_script( - "return Services.sysinfo.getProperty('name')").lower() - self.os_version = self.marionette.execute_script( - "return Services.sysinfo.getProperty('version')") - - def test_mandated_capabilities(self): - self.assertIn("browserName", self.caps) - self.assertIn("browserVersion", self.caps) - self.assertIn("platformName", self.caps) - self.assertIn("platformVersion", self.caps) - self.assertIn("acceptInsecureCerts", self.caps) - self.assertIn("timeouts", self.caps) - - self.assertEqual(self.caps["browserName"], self.appinfo["name"].lower()) - self.assertEqual(self.caps["browserVersion"], self.appinfo["version"]) - self.assertEqual(self.caps["platformName"], self.os_name) - self.assertEqual(self.caps["platformVersion"], self.os_version) - self.assertFalse(self.caps["acceptInsecureCerts"]) - self.assertDictEqual(self.caps["timeouts"], - {"implicit": 0, - "page load": 300000, - "script": 30000}) - - def test_supported_features(self): - self.assertIn("rotatable", self.caps) - - def test_additional_capabilities(self): - self.assertIn("moz:processID", self.caps) - self.assertEqual(self.caps["moz:processID"], self.appinfo["processID"]) - self.assertEqual(self.marionette.process_id, self.appinfo["processID"]) - - self.assertIn("moz:profile", self.caps) - if self.marionette.instance is not None: - if self.caps["browserName"] == "fennec": - current_profile = self.marionette.instance.runner.device.app_ctx.remote_profile - else: - current_profile = self.marionette.instance.runner.profile.profile - self.assertEqual(self.caps["moz:profile"], current_profile) - self.assertEqual(self.marionette.profile, current_profile) - - self.assertIn("moz:accessibilityChecks", self.caps) - self.assertFalse(self.caps["moz:accessibilityChecks"]) - self.assertIn("specificationLevel", self.caps) - self.assertEqual(self.caps["specificationLevel"], 0) - - def test_set_specification_level(self): - self.marionette.delete_session() - self.marionette.start_session({"desiredCapabilities": {"specificationLevel": 2}}) - caps = self.marionette.session_capabilities - self.assertEqual(2, caps["specificationLevel"]) - - self.marionette.delete_session() - self.marionette.start_session({"requiredCapabilities": {"specificationLevel": 3}}) - caps = self.marionette.session_capabilities - self.assertEqual(3, caps["specificationLevel"]) - - def test_we_can_pass_in_required_capabilities_on_session_start(self): - self.marionette.delete_session() - capabilities = {"requiredCapabilities": {"browserName": self.appinfo["name"].lower()}} - self.marionette.start_session(capabilities) - caps = self.marionette.session_capabilities - self.assertIn("browserName", caps) - - # Start a new session just to make sure we leave the browser in the - # same state it was before it started the test - self.marionette.start_session() - - def test_capability_types(self): - for value in ["", "invalid", True, 42, []]: - print("testing value {}".format(value)) - with self.assertRaises(SessionNotCreatedException): - print(" with desiredCapabilities") - self.marionette.delete_session() - self.marionette.start_session({"desiredCapabilities": value}) - with self.assertRaises(SessionNotCreatedException): - print(" with requiredCapabilities") - self.marionette.delete_session() - self.marionette.start_session({"requiredCapabilities": value}) - - def test_we_get_valid_uuid4_when_creating_a_session(self): - self.assertNotIn("{", self.marionette.session_id, - "Session ID has {{}} in it: {}".format( - self.marionette.session_id)) - - -class TestCapabilityMatching(MarionetteTestCase): - allowed = [None, "*"] - disallowed = ["", 42, True, {}, []] - - def setUp(self): - MarionetteTestCase.setUp(self) - self.browser_name = self.marionette.session_capabilities["browserName"] - self.platform_name = self.marionette.session_capabilities["platformName"] - self.delete_session() - - def delete_session(self): - if self.marionette.session is not None: - self.marionette.delete_session() - - def test_browser_name_desired(self): - self.marionette.start_session({"desiredCapabilities": {"browserName": self.browser_name}}) - self.assertEqual(self.marionette.session_capabilities["browserName"], self.browser_name) - - def test_browser_name_required(self): - self.marionette.start_session({"requiredCapabilities": {"browserName": self.browser_name}}) - self.assertEqual(self.marionette.session_capabilities["browserName"], self.browser_name) - - def test_browser_name_desired_allowed_types(self): - for typ in self.allowed: - self.delete_session() - self.marionette.start_session({"desiredCapabilities": {"browserName": typ}}) - self.assertEqual(self.marionette.session_capabilities["browserName"], self.browser_name) - - def test_browser_name_desired_disallowed_types(self): - for typ in self.disallowed: - with self.assertRaises(SessionNotCreatedException): - self.marionette.start_session({"desiredCapabilities": {"browserName": typ}}) - - def test_browser_name_required_allowed_types(self): - for typ in self.allowed: - self.delete_session() - self.marionette.start_session({"requiredCapabilities": {"browserName": typ}}) - self.assertEqual(self.marionette.session_capabilities["browserName"], self.browser_name) - - def test_browser_name_requried_disallowed_types(self): - for typ in self.disallowed: - with self.assertRaises(SessionNotCreatedException): - self.marionette.start_session({"requiredCapabilities": {"browserName": typ}}) - - def test_browser_name_prefers_required(self): - caps = {"desiredCapabilities": {"browserName": "invalid"}, - "requiredCapabilities": {"browserName": "*"}} - self.marionette.start_session(caps) - - def test_browser_name_error_on_invalid_required(self): - with self.assertRaises(SessionNotCreatedException): - caps = {"desiredCapabilities": {"browserName": "*"}, - "requiredCapabilities": {"browserName": "invalid"}} - self.marionette.start_session(caps) - - # TODO(ato): browser version comparison not implemented yet - - def test_platform_name_desired(self): - self.marionette.start_session({"desiredCapabilities": {"platformName": self.platform_name}}) - self.assertEqual(self.marionette.session_capabilities["platformName"], self.platform_name) - - def test_platform_name_required(self): - self.marionette.start_session({"requiredCapabilities": {"platformName": self.platform_name}}) - self.assertEqual(self.marionette.session_capabilities["platformName"], self.platform_name) - - def test_platform_name_desired_allowed_types(self): - for typ in self.allowed: - self.delete_session() - self.marionette.start_session({"desiredCapabilities": {"platformName": typ}}) - self.assertEqual(self.marionette.session_capabilities["platformName"], self.platform_name) - - def test_platform_name_desired_disallowed_types(self): - for typ in self.disallowed: - with self.assertRaises(SessionNotCreatedException): - self.marionette.start_session({"desiredCapabilities": {"platformName": typ}}) - - def test_platform_name_required_allowed_types(self): - for typ in self.allowed: - self.delete_session() - self.marionette.start_session({"requiredCapabilities": {"platformName": typ}}) - self.assertEqual(self.marionette.session_capabilities["platformName"], self.platform_name) - - def test_platform_name_requried_disallowed_types(self): - for typ in self.disallowed: - with self.assertRaises(SessionNotCreatedException): - self.marionette.start_session({"requiredCapabilities": {"platformName": typ}}) - - def test_platform_name_prefers_required(self): - caps = {"desiredCapabilities": {"platformName": "invalid"}, - "requiredCapabilities": {"platformName": "*"}} - self.marionette.start_session(caps) - - def test_platform_name_error_on_invalid_required(self): - with self.assertRaises(SessionNotCreatedException): - caps = {"desiredCapabilities": {"platformName": "*"}, - "requiredCapabilities": {"platformName": "invalid"}} - self.marionette.start_session(caps) - - # TODO(ato): platform version comparison not imlpemented yet - - def test_accept_insecure_certs(self): - for capability_type in ["desiredCapabilities", "requiredCapabilities"]: - print("testing {}".format(capability_type)) - for value in ["", 42, {}, []]: - print(" type {}".format(type(value))) - with self.assertRaises(SessionNotCreatedException): - self.marionette.start_session({capability_type: {"acceptInsecureCerts": value}}) - - self.delete_session() - self.marionette.start_session({"desiredCapabilities": {"acceptInsecureCerts": True}}) - self.assertTrue(self.marionette.session_capabilities["acceptInsecureCerts"]) - self.delete_session() - self.marionette.start_session({"requiredCapabilities": {"acceptInsecureCerts": True}}) - - self.assertTrue(self.marionette.session_capabilities["acceptInsecureCerts"]) - - def test_page_load_strategy(self): - for strategy in ["none", "eager", "normal"]: - print("valid strategy {}".format(strategy)) - self.delete_session() - self.marionette.start_session({"desiredCapabilities": {"pageLoadStrategy": strategy}}) - self.assertEqual(self.marionette.session_capabilities["pageLoadStrategy"], strategy) - - for value in ["", "EAGER", True, 42, {}, []]: - print("invalid strategy {}".format(value)) - with self.assertRaises(SessionNotCreatedException): - self.marionette.start_session({"desiredCapabilities": {"pageLoadStrategy": value}}) - - def test_proxy_default(self): - self.marionette.start_session() - self.assertNotIn("proxy", self.marionette.session_capabilities) - - def test_proxy_desired(self): - self.marionette.start_session({"desiredCapabilities": {"proxy": {"proxyType": "manual"}}}) - self.assertIn("proxy", self.marionette.session_capabilities) - self.assertEqual(self.marionette.session_capabilities["proxy"]["proxyType"], "manual") - self.assertEqual(self.marionette.get_pref("network.proxy.type"), 1) - - def test_proxy_required(self): - self.marionette.start_session({"requiredCapabilities": {"proxy": {"proxyType": "manual"}}}) - self.assertIn("proxy", self.marionette.session_capabilities) - self.assertEqual(self.marionette.session_capabilities["proxy"]["proxyType"], "manual") - self.assertEqual(self.marionette.get_pref("network.proxy.type"), 1) - - def test_timeouts(self): - timeouts = {u"implicit": 123, u"page load": 456, u"script": 789} - caps = {"desiredCapabilities": {"timeouts": timeouts}} - self.marionette.start_session(caps) - self.assertIn("timeouts", self.marionette.session_capabilities) - self.assertDictEqual(self.marionette.session_capabilities["timeouts"], timeouts) - self.assertDictEqual(self.marionette._send_message("getTimeouts"), timeouts) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_checkbox.py b/testing/marionette/harness/marionette_harness/tests/unit/test_checkbox.py deleted file mode 100644 index 8709d6e32..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_checkbox.py +++ /dev/null @@ -1,17 +0,0 @@ -# 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.by import By - -from marionette_harness import MarionetteTestCase - - -class TestCheckbox(MarionetteTestCase): - def test_selected(self): - test_html = self.marionette.absolute_url("test.html") - self.marionette.navigate(test_html) - box = self.marionette.find_element(By.NAME, "myCheckBox") - self.assertFalse(box.is_selected()) - box.click() - self.assertTrue(box.is_selected()) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_checkbox_chrome.py b/testing/marionette/harness/marionette_harness/tests/unit/test_checkbox_chrome.py deleted file mode 100644 index 8d800f939..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_checkbox_chrome.py +++ /dev/null @@ -1,36 +0,0 @@ -# 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.by import By - -from marionette_harness import MarionetteTestCase, WindowManagerMixin - - -class TestSelectedChrome(WindowManagerMixin, MarionetteTestCase): - - def setUp(self): - super(TestSelectedChrome, self).setUp() - - self.marionette.set_context("chrome") - - def open_window_with_js(): - self.marionette.execute_script(""" - window.open('chrome://marionette/content/test.xul', - '_blank', 'chrome,centerscreen'); - """) - - new_window = self.open_window(trigger=open_window_with_js) - self.marionette.switch_to_window(new_window) - - def tearDown(self): - try: - self.close_all_windows() - finally: - super(TestSelectedChrome, self).tearDown() - - def test_selected(self): - box = self.marionette.find_element(By.ID, "testBox") - self.assertFalse(box.is_selected()) - self.assertFalse(self.marionette.execute_script("arguments[0].checked = true;", [box])) - self.assertTrue(box.is_selected()) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_chrome.py b/testing/marionette/harness/marionette_harness/tests/unit/test_chrome.py deleted file mode 100644 index 8a9e53bd6..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_chrome.py +++ /dev/null @@ -1,51 +0,0 @@ -#Copyright 2007-2009 WebDriver committers -#Copyright 2007-2009 Google Inc. -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. - -from marionette_driver import By - -from marionette_harness import MarionetteTestCase, WindowManagerMixin - - -class ChromeTests(WindowManagerMixin, MarionetteTestCase): - - def setUp(self): - super(ChromeTests, self).setUp() - - self.marionette.set_context('chrome') - - def tearDown(self): - self.close_all_windows() - super(ChromeTests, self).tearDown() - - def test_hang_until_timeout(self): - def open_with_menu(): - menu = self.marionette.find_element(By.ID, 'aboutName') - menu.click() - - new_window = self.open_window(trigger=open_with_menu) - self.marionette.switch_to_window(new_window) - - try: - try: - # Raise an exception type which should not be thrown by Marionette - # while running this test. Otherwise it would mask eg. IOError as - # thrown for a socket timeout. - raise NotImplementedError('Exception should not cause a hang when ' - 'closing the chrome window') - finally: - self.marionette.close_chrome_window() - self.marionette.switch_to_window(self.start_window) - except NotImplementedError: - pass diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_chrome_async_finish.js b/testing/marionette/harness/marionette_harness/tests/unit/test_chrome_async_finish.js deleted file mode 100644 index 8d2df3ac2..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_chrome_async_finish.js +++ /dev/null @@ -1,6 +0,0 @@ -MARIONETTE_TIMEOUT = 60000; -MARIONETTE_CONTEXT = "chrome"; -ok(true); -(function () { - finish(); -})(); diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_chrome_element_css.py b/testing/marionette/harness/marionette_harness/tests/unit/test_chrome_element_css.py deleted file mode 100644 index fde7e8373..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_chrome_element_css.py +++ /dev/null @@ -1,23 +0,0 @@ -# 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.by import By - -from marionette_harness import MarionetteTestCase - - -class TestChromeElementCSS(MarionetteTestCase): - - def test_we_can_get_css_value_on_chrome_element(self): - self.marionette.navigate("about:blank") - with self.marionette.using_context("chrome"): - element = self.marionette.find_element(By.ID, "identity-icon") - favicon_image = element.value_of_css_property("list-style-image") - - self.assertIn("identity-icon.svg", favicon_image) - - element = self.marionette.find_element(By.ID, "identity-box") - background_colour = element.value_of_css_property("background-color") - - self.assertEqual("transparent", background_colour) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_clearing.py b/testing/marionette/harness/marionette_harness/tests/unit/test_clearing.py deleted file mode 100644 index 3fcad45fc..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_clearing.py +++ /dev/null @@ -1,72 +0,0 @@ -# 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.by import By -from marionette_driver.errors import InvalidElementStateException - -from marionette_harness import MarionetteTestCase - - -class TestClear(MarionetteTestCase): - def testWriteableTextInputShouldClear(self): - test_html = self.marionette.absolute_url("test_clearing.html") - self.marionette.navigate(test_html) - element = self.marionette.find_element(By.ID, "writableTextInput") - element.clear() - self.assertEqual("", element.get_property("value")) - - def testTextInputShouldNotClearWhenReadOnly(self): - test_html = self.marionette.absolute_url("test_clearing.html") - self.marionette.navigate(test_html) - element = self.marionette.find_element(By.ID,"readOnlyTextInput") - try: - element.clear() - self.fail("Should not have been able to clear") - except InvalidElementStateException: - pass - - def testWritableTextAreaShouldClear(self): - test_html = self.marionette.absolute_url("test_clearing.html") - self.marionette.navigate(test_html) - element = self.marionette.find_element(By.ID,"writableTextArea") - element.clear() - self.assertEqual("", element.get_property("value")) - - def testTextAreaShouldNotClearWhenDisabled(self): - test_html = self.marionette.absolute_url("test_clearing.html") - self.marionette.navigate(test_html) - element = self.marionette.find_element(By.ID,"textAreaNotenabled") - try: - element.clear() - self.fail("Should not have been able to clear") - except InvalidElementStateException: - pass - - def testTextAreaShouldNotClearWhenReadOnly(self): - test_html = self.marionette.absolute_url("test_clearing.html") - self.marionette.navigate(test_html) - element = self.marionette.find_element(By.ID,"textAreaReadOnly") - try: - element.clear() - self.fail("Should not have been able to clear") - except InvalidElementStateException: - pass - - def testContentEditableAreaShouldClear(self): - test_html = self.marionette.absolute_url("test_clearing.html") - self.marionette.navigate(test_html) - element = self.marionette.find_element(By.ID,"content-editable") - element.clear() - self.assertEqual("", element.text) - - def testTextInputShouldNotClearWhenDisabled(self): - test_html = self.marionette.absolute_url("test_clearing.html") - self.marionette.navigate(test_html) - try: - element = self.marionette.find_element(By.ID,"textInputnotenabled") - self.assertFalse(element.is_enabled()) - element.clear() - self.fail("Should not have been able to clear") - except InvalidElementStateException: - pass diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_click.py b/testing/marionette/harness/marionette_harness/tests/unit/test_click.py deleted file mode 100644 index 06019834a..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_click.py +++ /dev/null @@ -1,271 +0,0 @@ -# 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/. - -import urllib - -from marionette_driver.by import By -from marionette_driver import errors -from marionette_driver.wait import Wait - -from marionette_harness import MarionetteTestCase - - -def inline(doc): - return "data:text/html;charset=utf-8,{}".format(urllib.quote(doc)) - - -# The <a> element in the following HTML is not interactable because it -# is hidden by an overlay when scrolled into the top of the viewport. -# It should be interactable when scrolled in at the bottom of the -# viewport. -fixed_overlay = inline(""" -<style> -* { margin: 0; padding: 0; } -body { height: 300vh } -div, a { display: block } -div { - background-color: pink; - position: fixed; - width: 100%; - height: 40px; - top: 0; -} -a { - margin-top: 1000px; -} -</style> - -<div>overlay</div> -<a href=#>link</a> - -<script> -window.clicked = false; - -let link = document.querySelector("a"); -link.addEventListener("click", () => window.clicked = true); -</script> -""") - - -obscured_overlay = inline(""" -<style> -* { margin: 0; padding: 0; } -body { height: 100vh } -#overlay { - background-color: pink; - position: absolute; - width: 100%; - height: 100%; -} -</style> - -<div id=overlay></div> -<a id=obscured href=#>link</a> - -<script> -window.clicked = false; - -let link = document.querySelector("#obscured"); -link.addEventListener("click", () => window.clicked = true); -</script> -""") - - -class TestLegacyClick(MarionetteTestCase): - """Uses legacy Selenium element displayedness checks.""" - - def setUp(self): - MarionetteTestCase.setUp(self) - self.marionette.delete_session() - self.marionette.start_session() - - def test_click(self): - self.marionette.navigate(inline(""" - <button>click me</button> - <script> - window.clicks = 0; - let button = document.querySelector("button"); - button.addEventListener("click", () => window.clicks++); - </script> - """)) - button = self.marionette.find_element(By.TAG_NAME, "button") - button.click() - self.assertEqual(1, self.marionette.execute_script("return window.clicks", sandbox=None)) - - def test_click_number_link(self): - test_html = self.marionette.absolute_url("clicks.html") - self.marionette.navigate(test_html) - self.marionette.find_element(By.LINK_TEXT, "333333").click() - Wait(self.marionette, timeout=30, ignored_exceptions=errors.NoSuchElementException).until( - lambda m: m.find_element(By.ID, "username")) - self.assertEqual(self.marionette.title, "XHTML Test Page") - - def test_clicking_an_element_that_is_not_displayed_raises(self): - test_html = self.marionette.absolute_url("hidden.html") - self.marionette.navigate(test_html) - - with self.assertRaises(errors.ElementNotInteractableException): - self.marionette.find_element(By.ID, "child").click() - - def test_clicking_on_a_multiline_link(self): - test_html = self.marionette.absolute_url("clicks.html") - self.marionette.navigate(test_html) - self.marionette.find_element(By.ID, "overflowLink").click() - self.wait_for_condition(lambda mn: self.marionette.title == "XHTML Test Page") - - def test_scroll_into_view_near_end(self): - self.marionette.navigate(fixed_overlay) - link = self.marionette.find_element(By.TAG_NAME, "a") - link.click() - self.assertTrue(self.marionette.execute_script("return window.clicked", sandbox=None)) - - -class TestClick(TestLegacyClick): - """Uses WebDriver specification compatible element interactability - checks. - """ - - def setUp(self): - TestLegacyClick.setUp(self) - self.marionette.delete_session() - self.marionette.start_session( - {"requiredCapabilities": {"specificationLevel": 1}}) - - def test_click_element_obscured_by_absolute_positioned_element(self): - self.marionette.navigate(obscured_overlay) - overlay = self.marionette.find_element(By.ID, "overlay") - obscured = self.marionette.find_element(By.ID, "obscured") - - overlay.click() - with self.assertRaises(errors.ElementClickInterceptedException): - obscured.click() - - def test_centre_outside_viewport_vertically(self): - self.marionette.navigate(inline(""" - <style> - * { margin: 0; padding: 0; } - div { - display: block; - position: absolute; - background-color: blue; - width: 200px; - height: 200px; - - /* move centre point off viewport vertically */ - top: -105px; - } - </style> - - <div></div>""")) - - self.marionette.find_element(By.TAG_NAME, "div").click() - - def test_centre_outside_viewport_horizontally(self): - self.marionette.navigate(inline(""" - <style> - * { margin: 0; padding: 0; } - div { - display: block; - position: absolute; - background-color: blue; - width: 200px; - height: 200px; - - /* move centre point off viewport horizontally */ - left: -105px; - } - </style> - - <div></div>""")) - - self.marionette.find_element(By.TAG_NAME, "div").click() - - def test_centre_outside_viewport(self): - self.marionette.navigate(inline(""" - <style> - * { margin: 0; padding: 0; } - div { - display: block; - position: absolute; - background-color: blue; - width: 200px; - height: 200px; - - /* move centre point off viewport */ - left: -105px; - top: -105px; - } - </style> - - <div></div>""")) - - self.marionette.find_element(By.TAG_NAME, "div").click() - - def test_css_transforms(self): - self.marionette.navigate(inline(""" - <style> - * { margin: 0; padding: 0; } - div { - display: block; - background-color: blue; - width: 200px; - height: 200px; - - transform: translateX(-105px); - } - </style> - - <div></div>""")) - - self.marionette.find_element(By.TAG_NAME, "div").click() - - def test_input_file(self): - self.marionette.navigate(inline("<input type=file>")) - with self.assertRaises(errors.InvalidArgumentException): - self.marionette.find_element(By.TAG_NAME, "input").click() - - def test_container_element(self): - self.marionette.navigate(inline(""" - <select> - <option>foo</option> - </select>""")) - option = self.marionette.find_element(By.TAG_NAME, "option") - option.click() - self.assertTrue(option.get_property("selected")) - - def test_container_element_outside_view(self): - self.marionette.navigate(inline(""" - <select style="margin-top: 100vh"> - <option>foo</option> - </select>""")) - option = self.marionette.find_element(By.TAG_NAME, "option") - option.click() - self.assertTrue(option.get_property("selected")) - - def test_obscured_element(self): - self.marionette.navigate(obscured_overlay) - overlay = self.marionette.find_element(By.ID, "overlay") - obscured = self.marionette.find_element(By.ID, "obscured") - - overlay.click() - with self.assertRaises(errors.ElementClickInterceptedException): - obscured.click() - self.assertFalse(self.marionette.execute_script("return window.clicked", sandbox=None)) - - def test_pointer_events_none(self): - self.marionette.navigate(inline(""" - <button style="pointer-events: none">click me</button> - <script> - window.clicked = false; - let button = document.querySelector("button"); - button.addEventListener("click", () => window.clicked = true); - </script> - """)) - button = self.marionette.find_element(By.TAG_NAME, "button") - self.assertEqual("none", button.value_of_css_property("pointer-events")) - - with self.assertRaisesRegexp(errors.ElementClickInterceptedException, - "does not have pointer events enabled"): - button.click() - self.assertFalse(self.marionette.execute_script("return window.clicked", sandbox=None)) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_click_chrome.py b/testing/marionette/harness/marionette_harness/tests/unit/test_click_chrome.py deleted file mode 100644 index d16b4f105..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_click_chrome.py +++ /dev/null @@ -1,35 +0,0 @@ -# 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.by import By - -from marionette_harness import MarionetteTestCase - - -class TestClickChrome(MarionetteTestCase): - def setUp(self): - MarionetteTestCase.setUp(self) - self.root_window = self.marionette.current_window_handle - self.marionette.set_context("chrome") - self.marionette.execute_script( - "window.open('chrome://marionette/content/test.xul', 'foo', 'chrome,centerscreen')") - self.marionette.switch_to_window("foo") - self.assertNotEqual(self.root_window, self.marionette.current_window_handle) - - def tearDown(self): - self.assertNotEqual(self.root_window, self.marionette.current_window_handle) - self.marionette.execute_script("window.close()") - self.marionette.switch_to_window(self.root_window) - MarionetteTestCase.tearDown(self) - - def test_click(self): - def checked(): - return self.marionette.execute_script( - "return arguments[0].checked", - script_args=[box]) - - box = self.marionette.find_element(By.ID, "testBox") - self.assertFalse(checked()) - box.click() - self.assertTrue(checked()) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_click_scrolling.py b/testing/marionette/harness/marionette_harness/tests/unit/test_click_scrolling.py deleted file mode 100644 index 437c15e70..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_click_scrolling.py +++ /dev/null @@ -1,117 +0,0 @@ -# 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.by import By -from marionette_driver.errors import MoveTargetOutOfBoundsException - -from marionette_harness import MarionetteTestCase, skip, skip_if_mobile - - -class TestClickScrolling(MarionetteTestCase): - - - def test_clicking_on_anchor_scrolls_page(self): - scrollScript = """ - var pageY; - if (typeof(window.pageYOffset) == 'number') { - pageY = window.pageYOffset; - } else { - pageY = document.documentElement.scrollTop; - } - return pageY;""" - - test_html = self.marionette.absolute_url("macbeth.html") - self.marionette.navigate(test_html) - - self.marionette.find_element(By.PARTIAL_LINK_TEXT, "last speech").click() - y_offset = self.marionette.execute_script(scrollScript) - - # Focusing on to click, but not actually following, - # the link will scroll it in to view, which is a few - # pixels further than 0 - - self.assertTrue(y_offset > 300) - - def test_should_scroll_to_click_on_an_element_hidden_by_overflow(self): - test_html = self.marionette.absolute_url("click_out_of_bounds_overflow.html") - self.marionette.navigate(test_html) - - link = self.marionette.find_element(By.ID, "link") - try: - link.click() - except MoveTargetOutOfBoundsException: - self.fail("Should not be out of bounds") - - @skip("Bug 1200197 - Cannot interact with elements hidden inside overflow:scroll") - def test_should_be_able_to_click_on_an_element_hidden_by_overflow(self): - test_html = self.marionette.absolute_url("scroll.html") - self.marionette.navigate(test_html) - - link = self.marionette.find_element(By.ID, "line8") - link.click() - self.assertEqual("line8", self.marionette.find_element(By.ID, "clicked").text) - - def test_should_not_scroll_overflow_elements_which_are_visible(self): - test_html = self.marionette.absolute_url("scroll2.html") - self.marionette.navigate(test_html) - - list_el = self.marionette.find_element(By.TAG_NAME, "ul") - item = list_el.find_element(By.ID, "desired") - item.click() - y_offset = self.marionette.execute_script("return arguments[0].scrollTop;", script_args=[list_el]) - self.assertEqual(0, y_offset) - - def test_should_not_scroll_if_already_scrolled_and_element_is_in_view(self): - test_html = self.marionette.absolute_url("scroll3.html") - self.marionette.navigate(test_html) - - button1 = self.marionette.find_element(By.ID, "button1") - button2 = self.marionette.find_element(By.ID, "button2") - - button2.click() - scroll_top = self.marionette.execute_script("return document.body.scrollTop;") - button1.click() - - self.assertEqual(scroll_top, self.marionette.execute_script("return document.body.scrollTop;")) - - def test_should_be_able_to_click_radio_button_scrolled_into_view(self): - test_html = self.marionette.absolute_url("scroll4.html") - self.marionette.navigate(test_html) - - # If we dont throw we are good - self.marionette.find_element(By.ID, "radio").click() - - def test_should_scroll_elements_if_click_point_is_out_of_view_but_element_is_in_view(self): - test_html = self.marionette.absolute_url("element_outside_viewport.html") - - for s in ["top", "bottom"]: - self.marionette.navigate(test_html) - scroll_y = self.marionette.execute_script("return window.scrollY;") - self.marionette.find_element(By.ID, "{}-70".format(s)).click() - self.assertNotEqual(scroll_y, self.marionette.execute_script("return window.scrollY;")) - - for s in ["left", "right"]: - self.marionette.navigate(test_html) - scroll_x = self.marionette.execute_script("return window.scrollX;") - self.marionette.find_element(By.ID, "{}-70".format(s)).click() - self.assertNotEqual(scroll_x, self.marionette.execute_script("return window.scrollX;")) - - @skip_if_mobile("Bug 1293855 - Lists differ: [70, 70] != [70, 120]") - def test_should_not_scroll_elements_if_click_point_is_in_view(self): - test_html = self.marionette.absolute_url("element_outside_viewport.html") - - for s in ["top", "right", "bottom", "left"]: - for p in ["50", "30"]: - self.marionette.navigate(test_html) - scroll = self.marionette.execute_script("return [window.scrollX, window.scrollY];") - self.marionette.find_element(By.ID, "{0}-{1}".format(s, p)).click() - self.assertEqual(scroll, self.marionette.execute_script("return [window.scrollX, window.scrollY];")) - - @skip("Bug 1003687") - def test_should_scroll_overflow_elements_if_click_point_is_out_of_view_but_element_is_in_view(self): - test_html = self.marionette.absolute_url("scroll5.html") - self.marionette.navigate(test_html) - - self.marionette.find_element(By.ID, "inner").click() - self.assertEqual("clicked", self.marionette.find_element(By.ID, "clicked").text) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_cookies.py b/testing/marionette/harness/marionette_harness/tests/unit/test_cookies.py deleted file mode 100644 index f7841c73e..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_cookies.py +++ /dev/null @@ -1,115 +0,0 @@ -# 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/. - -import calendar -import random -import time - -from marionette_driver.errors import UnsupportedOperationException -from marionette_harness import MarionetteTestCase - - -class CookieTest(MarionetteTestCase): - - def setUp(self): - MarionetteTestCase.setUp(self) - test_url = self.marionette.absolute_url('test.html') - self.marionette.navigate(test_url) - self.COOKIE_A = {"name": "foo", - "value": "bar", - "path": "/", - "secure": False} - - def tearDown(self): - self.marionette.delete_all_cookies() - MarionetteTestCase.tearDown(self) - - def test_add_cookie(self): - self.marionette.add_cookie(self.COOKIE_A) - cookie_returned = str(self.marionette.execute_script("return document.cookie")) - self.assertTrue(self.COOKIE_A["name"] in cookie_returned) - - def test_adding_a_cookie_that_expired_in_the_past(self): - cookie = self.COOKIE_A.copy() - cookie["expiry"] = calendar.timegm(time.gmtime()) - 1 - self.marionette.add_cookie(cookie) - cookies = self.marionette.get_cookies() - self.assertEquals(0, len(cookies)) - - def test_chrome_error(self): - with self.marionette.using_context("chrome"): - self.assertRaises(UnsupportedOperationException, - self.marionette.add_cookie, self.COOKIE_A) - self.assertRaises(UnsupportedOperationException, - self.marionette.delete_cookie, self.COOKIE_A) - self.assertRaises(UnsupportedOperationException, - self.marionette.delete_all_cookies) - self.assertRaises(UnsupportedOperationException, - self.marionette.get_cookies) - - def test_delete_all_cookie(self): - self.marionette.add_cookie(self.COOKIE_A) - cookie_returned = str(self.marionette.execute_script("return document.cookie")) - print cookie_returned - self.assertTrue(self.COOKIE_A["name"] in cookie_returned) - self.marionette.delete_all_cookies() - self.assertFalse(self.marionette.get_cookies()) - - def test_delete_cookie(self): - self.marionette.add_cookie(self.COOKIE_A) - cookie_returned = str(self.marionette.execute_script("return document.cookie")) - self.assertTrue(self.COOKIE_A["name"] in cookie_returned) - self.marionette.delete_cookie("foo") - cookie_returned = str(self.marionette.execute_script("return document.cookie")) - self.assertFalse(self.COOKIE_A["name"] in cookie_returned) - - def test_should_get_cookie_by_name(self): - key = "key_{}".format(int(random.random()*10000000)) - self.marionette.execute_script("document.cookie = arguments[0] + '=set';", [key]) - - cookie = self.marionette.get_cookie(key) - self.assertEquals("set", cookie["value"]) - - def test_get_all_cookies(self): - key1 = "key_{}".format(int(random.random()*10000000)) - key2 = "key_{}".format(int(random.random()*10000000)) - - cookies = self.marionette.get_cookies() - count = len(cookies) - - one = {"name" :key1, - "value": "value"} - two = {"name":key2, - "value": "value"} - - self.marionette.add_cookie(one) - self.marionette.add_cookie(two) - - test_url = self.marionette.absolute_url('test.html') - self.marionette.navigate(test_url) - cookies = self.marionette.get_cookies() - self.assertEquals(count + 2, len(cookies)) - - def test_should_not_delete_cookies_with_a_similar_name(self): - cookieOneName = "fish" - cookie1 = {"name" :cookieOneName, - "value":"cod"} - cookie2 = {"name" :cookieOneName + "x", - "value": "earth"} - self.marionette.add_cookie(cookie1) - self.marionette.add_cookie(cookie2) - - self.marionette.delete_cookie(cookieOneName) - cookies = self.marionette.get_cookies() - - self.assertFalse(cookie1["name"] == cookies[0]["name"], msg=str(cookies)) - self.assertEquals(cookie2["name"] , cookies[0]["name"], msg=str(cookies)) - - def test_we_get_required_elements_when_available(self): - self.marionette.add_cookie(self.COOKIE_A) - cookies = self.marionette.get_cookies() - - self.assertIn("name", cookies[0], 'name not available') - self.assertIn("value", cookies[0], 'value not available') - self.assertIn("httpOnly", cookies[0], 'httpOnly not available') diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_crash.py b/testing/marionette/harness/marionette_harness/tests/unit/test_crash.py deleted file mode 100644 index 7e74f0857..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_crash.py +++ /dev/null @@ -1,155 +0,0 @@ -# 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/. - -import glob -import shutil - -from marionette_driver.errors import MarionetteException -# Import runner module to monkey patch mozcrash module -from mozrunner.base import runner - -from marionette_harness import MarionetteTestCase, expectedFailure, run_if_e10s - - -class MockMozCrash(object): - """Mock object to replace original mozcrash methods.""" - - def __init__(self, marionette): - self.marionette = marionette - - with self.marionette.using_context('chrome'): - self.crash_reporter_enabled = self.marionette.execute_script(""" - try { - Components.classes["@mozilla.org/toolkit/crash-reporter;1"]. - getService(Components.interfaces.nsICrashReporter); - return true; - } catch (exc) { - return false; - } - """) - - def check_for_crashes(self, dump_directory, *args, **kwargs): - minidump_files = glob.glob('{}/*.dmp'.format(dump_directory)) - shutil.rmtree(dump_directory, ignore_errors=True) - - if self.crash_reporter_enabled: - return len(minidump_files) - else: - return len(minidump_files) == 0 - - def log_crashes(self, logger, dump_directory, *args, **kwargs): - return self.check_for_crashes(dump_directory, *args, **kwargs) - - -class BaseCrashTestCase(MarionetteTestCase): - - # Reduce the timeout for faster processing of the tests - socket_timeout = 10 - - def setUp(self): - super(BaseCrashTestCase, self).setUp() - - self.mozcrash_mock = MockMozCrash(self.marionette) - self.crash_count = self.marionette.crashed - self.pid = self.marionette.process_id - self.remote_uri = self.marionette.absolute_url("javascriptPage.html") - - def tearDown(self): - self.marionette.crashed = self.crash_count - - super(BaseCrashTestCase, self).tearDown() - - def crash(self, chrome=True): - context = 'chrome' if chrome else 'content' - sandbox = None if chrome else 'system' - - # Monkey patch mozcrash to avoid crash info output only for our triggered crashes. - mozcrash = runner.mozcrash - runner.mozcrash = self.mozcrash_mock - - socket_timeout = self.marionette.client.socket_timeout - - self.marionette.set_context(context) - try: - self.marionette.client.socket_timeout = self.socket_timeout - self.marionette.execute_script(""" - // Copied from crash me simple - Components.utils.import("resource://gre/modules/ctypes.jsm"); - - // ctypes checks for NULL pointer derefs, so just go near-NULL. - var zero = new ctypes.intptr_t(8); - var badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t)); - var crash = badptr.contents; - """, sandbox=sandbox) - finally: - runner.mozcrash = mozcrash - self.marionette.client.socket_timeout = socket_timeout - - -class TestCrash(BaseCrashTestCase): - - def test_crash_chrome_process(self): - self.assertRaisesRegexp(IOError, 'Process crashed', - self.crash, chrome=True) - self.assertEqual(self.marionette.crashed, 1) - self.assertIsNone(self.marionette.session) - self.assertRaisesRegexp(MarionetteException, 'Please start a session', - self.marionette.get_url) - - self.marionette.start_session() - self.assertNotEqual(self.marionette.process_id, self.pid) - - # TODO: Bug 1314594 - Causes a hang for the communication between the - # chrome and frame script. - # self.marionette.get_url() - - @run_if_e10s("Content crashes only exist in e10s mode") - def test_crash_content_process(self): - # If e10s is disabled the chrome process crashes - self.marionette.navigate(self.remote_uri) - - self.assertRaisesRegexp(IOError, 'Content process crashed', - self.crash, chrome=False) - self.assertEqual(self.marionette.crashed, 1) - self.assertIsNone(self.marionette.session) - self.assertRaisesRegexp(MarionetteException, 'Please start a session', - self.marionette.get_url) - - self.marionette.start_session() - self.assertNotEqual(self.marionette.process_id, self.pid) - self.marionette.get_url() - - @expectedFailure - def test_unexpected_crash(self): - self.crash(chrome=True) - - -class TestCrashInSetUp(BaseCrashTestCase): - - def setUp(self): - super(TestCrashInSetUp, self).setUp() - - self.assertRaisesRegexp(IOError, 'Process crashed', - self.crash, chrome=True) - self.assertEqual(self.marionette.crashed, 1) - self.assertIsNone(self.marionette.session) - - def test_crash_in_setup(self): - self.marionette.start_session() - self.assertNotEqual(self.marionette.process_id, self.pid) - - -class TestCrashInTearDown(BaseCrashTestCase): - - def tearDown(self): - try: - self.assertRaisesRegexp(IOError, 'Process crashed', - self.crash, chrome=True) - finally: - self.assertEqual(self.marionette.crashed, 1) - self.assertIsNone(self.marionette.session) - super(TestCrashInTearDown, self).tearDown() - - def test_crash_in_teardown(self): - pass diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_data_driven.py b/testing/marionette/harness/marionette_harness/tests/unit/test_data_driven.py deleted file mode 100644 index 8e4ae0d32..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_data_driven.py +++ /dev/null @@ -1,67 +0,0 @@ -# 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_harness.marionette_test import ( - parameterized, - with_parameters, - MetaParameterized, - MarionetteTestCase -) - -class Parameterizable(object): - __metaclass__ = MetaParameterized - -class TestDataDriven(MarionetteTestCase): - def test_parameterized(self): - class Test(Parameterizable): - def __init__(self): - self.parameters = [] - - @parameterized('1', 'thing', named=43) - @parameterized('2', 'thing2') - def test(self, thing, named=None): - self.parameters.append((thing, named)) - - self.assertFalse(hasattr(Test, 'test')) - self.assertTrue(hasattr(Test, 'test_1')) - self.assertTrue(hasattr(Test, 'test_2')) - - test = Test() - test.test_1() - test.test_2() - - self.assertEquals(test.parameters, [('thing', 43), ('thing2', None)]) - - def test_with_parameters(self): - DATA = [('1', ('thing',), {'named': 43}), - ('2', ('thing2',), {'named': None})] - - class Test(Parameterizable): - def __init__(self): - self.parameters = [] - - @with_parameters(DATA) - def test(self, thing, named=None): - self.parameters.append((thing, named)) - - self.assertFalse(hasattr(Test, 'test')) - self.assertTrue(hasattr(Test, 'test_1')) - self.assertTrue(hasattr(Test, 'test_2')) - - test = Test() - test.test_1() - test.test_2() - - self.assertEquals(test.parameters, [('thing', 43), ('thing2', None)]) - - def test_parameterized_same_name_raises_error(self): - with self.assertRaises(KeyError): - class Test(Parameterizable): - @parameterized('1', 'thing', named=43) - @parameterized('1', 'thing2') - def test(self, thing, named=None): - pass - - def test_marionette_test_case_is_parameterizable(self): - self.assertTrue(issubclass(MarionetteTestCase.__metaclass__, MetaParameterized)) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_date_time_value.py b/testing/marionette/harness/marionette_harness/tests/unit/test_date_time_value.py deleted file mode 100644 index 2d224fff2..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_date_time_value.py +++ /dev/null @@ -1,29 +0,0 @@ -# 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 datetime import datetime - -from marionette_driver.by import By -from marionette_driver.date_time_value import DateTimeValue -from marionette_harness import MarionetteTestCase - - -class TestDateTime(MarionetteTestCase): - def test_set_date(self): - test_html = self.marionette.absolute_url("datetimePage.html") - self.marionette.navigate(test_html) - - element = self.marionette.find_element(By.ID, "date-test") - dt_value = DateTimeValue(element) - dt_value.date = datetime(1998, 6, 2) - self.assertEqual("1998-06-02", element.get_property("value")) - - def test_set_time(self): - test_html = self.marionette.absolute_url("datetimePage.html") - self.marionette.navigate(test_html) - - element = self.marionette.find_element(By.ID, "time-test") - dt_value = DateTimeValue(element) - dt_value.time = datetime(1998, 11, 19, 9, 8, 7) - self.assertEqual("09:08:07", element.get_property("value")) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_element_retrieval.py b/testing/marionette/harness/marionette_harness/tests/unit/test_element_retrieval.py deleted file mode 100644 index 9023a84ab..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_element_retrieval.py +++ /dev/null @@ -1,483 +0,0 @@ -# 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/. - -import re -import urllib - -from marionette_driver.by import By -from marionette_driver.errors import NoSuchElementException, InvalidSelectorException -from marionette_driver.marionette import HTMLElement - -from marionette_harness import MarionetteTestCase, skip - - -def inline(doc, doctype="html"): - if doctype == "html": - return "data:text/html;charset=utf-8,{}".format(urllib.quote(doc)) - elif doctype == "xhtml": - return "data:application/xhtml+xml,{}".format(urllib.quote( -r"""<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" - "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> - <head> - <title>XHTML might be the future</title> - </head> - - <body> - {} - </body> -</html>""".format(doc))) - - -id_html = inline("<p id=foo></p>", doctype="html") -id_xhtml = inline('<p id="foo"></p>', doctype="xhtml") -parent_child_html = inline("<div id=parent><p id=child></p></div>", doctype="html") -parent_child_xhtml = inline('<div id="parent"><p id="child"></p></div>', doctype="xhtml") -children_html = inline("<div><p>foo <p>bar</div>", doctype="html") -children_xhtml = inline("<div><p>foo</p> <p>bar</p></div>", doctype="xhtml") -class_html = inline("<p class='foo bar'>", doctype="html") -class_xhtml = inline('<p class="foo bar"></p>', doctype="xhtml") -name_html = inline("<p name=foo>", doctype="html") -name_xhtml = inline('<p name="foo"></p>', doctype="xhtml") -link_html = inline("<p><a href=#>foo bar</a>", doctype="html") -link_html_with_trailing_space = inline("<p><a href=#>a link with a trailing space </a>") -link_xhtml = inline('<p><a href="#">foo bar</a></p>', doctype="xhtml") - - -class TestFindElementHTML(MarionetteTestCase): - def setUp(self): - MarionetteTestCase.setUp(self) - self.marionette.timeout.implicit = 0 - - def test_id(self): - self.marionette.navigate(id_html) - expected = self.marionette.execute_script("return document.querySelector('p')") - found = self.marionette.find_element(By.ID, "foo") - self.assertIsInstance(found, HTMLElement) - self.assertEqual(expected, found) - - def test_child_element(self): - self.marionette.navigate(parent_child_html) - parent = self.marionette.find_element(By.ID, "parent") - child = self.marionette.find_element(By.ID, "child") - found = parent.find_element(By.TAG_NAME, "p") - self.assertEqual(found.tag_name, "p") - self.assertIsInstance(found, HTMLElement) - self.assertEqual(child, found) - - def test_tag_name(self): - self.marionette.navigate(children_html) - el = self.marionette.execute_script("return document.querySelector('p')") - found = self.marionette.find_element(By.TAG_NAME, "p") - self.assertIsInstance(found, HTMLElement) - self.assertEqual(el, found) - - def test_class_name(self): - self.marionette.navigate(class_html) - el = self.marionette.execute_script("return document.querySelector('.foo')") - found = self.marionette.find_element(By.CLASS_NAME, "foo") - self.assertIsInstance(found, HTMLElement) - self.assertEqual(el, found) - - def test_by_name(self): - self.marionette.navigate(name_html) - el = self.marionette.execute_script("return document.querySelector('[name=foo]')") - found = self.marionette.find_element(By.NAME, "foo") - self.assertIsInstance(found, HTMLElement) - self.assertEqual(el, found) - - def test_css_selector(self): - self.marionette.navigate(children_html) - el = self.marionette.execute_script("return document.querySelector('p')") - found = self.marionette.find_element(By.CSS_SELECTOR, "p") - self.assertIsInstance(found, HTMLElement) - self.assertEqual(el, found) - - def test_invalid_css_selector_should_throw(self): - with self.assertRaises(InvalidSelectorException): - self.marionette.find_element(By.CSS_SELECTOR, "#") - - def test_link_text(self): - self.marionette.navigate(link_html) - el = self.marionette.execute_script("return document.querySelector('a')") - found = self.marionette.find_element(By.LINK_TEXT, "foo bar") - self.assertIsInstance(found, HTMLElement) - self.assertEqual(el, found) - - def test_link_text_with_trailing_space(self): - self.marionette.navigate(link_html_with_trailing_space) - el = self.marionette.execute_script("return document.querySelector('a')") - found = self.marionette.find_element(By.LINK_TEXT, "a link with a trailing space") - self.assertIsInstance(found, HTMLElement) - self.assertEqual(el, found) - - def test_partial_link_text(self): - self.marionette.navigate(link_html) - el = self.marionette.execute_script("return document.querySelector('a')") - found = self.marionette.find_element(By.PARTIAL_LINK_TEXT, "foo") - self.assertIsInstance(found, HTMLElement) - self.assertEqual(el, found) - - def test_xpath(self): - self.marionette.navigate(id_html) - el = self.marionette.execute_script("return document.querySelector('#foo')") - found = self.marionette.find_element(By.XPATH, "id('foo')") - self.assertIsInstance(found, HTMLElement) - self.assertEqual(el, found) - - def test_not_found(self): - self.marionette.timeout.implicit = 0 - self.assertRaises(NoSuchElementException, self.marionette.find_element, By.CLASS_NAME, "cheese") - self.assertRaises(NoSuchElementException, self.marionette.find_element, By.CSS_SELECTOR, "cheese") - self.assertRaises(NoSuchElementException, self.marionette.find_element, By.ID, "cheese") - self.assertRaises(NoSuchElementException, self.marionette.find_element, By.LINK_TEXT, "cheese") - self.assertRaises(NoSuchElementException, self.marionette.find_element, By.NAME, "cheese") - self.assertRaises(NoSuchElementException, self.marionette.find_element, By.PARTIAL_LINK_TEXT, "cheese") - self.assertRaises(NoSuchElementException, self.marionette.find_element, By.TAG_NAME, "cheese") - self.assertRaises(NoSuchElementException, self.marionette.find_element, By.XPATH, "cheese") - - def test_not_found_implicit_wait(self): - self.marionette.timeout.implicit = 0.5 - self.assertRaises(NoSuchElementException, self.marionette.find_element, By.CLASS_NAME, "cheese") - self.assertRaises(NoSuchElementException, self.marionette.find_element, By.CSS_SELECTOR, "cheese") - self.assertRaises(NoSuchElementException, self.marionette.find_element, By.ID, "cheese") - self.assertRaises(NoSuchElementException, self.marionette.find_element, By.LINK_TEXT, "cheese") - self.assertRaises(NoSuchElementException, self.marionette.find_element, By.NAME, "cheese") - self.assertRaises(NoSuchElementException, self.marionette.find_element, By.PARTIAL_LINK_TEXT, "cheese") - self.assertRaises(NoSuchElementException, self.marionette.find_element, By.TAG_NAME, "cheese") - self.assertRaises(NoSuchElementException, self.marionette.find_element, By.XPATH, "cheese") - - def test_not_found_from_element(self): - self.marionette.timeout.implicit = 0 - self.marionette.navigate(id_html) - el = self.marionette.find_element(By.ID, "foo") - self.assertRaises(NoSuchElementException, el.find_element, By.CLASS_NAME, "cheese") - self.assertRaises(NoSuchElementException, el.find_element, By.CSS_SELECTOR, "cheese") - self.assertRaises(NoSuchElementException, el.find_element, By.ID, "cheese") - self.assertRaises(NoSuchElementException, el.find_element, By.LINK_TEXT, "cheese") - self.assertRaises(NoSuchElementException, el.find_element, By.NAME, "cheese") - self.assertRaises(NoSuchElementException, el.find_element, By.PARTIAL_LINK_TEXT, "cheese") - self.assertRaises(NoSuchElementException, el.find_element, By.TAG_NAME, "cheese") - self.assertRaises(NoSuchElementException, el.find_element, By.XPATH, "cheese") - - def test_not_found_implicit_wait_from_element(self): - self.marionette.timeout.implicit = 0.5 - self.marionette.navigate(id_html) - el = self.marionette.find_element(By.ID, "foo") - self.assertRaises(NoSuchElementException, el.find_element, By.CLASS_NAME, "cheese") - self.assertRaises(NoSuchElementException, el.find_element, By.CSS_SELECTOR, "cheese") - self.assertRaises(NoSuchElementException, el.find_element, By.ID, "cheese") - self.assertRaises(NoSuchElementException, el.find_element, By.LINK_TEXT, "cheese") - self.assertRaises(NoSuchElementException, el.find_element, By.NAME, "cheese") - self.assertRaises(NoSuchElementException, el.find_element, By.PARTIAL_LINK_TEXT, "cheese") - self.assertRaises(NoSuchElementException, el.find_element, By.TAG_NAME, "cheese") - self.assertRaises(NoSuchElementException, el.find_element, By.XPATH, "cheese") - - def test_css_selector_scope_doesnt_start_at_rootnode(self): - self.marionette.navigate(parent_child_html) - el = self.marionette.find_element(By.ID, "child") - parent = self.marionette.find_element(By.ID, "parent") - found = parent.find_element(By.CSS_SELECTOR, "p") - self.assertEqual(el, found) - - def test_unknown_selector(self): - with self.assertRaises(InvalidSelectorException): - self.marionette.find_elements("foo", "bar") - - def test_element_id_is_valid_uuid(self): - self.marionette.navigate(id_html) - el = self.marionette.find_element(By.TAG_NAME, "p") - uuid_regex = re.compile('^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$') - self.assertIsNotNone(re.search(uuid_regex, el.id), - 'UUID for the WebElement is not valid. ID is {}'\ - .format(el.id)) - - def test_invalid_xpath_selector(self): - with self.assertRaises(InvalidSelectorException): - self.marionette.find_element(By.XPATH, "count(//input)") - with self.assertRaises(InvalidSelectorException): - parent = self.marionette.execute_script("return document.documentElement") - parent.find_element(By.XPATH, "count(//input)") - - def test_invalid_css_selector(self): - with self.assertRaises(InvalidSelectorException): - self.marionette.find_element(By.CSS_SELECTOR, "") - with self.assertRaises(InvalidSelectorException): - parent = self.marionette.execute_script("return document.documentElement") - parent.find_element(By.CSS_SELECTOR, "") - - def test_finding_active_element_returns_element(self): - self.marionette.navigate(id_html) - active = self.marionette.execute_script("return document.activeElement") - self.assertEqual(active, self.marionette.get_active_element()) - - -class TestFindElementXHTML(MarionetteTestCase): - def setUp(self): - MarionetteTestCase.setUp(self) - self.marionette.timeout.implicit = 0 - - def test_id(self): - self.marionette.navigate(id_xhtml) - expected = self.marionette.execute_script("return document.querySelector('p')") - found = self.marionette.find_element(By.ID, "foo") - self.assertIsInstance(found, HTMLElement) - self.assertEqual(expected, found) - - def test_child_element(self): - self.marionette.navigate(parent_child_xhtml) - parent = self.marionette.find_element(By.ID, "parent") - child = self.marionette.find_element(By.ID, "child") - found = parent.find_element(By.TAG_NAME, "p") - self.assertEqual(found.tag_name, "p") - self.assertIsInstance(found, HTMLElement) - self.assertEqual(child, found) - - def test_tag_name(self): - self.marionette.navigate(children_xhtml) - el = self.marionette.execute_script("return document.querySelector('p')") - found = self.marionette.find_element(By.TAG_NAME, "p") - self.assertIsInstance(found, HTMLElement) - self.assertEqual(el, found) - - def test_class_name(self): - self.marionette.navigate(class_xhtml) - el = self.marionette.execute_script("return document.querySelector('.foo')") - found = self.marionette.find_element(By.CLASS_NAME, "foo") - self.assertIsInstance(found, HTMLElement) - self.assertEqual(el, found) - - def test_by_name(self): - self.marionette.navigate(name_xhtml) - el = self.marionette.execute_script("return document.querySelector('[name=foo]')") - found = self.marionette.find_element(By.NAME, "foo") - self.assertIsInstance(found, HTMLElement) - self.assertEqual(el, found) - - def test_css_selector(self): - self.marionette.navigate(children_xhtml) - el = self.marionette.execute_script("return document.querySelector('p')") - found = self.marionette.find_element(By.CSS_SELECTOR, "p") - self.assertIsInstance(found, HTMLElement) - self.assertEqual(el, found) - - def test_link_text(self): - self.marionette.navigate(link_xhtml) - el = self.marionette.execute_script("return document.querySelector('a')") - found = self.marionette.find_element(By.LINK_TEXT, "foo bar") - self.assertIsInstance(found, HTMLElement) - self.assertEqual(el, found) - - def test_partial_link_text(self): - self.marionette.navigate(link_xhtml) - el = self.marionette.execute_script("return document.querySelector('a')") - found = self.marionette.find_element(By.PARTIAL_LINK_TEXT, "foo") - self.assertIsInstance(found, HTMLElement) - self.assertEqual(el, found) - - def test_xpath(self): - self.marionette.navigate(id_xhtml) - el = self.marionette.execute_script("return document.querySelector('#foo')") - found = self.marionette.find_element(By.XPATH, "id('foo')") - self.assertIsInstance(found, HTMLElement) - self.assertEqual(el, found) - - def test_css_selector_scope_does_not_start_at_rootnode(self): - self.marionette.navigate(parent_child_xhtml) - el = self.marionette.find_element(By.ID, "child") - parent = self.marionette.find_element(By.ID, "parent") - found = parent.find_element(By.CSS_SELECTOR, "p") - self.assertEqual(el, found) - - def test_active_element(self): - self.marionette.navigate(id_xhtml) - active = self.marionette.execute_script("return document.activeElement") - self.assertEqual(active, self.marionette.get_active_element()) - - -class TestFindElementsHTML(MarionetteTestCase): - def setUp(self): - MarionetteTestCase.setUp(self) - self.marionette.timeout.implicit = 0 - - def assertItemsIsInstance(self, items, typ): - for item in items: - self.assertIsInstance(item, typ) - - def test_child_elements(self): - self.marionette.navigate(children_html) - parent = self.marionette.find_element(By.TAG_NAME, "div") - children = self.marionette.find_elements(By.TAG_NAME, "p") - found = parent.find_elements(By.TAG_NAME, "p") - self.assertItemsIsInstance(found, HTMLElement) - self.assertSequenceEqual(found, children) - - def test_tag_name(self): - self.marionette.navigate(children_html) - els = self.marionette.execute_script("return document.querySelectorAll('p')") - found = self.marionette.find_elements(By.TAG_NAME, "p") - self.assertItemsIsInstance(found, HTMLElement) - self.assertSequenceEqual(els, found) - - def test_class_name(self): - self.marionette.navigate(class_html) - els = self.marionette.execute_script("return document.querySelectorAll('.foo')") - found = self.marionette.find_elements(By.CLASS_NAME, "foo") - self.assertItemsIsInstance(found, HTMLElement) - self.assertSequenceEqual(els, found) - - def test_by_name(self): - self.marionette.navigate(name_html) - els = self.marionette.execute_script("return document.querySelectorAll('[name=foo]')") - found = self.marionette.find_elements(By.NAME, "foo") - self.assertItemsIsInstance(found, HTMLElement) - self.assertSequenceEqual(els, found) - - def test_css_selector(self): - self.marionette.navigate(children_html) - els = self.marionette.execute_script("return document.querySelectorAll('p')") - found = self.marionette.find_elements(By.CSS_SELECTOR, "p") - self.assertItemsIsInstance(found, HTMLElement) - self.assertSequenceEqual(els, found) - - def test_invalid_css_selector_should_throw(self): - with self.assertRaises(InvalidSelectorException): - self.marionette.find_elements(By.CSS_SELECTOR, "#") - - def test_link_text(self): - self.marionette.navigate(link_html) - els = self.marionette.execute_script("return document.querySelectorAll('a')") - found = self.marionette.find_elements(By.LINK_TEXT, "foo bar") - self.assertItemsIsInstance(found, HTMLElement) - self.assertSequenceEqual(els, found) - - def test_link_text_with_trailing_space(self): - self.marionette.navigate(link_html_with_trailing_space) - els = self.marionette.execute_script("return document.querySelectorAll('a')") - found = self.marionette.find_elements(By.LINK_TEXT, "a link with a trailing space") - self.assertItemsIsInstance(found, HTMLElement) - self.assertSequenceEqual(els, found) - - - def test_partial_link_text(self): - self.marionette.navigate(link_html) - els = self.marionette.execute_script("return document.querySelectorAll('a')") - found = self.marionette.find_elements(By.PARTIAL_LINK_TEXT, "foo") - self.assertItemsIsInstance(found, HTMLElement) - self.assertSequenceEqual(els, found) - - def test_xpath(self): - self.marionette.navigate(children_html) - els = self.marionette.execute_script("return document.querySelectorAll('p')") - found = self.marionette.find_elements(By.XPATH, ".//p") - self.assertItemsIsInstance(found, HTMLElement) - self.assertSequenceEqual(els, found) - - def test_css_selector_scope_doesnt_start_at_rootnode(self): - self.marionette.navigate(parent_child_html) - els = self.marionette.find_elements(By.ID, "child") - parent = self.marionette.find_element(By.ID, "parent") - found = parent.find_elements(By.CSS_SELECTOR, "p") - self.assertSequenceEqual(els, found) - - def test_unknown_selector(self): - with self.assertRaises(InvalidSelectorException): - self.marionette.find_element("foo", "bar") - - def test_element_id_is_valid_uuid(self): - self.marionette.navigate(id_html) - els = self.marionette.find_elements(By.TAG_NAME, "p") - uuid_regex = re.compile('^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$') - self.assertIsNotNone(re.search(uuid_regex, els[0].id), - 'UUID for the WebElement is not valid. ID is {}'\ - .format(els[0].id)) - - def test_invalid_xpath_selector(self): - with self.assertRaises(InvalidSelectorException): - self.marionette.find_elements(By.XPATH, "count(//input)") - with self.assertRaises(InvalidSelectorException): - parent = self.marionette.execute_script("return document.documentElement") - parent.find_elements(By.XPATH, "count(//input)") - - def test_invalid_css_selector(self): - with self.assertRaises(InvalidSelectorException): - self.marionette.find_elements(By.CSS_SELECTOR, "") - with self.assertRaises(InvalidSelectorException): - parent = self.marionette.execute_script("return document.documentElement") - parent.find_elements(By.CSS_SELECTOR, "") - - -class TestFindElementsXHTML(MarionetteTestCase): - def setUp(self): - MarionetteTestCase.setUp(self) - self.marionette.timeout.implicit = 0 - - def assertItemsIsInstance(self, items, typ): - for item in items: - self.assertIsInstance(item, typ) - - def test_child_elements(self): - self.marionette.navigate(children_xhtml) - parent = self.marionette.find_element(By.TAG_NAME, "div") - children = self.marionette.find_elements(By.TAG_NAME, "p") - found = parent.find_elements(By.TAG_NAME, "p") - self.assertItemsIsInstance(found, HTMLElement) - self.assertSequenceEqual(found, children) - - def test_tag_name(self): - self.marionette.navigate(children_xhtml) - els = self.marionette.execute_script("return document.querySelectorAll('p')") - found = self.marionette.find_elements(By.TAG_NAME, "p") - self.assertItemsIsInstance(found, HTMLElement) - self.assertSequenceEqual(els, found) - - def test_class_name(self): - self.marionette.navigate(class_xhtml) - els = self.marionette.execute_script("return document.querySelectorAll('.foo')") - found = self.marionette.find_elements(By.CLASS_NAME, "foo") - self.assertItemsIsInstance(found, HTMLElement) - self.assertSequenceEqual(els, found) - - def test_by_name(self): - self.marionette.navigate(name_xhtml) - els = self.marionette.execute_script("return document.querySelectorAll('[name=foo]')") - found = self.marionette.find_elements(By.NAME, "foo") - self.assertItemsIsInstance(found, HTMLElement) - self.assertSequenceEqual(els, found) - - def test_css_selector(self): - self.marionette.navigate(children_xhtml) - els = self.marionette.execute_script("return document.querySelectorAll('p')") - found = self.marionette.find_elements(By.CSS_SELECTOR, "p") - self.assertItemsIsInstance(found, HTMLElement) - self.assertSequenceEqual(els, found) - - def test_link_text(self): - self.marionette.navigate(link_xhtml) - els = self.marionette.execute_script("return document.querySelectorAll('a')") - found = self.marionette.find_elements(By.LINK_TEXT, "foo bar") - self.assertItemsIsInstance(found, HTMLElement) - self.assertSequenceEqual(els, found) - - def test_partial_link_text(self): - self.marionette.navigate(link_xhtml) - els = self.marionette.execute_script("return document.querySelectorAll('a')") - found = self.marionette.find_elements(By.PARTIAL_LINK_TEXT, "foo") - self.assertItemsIsInstance(found, HTMLElement) - self.assertSequenceEqual(els, found) - - @skip("XHTML namespace not yet supported") - def test_xpath(self): - self.marionette.navigate(children_xhtml) - els = self.marionette.execute_script("return document.querySelectorAll('p')") - found = self.marionette.find_elements(By.XPATH, "//xhtml:p") - self.assertItemsIsInstance(found, HTMLElement) - self.assertSequenceEqual(els, found) - - def test_css_selector_scope_doesnt_start_at_rootnode(self): - self.marionette.navigate(parent_child_xhtml) - els = self.marionette.find_elements(By.ID, "child") - parent = self.marionette.find_element(By.ID, "parent") - found = parent.find_elements(By.CSS_SELECTOR, "p") - self.assertSequenceEqual(els, found) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_element_state.py b/testing/marionette/harness/marionette_harness/tests/unit/test_element_state.py deleted file mode 100644 index 0344b4b9c..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_element_state.py +++ /dev/null @@ -1,162 +0,0 @@ -# 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/. - -import types -import urllib - -from marionette_driver.by import By - -from marionette_harness import MarionetteTestCase - - -boolean_attributes = { - "audio": ["autoplay", "controls", "loop", "muted"], - "button": ["autofocus", "disabled", "formnovalidate"], - "details": ["open"], - "dialog": ["open"], - "fieldset": ["disabled"], - "form": ["novalidate"], - "iframe": ["allowfullscreen"], - "img": ["ismap"], - "input": ["autofocus", "checked", "disabled", "formnovalidate", "multiple", "readonly", "required"], - "menuitem": ["checked", "default", "disabled"], - "object": ["typemustmatch"], - "ol": ["reversed"], - "optgroup": ["disabled"], - "option": ["disabled", "selected"], - "script": ["async", "defer"], - "select": ["autofocus", "disabled", "multiple", "required"], - "textarea": ["autofocus", "disabled", "readonly", "required"], - "track": ["default"], - "video": ["autoplay", "controls", "loop", "muted"], -} - - -def inline(doc, doctype="html"): - if doctype == "html": - return "data:text/html;charset=utf-8,{}".format(urllib.quote(doc)) - elif doctype == "xhtml": - return "data:application/xhtml+xml,{}".format(urllib.quote( -r"""<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" - "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> - <head> - <title>XHTML might be the future</title> - </head> - - <body> - {} - </body> -</html>""".format(doc))) - - -attribute = inline("<input foo=bar>") -input = inline("<input>") -disabled = inline("<input disabled=baz>") -check = inline("<input type=checkbox>") - - -class TestIsElementEnabled(MarionetteTestCase): - def test_is_enabled(self): - test_html = self.marionette.absolute_url("test.html") - self.marionette.navigate(test_html) - l = self.marionette.find_element(By.NAME, "myCheckBox") - self.assertTrue(l.is_enabled()) - self.marionette.execute_script("arguments[0].disabled = true;", [l]) - self.assertFalse(l.is_enabled()) - - -class TestIsElementDisplayed(MarionetteTestCase): - def test_is_displayed(self): - test_html = self.marionette.absolute_url("test.html") - self.marionette.navigate(test_html) - l = self.marionette.find_element(By.NAME, "myCheckBox") - self.assertTrue(l.is_displayed()) - self.marionette.execute_script("arguments[0].hidden = true;", [l]) - self.assertFalse(l.is_displayed()) - - -class TestGetElementAttribute(MarionetteTestCase): - def test_normal_attribute(self): - self.marionette.navigate(inline("<p style=foo>")) - el = self.marionette.find_element(By.TAG_NAME, "p") - attr = el.get_attribute("style") - self.assertIsInstance(attr, types.StringTypes) - self.assertEqual("foo", attr) - - def test_boolean_attributes(self): - for tag, attrs in boolean_attributes.iteritems(): - for attr in attrs: - print("testing boolean attribute <{0} {1}>".format(tag, attr)) - doc = inline("<{0} {1}>".format(tag, attr)) - self.marionette.navigate(doc) - el = self.marionette.find_element(By.TAG_NAME, tag) - res = el.get_attribute(attr) - self.assertIsInstance(res, types.StringTypes) - self.assertEqual("true", res) - - def test_global_boolean_attributes(self): - self.marionette.navigate(inline("<p hidden>foo")) - el = self.marionette.find_element(By.TAG_NAME, "p") - attr = el.get_attribute("hidden") - self.assertIsInstance(attr, types.StringTypes) - self.assertEqual("true", attr) - - self.marionette.navigate(inline("<p>foo")) - el = self.marionette.find_element(By.TAG_NAME, "p") - attr = el.get_attribute("hidden") - self.assertIsNone(attr) - - self.marionette.navigate(inline("<p itemscope>foo")) - el = self.marionette.find_element(By.TAG_NAME, "p") - attr = el.get_attribute("itemscope") - self.assertIsInstance(attr, types.StringTypes) - self.assertEqual("true", attr) - - self.marionette.navigate(inline("<p>foo")) - el = self.marionette.find_element(By.TAG_NAME, "p") - attr = el.get_attribute("itemscope") - self.assertIsNone(attr) - - # TODO(ato): Test for custom elements - - def test_xhtml(self): - doc = inline("<p hidden=\"true\">foo</p>", doctype="xhtml") - self.marionette.navigate(doc) - el = self.marionette.find_element(By.TAG_NAME, "p") - attr = el.get_attribute("hidden") - self.assertIsInstance(attr, types.StringTypes) - self.assertEqual("true", attr) - - -class TestGetElementProperty(MarionetteTestCase): - def test_get(self): - self.marionette.navigate(disabled) - el = self.marionette.find_element(By.TAG_NAME, "input") - prop = el.get_property("disabled") - self.assertIsInstance(prop, bool) - self.assertTrue(prop) - - def test_missing_property_returns_default(self): - self.marionette.navigate(input) - el = self.marionette.find_element(By.TAG_NAME, "input") - prop = el.get_property("checked") - self.assertIsInstance(prop, bool) - self.assertFalse(prop) - - def test_attribute_not_returned(self): - self.marionette.navigate(attribute) - el = self.marionette.find_element(By.TAG_NAME, "input") - self.assertEqual(el.get_property("foo"), None) - - def test_manipulated_element(self): - self.marionette.navigate(check) - el = self.marionette.find_element(By.TAG_NAME, "input") - self.assertEqual(el.get_property("checked"), False) - - el.click() - self.assertEqual(el.get_property("checked"), True) - - el.click() - self.assertEqual(el.get_property("checked"), False) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_element_state_chrome.py b/testing/marionette/harness/marionette_harness/tests/unit/test_element_state_chrome.py deleted file mode 100644 index 01ed355c4..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_element_state_chrome.py +++ /dev/null @@ -1,85 +0,0 @@ -# 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.by import By - -from marionette_harness import MarionetteTestCase, skip - - -class TestIsElementEnabledChrome(MarionetteTestCase): - def setUp(self): - MarionetteTestCase.setUp(self) - self.marionette.set_context("chrome") - self.win = self.marionette.current_window_handle - self.marionette.execute_script("window.open('chrome://marionette/content/test.xul', 'foo', 'chrome,centerscreen');") - self.marionette.switch_to_window('foo') - self.assertNotEqual(self.win, self.marionette.current_window_handle) - - def tearDown(self): - self.assertNotEqual(self.win, self.marionette.current_window_handle) - self.marionette.execute_script("window.close();") - self.marionette.switch_to_window(self.win) - MarionetteTestCase.tearDown(self) - - def test_enabled(self): - l = self.marionette.find_element(By.ID, "textInput") - self.assertTrue(l.is_enabled()) - self.marionette.execute_script("arguments[0].disabled = true;", [l]) - self.assertFalse(l.is_enabled()) - self.marionette.execute_script("arguments[0].disabled = false;", [l]) - - def test_can_get_element_rect(self): - l = self.marionette.find_element(By.ID, "textInput") - rect = l.rect - self.assertTrue(rect['x'] > 0) - self.assertTrue(rect['y'] > 0) - - -@skip("Switched off in bug 896043, and to be turned on in bug 896046") -class TestIsElementDisplayed(MarionetteTestCase): - def test_isDisplayed(self): - l = self.marionette.find_element(By.ID, "textInput") - self.assertTrue(l.is_displayed()) - self.marionette.execute_script("arguments[0].hidden = true;", [l]) - self.assertFalse(l.is_displayed()) - self.marionette.execute_script("arguments[0].hidden = false;", [l]) - - -class TestGetElementAttributeChrome(MarionetteTestCase): - def setUp(self): - MarionetteTestCase.setUp(self) - self.marionette.set_context("chrome") - self.win = self.marionette.current_window_handle - self.marionette.execute_script("window.open('chrome://marionette/content/test.xul', 'foo', 'chrome,centerscreen');") - self.marionette.switch_to_window('foo') - self.assertNotEqual(self.win, self.marionette.current_window_handle) - - def tearDown(self): - self.assertNotEqual(self.win, self.marionette.current_window_handle) - self.marionette.execute_script("window.close();") - self.marionette.switch_to_window(self.win) - MarionetteTestCase.tearDown(self) - - def test_get(self): - el = self.marionette.execute_script("return window.document.getElementById('textInput');") - self.assertEqual(el.get_attribute("id"), "textInput") - -class TestGetElementProperty(MarionetteTestCase): - def setUp(self): - MarionetteTestCase.setUp(self) - self.marionette.set_context("chrome") - self.win = self.marionette.current_window_handle - self.marionette.execute_script("window.open('chrome://marionette/content/test.xul', 'foo', 'chrome,centerscreen');") - self.marionette.switch_to_window('foo') - self.assertNotEqual(self.win, self.marionette.current_window_handle) - - def tearDown(self): - self.assertNotEqual(self.win, self.marionette.current_window_handle) - self.marionette.execute_script("window.close();") - self.marionette.switch_to_window(self.win) - MarionetteTestCase.tearDown(self) - - def test_get(self): - el = self.marionette.execute_script("return window.document.getElementById('textInput');") - self.assertEqual(el.get_property("id"), "textInput") diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_elementsize.py b/testing/marionette/harness/marionette_harness/tests/unit/test_elementsize.py deleted file mode 100644 index ebabd3344..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_elementsize.py +++ /dev/null @@ -1,17 +0,0 @@ -# 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.by import By - -from marionette_harness import MarionetteTestCase - - -class TestElementSize(MarionetteTestCase): - def testShouldReturnTheSizeOfALink(self): - test_html = self.marionette.absolute_url("testSize.html") - self.marionette.navigate(test_html) - shrinko = self.marionette.find_element(By.ID, 'linkId') - size = shrinko.rect - self.assertTrue(size['width'] > 0) - self.assertTrue(size['height'] > 0) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_elementsize_chrome.py b/testing/marionette/harness/marionette_harness/tests/unit/test_elementsize_chrome.py deleted file mode 100644 index e2bb34715..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_elementsize_chrome.py +++ /dev/null @@ -1,34 +0,0 @@ -# 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.by import By - -from marionette_harness import MarionetteTestCase, WindowManagerMixin - - -class TestElementSizeChrome(WindowManagerMixin, MarionetteTestCase): - - def setUp(self): - super(TestElementSizeChrome, self).setUp() - - self.marionette.set_context("chrome") - - def open_window_with_js(): - self.marionette.execute_script(""" - window.open('chrome://marionette/content/test2.xul', - 'foo', 'chrome,centerscreen'); - """) - - new_window = self.open_window(trigger=open_window_with_js) - self.marionette.switch_to_window(new_window) - - def tearDown(self): - self.close_all_windows() - super(TestElementSizeChrome, self).tearDown() - - def testShouldReturnTheSizeOfAnInput(self): - shrinko = self.marionette.find_element(By.ID, 'textInput') - size = shrinko.rect - self.assertTrue(size['width'] > 0) - self.assertTrue(size['height'] > 0) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_errors.py b/testing/marionette/harness/marionette_harness/tests/unit/test_errors.py deleted file mode 100644 index f6a9c285c..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_errors.py +++ /dev/null @@ -1,77 +0,0 @@ -# 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/. - -import sys - -from marionette_driver import errors - -from marionette_harness import marionette_test - - -def fake_cause(): - try: - raise ValueError("bar") - except ValueError as e: - return sys.exc_info() - -message = "foo" -cause = fake_cause() -stacktrace = "first\nsecond" - -class TestErrors(marionette_test.MarionetteTestCase): - def test_defaults(self): - exc = errors.MarionetteException() - self.assertIsNone(exc.message) - self.assertIsNone(exc.cause) - self.assertIsNone(exc.stacktrace) - - def test_construction(self): - exc = errors.MarionetteException( - message=message, cause=cause, stacktrace=stacktrace) - self.assertEquals(exc.message, message) - self.assertEquals(exc.cause, cause) - self.assertEquals(exc.stacktrace, stacktrace) - - def test_str(self): - exc = errors.MarionetteException( - message=message, cause=cause, stacktrace=stacktrace) - r = str(exc) - self.assertIn(message, r) - self.assertIn(", caused by {0!r}".format(cause[0]), r) - self.assertIn("\nstacktrace:\n\tfirst\n\tsecond", r) - - def test_cause_string(self): - exc = errors.MarionetteException(cause="foo") - self.assertEqual(exc.cause, "foo") - r = str(exc) - self.assertIn(", caused by foo", r) - - def test_cause_tuple(self): - exc = errors.MarionetteException(cause=cause) - self.assertEqual(exc.cause, cause) - r = str(exc) - self.assertIn(", caused by {0!r}".format(cause[0]), r) - - -class TestLookup(marionette_test.MarionetteTestCase): - def test_by_unknown_number(self): - self.assertEqual(errors.MarionetteException, errors.lookup(123456)) - - def test_by_known_string(self): - self.assertEqual(errors.NoSuchElementException, - errors.lookup("no such element")) - - def test_by_unknown_string(self): - self.assertEqual(errors.MarionetteException, errors.lookup("barbera")) - - def test_by_known_unicode_string(self): - self.assertEqual(errors.NoSuchElementException, - errors.lookup(u"no such element")) - - -class TestAllErrors(marionette_test.MarionetteTestCase): - def test_properties(self): - for exc in errors.es_: - self.assertTrue(hasattr(exc, "status"), - "expected exception to have attribute `status'") diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_execute_async_script.py b/testing/marionette/harness/marionette_harness/tests/unit/test_execute_async_script.py deleted file mode 100644 index 8a5472b3a..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_execute_async_script.py +++ /dev/null @@ -1,156 +0,0 @@ -# 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.errors import ( - JavascriptException, - ScriptTimeoutException, -) - -from marionette_harness import MarionetteTestCase - - -class TestExecuteAsyncContent(MarionetteTestCase): - def setUp(self): - super(TestExecuteAsyncContent, self).setUp() - self.marionette.timeout.script = 1 - - def test_execute_async_simple(self): - self.assertEqual(1, self.marionette.execute_async_script("arguments[arguments.length-1](1);")) - - def test_execute_async_ours(self): - self.assertEqual(1, self.marionette.execute_async_script("marionetteScriptFinished(1);")) - - def test_execute_async_timeout(self): - self.assertRaises(ScriptTimeoutException, self.marionette.execute_async_script, "var x = 1;") - - def test_execute_async_unique_timeout(self): - self.assertEqual(2, self.marionette.execute_async_script("setTimeout(function() {marionetteScriptFinished(2);}, 2000);", script_timeout=5000)) - self.assertRaises(ScriptTimeoutException, self.marionette.execute_async_script, "setTimeout(function() {marionetteScriptFinished(3);}, 2000);") - - def test_no_timeout(self): - self.marionette.timeout.script = 10 - self.assertTrue(self.marionette.execute_async_script(""" - var callback = arguments[arguments.length - 1]; - setTimeout(function() { callback(true); }, 500); - """)) - - def test_execute_async_unload(self): - self.marionette.timeout.script = 5 - unload = """ - window.location.href = "about:blank"; - """ - self.assertRaises(JavascriptException, self.marionette.execute_async_script, unload) - - def test_check_window(self): - self.assertTrue(self.marionette.execute_async_script("marionetteScriptFinished(window !=null && window != undefined);")) - - def test_same_context(self): - var1 = 'testing' - self.assertEqual(self.marionette.execute_script(""" - this.testvar = '{}'; - return this.testvar; - """.format(var1)), var1) - self.assertEqual(self.marionette.execute_async_script( - "marionetteScriptFinished(this.testvar);", new_sandbox=False), var1) - - def test_execute_no_return(self): - self.assertEqual(self.marionette.execute_async_script("marionetteScriptFinished()"), None) - - def test_execute_js_exception(self): - try: - self.marionette.execute_async_script(""" - let a = 1; - foo(bar); - """) - self.assertFalse(True) - except JavascriptException, inst: - self.assertTrue('foo(bar)' in inst.stacktrace) - - def test_execute_async_js_exception(self): - self.assertRaises(JavascriptException, - self.marionette.execute_async_script, """ - var callback = arguments[arguments.length - 1]; - callback(foo()); - """) - - def test_script_finished(self): - self.assertTrue(self.marionette.execute_async_script(""" - marionetteScriptFinished(true); - """)) - - def test_execute_permission(self): - self.assertRaises(JavascriptException, self.marionette.execute_async_script, """ -let prefs = Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefBranch); -marionetteScriptFinished(4); -""") - - def test_sandbox_reuse(self): - # Sandboxes between `execute_script()` invocations are shared. - self.marionette.execute_async_script("this.foobar = [23, 42];" - "marionetteScriptFinished();") - self.assertEqual(self.marionette.execute_async_script( - "marionetteScriptFinished(this.foobar);", new_sandbox=False), [23, 42]) - - self.marionette.execute_async_script("global.barfoo = [42, 23];" - "marionetteScriptFinished();") - self.assertEqual(self.marionette.execute_async_script( - "marionetteScriptFinished(global.barfoo);", new_sandbox=False), [42, 23]) - - def test_sandbox_refresh_arguments(self): - self.marionette.execute_async_script("this.foobar = [arguments[0], arguments[1]];" - "marionetteScriptFinished();", - script_args=[23, 42]) - self.assertEqual(self.marionette.execute_async_script( - "marionetteScriptFinished(this.foobar);", new_sandbox=False), - [23, 42]) - - self.marionette.execute_async_script("global.barfoo = [arguments[0], arguments[1]];" - "marionetteScriptFinished()", - script_args=[42, 23], new_sandbox=False) - self.assertEqual(self.marionette.execute_async_script( - "marionetteScriptFinished(global.barfoo);", new_sandbox=False), - [42, 23]) - - # Functions defined in higher privilege scopes, such as the privileged - # content frame script listener.js runs in, cannot be accessed from - # content. This tests that it is possible to introspect the objects on - # `arguments` without getting permission defined errors. This is made - # possible because the last argument is always the callback/complete - # function. - # - # See bug 1290966. - def test_introspection_of_arguments(self): - self.marionette.execute_async_script( - "arguments[0].cheese; __webDriverCallback();", - script_args=[], sandbox=None) - - -class TestExecuteAsyncChrome(TestExecuteAsyncContent): - def setUp(self): - super(TestExecuteAsyncChrome, self).setUp() - self.marionette.set_context("chrome") - - def test_execute_async_unload(self): - pass - - def test_execute_permission(self): - self.assertEqual(5, self.marionette.execute_async_script(""" -var c = Components.classes; -marionetteScriptFinished(5); -""")) - - def test_execute_async_js_exception(self): - # Javascript exceptions are not propagated in chrome code - self.marionette.timeout.script = 0.2 - self.assertRaises(ScriptTimeoutException, - self.marionette.execute_async_script, """ - var callback = arguments[arguments.length - 1]; - setTimeout("callback(foo())", 50); - """) - self.assertRaises(JavascriptException, - self.marionette.execute_async_script, """ - var callback = arguments[arguments.length - 1]; - setTimeout("callback(foo())", 50); - """, debug_script=True) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_execute_isolate.py b/testing/marionette/harness/marionette_harness/tests/unit/test_execute_isolate.py deleted file mode 100644 index 7e09451e4..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_execute_isolate.py +++ /dev/null @@ -1,37 +0,0 @@ -# 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.errors import ScriptTimeoutException - -from marionette_harness import MarionetteTestCase - - -class TestExecuteIsolationContent(MarionetteTestCase): - def setUp(self): - super(TestExecuteIsolationContent, self).setUp() - self.content = True - - def test_execute_async_isolate(self): - # Results from one execute call that has timed out should not - # contaminate a future call. - multiplier = "*3" if self.content else "*1" - self.marionette.timeout.script = 0.5 - self.assertRaises(ScriptTimeoutException, - self.marionette.execute_async_script, - ("setTimeout(function() {{ marionetteScriptFinished(5{}); }}, 3000);" - .format(multiplier))) - - self.marionette.timeout.script = 6 - result = self.marionette.execute_async_script(""" - setTimeout(function() {{ marionetteScriptFinished(10{}); }}, 5000); - """.format(multiplier)) - self.assertEqual(result, 30 if self.content else 10) - -class TestExecuteIsolationChrome(TestExecuteIsolationContent): - def setUp(self): - super(TestExecuteIsolationChrome, self).setUp() - self.marionette.set_context("chrome") - self.content = False - - diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_execute_sandboxes.py b/testing/marionette/harness/marionette_harness/tests/unit/test_execute_sandboxes.py deleted file mode 100644 index d7cb0444b..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_execute_sandboxes.py +++ /dev/null @@ -1,79 +0,0 @@ -# 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.errors import JavascriptException - -from marionette_harness import MarionetteTestCase - - -class TestExecuteSandboxes(MarionetteTestCase): - def setUp(self): - super(TestExecuteSandboxes, self).setUp() - - def test_execute_system_sandbox(self): - # Test that "system" sandbox has elevated privileges in execute_script - result = self.marionette.execute_script( - "return Components.interfaces.nsIPermissionManager.ALLOW_ACTION", - sandbox="system") - self.assertEqual(result, 1) - - def test_execute_async_system_sandbox(self): - # Test that "system" sandbox has elevated privileges in - # execute_async_script. - result = self.marionette.execute_async_script(""" - const Ci = Components.interfaces; - let result = Ci.nsIPermissionManager.ALLOW_ACTION; - marionetteScriptFinished(result);""", - sandbox="system") - self.assertEqual(result, 1) - - def test_execute_switch_sandboxes(self): - # Test that sandboxes are retained when switching between them - # for execute_script. - self.marionette.execute_script("foo = 1", sandbox="1") - self.marionette.execute_script("foo = 2", sandbox="2") - foo = self.marionette.execute_script( - "return foo", sandbox="1", new_sandbox=False) - self.assertEqual(foo, 1) - foo = self.marionette.execute_script( - "return foo", sandbox="2", new_sandbox=False) - self.assertEqual(foo, 2) - - def test_execute_new_sandbox(self): - # test that clearing a sandbox does not affect other sandboxes - self.marionette.execute_script("foo = 1", sandbox="1") - self.marionette.execute_script("foo = 2", sandbox="2") - - # deprecate sandbox 1 by asking explicitly for a fresh one - with self.assertRaises(JavascriptException): - self.marionette.execute_script("return foo", - sandbox="1", new_sandbox=True) - - foo = self.marionette.execute_script( - "return foo", sandbox="2", new_sandbox=False) - self.assertEqual(foo, 2) - - def test_execute_async_switch_sandboxes(self): - # Test that sandboxes are retained when switching between them - # for execute_async_script. - self.marionette.execute_async_script( - "foo = 1; marionetteScriptFinished()", sandbox="1") - self.marionette.execute_async_script( - "foo = 2; marionetteScriptFinished()", sandbox='2') - foo = self.marionette.execute_async_script( - "marionetteScriptFinished(foo)", - sandbox="1", - new_sandbox=False) - self.assertEqual(foo, 1) - foo = self.marionette.execute_async_script( - "marionetteScriptFinished(foo)", - sandbox="2", - new_sandbox=False) - self.assertEqual(foo, 2) - - -class TestExecuteSandboxesChrome(TestExecuteSandboxes): - def setUp(self): - super(TestExecuteSandboxesChrome, self).setUp() - self.marionette.set_context("chrome") diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_execute_script.py b/testing/marionette/harness/marionette_harness/tests/unit/test_execute_script.py deleted file mode 100644 index 1ef4549d3..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_execute_script.py +++ /dev/null @@ -1,402 +0,0 @@ -# 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/. - -import os -import urllib - -from marionette_driver import By, errors -from marionette_driver.marionette import HTMLElement -from marionette_driver.wait import Wait - -from marionette_harness import MarionetteTestCase, skip_if_mobile, WindowManagerMixin - - -def inline(doc): - return "data:text/html;charset=utf-8,{}".format(urllib.quote(doc)) - - -elements = inline("<p>foo</p> <p>bar</p>") - -globals = set([ - "atob", - "Audio", - "btoa", - "document", - "navigator", - "URL", - "window", - ]) - - -class TestExecuteSimpleTestContent(MarionetteTestCase): - def test_stack_trace(self): - try: - self.marionette.execute_js_script(""" - let a = 1; - throwHere(); - """, filename="file.js") - self.assertFalse(True) - except errors.JavascriptException as e: - self.assertIn("throwHere is not defined", e.message) - self.assertIn("@file.js:2", e.stacktrace) - - -class TestExecuteContent(MarionetteTestCase): - - def assert_is_defined(self, property, sandbox="default"): - self.assertTrue(self.marionette.execute_script( - "return typeof arguments[0] != 'undefined'", [property], sandbox=sandbox), - "property {} is undefined".format(property)) - - def test_return_number(self): - self.assertEqual(1, self.marionette.execute_script("return 1")) - self.assertEqual(1.5, self.marionette.execute_script("return 1.5")) - - def test_return_boolean(self): - self.assertTrue(self.marionette.execute_script("return true")) - - def test_return_string(self): - self.assertEqual("foo", self.marionette.execute_script("return 'foo'")) - - def test_return_array(self): - self.assertEqual( - [1, 2], self.marionette.execute_script("return [1, 2]")) - self.assertEqual( - [1.25, 1.75], self.marionette.execute_script("return [1.25, 1.75]")) - self.assertEqual( - [True, False], self.marionette.execute_script("return [true, false]")) - self.assertEqual( - ["foo", "bar"], self.marionette.execute_script("return ['foo', 'bar']")) - self.assertEqual( - [1, 1.5, True, "foo"], self.marionette.execute_script("return [1, 1.5, true, 'foo']")) - self.assertEqual( - [1, [2]], self.marionette.execute_script("return [1, [2]]")) - - def test_return_object(self): - self.assertEqual( - {"foo": 1}, self.marionette.execute_script("return {foo: 1}")) - self.assertEqual( - {"foo": 1.5}, self.marionette.execute_script("return {foo: 1.5}")) - self.assertEqual( - {"foo": True}, self.marionette.execute_script("return {foo: true}")) - self.assertEqual( - {"foo": "bar"}, self.marionette.execute_script("return {foo: 'bar'}")) - self.assertEqual( - {"foo": [1, 2]}, self.marionette.execute_script("return {foo: [1, 2]}")) - self.assertEqual( - {"foo": {"bar": [1, 2]}}, - self.marionette.execute_script("return {foo: {bar: [1, 2]}}")) - - def test_no_return_value(self): - self.assertIsNone(self.marionette.execute_script("true")) - - def test_argument_null(self): - self.assertIsNone(self.marionette.execute_script("return arguments[0]", [None])) - - def test_argument_number(self): - self.assertEqual( - 1, self.marionette.execute_script("return arguments[0]", [1])) - self.assertEqual( - 1.5, self.marionette.execute_script("return arguments[0]", [1.5])) - - def test_argument_boolean(self): - self.assertTrue(self.marionette.execute_script("return arguments[0]", [True])) - - def test_argument_string(self): - self.assertEqual( - "foo", self.marionette.execute_script("return arguments[0]", ["foo"])) - - def test_argument_array(self): - self.assertEqual( - [1, 2], self.marionette.execute_script("return arguments[0]", [[1, 2]])) - - def test_argument_object(self): - self.assertEqual({"foo": 1}, self.marionette.execute_script( - "return arguments[0]", [{"foo": 1}])) - - def test_globals(self): - for property in globals: - self.assert_is_defined(property) - self.assert_is_defined("Components") - self.assert_is_defined("window.wrappedJSObject") - - def test_system_globals(self): - for property in globals: - self.assert_is_defined(property, sandbox="system") - self.assert_is_defined("Components", sandbox="system") - self.assert_is_defined("window.wrappedJSObject") - - def test_exception(self): - self.assertRaises(errors.JavascriptException, - self.marionette.execute_script, "return foo") - - def test_stacktrace(self): - with self.assertRaises(errors.JavascriptException) as cm: - self.marionette.execute_script("return b") - - # by default execute_script pass the name of the python file - self.assertIn(os.path.basename(__file__.replace(".pyc", ".py")), - cm.exception.stacktrace) - self.assertIn("b is not defined", cm.exception.message) - self.assertIn("return b", cm.exception.stacktrace) - - def test_permission(self): - with self.assertRaises(errors.JavascriptException): - self.marionette.execute_script(""" - var c = Components.classes["@mozilla.org/preferences-service;1"]; - """) - - def test_return_web_element(self): - self.marionette.navigate(elements) - expected = self.marionette.find_element(By.TAG_NAME, "p") - actual = self.marionette.execute_script( - "return document.querySelector('p')") - self.assertEqual(expected, actual) - - def test_return_web_element_array(self): - self.marionette.navigate(elements) - expected = self.marionette.find_elements(By.TAG_NAME, "p") - actual = self.marionette.execute_script(""" - let els = document.querySelectorAll('p') - return [els[0], els[1]]""") - self.assertEqual(expected, actual) - - # Bug 938228 identifies a problem with unmarshaling NodeList - # objects from the DOM. document.querySelectorAll returns this - # construct. - def test_return_web_element_nodelist(self): - self.marionette.navigate(elements) - expected = self.marionette.find_elements(By.TAG_NAME, "p") - actual = self.marionette.execute_script( - "return document.querySelectorAll('p')") - self.assertEqual(expected, actual) - - def test_sandbox_reuse(self): - # Sandboxes between `execute_script()` invocations are shared. - self.marionette.execute_script("this.foobar = [23, 42];") - self.assertEqual(self.marionette.execute_script( - "return this.foobar;", new_sandbox=False), [23, 42]) - - self.marionette.execute_script("global.barfoo = [42, 23];") - self.assertEqual(self.marionette.execute_script( - "return global.barfoo;", new_sandbox=False), [42, 23]) - - def test_sandbox_refresh_arguments(self): - self.marionette.execute_script( - "this.foobar = [arguments[0], arguments[1]]", [23, 42]) - self.assertEqual(self.marionette.execute_script( - "return this.foobar", new_sandbox=False), [23, 42]) - - def test_wrappedjsobject(self): - try: - self.marionette.execute_script("window.wrappedJSObject.foo = 3") - self.assertEqual( - self.marionette.execute_script("return window.wrappedJSObject.foo"), 3) - finally: - self.marionette.execute_script("delete window.wrappedJSObject.foo") - - def test_system_sandbox_wrappedjsobject(self): - self.marionette.execute_script( - "window.wrappedJSObject.foo = 4", sandbox="system") - self.assertEqual(self.marionette.execute_script( - "return window.wrappedJSObject.foo", sandbox="system"), 4) - - def test_system_dead_object(self): - self.marionette.execute_script( - "window.wrappedJSObject.foo = function() { return 'yo' }", - sandbox="system") - self.marionette.execute_script( - "dump(window.wrappedJSObject.foo)", sandbox="system") - - self.marionette.execute_script( - "window.wrappedJSObject.foo = function() { return 'yolo' }", - sandbox="system") - typ = self.marionette.execute_script( - "return typeof window.wrappedJSObject.foo", sandbox="system") - self.assertEqual("function", typ) - obj = self.marionette.execute_script( - "return window.wrappedJSObject.foo.toString()", sandbox="system") - self.assertIn("yolo", obj) - - def test_lasting_side_effects(self): - def send(script): - return self.marionette._send_message( - "executeScript", {"script": script}, key="value") - - send("window.foo = 1") - foo = send("return window.foo") - self.assertEqual(1, foo) - - for property in globals: - exists = send("return typeof {} != 'undefined'".format(property)) - self.assertTrue(exists, "property {} is undefined".format(property)) - - self.assertTrue(send("return typeof Components.utils == 'undefined'")) - self.assertTrue(send("return typeof window.wrappedJSObject == 'undefined'")) - - def test_no_callback(self): - self.assertTrue(self.marionette.execute_script( - "return typeof arguments[0] == 'undefined'")) - - def test_window_set_timeout_is_not_cancelled(self): - def content_timeout_triggered(mn): - return mn.execute_script("return window.n", sandbox=None) > 0 - - # subsequent call to execute_script after this - # should not cancel the setTimeout event - self.marionette.navigate(inline(""" - <script> - window.n = 0; - setTimeout(() => ++window.n, 4000); - </script>""")) - - # as debug builds are inherently slow, - # we need to assert the event did not already fire - self.assertEqual(0, self.marionette.execute_script( - "return window.n", sandbox=None), - "setTimeout already fired") - - # if event was cancelled, this will time out - Wait(self.marionette, timeout=8).until( - content_timeout_triggered, - message="Scheduled setTimeout event was cancelled by call to execute_script") - - def test_privileged_code_inspection(self): - # test permission denied on toString of unload event handler - self.marionette.navigate(inline(""" - <script> - window.addEventListener = (type, handler) => handler.toString(); - </script>""")) - self.marionette.execute_script("", sandbox=None) - - # test inspection of arguments - self.marionette.execute_script("__webDriverArguments.toString()") - - -class TestExecuteChrome(WindowManagerMixin, TestExecuteContent): - - def setUp(self): - super(TestExecuteChrome, self).setUp() - - self.marionette.set_context("chrome") - - def tearDown(self): - super(TestExecuteChrome, self).tearDown() - - def test_permission(self): - self.assertEqual(1, self.marionette.execute_script(""" - var c = Components.classes["@mozilla.org/preferences-service;1"]; return 1;""")) - - @skip_if_mobile("New windows not supported in Fennec") - def test_unmarshal_element_collection(self): - - def open_window_with_js(): - self.marionette.execute_script( - "window.open('chrome://marionette/content/test.xul', 'xul', 'chrome');") - - try: - win = self.open_window(trigger=open_window_with_js) - self.marionette.switch_to_window(win) - - expected = self.marionette.find_elements(By.TAG_NAME, "textbox") - actual = self.marionette.execute_script( - "return document.querySelectorAll('textbox')") - self.assertEqual(expected, actual) - - finally: - self.close_all_windows() - - def test_async_script_timeout(self): - with self.assertRaises(errors.ScriptTimeoutException): - self.marionette.execute_async_script(""" - var cb = arguments[arguments.length - 1]; - setTimeout(function() { cb() }, 250); - """, script_timeout=100) - - @skip_if_mobile("New windows not supported in Fennec") - def test_invalid_chrome_handle(self): - try: - win = self.open_window() - self.marionette.switch_to_window(win) - - # Close new window and don't switch back to the original one - self.marionette.close_chrome_window() - self.assertNotEqual(self.start_window, win) - - # Call execute_script on an invalid chrome handle - with self.marionette.using_context('chrome'): - self.marionette.execute_script(""" - return true; - """) - - finally: - self.close_all_windows() - - def test_lasting_side_effects(self): - pass - - def test_return_web_element(self): - pass - - def test_return_web_element_array(self): - pass - - def test_return_web_element_nodelist(self): - pass - - def test_window_set_timeout_is_not_cancelled(self): - pass - - def test_privileged_code_inspection(self): - pass - - -class TestElementCollections(MarionetteTestCase): - - def assertSequenceIsInstance(self, seq, typ): - for item in seq: - self.assertIsInstance(item, typ) - - def test_array(self): - self.marionette.navigate(inline("<p>foo <p>bar")) - els = self.marionette.execute_script("return Array.from(document.querySelectorAll('p'))") - self.assertIsInstance(els, list) - self.assertEqual(2, len(els)) - self.assertSequenceIsInstance(els, HTMLElement) - - def test_html_all_collection(self): - self.marionette.navigate(inline("<p>foo <p>bar")) - els = self.marionette.execute_script("return document.all") - self.assertIsInstance(els, list) - # <html>, <head>, <body>, <p>, <p> - self.assertEqual(5, len(els)) - self.assertSequenceIsInstance(els, HTMLElement) - - def test_html_collection(self): - self.marionette.navigate(inline("<p>foo <p>bar")) - els = self.marionette.execute_script("return document.getElementsByTagName('p')") - self.assertIsInstance(els, list) - self.assertEqual(2, len(els)) - self.assertSequenceIsInstance(els, HTMLElement) - - def test_html_form_controls_collection(self): - self.marionette.navigate(inline("<form><input><input></form>")) - els = self.marionette.execute_script("return document.forms[0].elements") - self.assertIsInstance(els, list) - self.assertEqual(2, len(els)) - self.assertSequenceIsInstance(els, HTMLElement) - - def test_html_options_collection(self): - self.marionette.navigate(inline("<select><option><option></select>")) - els = self.marionette.execute_script("return document.querySelector('select').options") - self.assertIsInstance(els, list) - self.assertEqual(2, len(els)) - self.assertSequenceIsInstance(els, HTMLElement) - - def test_node_list(self): - self.marionette.navigate(inline("<p>foo <p>bar")) - els = self.marionette.execute_script("return document.querySelectorAll('p')") - self.assertIsInstance(els, list) - self.assertEqual(2, len(els)) - self.assertSequenceIsInstance(els, HTMLElement) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_expected.py b/testing/marionette/harness/marionette_harness/tests/unit/test_expected.py deleted file mode 100644 index ff8717c69..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_expected.py +++ /dev/null @@ -1,228 +0,0 @@ -# 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/. - -import urllib - -from marionette_driver import expected -from marionette_driver.by import By - -from marionette_harness import marionette_test - - -def inline(doc): - return "data:text/html;charset=utf-8,{}".format(urllib.quote(doc)) - -static_element = inline("""<p>foo</p>""") -static_elements = static_element + static_element - -remove_element_by_tag_name = \ - """var el = document.getElementsByTagName('{}')[0]; - document.getElementsByTagName("body")[0].remove(el);""" - -hidden_element = inline("<p style='display: none'>hidden</p>") - -selected_element = inline("<option selected>selected</option>") -unselected_element = inline("<option>unselected</option>") - -enabled_element = inline("<input>") -disabled_element = inline("<input disabled>") - -def no_such_element(marionette): - return marionette.find_element(By.ID, "nosuchelement") - -def no_such_elements(marionette): - return marionette.find_elements(By.ID, "nosuchelement") - -def p(marionette): - return marionette.find_element(By.TAG_NAME, "p") - -def ps(marionette): - return marionette.find_elements(By.TAG_NAME, "p") - -class TestExpected(marionette_test.MarionetteTestCase): - def test_element_present_func(self): - self.marionette.navigate(static_element) - el = expected.element_present(p)(self.marionette) - self.assertIsNotNone(el) - - def test_element_present_locator(self): - self.marionette.navigate(static_element) - el = expected.element_present(By.TAG_NAME, "p")(self.marionette) - self.assertIsNotNone(el) - - def test_element_present_not_present(self): - r = expected.element_present(no_such_element)(self.marionette) - self.assertIsInstance(r, bool) - self.assertFalse(r) - - def test_element_not_present_func(self): - r = expected.element_not_present(no_such_element)(self.marionette) - self.assertIsInstance(r, bool) - self.assertTrue(r) - - def test_element_not_present_locator(self): - r = expected.element_not_present(By.ID, "nosuchelement")(self.marionette) - self.assertIsInstance(r, bool) - self.assertTrue(r) - - def test_element_not_present_is_present(self): - self.marionette.navigate(static_element) - r = expected.element_not_present(p)(self.marionette) - self.assertIsInstance(r, bool) - self.assertFalse(r) - - def test_element_stale(self): - self.marionette.navigate(static_element) - el = self.marionette.find_element(By.TAG_NAME, "p") - self.assertIsNotNone(el) - self.marionette.execute_script(remove_element_by_tag_name.format("p")) - r = expected.element_stale(el)(self.marionette) - self.assertTrue(r) - - def test_element_stale_is_not_stale(self): - self.marionette.navigate(static_element) - el = self.marionette.find_element(By.TAG_NAME, "p") - r = expected.element_stale(el)(self.marionette) - self.assertFalse(r) - - def test_elements_present_func(self): - self.marionette.navigate(static_elements) - els = expected.elements_present(ps)(self.marionette) - self.assertEqual(len(els), 2) - - def test_elements_present_locator(self): - self.marionette.navigate(static_elements) - els = expected.elements_present(By.TAG_NAME, "p")(self.marionette) - self.assertEqual(len(els), 2) - - def test_elements_present_not_present(self): - r = expected.elements_present(no_such_elements)(self.marionette) - self.assertEqual(r, []) - - def test_elements_not_present_func(self): - r = expected.element_not_present(no_such_elements)(self.marionette) - self.assertIsInstance(r, bool) - self.assertTrue(r) - - def test_elements_not_present_locator(self): - r = expected.element_not_present(By.ID, "nosuchelement")(self.marionette) - self.assertIsInstance(r, bool) - self.assertTrue(r) - - def test_elements_not_present_is_present(self): - self.marionette.navigate(static_elements) - r = expected.elements_not_present(ps)(self.marionette) - self.assertIsInstance(r, bool) - self.assertFalse(r) - - def test_element_displayed(self): - self.marionette.navigate(static_element) - el = self.marionette.find_element(By.TAG_NAME, "p") - visible = expected.element_displayed(el)(self.marionette) - self.assertTrue(visible) - - def test_element_displayed_locator(self): - self.marionette.navigate(static_element) - visible = expected.element_displayed(By.TAG_NAME, "p")(self.marionette) - self.assertTrue(visible) - - def test_element_displayed_when_hidden(self): - self.marionette.navigate(hidden_element) - el = self.marionette.find_element(By.TAG_NAME, "p") - visible = expected.element_displayed(el)(self.marionette) - self.assertFalse(visible) - - def test_element_displayed_when_hidden_locator(self): - self.marionette.navigate(hidden_element) - visible = expected.element_displayed(By.TAG_NAME, "p")(self.marionette) - self.assertFalse(visible) - - def test_element_displayed_when_not_present(self): - self.marionette.navigate("about:blank") - visible = expected.element_displayed(By.TAG_NAME, "p")(self.marionette) - self.assertFalse(visible) - - def test_element_displayed_when_stale_element(self): - self.marionette.navigate(static_element) - el = self.marionette.find_element(By.TAG_NAME, "p") - self.marionette.navigate("about:blank") - missing = expected.element_displayed(el)(self.marionette) - self.assertFalse(missing) - - def test_element_not_displayed(self): - self.marionette.navigate(hidden_element) - el = self.marionette.find_element(By.TAG_NAME, "p") - hidden = expected.element_not_displayed(el)(self.marionette) - self.assertTrue(hidden) - - def test_element_not_displayed_locator(self): - self.marionette.navigate(hidden_element) - hidden = expected.element_not_displayed(By.TAG_NAME, "p")(self.marionette) - self.assertTrue(hidden) - - def test_element_not_displayed_when_visible(self): - self.marionette.navigate(static_element) - el = self.marionette.find_element(By.TAG_NAME, "p") - hidden = expected.element_not_displayed(el)(self.marionette) - self.assertFalse(hidden) - - def test_element_not_displayed_when_visible_locator(self): - self.marionette.navigate(static_element) - hidden = expected.element_not_displayed(By.TAG_NAME, "p")(self.marionette) - self.assertFalse(hidden) - - def test_element_not_displayed_when_stale_element(self): - self.marionette.navigate(static_element) - el = self.marionette.find_element(By.TAG_NAME, "p") - self.marionette.navigate("about:blank") - missing = expected.element_not_displayed(el)(self.marionette) - self.assertTrue(missing) - - def test_element_selected(self): - self.marionette.navigate(selected_element) - el = self.marionette.find_element(By.TAG_NAME, "option") - selected = expected.element_selected(el)(self.marionette) - self.assertTrue(selected) - - def test_element_selected_when_not_selected(self): - self.marionette.navigate(unselected_element) - el = self.marionette.find_element(By.TAG_NAME, "option") - unselected = expected.element_selected(el)(self.marionette) - self.assertFalse(unselected) - - def test_element_not_selected(self): - self.marionette.navigate(unselected_element) - el = self.marionette.find_element(By.TAG_NAME, "option") - unselected = expected.element_not_selected(el)(self.marionette) - self.assertTrue(unselected) - - def test_element_not_selected_when_selected(self): - self.marionette.navigate(selected_element) - el = self.marionette.find_element(By.TAG_NAME, "option") - selected = expected.element_not_selected(el)(self.marionette) - self.assertFalse(selected) - - def test_element_enabled(self): - self.marionette.navigate(enabled_element) - el = self.marionette.find_element(By.TAG_NAME, "input") - enabled = expected.element_enabled(el)(self.marionette) - self.assertTrue(enabled) - - def test_element_enabled_when_disabled(self): - self.marionette.navigate(disabled_element) - el = self.marionette.find_element(By.TAG_NAME, "input") - disabled = expected.element_enabled(el)(self.marionette) - self.assertFalse(disabled) - - def test_element_not_enabled(self): - self.marionette.navigate(disabled_element) - el = self.marionette.find_element(By.TAG_NAME, "input") - disabled = expected.element_not_enabled(el)(self.marionette) - self.assertTrue(disabled) - - def test_element_not_enabled_when_enabled(self): - self.marionette.navigate(enabled_element) - el = self.marionette.find_element(By.TAG_NAME, "input") - enabled = expected.element_not_enabled(el)(self.marionette) - self.assertFalse(enabled) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_expectedfail.py b/testing/marionette/harness/marionette_harness/tests/unit/test_expectedfail.py deleted file mode 100644 index 138a36c58..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_expectedfail.py +++ /dev/null @@ -1,11 +0,0 @@ -# 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_harness import MarionetteTestCase - - -class TestFail(MarionetteTestCase): - def test_fails(self): - # this test is supposed to fail! - self.assertEquals(True, False) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_file_upload.py b/testing/marionette/harness/marionette_harness/tests/unit/test_file_upload.py deleted file mode 100644 index f67be9556..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_file_upload.py +++ /dev/null @@ -1,152 +0,0 @@ -# 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/. - -import contextlib -import urllib - -from tempfile import NamedTemporaryFile as tempfile - -from marionette_driver import By, errors, expected -from marionette_driver.wait import Wait -from marionette_harness import MarionetteTestCase, skip - - -single = "data:text/html,{}".format(urllib.quote("<input type=file>")) -multiple = "data:text/html,{}".format(urllib.quote("<input type=file multiple>")) -upload = lambda url: "data:text/html,{}".format(urllib.quote(""" - <form action='{}' method=post enctype='multipart/form-data'> - <input type=file> - <input type=submit> - </form>""".format(url))) - - -class TestFileUpload(MarionetteTestCase): - def test_sets_one_file(self): - self.marionette.navigate(single) - input = self.input - - exp = None - with tempfile() as f: - input.send_keys(f.name) - exp = [f.name] - - files = self.get_file_names(input) - self.assertEqual(len(files), 1) - self.assertFileNamesEqual(files, exp) - - def test_sets_multiple_files(self): - self.marionette.navigate(multiple) - input = self.input - - exp = None - with contextlib.nested(tempfile(), tempfile()) as (a, b): - input.send_keys(a.name) - input.send_keys(b.name) - exp = [a.name, b.name] - - files = self.get_file_names(input) - self.assertEqual(len(files), 2) - self.assertFileNamesEqual(files, exp) - - def test_sets_multiple_indentical_files(self): - self.marionette.navigate(multiple) - input = self.input - - exp = [] - with tempfile() as f: - input.send_keys(f.name) - input.send_keys(f.name) - exp = f.name - - files = self.get_file_names(input) - self.assertEqual(len(files), 2) - self.assertFileNamesEqual(files, exp) - - def test_clear_file(self): - self.marionette.navigate(single) - input = self.input - - with tempfile() as f: - input.send_keys(f.name) - - self.assertEqual(len(self.get_files(input)), 1) - input.clear() - self.assertEqual(len(self.get_files(input)), 0) - - def test_clear_files(self): - self.marionette.navigate(multiple) - input = self.input - - with contextlib.nested(tempfile(), tempfile()) as (a, b): - input.send_keys(a.name) - input.send_keys(b.name) - - self.assertEqual(len(self.get_files(input)), 2) - input.clear() - self.assertEqual(len(self.get_files(input)), 0) - - def test_illegal_file(self): - self.marionette.navigate(single) - with self.assertRaisesRegexp(errors.MarionetteException, "File not found"): - self.input.send_keys("rochefort") - - def test_upload(self): - self.marionette.navigate( - upload(self.marionette.absolute_url("file_upload"))) - url = self.marionette.get_url() - - with tempfile() as f: - f.write("camembert") - f.flush() - self.input.send_keys(f.name) - self.submit.click() - - Wait(self.marionette).until(lambda m: m.get_url() != url) - self.assertIn("multipart/form-data", self.body.text) - - def test_change_event(self): - self.marionette.navigate(single) - self.marionette.execute_script(""" - window.changeEvs = []; - let el = arguments[arguments.length - 1]; - el.addEventListener("change", ev => window.changeEvs.push(ev)); - console.log(window.changeEvs.length); - """, script_args=(self.input,), sandbox=None) - - with tempfile() as f: - self.input.send_keys(f.name) - - nevs = self.marionette.execute_script( - "return window.changeEvs.length", sandbox=None) - self.assertEqual(1, nevs) - - def find_inputs(self): - return self.marionette.find_elements(By.TAG_NAME, "input") - - @property - def input(self): - return self.find_inputs()[0] - - @property - def submit(self): - return self.find_inputs()[1] - - @property - def body(self): - return Wait(self.marionette).until( - expected.element_present(By.TAG_NAME, "body")) - - def get_file_names(self, el): - fl = self.get_files(el) - return [f["name"] for f in fl] - - def get_files(self, el): - return self.marionette.execute_script( - "return arguments[0].files", script_args=[el]) - - def assertFileNamesEqual(self, act, exp): - # File array returned from browser doesn't contain full path names, - # this cuts off the path of the expected files. - filenames = [f.rsplit("/", 0)[-1] for f in act] - self.assertListEqual(filenames, act) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_findelement_chrome.py b/testing/marionette/harness/marionette_harness/tests/unit/test_findelement_chrome.py deleted file mode 100644 index e6b2d63bf..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_findelement_chrome.py +++ /dev/null @@ -1,82 +0,0 @@ -# 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.by import By -from marionette_driver.errors import NoSuchElementException -from marionette_driver.marionette import HTMLElement - -from marionette_harness import MarionetteTestCase - - -class TestElementsChrome(MarionetteTestCase): - def setUp(self): - MarionetteTestCase.setUp(self) - self.marionette.set_context("chrome") - self.win = self.marionette.current_window_handle - self.marionette.execute_script("window.open('chrome://marionette/content/test.xul', 'foo', 'chrome,centerscreen');") - self.marionette.switch_to_window('foo') - self.assertNotEqual(self.win, self.marionette.current_window_handle) - - def tearDown(self): - self.assertNotEqual(self.win, self.marionette.current_window_handle) - self.marionette.execute_script("window.close();") - self.marionette.switch_to_window(self.win) - MarionetteTestCase.tearDown(self) - - def test_id(self): - el = self.marionette.execute_script("return window.document.getElementById('textInput');") - found_el = self.marionette.find_element(By.ID, "textInput") - self.assertEqual(HTMLElement, type(found_el)) - self.assertEqual(el, found_el) - - def test_that_we_can_find_elements_from_css_selectors(self): - el = self.marionette.execute_script("return window.document.getElementById('textInput');") - found_el = self.marionette.find_element(By.CSS_SELECTOR, "#textInput") - self.assertEqual(HTMLElement, type(found_el)) - self.assertEqual(el, found_el) - - def test_child_element(self): - el = self.marionette.find_element(By.ID, "textInput") - parent = self.marionette.find_element(By.ID, "things") - found_el = parent.find_element(By.TAG_NAME, "textbox") - self.assertEqual(HTMLElement, type(found_el)) - self.assertEqual(el, found_el) - - def test_child_elements(self): - el = self.marionette.find_element(By.ID, "textInput3") - parent = self.marionette.find_element(By.ID, "things") - found_els = parent.find_elements(By.TAG_NAME, "textbox") - self.assertTrue(el.id in [found_el.id for found_el in found_els]) - - def test_tag_name(self): - el = self.marionette.execute_script("return window.document.getElementsByTagName('vbox')[0];") - found_el = self.marionette.find_element(By.TAG_NAME, "vbox") - self.assertEquals('vbox', found_el.tag_name) - self.assertEqual(HTMLElement, type(found_el)) - self.assertEqual(el, found_el) - - def test_class_name(self): - el = self.marionette.execute_script("return window.document.getElementsByClassName('asdf')[0];") - found_el = self.marionette.find_element(By.CLASS_NAME, "asdf") - self.assertEqual(HTMLElement, type(found_el)) - self.assertEqual(el, found_el) - - def test_xpath(self): - el = self.marionette.execute_script("return window.document.getElementById('testBox');") - found_el = self.marionette.find_element(By.XPATH, "id('testBox')") - self.assertEqual(HTMLElement, type(found_el)) - self.assertEqual(el, found_el) - - def test_not_found(self): - self.marionette.timeout.implicit = 1 - self.assertRaises(NoSuchElementException, self.marionette.find_element, By.ID, "I'm not on the page") - self.marionette.timeout.implicit = 0 - self.assertRaises(NoSuchElementException, self.marionette.find_element, By.ID, "I'm not on the page") - - def test_timeout(self): - self.assertRaises(NoSuchElementException, self.marionette.find_element, By.ID, "myid") - self.marionette.timeout.implicit = 4 - self.marionette.execute_script("window.setTimeout(function() {var b = window.document.createElement('button'); b.id = 'myid'; document.getElementById('things').appendChild(b);}, 1000)") - self.assertEqual(HTMLElement, type(self.marionette.find_element(By.ID, "myid"))) - self.marionette.execute_script("window.document.getElementById('things').removeChild(window.document.getElementById('myid'));") diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_geckoinstance.py b/testing/marionette/harness/marionette_harness/tests/unit/test_geckoinstance.py deleted file mode 100644 index 540550296..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_geckoinstance.py +++ /dev/null @@ -1,25 +0,0 @@ -# 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.geckoinstance import apps, GeckoInstance - -from marionette_harness import MarionetteTestCase - - -class TestGeckoInstance(MarionetteTestCase): - - def test_create(self): - """Test that the correct gecko instance is determined.""" - for app in apps: - # If app has been specified we directly return the appropriate instance class - self.assertEqual(type(GeckoInstance.create(app=app, bin="n/a")), - apps[app]) - - # Unknown applications and binaries should fail - self.assertRaises(NotImplementedError, GeckoInstance.create, - app="n/a", bin=self.marionette.bin) - self.assertRaises(NotImplementedError, GeckoInstance.create, - bin="n/a") - self.assertRaises(NotImplementedError, GeckoInstance.create, - bin=None) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_getactiveframe_oop.py b/testing/marionette/harness/marionette_harness/tests/unit/test_getactiveframe_oop.py deleted file mode 100644 index 9325d4892..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_getactiveframe_oop.py +++ /dev/null @@ -1,93 +0,0 @@ -# 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.by import By - -from marionette_harness import MarionetteTestCase - - -OOP_BY_DEFAULT = "dom.ipc.browser_frames.oop_by_default" -BROWSER_FRAMES_ENABLED = "dom.mozBrowserFramesEnabeld" - - -class TestGetActiveFrameOOP(MarionetteTestCase): - def setUp(self): - super(TestGetActiveFrameOOP, self).setUp() - with self.marionette.using_context("chrome"): - self.oop_by_default = self.marionette.get_pref(OOP_BY_DEFAULT) - self.mozBrowserFramesEnabled = self.marionette.get_pref(BROWSER_FRAMES_ENABLED) - self.marionette.set_pref(OOP_BY_DEFAULT, True) - self.marionette.set_pref(BROWSER_FRAMES_ENABLED, True) - - def tearDown(self): - with self.marionette.using_context("chrome"): - if self.oop_by_default is None: - self.marionette.clear_pref(OOP_BY_DEFAULT) - else: - self.marionette.set_pref(OOP_BY_DEFAULT, self.oop_by_default) - - if self.mozBrowserFramesEnabled is None: - self.marionette.clear_pref(BROWSER_FRAMES_ENABLED) - else: - self.marionette.set_pref(BROWSER_FRAMES_ENABLED, self.mozBrowserFramesEnabled) - - def test_active_frame_oop(self): - self.marionette.navigate(self.marionette.absolute_url("test.html")) - self.marionette.push_permission('browser', True) - - # Create first OOP frame - self.marionette.execute_script(""" - let iframe1 = document.createElement("iframe"); - iframe1.id = "remote_iframe1"; - iframe1.setAttribute('remote', true); - iframe1.setAttribute('mozbrowser', true); - iframe1.style.height = "100px"; - iframe1.style.width = "100%%"; - iframe1.src = "{}"; - document.body.appendChild(iframe1); - """.format(self.marionette.absolute_url("test_oop_1.html"))) - - # Currently no active frame - self.assertEqual(self.marionette.get_active_frame(), None) - self.assertTrue("test.html" in self.marionette.get_url()) - - # Switch to iframe1, get active frame - frame = self.marionette.find_element(By.ID, 'remote_iframe1') - self.marionette.switch_to_frame(frame) - active_frame1 = self.marionette.get_active_frame() - self.assertNotEqual(active_frame1.id, None) - - # Switch to top-level then back to active frame, verify correct frame - self.marionette.switch_to_frame() - self.marionette.switch_to_frame(active_frame1) - self.assertTrue("test_oop_1.html" in self.marionette.execute_script("return document.wrappedJSObject.location.href")) - - # Create another OOP frame - self.marionette.switch_to_frame() - self.marionette.execute_script(""" - let iframe2 = document.createElement("iframe"); - iframe2.setAttribute('mozbrowser', true); - iframe2.setAttribute('remote', true); - iframe2.id = "remote_iframe2"; - iframe2.style.height = "100px"; - iframe2.style.width = "100%%"; - iframe2.src = "{}"; - document.body.appendChild(iframe2); - """.format(self.marionette.absolute_url("test_oop_2.html"))) - - # Switch to iframe2, get active frame - frame2 = self.marionette.find_element(By.ID, 'remote_iframe2') - self.marionette.switch_to_frame(frame2) - active_frame2 = self.marionette.get_active_frame() - self.assertNotEqual(active_frame2.id, None) - - # Switch to top-level then back to active frame 1, verify correct frame - self.marionette.switch_to_frame() - self.marionette.switch_to_frame(active_frame1) - self.assertTrue("test_oop_1.html" in self.marionette.execute_script("return document.wrappedJSObject.location.href")) - - # Switch to top-level then back to active frame 2, verify correct frame - self.marionette.switch_to_frame() - self.marionette.switch_to_frame(active_frame2) - self.assertTrue("test_oop_2.html" in self.marionette.execute_script("return document.wrappedJSObject.location.href")) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_implicit_waits.py b/testing/marionette/harness/marionette_harness/tests/unit/test_implicit_waits.py deleted file mode 100644 index 954443ac3..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_implicit_waits.py +++ /dev/null @@ -1,26 +0,0 @@ -# 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.by import By -from marionette_driver.errors import NoSuchElementException - -from marionette_harness import MarionetteTestCase - - -class TestImplicitWaits(MarionetteTestCase): - def test_implicitly_wait_for_single_element(self): - test_html = self.marionette.absolute_url("test_dynamic.html") - self.marionette.navigate(test_html) - add = self.marionette.find_element(By.ID, "adder") - self.marionette.timeout.implicit = 30 - add.click() - # all is well if this does not throw - self.marionette.find_element(By.ID, "box0") - - def test_implicit_wait_reaches_timeout(self): - test_html = self.marionette.absolute_url("test_dynamic.html") - self.marionette.navigate(test_html) - self.marionette.timeout.implicit = 3 - with self.assertRaises(NoSuchElementException): - self.marionette.find_element(By.ID, "box0") diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_import_script.py b/testing/marionette/harness/marionette_harness/tests/unit/test_import_script.py deleted file mode 100644 index e86de2bd5..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_import_script.py +++ /dev/null @@ -1,138 +0,0 @@ -# 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/. - -import os - -from marionette_driver.by import By -from marionette_driver.errors import JavascriptException - -from marionette_harness import ( - MarionetteTestCase, - skip_if_chrome, - skip_if_mobile, - WindowManagerMixin, -) - - -class TestImportScriptContent(WindowManagerMixin, MarionetteTestCase): - contexts = set(["chrome", "content"]) - - script_file = os.path.abspath( - os.path.join(__file__, os.path.pardir, "importscript.js")) - another_script_file = os.path.abspath( - os.path.join(__file__, os.path.pardir, "importanotherscript.js")) - - def setUp(self): - super(TestImportScriptContent, self).setUp() - - for context in self.contexts: - with self.marionette.using_context(context): - self.marionette.clear_imported_scripts() - self.reset_context() - - def tearDown(self): - self.close_all_windows() - - super(TestImportScriptContent, self).tearDown() - - def reset_context(self): - self.marionette.set_context("content") - - @property - def current_context(self): - return self.marionette._send_message("getContext", key="value") - - @property - def other_context(self): - return self.contexts.copy().difference([self.current_context]).pop() - - def is_defined(self, symbol): - return self.marionette.execute_script( - "return typeof {} != 'undefined'".format(symbol)) - - def assert_defined(self, symbol, msg=None): - if msg is None: - msg = "Expected symbol {} to be defined".format(symbol) - self.assertTrue(self.is_defined(symbol), msg) - - def assert_undefined(self, symbol, msg=None): - if msg is None: - msg = "Expected symbol {} to be undefined".format(symbol) - self.assertFalse(self.is_defined(symbol), msg) - - def assert_scripts_cleared(self): - self.marionette.import_script(self.script_file) - self.assert_defined("testFunc") - self.marionette.clear_imported_scripts() - self.assert_undefined("testFunc") - - def test_import_script(self): - self.marionette.import_script(self.script_file) - self.assertEqual( - "i'm a test function!", self.marionette.execute_script("return testFunc();")) - self.assertEqual("i'm a test function!", self.marionette.execute_async_script( - "marionetteScriptFinished(testFunc());")) - - def test_import_script_twice(self): - self.marionette.import_script(self.script_file) - self.assert_defined("testFunc") - - # TODO(ato): Note that the WebDriver command primitives - # does not allow us to check what scripts have been imported. - # I suspect we must to do this through an xpcshell test. - - self.marionette.import_script(self.script_file) - self.assert_defined("testFunc") - - def test_import_script_and_clear(self): - self.marionette.import_script(self.script_file) - self.assert_defined("testFunc") - self.marionette.clear_imported_scripts() - self.assert_scripts_cleared() - self.assert_undefined("testFunc") - with self.assertRaises(JavascriptException): - self.marionette.execute_script("return testFunc()") - with self.assertRaises(JavascriptException): - self.marionette.execute_async_script( - "marionetteScriptFinished(testFunc())") - - def test_clear_scripts_in_other_context(self): - self.marionette.import_script(self.script_file) - self.assert_defined("testFunc") - - # clearing other context's script file should not affect ours - with self.marionette.using_context(self.other_context): - self.marionette.clear_imported_scripts() - self.assert_undefined("testFunc") - - self.assert_defined("testFunc") - - def test_multiple_imports(self): - self.marionette.import_script(self.script_file) - self.marionette.import_script(self.another_script_file) - self.assert_defined("testFunc") - self.assert_defined("testAnotherFunc") - - @skip_if_chrome("Needs content scope") - @skip_if_mobile("New windows not supported in Fennec") - def test_imports_apply_globally(self): - self.marionette.navigate( - self.marionette.absolute_url("test_windows.html")) - - def open_window_with_link(): - self.marionette.find_element(By.LINK_TEXT, "Open new window").click() - - new_window = self.open_window(trigger=open_window_with_link) - self.marionette.switch_to_window(new_window) - - self.marionette.import_script(self.script_file) - self.marionette.close_chrome_window() - - self.marionette.switch_to_window(self.start_window) - self.assert_defined("testFunc") - - -class TestImportScriptChrome(TestImportScriptContent): - def reset_context(self): - self.marionette.set_context("chrome") diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_key_actions.py b/testing/marionette/harness/marionette_harness/tests/unit/test_key_actions.py deleted file mode 100644 index 60e38b3c6..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_key_actions.py +++ /dev/null @@ -1,91 +0,0 @@ -# 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.by import By -from marionette_driver.keys import Keys -from marionette_driver.marionette import Actions - -from marionette_harness import MarionetteTestCase, skip_if_mobile, WindowManagerMixin - - -class TestKeyActions(WindowManagerMixin, MarionetteTestCase): - - def setUp(self): - super(TestKeyActions, self).setUp() - if self.marionette.session_capabilities["platformName"] == "darwin": - self.mod_key = Keys.META - else: - self.mod_key = Keys.CONTROL - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - self.reporter_element = self.marionette.find_element(By.ID, "keyReporter") - self.reporter_element.click() - self.key_action = Actions(self.marionette) - - @property - def key_reporter_value(self): - return self.reporter_element.get_property("value") - - def test_key_action_basic_input(self): - self.key_action.key_down("a").key_down("b").key_down("c").perform() - self.assertEqual(self.key_reporter_value, "abc") - - def test_upcase_input(self): - (self.key_action.key_down(Keys.SHIFT) - .key_down("a") - .key_up(Keys.SHIFT) - .key_down("b") - .key_down("c") - .perform()) - self.assertEqual(self.key_reporter_value, "Abc") - - def test_replace_input(self): - self.key_action.key_down("a").key_down("b").key_down("c").perform() - self.assertEqual(self.key_reporter_value, "abc") - (self.key_action.key_down(self.mod_key) - .key_down("a") - .key_up(self.mod_key) - .key_down("x") - .perform()) - self.assertEqual(self.key_reporter_value, "x") - - def test_clear_input(self): - self.key_action.key_down("a").key_down("b").key_down("c").perform() - self.assertEqual(self.key_reporter_value, "abc") - (self.key_action.key_down(self.mod_key) - .key_down("a") - .key_down("x") - .perform()) - self.assertEqual(self.key_reporter_value, "") - self.key_action.key_down("a").key_down("b").key_down("c").perform() - self.assertEqual(self.key_reporter_value, "abc") - - def test_input_with_wait(self): - self.key_action.key_down("a").key_down("b").key_down("c").perform() - (self.key_action.key_down(self.mod_key) - .key_down("a") - .wait(.5) - .key_down("x") - .perform()) - self.assertEqual(self.key_reporter_value, "") - - @skip_if_mobile("Interacting with chrome windows not available for Fennec") - def test_open_in_new_window_shortcut(self): - - def open_window_with_action(): - el = self.marionette.find_element(By.ID, "updatediv") - # Ensure that the element is in the current view port because press() doesn't - # handle that inside the action chain (bug 1295538). - self.marionette.execute_script('arguments[0].scrollIntoView()', script_args=[el]) - (self.key_action.key_down(Keys.SHIFT) - .press(el) - .release() - .key_up(Keys.SHIFT) - .perform()) - - new_window = self.open_window(trigger=open_window_with_action) - self.marionette.switch_to_window(new_window) - self.marionette.close_chrome_window() - self.marionette.switch_to_window(self.start_window) - self.assertEqual(self.key_reporter_value, "") diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_localization.py b/testing/marionette/harness/marionette_harness/tests/unit/test_localization.py deleted file mode 100644 index 1b89f6f34..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_localization.py +++ /dev/null @@ -1,56 +0,0 @@ -# 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 -from marionette_driver.errors import ( - InvalidArgumentException, - NoSuchElementException, - UnknownException -) -from marionette_driver.localization import L10n - -from marionette_harness import MarionetteTestCase - - -class TestL10n(MarionetteTestCase): - - def setUp(self): - super(TestL10n, self).setUp() - - self.l10n = L10n(self.marionette) - - def test_localize_entity(self): - dtds = ['chrome://marionette/content/test_dialog.dtd'] - value = self.l10n.localize_entity(dtds, 'testDialog.title') - - self.assertEqual(value, 'Test Dialog') - - def test_localize_entity_invalid_arguments(self): - dtds = ['chrome://marionette/content/test_dialog.dtd'] - - self.assertRaises(NoSuchElementException, - self.l10n.localize_entity, dtds, 'notExistent') - self.assertRaises(InvalidArgumentException, - self.l10n.localize_entity, dtds[0], 'notExistent') - self.assertRaises(InvalidArgumentException, - self.l10n.localize_entity, dtds, True) - - def test_localize_property(self): - properties = ['chrome://marionette/content/test_dialog.properties'] - - value = self.l10n.localize_property(properties, 'testDialog.title') - self.assertEqual(value, 'Test Dialog') - - self.assertRaises(NoSuchElementException, - self.l10n.localize_property, properties, 'notExistent') - - def test_localize_property_invalid_arguments(self): - properties = ['chrome://global/locale/filepicker.properties'] - - self.assertRaises(NoSuchElementException, - self.l10n.localize_property, properties, 'notExistent') - self.assertRaises(InvalidArgumentException, - self.l10n.localize_property, properties[0], 'notExistent') - self.assertRaises(InvalidArgumentException, - self.l10n.localize_property, properties, True) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_log.py b/testing/marionette/harness/marionette_harness/tests/unit/test_log.py deleted file mode 100644 index dd3cf82b4..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_log.py +++ /dev/null @@ -1,64 +0,0 @@ -# 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_harness import MarionetteTestCase - - -class TestLog(MarionetteTestCase): - def setUp(self): - MarionetteTestCase.setUp(self) - # clears log cache - self.marionette.get_logs() - - def test_log(self): - self.marionette.log("foo") - logs = self.marionette.get_logs() - self.assertEqual("INFO", logs[0][0]) - self.assertEqual("foo", logs[0][1]) - - def test_log_level(self): - self.marionette.log("foo", "ERROR") - logs = self.marionette.get_logs() - self.assertEqual("ERROR", logs[0][0]) - self.assertEqual("foo", logs[0][1]) - - def test_clear(self): - self.marionette.log("foo") - self.assertEqual(1, len(self.marionette.get_logs())) - self.assertEqual(0, len(self.marionette.get_logs())) - - def test_multiple_entries(self): - self.marionette.log("foo") - self.marionette.log("bar") - self.assertEqual(2, len(self.marionette.get_logs())) - - def test_log_from_sync_script(self): - self.marionette.execute_script("log('foo')") - logs = self.marionette.get_logs() - self.assertEqual("INFO", logs[0][0]) - self.assertEqual("foo", logs[0][1]) - - def test_log_from_sync_script_level(self): - self.marionette.execute_script("log('foo', 'ERROR')") - logs = self.marionette.get_logs() - self.assertEqual("ERROR", logs[0][0]) - self.assertEqual("foo", logs[0][1]) - - def test_log_from_async_script(self): - self.marionette.execute_async_script("log('foo'); arguments[0]();") - logs = self.marionette.get_logs() - self.assertEqual("INFO", logs[0][0]) - self.assertEqual("foo", logs[0][1]) - - def test_log_from_async_script_variable_arguments(self): - self.marionette.execute_async_script("log('foo', 'ERROR'); arguments[0]();") - logs = self.marionette.get_logs() - self.assertEqual("ERROR", logs[0][0]) - self.assertEqual("foo", logs[0][1]) - - -class TestLogChrome(TestLog): - def setUp(self): - TestLog.setUp(self) - self.marionette.set_context("chrome") diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_marionette.py b/testing/marionette/harness/marionette_harness/tests/unit/test_marionette.py deleted file mode 100644 index e68312872..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_marionette.py +++ /dev/null @@ -1,67 +0,0 @@ -# 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/. - -import itertools -import time - -from marionette_driver import errors - -from marionette_harness import MarionetteTestCase, run_if_manage_instance, skip_if_mobile - - -class TestMarionette(MarionetteTestCase): - - def test_correct_test_name(self): - """Test that the correct test name gets set.""" - expected_test_name = '{module}.py {cls}.{func}'.format( - module=__name__, - cls=self.__class__.__name__, - func=self.test_correct_test_name.__name__, - ) - - self.assertEqual(self.marionette.test_name, expected_test_name) - - @run_if_manage_instance("Only runnable if Marionette manages the instance") - @skip_if_mobile("Bug 1322993 - Missing temporary folder") - def test_wait_for_port_non_existing_process(self): - """Test that wait_for_port doesn't run into a timeout if instance is not running.""" - self.marionette.quit() - self.assertIsNotNone(self.marionette.instance.runner.returncode) - start_time = time.time() - self.assertFalse(self.marionette.wait_for_port(timeout=5)) - self.assertLess(time.time() - start_time, 5) - - -class TestProtocol2Errors(MarionetteTestCase): - def setUp(self): - MarionetteTestCase.setUp(self) - self.op = self.marionette.protocol - self.marionette.protocol = 2 - - def tearDown(self): - self.marionette.protocol = self.op - MarionetteTestCase.tearDown(self) - - def test_malformed_packet(self): - req = ["error", "message", "stacktrace"] - ps = [] - for p in [p for i in range(0, len(req) + 1) for p in itertools.permutations(req, i)]: - ps.append(dict((x, None) for x in p)) - - for p in filter(lambda p: len(p) < 3, ps): - self.assertRaises(KeyError, self.marionette._handle_error, p) - - def test_known_error_status(self): - with self.assertRaises(errors.NoSuchElementException): - self.marionette._handle_error( - {"error": errors.NoSuchElementException.status, - "message": None, - "stacktrace": None}) - - def test_unknown_error_status(self): - with self.assertRaises(errors.MarionetteException): - self.marionette._handle_error( - {"error": "barbera", - "message": None, - "stacktrace": None}) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_modal_dialogs.py b/testing/marionette/harness/marionette_harness/tests/unit/test_modal_dialogs.py deleted file mode 100644 index f7108bdff..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_modal_dialogs.py +++ /dev/null @@ -1,198 +0,0 @@ -# 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.by import By -from marionette_driver.errors import NoAlertPresentException, ElementNotInteractableException -from marionette_driver.marionette import Alert -from marionette_driver.wait import Wait - -from marionette_harness import MarionetteTestCase, skip_if_e10s - - -class TestTabModals(MarionetteTestCase): - - def setUp(self): - super(TestTabModals, self).setUp() - self.marionette.set_pref("prompts.tab_modal.enabled", True) - self.marionette.navigate(self.marionette.absolute_url('modal_dialogs.html')) - - def tearDown(self): - # Ensure an alert is absent before proceeding past this test. - Wait(self.marionette).until(lambda _: not self.alert_present()) - self.marionette.execute_script("window.onbeforeunload = null;") - self.marionette.clear_pref("prompts.tab_modal.enabled") - super(TestTabModals, self).tearDown() - - def alert_present(self): - try: - Alert(self.marionette).text - return True - except NoAlertPresentException: - return False - - def wait_for_alert(self): - Wait(self.marionette).until(lambda _: self.alert_present()) - - def test_no_alert_raises(self): - self.assertRaises(NoAlertPresentException, Alert(self.marionette).accept) - self.assertRaises(NoAlertPresentException, Alert(self.marionette).dismiss) - - def test_alert_accept(self): - self.marionette.find_element(By.ID, 'modal-alert').click() - self.wait_for_alert() - alert = self.marionette.switch_to_alert() - alert.accept() - - def test_alert_dismiss(self): - self.marionette.find_element(By.ID, 'modal-alert').click() - self.wait_for_alert() - alert = self.marionette.switch_to_alert() - alert.dismiss() - - def test_confirm_accept(self): - self.marionette.find_element(By.ID, 'modal-confirm').click() - self.wait_for_alert() - alert = self.marionette.switch_to_alert() - alert.accept() - self.wait_for_condition(lambda mn: mn.find_element(By.ID, 'confirm-result').text == 'true') - - def test_confirm_dismiss(self): - self.marionette.find_element(By.ID, 'modal-confirm').click() - self.wait_for_alert() - alert = self.marionette.switch_to_alert() - alert.dismiss() - self.wait_for_condition(lambda mn: mn.find_element(By.ID, 'confirm-result').text == 'false') - - def test_prompt_accept(self): - self.marionette.find_element(By.ID, 'modal-prompt').click() - self.wait_for_alert() - alert = self.marionette.switch_to_alert() - alert.accept() - self.wait_for_condition(lambda mn: mn.find_element(By.ID, 'prompt-result').text == '') - - def test_prompt_dismiss(self): - self.marionette.find_element(By.ID, 'modal-prompt').click() - self.wait_for_alert() - alert = self.marionette.switch_to_alert() - alert.dismiss() - self.wait_for_condition(lambda mn: mn.find_element(By.ID, 'prompt-result').text == 'null') - - def test_alert_text(self): - with self.assertRaises(NoAlertPresentException): - alert = self.marionette.switch_to_alert() - alert.text - self.marionette.find_element(By.ID, 'modal-alert').click() - self.wait_for_alert() - alert = self.marionette.switch_to_alert() - self.assertEqual(alert.text, 'Marionette alert') - alert.accept() - - def test_prompt_text(self): - with self.assertRaises(NoAlertPresentException): - alert = self.marionette.switch_to_alert() - alert.text - self.marionette.find_element(By.ID, 'modal-prompt').click() - self.wait_for_alert() - alert = self.marionette.switch_to_alert() - self.assertEqual(alert.text, 'Marionette prompt') - alert.accept() - - def test_confirm_text(self): - with self.assertRaises(NoAlertPresentException): - alert = self.marionette.switch_to_alert() - alert.text - self.marionette.find_element(By.ID, 'modal-confirm').click() - self.wait_for_alert() - alert = self.marionette.switch_to_alert() - self.assertEqual(alert.text, 'Marionette confirm') - alert.accept() - - def test_set_text_throws(self): - self.assertRaises(NoAlertPresentException, Alert(self.marionette).send_keys, "Foo") - self.marionette.find_element(By.ID, 'modal-alert').click() - self.wait_for_alert() - alert = self.marionette.switch_to_alert() - self.assertRaises(ElementNotInteractableException, alert.send_keys, "Foo") - alert.accept() - - def test_set_text_accept(self): - self.marionette.find_element(By.ID, 'modal-prompt').click() - self.wait_for_alert() - alert = self.marionette.switch_to_alert() - alert.send_keys("Some text!"); - alert.accept() - self.wait_for_condition(lambda mn: mn.find_element(By.ID, 'prompt-result').text == 'Some text!') - - def test_set_text_dismiss(self): - self.marionette.find_element(By.ID, 'modal-prompt').click() - self.wait_for_alert() - alert = self.marionette.switch_to_alert() - alert.send_keys("Some text!"); - alert.dismiss() - self.wait_for_condition(lambda mn: mn.find_element(By.ID, 'prompt-result').text == 'null') - - def test_onbeforeunload_dismiss(self): - start_url = self.marionette.get_url() - self.marionette.find_element(By.ID, 'onbeforeunload-handler').click() - self.wait_for_condition( - lambda mn: mn.execute_script(""" - return window.onbeforeunload !== null; - """)) - self.marionette.navigate("about:blank") - self.wait_for_alert() - alert = self.marionette.switch_to_alert() - self.assertTrue(alert.text.startswith("This page is asking you to confirm")) - alert.dismiss() - self.assertTrue(self.marionette.get_url().startswith(start_url)) - - def test_onbeforeunload_accept(self): - self.marionette.find_element(By.ID, 'onbeforeunload-handler').click() - self.wait_for_condition( - lambda mn: mn.execute_script(""" - return window.onbeforeunload !== null; - """)) - self.marionette.navigate("about:blank") - self.wait_for_alert() - alert = self.marionette.switch_to_alert() - self.assertTrue(alert.text.startswith("This page is asking you to confirm")) - alert.accept() - self.wait_for_condition(lambda mn: mn.get_url() == "about:blank") - - @skip_if_e10s("Bug 1325044") - def test_unrelated_command_when_alert_present(self): - click_handler = self.marionette.find_element(By.ID, 'click-handler') - text = self.marionette.find_element(By.ID, 'click-result').text - self.assertEqual(text, '') - - self.marionette.find_element(By.ID, 'modal-alert').click() - self.wait_for_alert() - - # Commands succeed, but because the dialog blocks the event loop, - # our actions aren't reflected on the page. - text = self.marionette.find_element(By.ID, 'click-result').text - self.assertEqual(text, '') - click_handler.click() - text = self.marionette.find_element(By.ID, 'click-result').text - self.assertEqual(text, '') - - alert = self.marionette.switch_to_alert() - alert.accept() - - Wait(self.marionette).until(lambda _: not self.alert_present()) - - click_handler.click() - text = self.marionette.find_element(By.ID, 'click-result').text - self.assertEqual(text, 'result') - - -class TestGlobalModals(TestTabModals): - - def setUp(self): - super(TestGlobalModals, self).setUp() - self.marionette.set_pref("prompts.tab_modal.enabled", False) - - def test_unrelated_command_when_alert_present(self): - # The assumptions in this test do not hold on certain platforms, and not when - # e10s is enabled. - pass diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_mouse_action.py b/testing/marionette/harness/marionette_harness/tests/unit/test_mouse_action.py deleted file mode 100644 index 246068215..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_mouse_action.py +++ /dev/null @@ -1,114 +0,0 @@ -# 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.by import By -from marionette_driver.keys import Keys -from marionette_driver.marionette import Actions - -from marionette_harness import MarionetteTestCase - - -class TestMouseAction(MarionetteTestCase): - def setUp(self): - MarionetteTestCase.setUp(self) - if self.marionette.session_capabilities["platformName"] == "darwin": - self.mod_key = Keys.META - else: - self.mod_key = Keys.CONTROL - self.action = Actions(self.marionette) - - def test_click_action(self): - test_html = self.marionette.absolute_url("test.html") - self.marionette.navigate(test_html) - link = self.marionette.find_element(By.ID, "mozLink") - self.action.click(link).perform() - self.assertEqual("Clicked", self.marionette.execute_script( - "return document.getElementById('mozLink').innerHTML")) - - def test_clicking_element_out_of_view_succeeds(self): - # The action based click doesn"t check for visibility. - test_html = self.marionette.absolute_url("hidden.html") - self.marionette.navigate(test_html) - el = self.marionette.find_element(By.ID, "child") - self.action.click(el).perform() - - def test_double_click_action(self): - test_html = self.marionette.absolute_url("double_click.html") - self.marionette.navigate(test_html) - el = self.marionette.find_element(By.ID, "one-word-div") - self.action.double_click(el).perform() - el.send_keys(self.mod_key + "c") - rel = self.marionette.find_element(By.ID, "input-field") - rel.send_keys(self.mod_key + "v") - self.assertEqual("zyxw", rel.get_property("value")) - - def test_context_click_action(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - click_el = self.marionette.find_element(By.ID, "resultContainer") - - def context_menu_state(): - with self.marionette.using_context("chrome"): - cm_el = self.marionette.find_element(By.ID, "contentAreaContextMenu") - return cm_el.get_property("state") - - self.assertEqual("closed", context_menu_state()) - self.action.context_click(click_el).perform() - self.wait_for_condition(lambda _: context_menu_state() == "open") - - with self.marionette.using_context("chrome"): - self.marionette.find_element(By.ID, "main-window").send_keys(Keys.ESCAPE) - self.wait_for_condition(lambda _: context_menu_state() == "closed") - - def test_middle_click_action(self): - test_html = self.marionette.absolute_url("clicks.html") - self.marionette.navigate(test_html) - - self.marionette.find_element(By.ID, "addbuttonlistener").click() - - el = self.marionette.find_element(By.ID, "showbutton") - self.action.middle_click(el).perform() - - self.wait_for_condition(lambda _: el.get_property("innerHTML") == "1") - - def test_chrome_click(self): - self.marionette.navigate("about:blank") - data_uri = "data:text/html,<html></html>" - with self.marionette.using_context("chrome"): - urlbar = self.marionette.find_element(By.ID, "urlbar") - urlbar.send_keys(data_uri) - go_button = self.marionette.find_element(By.ID, "urlbar-go-button") - self.action.click(go_button).perform() - self.wait_for_condition(lambda mn: mn.get_url() == data_uri) - - def test_chrome_double_click(self): - self.marionette.navigate("about:blank") - test_word = "quux" - - with self.marionette.using_context("chrome"): - urlbar = self.marionette.find_element(By.ID, "urlbar") - self.assertEqual("", urlbar.get_property("value")) - - urlbar.send_keys(test_word) - self.assertEqual(urlbar.get_property("value"), test_word) - (self.action.double_click(urlbar).perform() - .key_down(self.mod_key) - .key_down("x").perform()) - self.assertEqual(urlbar.get_property("value"), "") - - def test_chrome_context_click_action(self): - self.marionette.set_context("chrome") - def context_menu_state(): - cm_el = self.marionette.find_element(By.ID, "tabContextMenu") - return cm_el.get_property("state") - - currtab = self.marionette.execute_script("return gBrowser.selectedTab") - self.assertEqual("closed", context_menu_state()) - self.action.context_click(currtab).perform() - self.wait_for_condition(lambda _: context_menu_state() == "open") - - (self.marionette.find_element(By.ID, "main-window") - .send_keys(Keys.ESCAPE)) - - self.wait_for_condition(lambda _: context_menu_state() == "closed") diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_navigation.py b/testing/marionette/harness/marionette_harness/tests/unit/test_navigation.py deleted file mode 100644 index 75ed37ecd..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_navigation.py +++ /dev/null @@ -1,447 +0,0 @@ -# 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/. - -import contextlib -import time -import urllib - -from marionette_driver import By, errors, expected, Wait -from marionette_harness import ( - MarionetteTestCase, - run_if_e10s, - run_if_manage_instance, - skip, - skip_if_mobile, - WindowManagerMixin, -) - - -def inline(doc): - return "data:text/html;charset=utf-8,%s" % urllib.quote(doc) - - -class TestBackForwardNavigation(WindowManagerMixin, MarionetteTestCase): - - def setUp(self): - super(TestBackForwardNavigation, self).setUp() - - self.test_page = self.marionette.absolute_url('test.html') - - def open_with_link(): - link = self.marionette.find_element(By.ID, "new-blank-tab") - link.click() - - # Always use a blank new tab for an empty history - self.marionette.navigate(self.marionette.absolute_url("windowHandles.html")) - self.new_tab = self.open_tab(open_with_link) - self.marionette.switch_to_window(self.new_tab) - Wait(self.marionette, timeout=self.marionette.timeout.page_load).until( - lambda _: self.history_length == 1, - message="The newly opened tab doesn't have a browser history length of 1") - - def tearDown(self): - self.marionette.switch_to_parent_frame() - self.close_all_tabs() - - super(TestBackForwardNavigation, self).tearDown() - - @property - def history_length(self): - return self.marionette.execute_script("return window.history.length;") - - def run_test(self, test_pages): - # Helper method to run simple back and forward testcases. - for index, page in enumerate(test_pages): - if "error" in page: - with self.assertRaises(page["error"]): - self.marionette.navigate(page["url"]) - else: - self.marionette.navigate(page["url"]) - self.assertEqual(page["url"], self.marionette.get_url()) - self.assertEqual(self.history_length, index + 1) - - for page in test_pages[-2::-1]: - if "error" in page: - with self.assertRaises(page["error"]): - self.marionette.go_back() - else: - self.marionette.go_back() - self.assertEqual(page["url"], self.marionette.get_url()) - - for page in test_pages[1::]: - if "error" in page: - with self.assertRaises(page["error"]): - self.marionette.go_forward() - else: - self.marionette.go_forward() - self.assertEqual(page["url"], self.marionette.get_url()) - - def test_no_history_items(self): - # Both methods should not raise a failure if no navigation is possible - self.marionette.go_back() - self.marionette.go_forward() - - def test_data_urls(self): - test_pages = [ - {"url": inline("<p>foobar</p>")}, - {"url": self.test_page}, - {"url": inline("<p>foobar</p>")}, - ] - self.run_test(test_pages) - - def test_same_document_hash_change(self): - test_pages = [ - {"url": "{}#23".format(self.test_page)}, - {"url": self.test_page}, - {"url": "{}#42".format(self.test_page)}, - ] - self.run_test(test_pages) - - @skip("Causes crashes for JS GC (bug 1344863) and a11y (bug 1344868)") - def test_frameset(self): - test_pages = [ - {"url": self.marionette.absolute_url("frameset.html")}, - {"url": self.test_page}, - {"url": self.marionette.absolute_url("frameset.html")}, - ] - self.run_test(test_pages) - - def test_frameset_after_navigating_in_frame(self): - test_element_locator = (By.ID, "email") - - self.marionette.navigate(self.test_page) - self.assertEqual(self.marionette.get_url(), self.test_page) - self.assertEqual(self.history_length, 1) - page = self.marionette.absolute_url("frameset.html") - self.marionette.navigate(page) - self.assertEqual(self.marionette.get_url(), page) - self.assertEqual(self.history_length, 2) - frame = self.marionette.find_element(By.ID, "fifth") - self.marionette.switch_to_frame(frame) - link = self.marionette.find_element(By.ID, "linkId") - link.click() - - # We cannot use get_url() to wait until the target page has been loaded, - # because it will return the URL of the top browsing context and doesn't - # wait for the page load to be complete. - Wait(self.marionette, timeout=self.marionette.timeout.page_load).until( - expected.element_present(*test_element_locator), - message="Target element 'email' has not been found") - self.assertEqual(self.history_length, 3) - - # Go back to the frame the click navigated away from - self.marionette.go_back() - self.assertEqual(self.marionette.get_url(), page) - with self.assertRaises(errors.NoSuchElementException): - self.marionette.find_element(*test_element_locator) - - # Go back to the non-frameset page - self.marionette.switch_to_parent_frame() - self.marionette.go_back() - self.assertEqual(self.marionette.get_url(), self.test_page) - - # Go forward to the frameset page - self.marionette.go_forward() - self.assertEqual(self.marionette.get_url(), page) - - # Go forward to the frame the click navigated to - # TODO: See above for automatic browser context switches. Hard to do here - frame = self.marionette.find_element(By.ID, "fifth") - self.marionette.switch_to_frame(frame) - self.marionette.go_forward() - self.marionette.find_element(*test_element_locator) - self.assertEqual(self.marionette.get_url(), page) - - def test_image_document_to_html(self): - test_pages = [ - {"url": self.marionette.absolute_url('black.png')}, - {"url": self.test_page}, - {"url": self.marionette.absolute_url('white.png')}, - ] - self.run_test(test_pages) - - def test_image_document_to_image_document(self): - test_pages = [ - {"url": self.marionette.absolute_url('black.png')}, - {"url": self.marionette.absolute_url('white.png')}, - ] - self.run_test(test_pages) - - @run_if_e10s("Requires e10s mode enabled") - def test_remoteness_change(self): - # TODO: Verify that a remoteness change happened - # like: self.assertNotEqual(self.marionette.current_window_handle, self.new_tab) - - # about:robots is always a non-remote page for now - test_pages = [ - {"url": "about:robots"}, - {"url": self.test_page}, - {"url": "about:robots"}, - ] - self.run_test(test_pages) - - def test_navigate_to_requested_about_page_after_error_page(self): - test_pages = [ - {"url": "about:neterror"}, - {"url": self.marionette.absolute_url("test.html")}, - {"url": "about:blocked"}, - ] - self.run_test(test_pages) - - def test_timeout_error(self): - # Bug 1354908 - Disabled on Windows XP due to intermittent failures - caps = self.marionette.session_capabilities - if caps["platformName"] == "windows_nt" and float(caps["platformVersion"]) < 6: - return - - urls = [ - self.marionette.absolute_url('slow'), - self.test_page, - self.marionette.absolute_url('slow'), - ] - - # First, load all pages completely to get them added to the cache - for index, url in enumerate(urls): - self.marionette.navigate(url) - self.assertEqual(url, self.marionette.get_url()) - self.assertEqual(self.history_length, index + 1) - - self.marionette.go_back() - self.assertEqual(urls[1], self.marionette.get_url()) - - # Force triggering a timeout error - self.marionette.timeout.page_load = 0.1 - with self.assertRaises(errors.TimeoutException): - self.marionette.go_back() - self.assertEqual(urls[0], self.marionette.get_url()) - self.marionette.timeout.page_load = 300000 - - self.marionette.go_forward() - self.assertEqual(urls[1], self.marionette.get_url()) - - # Force triggering a timeout error - self.marionette.timeout.page_load = 0.1 - with self.assertRaises(errors.TimeoutException): - self.marionette.go_forward() - self.assertEqual(urls[2], self.marionette.get_url()) - self.marionette.timeout.page_load = 300000 - - def test_certificate_error(self): - test_pages = [ - {"url": self.fixtures.where_is("/test.html", on="https"), - "error": errors.InsecureCertificateException}, - {"url": self.test_page}, - {"url": self.fixtures.where_is("/test.html", on="https"), - "error": errors.InsecureCertificateException}, - ] - self.run_test(test_pages) - - -class TestNavigate(WindowManagerMixin, MarionetteTestCase): - - def setUp(self): - super(TestNavigate, self).setUp() - - self.marionette.navigate("about:") - self.test_doc = self.marionette.absolute_url("test.html") - self.iframe_doc = self.marionette.absolute_url("test_iframe.html") - - def tearDown(self): - self.marionette.timeout.reset() - self.close_all_tabs() - - super(TestNavigate, self).tearDown() - - @property - def location_href(self): - # Windows 8 has recently seen a proliferation of intermittent - # test failures to do with failing to compare "about:blank" == - # u"about:blank". For the sake of consistenty, we encode the - # returned URL as Unicode here to ensure that the values are - # absolutely of the same type. - # - # (https://bugzilla.mozilla.org/show_bug.cgi?id=1322862) - return self.marionette.execute_script("return window.location.href").encode("utf-8") - - def test_set_location_through_execute_script(self): - self.marionette.execute_script( - "window.location.href = '%s'" % self.test_doc) - Wait(self.marionette).until( - lambda _: self.test_doc == self.location_href) - self.assertEqual("Marionette Test", self.marionette.title) - - def test_navigate_chrome_error(self): - with self.marionette.using_context("chrome"): - self.assertRaises(errors.UnsupportedOperationException, - self.marionette.navigate, "about:blank") - self.assertRaises(errors.UnsupportedOperationException, self.marionette.go_back) - self.assertRaises(errors.UnsupportedOperationException, self.marionette.go_forward) - self.assertRaises(errors.UnsupportedOperationException, self.marionette.refresh) - - def test_get_current_url_returns_top_level_browsing_context_url(self): - self.marionette.navigate(self.iframe_doc) - self.assertEqual(self.iframe_doc, self.location_href) - frame = self.marionette.find_element(By.CSS_SELECTOR, "#test_iframe") - self.marionette.switch_to_frame(frame) - self.assertEqual(self.iframe_doc, self.marionette.get_url()) - - def test_get_current_url(self): - self.marionette.navigate(self.test_doc) - self.assertEqual(self.test_doc, self.marionette.get_url()) - self.marionette.navigate("about:blank") - self.assertEqual("about:blank", self.marionette.get_url()) - - def test_refresh(self): - self.marionette.navigate(self.test_doc) - self.assertEqual("Marionette Test", self.marionette.title) - self.assertTrue(self.marionette.execute_script( - """var elem = window.document.createElement('div'); elem.id = 'someDiv'; - window.document.body.appendChild(elem); return true;""")) - self.assertFalse(self.marionette.execute_script( - "return window.document.getElementById('someDiv') == undefined")) - self.marionette.refresh() - # TODO(ato): Bug 1291320 - time.sleep(0.2) - self.assertEqual("Marionette Test", self.marionette.title) - self.assertTrue(self.marionette.execute_script( - "return window.document.getElementById('someDiv') == undefined")) - - def test_navigate_in_child_frame_changes_to_top(self): - frame_html = self.marionette.absolute_url("frameset.html") - - self.marionette.navigate(frame_html) - frame = self.marionette.find_element(By.NAME, "third") - self.marionette.switch_to_frame(frame) - self.assertRaises(errors.NoSuchElementException, - self.marionette.find_element, By.NAME, "third") - - self.marionette.navigate(frame_html) - self.marionette.find_element(By.NAME, "third") - - @skip_if_mobile("Bug 1323755 - Socket timeout") - def test_invalid_protocol(self): - with self.assertRaises(errors.MarionetteException): - self.marionette.navigate("thisprotocoldoesnotexist://") - - def test_find_element_state_complete(self): - self.marionette.navigate(self.test_doc) - state = self.marionette.execute_script( - "return window.document.readyState") - self.assertEqual("complete", state) - self.assertTrue(self.marionette.find_element(By.ID, "mozLink")) - - def test_error_when_exceeding_page_load_timeout(self): - self.marionette.timeout.page_load = 0.1 - with self.assertRaises(errors.TimeoutException): - self.marionette.navigate(self.marionette.absolute_url("slow")) - - def test_navigate_to_same_image_document_twice(self): - self.marionette.navigate(self.fixtures.where_is("black.png")) - self.assertIn("black.png", self.marionette.title) - self.marionette.navigate(self.fixtures.where_is("black.png")) - self.assertIn("black.png", self.marionette.title) - - def test_navigate_hash_change(self): - doc = inline("<p id=foo>") - self.marionette.navigate(doc) - self.marionette.execute_script("window.visited = true", sandbox=None) - self.marionette.navigate("{}#foo".format(doc)) - self.assertTrue(self.marionette.execute_script( - "return window.visited", sandbox=None)) - - @skip_if_mobile("Bug 1334095 - Timeout: No new tab has been opened") - def test_about_blank_for_new_docshell(self): - """ Bug 1312674 - Hang when loading about:blank for a new docshell.""" - def open_with_link(): - link = self.marionette.find_element(By.ID, "new-blank-tab") - link.click() - - # Open a new tab to get a new docshell created - self.marionette.navigate(self.marionette.absolute_url("windowHandles.html")) - new_tab = self.open_tab(trigger=open_with_link) - self.marionette.switch_to_window(new_tab) - self.assertEqual(self.marionette.get_url(), "about:blank") - - self.marionette.navigate('about:blank') - self.marionette.close() - self.marionette.switch_to_window(self.start_window) - - @skip("Bug 1332064 - NoSuchElementException: Unable to locate element: :focus") - @run_if_manage_instance("Only runnable if Marionette manages the instance") - @skip_if_mobile("Bug 1322993 - Missing temporary folder") - def test_focus_after_navigation(self): - self.marionette.quit() - self.marionette.start_session() - - self.marionette.navigate(inline("<input autofocus>")) - active_el = self.marionette.execute_script("return document.activeElement") - focus_el = self.marionette.find_element(By.CSS_SELECTOR, ":focus") - self.assertEqual(active_el, focus_el) - - -class TestTLSNavigation(MarionetteTestCase): - insecure_tls = {"acceptInsecureCerts": True} - secure_tls = {"acceptInsecureCerts": False} - - def setUp(self): - MarionetteTestCase.setUp(self) - self.marionette.delete_session() - self.capabilities = self.marionette.start_session( - {"requiredCapabilities": self.insecure_tls}) - - def tearDown(self): - try: - self.marionette.delete_session() - except: - pass - MarionetteTestCase.tearDown(self) - - @contextlib.contextmanager - def safe_session(self): - try: - self.capabilities = self.marionette.start_session( - {"requiredCapabilities": self.secure_tls}) - self.assertFalse(self.capabilities["acceptInsecureCerts"]) - yield self.marionette - finally: - self.marionette.delete_session() - - @contextlib.contextmanager - def unsafe_session(self): - try: - self.capabilities = self.marionette.start_session( - {"requiredCapabilities": self.insecure_tls}) - self.assertTrue(self.capabilities["acceptInsecureCerts"]) - yield self.marionette - finally: - self.marionette.delete_session() - - def test_navigate_by_command(self): - self.marionette.navigate( - self.fixtures.where_is("/test.html", on="https")) - self.assertIn("https", self.marionette.get_url()) - - def test_navigate_by_click(self): - link_url = self.fixtures.where_is("/test.html", on="https") - self.marionette.navigate( - inline("<a href=%s>https is the future</a>" % link_url)) - self.marionette.find_element(By.TAG_NAME, "a").click() - self.assertIn("https", self.marionette.get_url()) - - def test_deactivation(self): - invalid_cert_url = self.fixtures.where_is("/test.html", on="https") - - print "with safe session" - with self.safe_session() as session: - with self.assertRaises(errors.InsecureCertificateException): - session.navigate(invalid_cert_url) - - print "with unsafe session" - with self.unsafe_session() as session: - session.navigate(invalid_cert_url) - - print "with safe session again" - with self.safe_session() as session: - with self.assertRaises(errors.InsecureCertificateException): - session.navigate(invalid_cert_url) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_pagesource.py b/testing/marionette/harness/marionette_harness/tests/unit/test_pagesource.py deleted file mode 100644 index c88666986..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_pagesource.py +++ /dev/null @@ -1,33 +0,0 @@ -# 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_harness import MarionetteTestCase - - -class TestPageSource(MarionetteTestCase): - def testShouldReturnTheSourceOfAPage(self): - test_html = self.marionette.absolute_url("testPageSource.html") - self.marionette.navigate(test_html) - source = self.marionette.page_source - from_web_api = self.marionette.execute_script("return document.documentElement.outerHTML") - self.assertTrue("<html" in source) - self.assertTrue("PageSource" in source) - self.assertEqual(source, from_web_api) - - def testShouldReturnTheSourceOfAPageWhenThereAreUnicodeChars(self): - test_html = self.marionette.absolute_url("testPageSourceWithUnicodeChars.html") - self.marionette.navigate(test_html) - # if we don't throw on the next line we are good! - source = self.marionette.page_source - from_web_api = self.marionette.execute_script("return document.documentElement.outerHTML") - self.assertEqual(source, from_web_api) - - def testShouldReturnAXMLDocumentSource(self): - test_xml = self.marionette.absolute_url("testPageSource.xml") - self.marionette.navigate(test_xml) - source = self.marionette.page_source - from_web_api = self.marionette.execute_script("return document.documentElement.outerHTML") - import re - self.assertEqual(re.sub("\s", "", source), "<xml><foo><bar>baz</bar></foo></xml>") - self.assertEqual(source, from_web_api) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_pagesource_chrome.py b/testing/marionette/harness/marionette_harness/tests/unit/test_pagesource_chrome.py deleted file mode 100644 index 5f60e6010..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_pagesource_chrome.py +++ /dev/null @@ -1,29 +0,0 @@ -# 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_harness import MarionetteTestCase, WindowManagerMixin - - -class TestPageSourceChrome(WindowManagerMixin, MarionetteTestCase): - - def setUp(self): - super(TestPageSourceChrome, self).setUp() - self.marionette.set_context("chrome") - - def open_with_js(): - self.marionette.execute_script(""" - window.open('chrome://marionette/content/test.xul', - 'foo', 'chrome,centerscreen'); - """) - - new_window = self.open_window(open_with_js) - self.marionette.switch_to_window(new_window) - - def tearDown(self): - self.close_all_windows() - super(TestPageSourceChrome, self).tearDown() - - def testShouldReturnXULDetails(self): - source = self.marionette.page_source - self.assertTrue('<textbox id="textInput"' in source) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_position.py b/testing/marionette/harness/marionette_harness/tests/unit/test_position.py deleted file mode 100644 index 2cc4d5947..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_position.py +++ /dev/null @@ -1,19 +0,0 @@ -# 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.by import By - -from marionette_harness import MarionetteTestCase - - -class TestPosition(MarionetteTestCase): - - def test_should_get_element_position_back(self): - test_url = self.marionette.absolute_url('rectangles.html') - self.marionette.navigate(test_url) - - r2 = self.marionette.find_element(By.ID, "r2") - location = r2.rect - self.assertEqual(11, location['x']) - self.assertEqual(10, location['y']) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_prefs.py b/testing/marionette/harness/marionette_harness/tests/unit/test_prefs.py deleted file mode 100644 index 9cfbe1df1..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_prefs.py +++ /dev/null @@ -1,167 +0,0 @@ -# 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.errors import JavascriptException - -from marionette_harness import MarionetteTestCase - - -class TestPreferences(MarionetteTestCase): - prefs = { - "bool": "marionette.test.bool", - "int": "marionette.test.int", - "string": "marionette.test.string", - } - - def tearDown(self): - for pref in self.prefs.values(): - self.marionette.clear_pref(pref) - - super(TestPreferences, self).tearDown() - - def test_clear_pref(self): - self.assertIsNone(self.marionette.get_pref(self.prefs["bool"])) - - self.marionette.set_pref(self.prefs["bool"], True) - self.assertTrue(self.marionette.get_pref(self.prefs["bool"])) - - self.marionette.clear_pref(self.prefs["bool"]) - self.assertIsNone(self.marionette.get_pref(self.prefs["bool"])) - - def test_get_and_set_pref(self): - # By default none of the preferences are set - self.assertIsNone(self.marionette.get_pref(self.prefs["bool"])) - self.assertIsNone(self.marionette.get_pref(self.prefs["int"])) - self.assertIsNone(self.marionette.get_pref(self.prefs["string"])) - - # Test boolean values - self.marionette.set_pref(self.prefs["bool"], True) - value = self.marionette.get_pref(self.prefs["bool"]) - self.assertTrue(value) - self.assertEqual(type(value), bool) - - # Test int values - self.marionette.set_pref(self.prefs["int"], 42) - value = self.marionette.get_pref(self.prefs["int"]) - self.assertEqual(value, 42) - self.assertEqual(type(value), int) - - # Test string values - self.marionette.set_pref(self.prefs["string"], "abc") - value = self.marionette.get_pref(self.prefs["string"]) - self.assertEqual(value, "abc") - self.assertTrue(isinstance(value, basestring)) - - # Test reset value - self.marionette.set_pref(self.prefs["string"], None) - self.assertIsNone(self.marionette.get_pref(self.prefs["string"])) - - def test_get_set_pref_default_branch(self): - pref_default = "marionette.test.pref_default1" - self.assertIsNone(self.marionette.get_pref(self.prefs["string"])) - - self.marionette.set_pref(pref_default, "default_value", default_branch=True) - self.assertEqual(self.marionette.get_pref(pref_default), "default_value") - self.assertEqual(self.marionette.get_pref(pref_default, default_branch=True), - "default_value") - - self.marionette.set_pref(pref_default, "user_value") - self.assertEqual(self.marionette.get_pref(pref_default), "user_value") - self.assertEqual(self.marionette.get_pref(pref_default, default_branch=True), - "default_value") - - self.marionette.clear_pref(pref_default) - self.assertEqual(self.marionette.get_pref(pref_default), "default_value") - - def test_get_pref_value_type(self): - # Without a given value type the properties URL will be returned only - pref_complex = "browser.menu.showCharacterEncoding" - properties_file = "chrome://browser/locale/browser.properties" - self.assertEqual(self.marionette.get_pref(pref_complex, default_branch=True), - properties_file) - - # Otherwise the property named like the pref will be translated - value = self.marionette.get_pref(pref_complex, default_branch=True, - value_type="nsIPrefLocalizedString") - self.assertNotEqual(value, properties_file) - - def test_set_prefs(self): - # By default none of the preferences are set - self.assertIsNone(self.marionette.get_pref(self.prefs["bool"])) - self.assertIsNone(self.marionette.get_pref(self.prefs["int"])) - self.assertIsNone(self.marionette.get_pref(self.prefs["string"])) - - # Set a value on the default branch first - pref_default = "marionette.test.pref_default2" - self.assertIsNone(self.marionette.get_pref(pref_default)) - self.marionette.set_prefs({pref_default: "default_value"}, default_branch=True) - - # Set user values - prefs = {self.prefs["bool"]: True, self.prefs["int"]: 42, - self.prefs["string"]: "abc", pref_default: "user_value"} - self.marionette.set_prefs(prefs) - - self.assertTrue(self.marionette.get_pref(self.prefs["bool"])) - self.assertEqual(self.marionette.get_pref(self.prefs["int"]), 42) - self.assertEqual(self.marionette.get_pref(self.prefs["string"]), "abc") - self.assertEqual(self.marionette.get_pref(pref_default), "user_value") - self.assertEqual(self.marionette.get_pref(pref_default, default_branch=True), - "default_value") - - def test_using_prefs(self): - # Test that multiple preferences can be set with "using_prefs", and that - # they are set correctly and unset correctly after leaving the context - # manager. - pref_not_existent = "marionette.test.not_existent1" - pref_default = "marionette.test.pref_default3" - - self.marionette.set_prefs({self.prefs["string"]: "abc", - self.prefs["int"]: 42, - self.prefs["bool"]: False, - }) - self.assertFalse(self.marionette.get_pref(self.prefs["bool"])) - self.assertEqual(self.marionette.get_pref(self.prefs["int"]), 42) - self.assertEqual(self.marionette.get_pref(self.prefs["string"]), "abc") - self.assertIsNone(self.marionette.get_pref(pref_not_existent)) - - with self.marionette.using_prefs({self.prefs["bool"]: True, - self.prefs["int"]: 24, - self.prefs["string"]: "def", - pref_not_existent: "existent"}): - - self.assertTrue(self.marionette.get_pref(self.prefs["bool"]), True) - self.assertEquals(self.marionette.get_pref(self.prefs["int"]), 24) - self.assertEquals(self.marionette.get_pref(self.prefs["string"]), "def") - self.assertEquals(self.marionette.get_pref(pref_not_existent), "existent") - - self.assertFalse(self.marionette.get_pref(self.prefs["bool"])) - self.assertEqual(self.marionette.get_pref(self.prefs["int"]), 42) - self.assertEqual(self.marionette.get_pref(self.prefs["string"]), "abc") - self.assertIsNone(self.marionette.get_pref(pref_not_existent)) - - # Using context with default branch - self.marionette.set_pref(pref_default, "default_value", default_branch=True) - self.assertEqual(self.marionette.get_pref(pref_default, default_branch=True), - "default_value") - - with self.marionette.using_prefs({pref_default: "new_value"}, default_branch=True): - self.assertEqual(self.marionette.get_pref(pref_default, default_branch=True), - "new_value") - - self.assertEqual(self.marionette.get_pref(pref_default, default_branch=True), - "default_value") - - def test_using_prefs_exception(self): - # Test that throwing an exception inside the context manager doesn"t - # prevent the preferences from being restored at context manager exit. - self.marionette.set_pref(self.prefs["string"], "abc") - - try: - with self.marionette.using_prefs({self.prefs["string"]: "def"}): - self.assertEquals(self.marionette.get_pref(self.prefs["string"]), "def") - self.marionette.execute_script("return foo.bar.baz;") - except JavascriptException: - pass - - self.assertEquals(self.marionette.get_pref(self.prefs["string"]), "abc") diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_profile_management.py b/testing/marionette/harness/marionette_harness/tests/unit/test_profile_management.py deleted file mode 100644 index f8ec952b2..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_profile_management.py +++ /dev/null @@ -1,34 +0,0 @@ -# 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_harness import MarionetteTestCase - - -class TestProfileManagement(MarionetteTestCase): - - def setUp(self): - MarionetteTestCase.setUp(self) - self.marionette.enforce_gecko_prefs( - {"marionette.test.bool": True, - "marionette.test.string": "testing", - "marionette.test.int": 3 - }) - self.marionette.set_context("chrome") - - def test_preferences_are_set(self): - self.assertTrue(self.marionette.get_pref("marionette.test.bool")) - self.assertEqual(self.marionette.get_pref("marionette.test.string"), "testing") - self.assertEqual(self.marionette.get_pref("marionette.test.int"), 3) - - def test_change_preference(self): - self.assertTrue(self.marionette.get_pref("marionette.test.bool")) - - self.marionette.enforce_gecko_prefs({"marionette.test.bool": False}) - - self.assertFalse(self.marionette.get_pref("marionette.test.bool")) - - def test_clean_profile(self): - self.marionette.restart(clean=True) - - self.assertEqual(self.marionette.get_pref("marionette.test.bool"), None) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_proxy.py b/testing/marionette/harness/marionette_harness/tests/unit/test_proxy.py deleted file mode 100644 index 887d69b76..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_proxy.py +++ /dev/null @@ -1,252 +0,0 @@ -# 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.errors import InvalidArgumentException - -from marionette_harness import MarionetteTestCase - - -class TestProxy(MarionetteTestCase): - - def setUp(self): - super(TestProxy, self).setUp() - self.marionette.delete_session() - - def test_that_we_can_set_a_autodetect_proxy(self): - capabilities = {"requiredCapabilities": - { - "proxy":{ - "proxyType": "autodetect", - } - } - } - self.marionette.start_session(capabilities) - result = None - with self.marionette.using_context('chrome'): - result = self.marionette.execute_script("""return { - "proxyType" : Services.prefs.getIntPref('network.proxy.type'), - } - """) - - self.assertEqual(result["proxyType"], 4) - - def test_that_capabilities_returned_have_proxy_details(self): - capabilities = {"requiredCapabilities": - { - "proxy":{ - "proxyType": "autodetect", - } - } - } - self.marionette.start_session(capabilities) - result = self.marionette.session_capabilities - - self.assertEqual(result["proxy"]["proxyType"], "autodetect") - - def test_that_we_can_set_a_system_proxy(self): - capabilities = {"requiredCapabilities": - { - "proxy":{ - "proxyType": "system", - } - } - } - self.marionette.start_session(capabilities) - result = None - with self.marionette.using_context('chrome'): - result = self.marionette.execute_script("""return { - "proxyType" : Services.prefs.getIntPref('network.proxy.type'), - } - """) - - self.assertEqual(result["proxyType"], 5) - - def test_we_can_set_a_pac_proxy(self): - url = "http://marionette.test" - capabilities = {"requiredCapabilities": - { - "proxy":{ - "proxyType": "pac", - "proxyAutoconfigUrl": url, - } - } - } - self.marionette.start_session(capabilities) - result = None - with self.marionette.using_context('chrome'): - result = self.marionette.execute_script("""return { - "proxyType" : Services.prefs.getIntPref('network.proxy.type'), - "proxyAutoconfigUrl" : Services.prefs.getCharPref('network.proxy.autoconfig_url'), - } - """) - - self.assertEqual(result["proxyType"], 2) - self.assertEqual(result["proxyAutoconfigUrl"], url, 'proxyAutoconfigUrl was not set') - - def test_that_we_can_set_a_manual_proxy(self): - port = 4444 - url = "http://marionette.test" - capabilities = {"requiredCapabilities": - { - "proxy":{ - "proxyType": "manual", - "ftpProxy": url, - "ftpProxyPort": port, - "httpProxy": url, - "httpProxyPort": port, - "sslProxy": url, - "sslProxyPort": port, - } - } - } - self.marionette.start_session(capabilities) - result = None - with self.marionette.using_context('chrome'): - result = self.marionette.execute_script("""return { - "proxyType" : Services.prefs.getIntPref('network.proxy.type'), - "httpProxy" : Services.prefs.getCharPref('network.proxy.http'), - "httpProxyPort": Services.prefs.getIntPref('network.proxy.http_port'), - "sslProxy": Services.prefs.getCharPref('network.proxy.ssl'), - "sslProxyPort": Services.prefs.getIntPref('network.proxy.ssl_port'), - "ftpProxy": Services.prefs.getCharPref('network.proxy.ftp'), - "ftpProxyPort": Services.prefs.getIntPref('network.proxy.ftp_port'), - } - """) - - self.assertEqual(result["proxyType"], 1) - self.assertEqual(result["httpProxy"], url, 'httpProxy was not set') - self.assertEqual(result["httpProxyPort"], port, 'httpProxyPort was not set') - self.assertEqual(result["sslProxy"], url, 'sslProxy url was not set') - self.assertEqual(result["sslProxyPort"], port, 'sslProxyPort was not set') - self.assertEqual(result["ftpProxy"], url, 'ftpProxy was not set') - self.assertEqual(result["ftpProxyPort"], port, 'ftpProxyPort was not set') - - def test_we_can_set_a_manual_proxy_with_a_socks_proxy_with_socks_version(self): - port = 4444 - url = "http://marionette.test" - capabilities = {"requiredCapabilities": - { - "proxy":{ - "proxyType": "manual", - "socksProxy": url, - "socksProxyPort": port, - "socksVersion": 4, - "socksUsername": "cake", - "socksPassword": "made with cake" - } - } - } - self.marionette.start_session(capabilities) - result = None - with self.marionette.using_context('chrome'): - result = self.marionette.execute_script("""return { - "proxyType" : Services.prefs.getIntPref('network.proxy.type'), - "socksProxy" : Services.prefs.getCharPref('network.proxy.socks'), - "socksProxyPort": Services.prefs.getIntPref('network.proxy.socks_port'), - "socksVersion": Services.prefs.getIntPref('network.proxy.socks_version'), - } - """) - self.assertEqual(result["socksProxy"], url, 'socksProxy was not set') - self.assertEqual(result["socksProxyPort"], port, 'socksProxyPort was not set') - self.assertEqual(result["socksVersion"], 4, 'socksVersion was not set to 4') - - def test_we_can_set_a_manual_proxy_with_a_socks_proxy_with_no_socks_version(self): - port = 4444 - url = "http://marionette.test" - capabilities = {"requiredCapabilities": - { - "proxy":{ - "proxyType": "manual", - "socksProxy": url, - "socksProxyPort": port, - "socksUsername": "cake", - "socksPassword": "made with cake" - } - } - } - self.marionette.start_session(capabilities) - result = None - with self.marionette.using_context('chrome'): - result = self.marionette.execute_script("""return { - "proxyType" : Services.prefs.getIntPref('network.proxy.type'), - "socksProxy" : Services.prefs.getCharPref('network.proxy.socks'), - "socksProxyPort": Services.prefs.getIntPref('network.proxy.socks_port'), - "socksVersion": Services.prefs.getIntPref('network.proxy.socks_version'), - - } - """) - self.assertEqual(result["socksProxy"], url, 'socksProxy was not set') - self.assertEqual(result["socksProxyPort"], port, 'socksProxyPort was not set') - self.assertEqual(result["socksVersion"], 5, 'socksVersion was not set to 5') - - def test_when_not_all_manual_proxy_details_are_in_capabilities(self): - port = 4444 - url = "http://marionette.test" - capabilities = {"requiredCapabilities": - { - "proxy":{ - "proxyType": "manual", - "ftpProxy": url, - "ftpProxyPort": port, - } - } - } - self.marionette.start_session(capabilities) - result = None - with self.marionette.using_context('chrome'): - result = self.marionette.execute_script("""return { - "proxyType" : Services.prefs.getIntPref('network.proxy.type'), - "httpProxy" : Services.prefs.getCharPref('network.proxy.http'), - "httpProxyPort": Services.prefs.getIntPref('network.proxy.http_port'), - "sslProxy": Services.prefs.getCharPref('network.proxy.ssl'), - "sslProxyPort": Services.prefs.getIntPref('network.proxy.ssl_port'), - "ftpProxy": Services.prefs.getCharPref('network.proxy.ftp'), - "ftpProxyPort": Services.prefs.getIntPref('network.proxy.ftp_port'), - } - """) - - self.assertEqual(result["proxyType"], 1) - self.assertNotEqual(result["httpProxy"], url, - 'httpProxy was set. {}'.format(result["httpProxy"])) - self.assertNotEqual(result["httpProxyPort"], port, 'httpProxyPort was set') - self.assertNotEqual(result["sslProxy"], url, 'sslProxy url was set') - self.assertNotEqual(result["sslProxyPort"], port, 'sslProxyPort was set') - self.assertEqual(result["ftpProxy"], url, 'ftpProxy was set') - self.assertEqual(result["ftpProxyPort"], port, 'ftpProxyPort was set') - - - - def test_proxy_is_a_string_should_throw_invalid_argument(self): - capabilities = {"requiredCapabilities": - { - "proxy":"I really should be a dictionary" - } - } - try: - self.marionette.start_session(capabilities) - self.fail("We should have started a session because proxy should be a dict") - except InvalidArgumentException as e: - assert e.message == "Value of 'proxy' should be an object" - - def test_proxy_is_passed_in_with_no_proxy_doesnt_set_it(self): - capabilities = {"requiredCapabilities": - { - "proxy": {"proxyType": "NOPROXY"}, - } - } - self.marionette.start_session(capabilities) - result = None - with self.marionette.using_context('chrome'): - result = self.marionette.execute_script("""return { - "proxyType": Services.prefs.getIntPref('network.proxy.type'), - }; - """) - - self.assertEqual(result["proxyType"], 0) - - def tearDown(self): - if not self.marionette.session: - self.marionette.start_session() - else: - self.marionette.restart(clean=True) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_quit_restart.py b/testing/marionette/harness/marionette_harness/tests/unit/test_quit_restart.py deleted file mode 100644 index 38c678556..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_quit_restart.py +++ /dev/null @@ -1,173 +0,0 @@ -# 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.errors import MarionetteException - -from marionette_harness import MarionetteTestCase - - -class TestQuitRestart(MarionetteTestCase): - - def setUp(self): - MarionetteTestCase.setUp(self) - - self.pid = self.marionette.process_id - self.session_id = self.marionette.session_id - - self.assertNotEqual(self.marionette.get_pref("browser.startup.page"), 3) - self.marionette.set_pref("browser.startup.page", 3) - - def tearDown(self): - # Ensure to restart a session if none exist for clean-up - if not self.marionette.session: - self.marionette.start_session() - - self.marionette.clear_pref("browser.startup.page") - - MarionetteTestCase.tearDown(self) - - def test_force_restart(self): - self.marionette.restart() - self.assertEqual(self.marionette.session_id, self.session_id) - - # A forced restart will cause a new process id - self.assertNotEqual(self.marionette.process_id, self.pid) - - # If a preference value is not forced, a restart will cause a reset - self.assertNotEqual(self.marionette.get_pref("browser.startup.page"), 3) - - def test_force_quit(self): - self.marionette.quit() - - self.assertEqual(self.marionette.session, None) - with self.assertRaisesRegexp(MarionetteException, "Please start a session"): - self.marionette.get_url() - - self.marionette.start_session() - self.assertNotEqual(self.marionette.session_id, self.session_id) - self.assertNotEqual(self.marionette.get_pref("browser.startup.page"), 3) - - def test_in_app_clean_restart(self): - with self.assertRaises(ValueError): - self.marionette.restart(in_app=True, clean=True) - - def test_in_app_restart(self): - self.marionette.restart(in_app=True) - self.assertEqual(self.marionette.session_id, self.session_id) - - # An in-app restart will keep the same process id only on Linux - if self.marionette.session_capabilities['platformName'] == 'linux': - self.assertEqual(self.marionette.process_id, self.pid) - else: - self.assertNotEqual(self.marionette.process_id, self.pid) - - # If a preference value is not forced, a restart will cause a reset - self.assertNotEqual(self.marionette.get_pref("browser.startup.page"), 3) - - def test_in_app_restart_with_callback(self): - self.marionette.restart(in_app=True, - callback=lambda: self.shutdown(restart=True)) - - self.assertEqual(self.marionette.session_id, self.session_id) - - # An in-app restart will keep the same process id only on Linux - if self.marionette.session_capabilities['platformName'] == 'linux': - self.assertEqual(self.marionette.process_id, self.pid) - else: - self.assertNotEqual(self.marionette.process_id, self.pid) - - # If a preference value is not forced, a restart will cause a reset - self.assertNotEqual(self.marionette.get_pref("browser.startup.page"), 3) - - def test_in_app_quit(self): - self.marionette.quit(in_app=True) - - self.assertEqual(self.marionette.session, None) - with self.assertRaisesRegexp(MarionetteException, "Please start a session"): - self.marionette.get_url() - - self.marionette.start_session() - self.assertNotEqual(self.marionette.session_id, self.session_id) - self.assertNotEqual(self.marionette.get_pref("browser.startup.page"), 3) - - def test_in_app_quit_with_callback(self): - self.marionette.quit(in_app=True, callback=self.shutdown) - self.assertEqual(self.marionette.session, None) - with self.assertRaisesRegexp(MarionetteException, "Please start a session"): - self.marionette.get_url() - - self.marionette.start_session() - self.assertNotEqual(self.marionette.session_id, self.session_id) - self.assertNotEqual(self.marionette.get_pref("browser.startup.page"), 3) - - def test_reset_context_after_quit_by_set_context(self): - # Check that we are in content context which is used by default in Marionette - self.assertNotIn('chrome://', self.marionette.get_url(), - "Context doesn't default to content") - - self.marionette.set_context('chrome') - self.marionette.quit(in_app=True) - self.assertEqual(self.marionette.session, None) - self.marionette.start_session() - self.assertNotIn('chrome://', self.marionette.get_url(), - "Not in content context after quit with using_context") - - def test_reset_context_after_quit_by_using_context(self): - # Check that we are in content context which is used by default in Marionette - self.assertNotIn('chrome://', self.marionette.get_url(), - "Context doesn't default to content") - - with self.marionette.using_context('chrome'): - self.marionette.quit(in_app=True) - self.assertEqual(self.marionette.session, None) - self.marionette.start_session() - self.assertNotIn('chrome://', self.marionette.get_url(), - "Not in content context after quit with using_context") - - def test_keep_context_after_restart_by_set_context(self): - # Check that we are in content context which is used by default in Marionette - self.assertNotIn('chrome://', self.marionette.get_url(), - "Context doesn't default to content") - - # restart while we are in chrome context - self.marionette.set_context('chrome') - self.marionette.restart(in_app=True) - - # An in-app restart will keep the same process id only on Linux - if self.marionette.session_capabilities['platformName'] == 'linux': - self.assertEqual(self.marionette.process_id, self.pid) - else: - self.assertNotEqual(self.marionette.process_id, self.pid) - - self.assertIn('chrome://', self.marionette.get_url(), - "Not in chrome context after a restart with set_context") - - def test_keep_context_after_restart_by_using_context(self): - # Check that we are in content context which is used by default in Marionette - self.assertNotIn('chrome://', self.marionette.get_url(), - "Context doesn't default to content") - - # restart while we are in chrome context - with self.marionette.using_context('chrome'): - self.marionette.restart(in_app=True) - - # An in-app restart will keep the same process id only on Linux - if self.marionette.session_capabilities['platformName'] == 'linux': - self.assertEqual(self.marionette.process_id, self.pid) - else: - self.assertNotEqual(self.marionette.process_id, self.pid) - - self.assertIn('chrome://', self.marionette.get_url(), - "Not in chrome context after a restart with using_context") - - def shutdown(self, restart=False): - self.marionette.set_context("chrome") - self.marionette.execute_script(""" - Components.utils.import("resource://gre/modules/Services.jsm"); - let flags = Ci.nsIAppStartup.eAttemptQuit - if(arguments[0]) { - flags |= Ci.nsIAppStartup.eRestart; - } - Services.startup.quit(flags); - """, script_args=[restart]) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_rendered_element.py b/testing/marionette/harness/marionette_harness/tests/unit/test_rendered_element.py deleted file mode 100644 index 508870e91..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_rendered_element.py +++ /dev/null @@ -1,34 +0,0 @@ -#Copyright 2007-2009 WebDriver committers -#Copyright 2007-2009 Google Inc. -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. - -from marionette_driver.by import By - -from marionette_harness import MarionetteTestCase - - -class RenderedElementTests(MarionetteTestCase): - - def testWeCanGetComputedStyleValueOnElement(self): - test_url = self.marionette.absolute_url('javascriptPage.html') - self.marionette.navigate(test_url) - element = self.marionette.find_element(By.ID, "green-parent") - backgroundColour = element.value_of_css_property("background-color") - - self.assertEqual("rgb(0, 128, 0)", backgroundColour) - - element = self.marionette.find_element(By.ID, "red-item") - backgroundColour = element.value_of_css_property("background-color") - - self.assertEqual("rgb(255, 0, 0)", backgroundColour) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_report.py b/testing/marionette/harness/marionette_harness/tests/unit/test_report.py deleted file mode 100644 index f22c3db4b..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_report.py +++ /dev/null @@ -1,29 +0,0 @@ -# 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_harness import MarionetteTestCase, expectedFailure, skip - - -class TestReport(MarionetteTestCase): - - def test_pass(self): - assert True - - def test_fail(self): - assert False - - @skip('Skip Message') - def test_skip(self): - assert False - - @expectedFailure - def test_expected_fail(self): - assert False - - @expectedFailure - def test_unexpected_pass(self): - assert True - - def test_error(self): - raise Exception() diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_run_js_test.py b/testing/marionette/harness/marionette_harness/tests/unit/test_run_js_test.py deleted file mode 100644 index 134223ce1..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_run_js_test.py +++ /dev/null @@ -1,10 +0,0 @@ -# Any copyright is dedicated to the Public Domain. -# http://creativecommons.org/publicdomain/zero/1.0/ - -from marionette_harness import MarionetteTestCase - - -class TestRunJSTest(MarionetteTestCase): - def test_basic(self): - self.run_js_test('test_simpletest_pass.js') - self.run_js_test('test_simpletest_fail.js') diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_screen_orientation.py b/testing/marionette/harness/marionette_harness/tests/unit/test_screen_orientation.py deleted file mode 100644 index 830795a1e..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_screen_orientation.py +++ /dev/null @@ -1,86 +0,0 @@ -# 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 errors -from mozrunner.devices.emulator_screen import EmulatorScreen - -from marionette_harness import MarionetteTestCase, skip_if_desktop, skip_if_mobile - - -default_orientation = "portrait-primary" -unknown_orientation = "Unknown screen orientation: {}" - - -class TestScreenOrientation(MarionetteTestCase): - def setUp(self): - MarionetteTestCase.setUp(self) - self.is_mobile = self.marionette.session_capabilities.get("rotatable", False) - - def tearDown(self): - if self.is_mobile: - self.marionette.set_orientation(default_orientation) - self.assertEqual(self.marionette.orientation, default_orientation, "invalid state") - MarionetteTestCase.tearDown(self) - - @skip_if_desktop("Not supported in Firefox") - def test_set_orientation_to_portrait_primary(self): - self.marionette.set_orientation("portrait-primary") - new_orientation = self.marionette.orientation - self.assertEqual(new_orientation, "portrait-primary") - - @skip_if_desktop("Not supported in Firefox") - def test_set_orientation_to_landscape_primary(self): - self.marionette.set_orientation("landscape-primary") - new_orientation = self.marionette.orientation - self.assertEqual(new_orientation, "landscape-primary") - - @skip_if_desktop("Not supported in Firefox") - def test_set_orientation_to_portrait_secondary(self): - self.marionette.set_orientation("portrait-secondary") - new_orientation = self.marionette.orientation - self.assertEqual(new_orientation, "portrait-secondary") - - @skip_if_desktop("Not supported in Firefox") - def test_set_orientation_to_landscape_secondary(self): - self.marionette.set_orientation("landscape-secondary") - new_orientation = self.marionette.orientation - self.assertEqual(new_orientation, "landscape-secondary") - - @skip_if_desktop("Not supported in Firefox") - def test_set_orientation_to_shorthand_portrait(self): - # Set orientation to something other than portrait-primary first, since the default is - # portrait-primary. - self.marionette.set_orientation("landscape-primary") - self.assertEqual(self.marionette.orientation, "landscape-primary", "invalid state") - - self.marionette.set_orientation("portrait") - new_orientation = self.marionette.orientation - self.assertEqual(new_orientation, "portrait-primary") - - @skip_if_desktop("Not supported in Firefox") - def test_set_orientation_to_shorthand_landscape(self): - self.marionette.set_orientation("landscape") - new_orientation = self.marionette.orientation - self.assertEqual(new_orientation, "landscape-primary") - - @skip_if_desktop("Not supported in Firefox") - def test_set_orientation_with_mixed_casing(self): - self.marionette.set_orientation("lAnDsCaPe") - new_orientation = self.marionette.orientation - self.assertEqual(new_orientation, "landscape-primary") - - @skip_if_desktop("Not supported in Firefox") - def test_set_invalid_orientation(self): - with self.assertRaisesRegexp(errors.MarionetteException, unknown_orientation.format("cheese")): - self.marionette.set_orientation("cheese") - - @skip_if_desktop("Not supported in Firefox") - def test_set_null_orientation(self): - with self.assertRaisesRegexp(errors.MarionetteException, unknown_orientation.format("null")): - self.marionette.set_orientation(None) - - @skip_if_mobile("Specific test for Firefox") - def test_unsupported_operation_on_desktop(self): - with self.assertRaises(errors.UnsupportedOperationException): - self.marionette.set_orientation("landscape-primary") diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_screenshot.py b/testing/marionette/harness/marionette_harness/tests/unit/test_screenshot.py deleted file mode 100644 index aa0e6ab1c..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_screenshot.py +++ /dev/null @@ -1,428 +0,0 @@ -# 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/. - -import base64 -import hashlib -import imghdr -import struct -import urllib - -from marionette_driver import By -from marionette_driver.errors import JavascriptException, NoSuchWindowException -from marionette_harness import ( - MarionetteTestCase, - skip, - skip_if_mobile, - WindowManagerMixin, -) - - -def inline(doc, mime="text/html;charset=utf-8"): - return "data:{0},{1}".format(mime, urllib.quote(doc)) - - -box = inline("<body><div id='box'><p id='green' style='width: 50px; height: 50px; " - "background: silver;'></p></div></body>") -input = inline("<body><input id='text-input'></input></body>") -long = inline("<body style='height: 300vh'><p style='margin-top: 100vh'>foo</p></body>") -short = inline("<body style='height: 10vh'></body>") -svg = inline(""" - <svg xmlns="http://www.w3.org/2000/svg" height="20" width="20"> - <rect height="20" width="20"/> - </svg>""", mime="image/svg+xml") - - -class ScreenCaptureTestCase(MarionetteTestCase): - - def setUp(self): - super(ScreenCaptureTestCase, self).setUp() - - self._device_pixel_ratio = None - - @property - def device_pixel_ratio(self): - if self._device_pixel_ratio is None: - self._device_pixel_ratio = self.marionette.execute_script(""" - return window.devicePixelRatio - """) - return self._device_pixel_ratio - - @property - def document_element(self): - return self.marionette.find_element(By.CSS_SELECTOR, ":root") - - @property - def page_y_offset(self): - return self.marionette.execute_script("return window.pageYOffset") - - @property - def viewport_dimensions(self): - return self.marionette.execute_script(""" - return [arguments[0].clientWidth, - arguments[0].clientHeight]; - """, script_args=[self.document_element]) - - def assert_png(self, screenshot): - """Test that screenshot is a Base64 encoded PNG file.""" - image = base64.decodestring(screenshot) - self.assertEqual(imghdr.what("", image), "png") - - def assert_formats(self, element=None): - if element is None: - element = self.document_element - - screenshot_default = self.marionette.screenshot(element=element) - screenshot_image = self.marionette.screenshot(element=element, format="base64") - binary1 = self.marionette.screenshot(element=element, format="binary") - binary2 = self.marionette.screenshot(element=element, format="binary") - hash1 = self.marionette.screenshot(element=element, format="hash") - hash2 = self.marionette.screenshot(element=element, format="hash") - - # Valid data should have been returned - self.assert_png(screenshot_image) - self.assertEqual(imghdr.what("", binary1), "png") - self.assertEqual(screenshot_image, base64.b64encode(binary1)) - self.assertEqual(hash1, hashlib.sha256(screenshot_image).hexdigest()) - - # Different formats produce different data - self.assertNotEqual(screenshot_image, binary1) - self.assertNotEqual(screenshot_image, hash1) - self.assertNotEqual(binary1, hash1) - - # A second capture should be identical - self.assertEqual(screenshot_image, screenshot_default) - self.assertEqual(binary1, binary2) - self.assertEqual(hash1, hash2) - - def get_element_dimensions(self, element): - rect = element.rect - return rect["width"], rect["height"] - - def get_image_dimensions(self, screenshot): - self.assert_png(screenshot) - image = base64.decodestring(screenshot) - width, height = struct.unpack(">LL", image[16:24]) - return int(width), int(height) - - def scale(self, rect): - return (int(rect[0] * self.device_pixel_ratio), - int(rect[1] * self.device_pixel_ratio)) - - -class TestScreenCaptureChrome(WindowManagerMixin, ScreenCaptureTestCase): - - def setUp(self): - super(TestScreenCaptureChrome, self).setUp() - self.marionette.set_context("chrome") - - def tearDown(self): - self.close_all_windows() - super(TestScreenCaptureChrome, self).tearDown() - - @property - def window_dimensions(self): - return tuple(self.marionette.execute_script(""" - let el = document.documentElement; - let rect = el.getBoundingClientRect(); - return [rect.width, rect.height]; - """)) - - def open_dialog(self, url=None, width=None, height=None): - if url is None: - url = "chrome://marionette/content/test_dialog.xul" - - def opener(): - features = "chrome" - if height is not None: - features += ",height={}".format(height) - if width is not None: - features += ",width={}".format(width) - - self.marionette.execute_script(""" - window.open(arguments[0], "", arguments[1]); - """, script_args=[url, features]) - - return self.open_window(opener) - - def test_capture_different_context(self): - """Check that screenshots in content and chrome are different.""" - with self.marionette.using_context("content"): - screenshot_content = self.marionette.screenshot() - screenshot_chrome = self.marionette.screenshot() - self.assertNotEqual(screenshot_content, screenshot_chrome) - - @skip_if_mobile("Fennec doesn't support other chrome windows") - def test_capture_element(self): - dialog = self.open_dialog() - self.marionette.switch_to_window(dialog) - - # Ensure we only capture the element - el = self.marionette.find_element(By.ID, "test-list") - screenshot_element = self.marionette.screenshot(element=el) - self.assertEqual(self.scale(self.get_element_dimensions(el)), - self.get_image_dimensions(screenshot_element)) - - # Ensure we do not capture the full window - screenshot_dialog = self.marionette.screenshot() - self.assertNotEqual(screenshot_dialog, screenshot_element) - - self.marionette.close_chrome_window() - self.marionette.switch_to_window(self.start_window) - - @skip_if_mobile("Fennec doesn't support other chrome windows") - def test_capture_flags(self): - dialog = self.open_dialog() - self.marionette.switch_to_window(dialog) - - textbox = self.marionette.find_element(By.ID, "text-box") - textbox.send_keys("") - screenshot_focus = self.marionette.screenshot() - - self.marionette.execute_script("arguments[0].blur();", script_args=[textbox]) - screenshot_no_focus = self.marionette.screenshot() - - self.marionette.close_chrome_window() - self.marionette.switch_to_window(self.start_window) - - self.assertNotEqual(screenshot_focus, screenshot_no_focus) - - def test_capture_full_area(self): - # A full capture is not the outer dimensions of the window, - # but instead the bounding box of the window's root node (documentElement). - screenshot_full = self.marionette.screenshot() - screenshot_root = self.marionette.screenshot(element=self.document_element) - - self.assert_png(screenshot_full) - self.assert_png(screenshot_root) - self.assertEqual(screenshot_root, screenshot_full) - self.assertEqual(self.scale(self.get_element_dimensions(self.document_element)), - self.get_image_dimensions(screenshot_full)) - - @skip_if_mobile("Fennec doesn't support other chrome windows") - def test_capture_viewport(self): - # Load a HTML test page into the chrome window to get scrollbars - test_page = self.marionette.absolute_url("test.html") - dialog = self.open_dialog(url=test_page, width=50, height=50) - self.marionette.switch_to_window(dialog) - - # Size of screenshot has to match viewport size - screenshot = self.marionette.screenshot(full=False) - self.assert_png(screenshot) - self.assertEqual(self.scale(self.viewport_dimensions), - self.get_image_dimensions(screenshot)) - self.assertNotEqual(self.scale(self.window_dimensions), - self.get_image_dimensions(screenshot)) - - self.marionette.close_chrome_window() - self.marionette.switch_to_window(self.start_window) - - @skip_if_mobile("Fennec doesn't support other chrome windows") - def test_capture_window_already_closed(self): - dialog = self.open_dialog() - self.marionette.switch_to_window(dialog) - self.marionette.close_chrome_window() - - self.assertRaises(NoSuchWindowException, self.marionette.screenshot) - self.marionette.switch_to_window(self.start_window) - - @skip_if_mobile("Fennec doesn't support other chrome windows") - def test_formats(self): - dialog = self.open_dialog() - self.marionette.switch_to_window(dialog) - - self.assert_formats() - - self.marionette.close_chrome_window() - self.marionette.switch_to_window(self.start_window) - - def test_format_unknown(self): - with self.assertRaises(ValueError): - self.marionette.screenshot(format="cheese") - - @skip_if_mobile("Fennec doesn't support other chrome windows") - def test_highlight_elements(self): - dialog = self.open_dialog() - self.marionette.switch_to_window(dialog) - - # Highlighting the element itself shouldn't make the image larger - element = self.marionette.find_element(By.ID, "test-list") - screenshot_element = self.marionette.screenshot(element=element) - screenshot_highlight = self.marionette.screenshot(element=element, - highlights=[element]) - self.assertEqual(self.scale(self.get_element_dimensions(element)), - self.get_image_dimensions(screenshot_element)) - self.assertNotEqual(screenshot_element, screenshot_highlight) - - # Highlighting a sub element - button = self.marionette.find_element(By.ID, "choose-button") - screenshot_highlight_button = self.marionette.screenshot(element=element, - highlights=[button]) - self.assertNotEqual(screenshot_element, screenshot_highlight_button) - self.assertNotEqual(screenshot_highlight, screenshot_highlight_button) - - self.marionette.close_chrome_window() - self.marionette.switch_to_window(self.start_window) - - def test_highlight_element_not_seen(self): - """Check that for not found elements an exception is raised.""" - with self.marionette.using_context('content'): - self.marionette.navigate(box) - content_element = self.marionette.find_element(By.ID, "green") - - self.assertRaisesRegexp(JavascriptException, "Element reference not seen before", - self.marionette.screenshot, highlights=[content_element]) - - chrome_document_element = self.document_element - with self.marionette.using_context('content'): - self.assertRaisesRegexp(JavascriptException, "Element reference not seen before", - self.marionette.screenshot, - highlights=[chrome_document_element]) - - -class TestScreenCaptureContent(WindowManagerMixin, ScreenCaptureTestCase): - - def setUp(self): - super(TestScreenCaptureContent, self).setUp() - self.marionette.set_context("content") - - def tearDown(self): - self.close_all_tabs() - super(TestScreenCaptureContent, self).tearDown() - - @property - def scroll_dimensions(self): - return tuple(self.marionette.execute_script(""" - return [document.body.scrollWidth, document.body.scrollHeight] - """)) - - @skip_if_mobile("Needs application independent method to open a new tab") - def test_capture_tab_already_closed(self): - tab = self.open_tab() - self.marionette.switch_to_window(tab) - self.marionette.close() - - self.assertRaises(NoSuchWindowException, self.marionette.screenshot) - self.marionette.switch_to_window(self.start_tab) - - def test_capture_element(self): - self.marionette.navigate(box) - el = self.marionette.find_element(By.TAG_NAME, "div") - screenshot = self.marionette.screenshot(element=el) - self.assert_png(screenshot) - self.assertEqual(self.scale(self.get_element_dimensions(el)), - self.get_image_dimensions(screenshot)) - - @skip("Bug 1213875") - def test_capture_element_scrolled_into_view(self): - self.marionette.navigate(long) - el = self.marionette.find_element(By.TAG_NAME, "p") - screenshot = self.marionette.screenshot(element=el) - self.assert_png(screenshot) - self.assertEqual(self.scale(self.get_element_dimensions(el)), - self.get_image_dimensions(screenshot)) - self.assertGreater(self.page_y_offset, 0) - - @skip("Bug 1330560 - AssertionError: u'iVBORw0KGgoA... (images unexpectedly equal)") - def test_capture_flags(self): - self.marionette.navigate(input) - - textbox = self.marionette.find_element(By.ID, "text-input") - textbox.send_keys("") - screenshot_focus = self.marionette.screenshot() - - self.marionette.execute_script("arguments[0].blur();", script_args=[textbox]) - screenshot_no_focus = self.marionette.screenshot() - - self.assertNotEqual(screenshot_focus, screenshot_no_focus) - - @skip_if_mobile("Bug 1330642 - Tuples differ: (1960, 11130) != (1960, 11129)") - def test_capture_html_document_element(self): - self.marionette.navigate(long) - screenshot = self.marionette.screenshot() - self.assert_png(screenshot) - self.assertEqual(self.scale(self.scroll_dimensions), - self.get_image_dimensions(screenshot)) - - def test_capture_svg_document_element(self): - self.marionette.navigate(svg) - screenshot = self.marionette.screenshot() - self.assert_png(screenshot) - self.assertEqual(self.scale(self.get_element_dimensions(self.document_element)), - self.get_image_dimensions(screenshot)) - - def test_capture_viewport(self): - url = self.marionette.absolute_url("clicks.html") - self.marionette.navigate(short) - self.marionette.navigate(url) - screenshot = self.marionette.screenshot(full=False) - self.assert_png(screenshot) - self.assertEqual(self.scale(self.viewport_dimensions), - self.get_image_dimensions(screenshot)) - - def test_capture_viewport_after_scroll(self): - self.marionette.navigate(long) - before = self.marionette.screenshot() - el = self.marionette.find_element(By.TAG_NAME, "p") - self.marionette.execute_script( - "arguments[0].scrollIntoView()", script_args=[el]) - after = self.marionette.screenshot(full=False) - self.assertNotEqual(before, after) - self.assertGreater(self.page_y_offset, 0) - - def test_formats(self): - self.marionette.navigate(box) - - # Use a smaller region to speed up the test - element = self.marionette.find_element(By.TAG_NAME, "div") - self.assert_formats(element=element) - - def test_format_unknown(self): - with self.assertRaises(ValueError): - self.marionette.screenshot(format="cheese") - - def test_highlight_elements(self): - self.marionette.navigate(box) - element = self.marionette.find_element(By.TAG_NAME, "div") - - # Highlighting the element itself shouldn't make the image larger - screenshot_element = self.marionette.screenshot(element=element) - screenshot_highlight = self.marionette.screenshot(element=element, - highlights=[element]) - self.assertEqual(self.scale(self.get_element_dimensions(element)), - self.get_image_dimensions(screenshot_highlight)) - self.assertNotEqual(screenshot_element, screenshot_highlight) - - # Highlighting a sub element - paragraph = self.marionette.find_element(By.ID, "green") - screenshot_highlight_paragraph = self.marionette.screenshot(element=element, - highlights=[paragraph]) - self.assertNotEqual(screenshot_element, screenshot_highlight_paragraph) - self.assertNotEqual(screenshot_highlight, screenshot_highlight_paragraph) - - def test_scroll_default(self): - self.marionette.navigate(long) - before = self.page_y_offset - el = self.marionette.find_element(By.TAG_NAME, "p") - self.marionette.screenshot(element=el, format="hash") - self.assertNotEqual(before, self.page_y_offset) - - def test_scroll(self): - self.marionette.navigate(long) - before = self.page_y_offset - el = self.marionette.find_element(By.TAG_NAME, "p") - self.marionette.screenshot(element=el, format="hash", scroll=True) - self.assertNotEqual(before, self.page_y_offset) - - def test_scroll_off(self): - self.marionette.navigate(long) - el = self.marionette.find_element(By.TAG_NAME, "p") - before = self.page_y_offset - self.marionette.screenshot(element=el, format="hash", scroll=False) - self.assertEqual(before, self.page_y_offset) - - def test_scroll_no_element(self): - self.marionette.navigate(long) - before = self.page_y_offset - self.marionette.screenshot(format="hash", scroll=True) - self.assertEqual(before, self.page_y_offset) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_select.py b/testing/marionette/harness/marionette_harness/tests/unit/test_select.py deleted file mode 100644 index 3c9522bea..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_select.py +++ /dev/null @@ -1,164 +0,0 @@ -# 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/. - -import urllib - -from marionette_driver.by import By - -from marionette_harness import MarionetteTestCase - - -def inline(doc): - return "data:text/html;charset=utf-8,{}".format(urllib.quote(doc)) - - -class SelectTestCase(MarionetteTestCase): - def assertSelected(self, option_element): - self.assertTrue(option_element.is_selected(), "<option> element not selected") - self.assertTrue(self.marionette.execute_script( - "return arguments[0].selected", script_args=[option_element], sandbox=None), - "<option> selected attribute not updated") - - def assertNotSelected(self, option_element): - self.assertFalse(option_element.is_selected(), "<option> is selected") - self.assertFalse(self.marionette.execute_script( - "return arguments[0].selected", script_args=[option_element], sandbox=None), - "<option> selected attribute not updated") - - -class TestSelect(SelectTestCase): - def test_single(self): - self.marionette.navigate(inline(""" - <select> - <option>first - <option>second - </select>""")) - select = self.marionette.find_element(By.TAG_NAME, "select") - options = self.marionette.find_elements(By.TAG_NAME, "option") - - self.assertSelected(options[0]) - options[1].click() - self.assertSelected(options[1]) - - def test_deselect(self): - self.marionette.navigate(inline(""" - <select> - <option>first - <option>second - <option>third - </select>""")) - select = self.marionette.find_element(By.TAG_NAME, "select") - options = self.marionette.find_elements(By.TAG_NAME, "option") - - options[0].click() - self.assertSelected(options[0]) - options[1].click() - self.assertSelected(options[1]) - options[2].click() - self.assertSelected(options[2]) - options[0].click() - self.assertSelected(options[0]) - - def test_out_of_view(self): - self.marionette.navigate(inline(""" - <select> - <option>1 - <option>2 - <option>3 - <option>4 - <option>5 - <option>6 - <option>7 - <option>8 - <option>9 - <option>10 - <option>11 - <option>12 - <option>13 - <option>14 - <option>15 - <option>16 - <option>17 - <option>18 - <option>19 - <option>20 - </select>""")) - select = self.marionette.find_element(By.TAG_NAME, "select") - options = self.marionette.find_elements(By.TAG_NAME, "option") - - options[14].click() - self.assertSelected(options[14]) - - -class TestSelectMultiple(SelectTestCase): - def test_single(self): - self.marionette.navigate(inline("<select multiple> <option>first </select>")) - option = self.marionette.find_element(By.TAG_NAME, "option") - option.click() - self.assertSelected(option) - - def test_multiple(self): - self.marionette.navigate(inline(""" - <select multiple> - <option>first - <option>second - <option>third - </select>""")) - select = self.marionette.find_element(By.TAG_NAME, "select") - options = select.find_elements(By.TAG_NAME, "option") - - options[1].click() - self.assertSelected(options[1]) - - options[2].click() - self.assertSelected(options[2]) - self.assertSelected(options[1]) - - def test_deselect_selected(self): - self.marionette.navigate(inline("<select multiple> <option>first </select>")) - option = self.marionette.find_element(By.TAG_NAME, "option") - option.click() - self.assertSelected(option) - option.click() - self.assertNotSelected(option) - - def test_deselect_preselected(self): - self.marionette.navigate(inline(""" - <select multiple> - <option selected>first - </select>""")) - option = self.marionette.find_element(By.TAG_NAME, "option") - self.assertSelected(option) - option.click() - self.assertNotSelected(option) - - def test_out_of_view(self): - self.marionette.navigate(inline(""" - <select multiple> - <option>1 - <option>2 - <option>3 - <option>4 - <option>5 - <option>6 - <option>7 - <option>8 - <option>9 - <option>10 - <option>11 - <option>12 - <option>13 - <option>14 - <option>15 - <option>16 - <option>17 - <option>18 - <option>19 - <option>20 - </select>""")) - select = self.marionette.find_element(By.TAG_NAME, "select") - options = self.marionette.find_elements(By.TAG_NAME, "option") - - options[-1].click() - self.assertSelected(options[-1]) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_session.py b/testing/marionette/harness/marionette_harness/tests/unit/test_session.py deleted file mode 100644 index 1676df51f..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_session.py +++ /dev/null @@ -1,56 +0,0 @@ -# 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 errors - -from marionette_harness import MarionetteTestCase - - -class TestSession(MarionetteTestCase): - def setUp(self): - super(TestSession, self).setUp() - self.marionette.delete_session() - - def test_new_session_returns_capabilities(self): - # Sends newSession - caps = self.marionette.start_session() - - # Check that session was created. This implies the server - # sent us the sessionId and status fields. - self.assertIsNotNone(self.marionette.session) - - # Required capabilities mandated by WebDriver spec - self.assertIn("browserName", caps) - self.assertIn("browserVersion", caps) - self.assertIn("platformName", caps) - self.assertIn("platformVersion", caps) - - # Optional capabilities we want Marionette to support - self.assertIn("rotatable", caps) - - def test_get_session_id(self): - # Sends newSession - self.marionette.start_session() - - self.assertTrue(self.marionette.session_id is not None) - self.assertTrue(isinstance(self.marionette.session_id, unicode)) - - def test_set_the_session_id(self): - # Sends newSession - self.marionette.start_session(session_id="ILoveCheese") - - self.assertEqual(self.marionette.session_id, "ILoveCheese") - self.assertTrue(isinstance(self.marionette.session_id, unicode)) - - def test_session_already_started(self): - self.marionette.start_session() - self.assertTrue(isinstance(self.marionette.session_id, unicode)) - with self.assertRaises(errors.SessionNotCreatedException): - self.marionette._send_message("newSession", {}) - - def test_no_session(self): - with self.assertRaises(errors.InvalidSessionIdException): - self.marionette.get_url() - self.marionette.start_session() - self.marionette.get_url() diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_set_window_size.py b/testing/marionette/harness/marionette_harness/tests/unit/test_set_window_size.py deleted file mode 100644 index e1bd5e684..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_set_window_size.py +++ /dev/null @@ -1,84 +0,0 @@ -# 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_harness import MarionetteTestCase - - -class TestSetWindowSize(MarionetteTestCase): - def setUp(self): - super(MarionetteTestCase, self).setUp() - self.start_size = self.marionette.window_size - self.max_width = self.marionette.execute_script("return window.screen.availWidth;") - self.max_height = self.marionette.execute_script("return window.screen.availHeight;") - - def tearDown(self): - # WebDriver spec says a resize cannot result in window being maximized, an - # error is returned if that is the case; therefore if the window is maximized - # at the start of this test, returning to the original size via set_window_size - # size will result in error; so reset to original size minus 1 pixel width - if self.start_size['width'] == self.max_width and self.start_size['height'] == self.max_height: - self.start_size['width']-=1 - self.marionette.set_window_size(self.start_size['width'], self.start_size['height']) - super(MarionetteTestCase, self).tearDown() - - def test_that_we_can_get_and_set_window_size(self): - # event handler - self.marionette.execute_script(""" - window.wrappedJSObject.rcvd_event = false; - window.onresize = function() { - window.wrappedJSObject.rcvd_event = true; - }; - """) - - # valid size - width = self.max_width - 100 - height = self.max_height - 100 - self.marionette.set_window_size(width, height) - self.wait_for_condition(lambda m: m.execute_script("return window.wrappedJSObject.rcvd_event;")) - size = self.marionette.window_size - self.assertEqual(size['width'], width, - "Window width is {0} but should be {1}".format(size['width'], width)) - self.assertEqual(size['height'], height, - "Window height is {0} but should be {1}".format(size['height'], height)) - - def test_that_we_can_get_new_size_when_set_window_size(self): - actual = self.marionette.window_size - width = actual['width'] - 50 - height = actual['height'] - 50 - size = self.marionette.set_window_size(width, height) - self.assertIsNotNone(size, "Response is None") - self.assertEqual(size['width'], width, - "New width is {0} but should be {1}".format(size['width'], width)) - self.assertEqual(size['height'], height, - "New height is {0} but should be {1}".format(size['height'], height)) - - def test_possible_to_request_window_larger_than_screen(self): - self.marionette.set_window_size(4 * self.max_width, 4 * self.max_height) - size = self.marionette.window_size - - # In X the window size may be greater than the bounds of the screen - self.assertGreaterEqual(size["width"], self.max_width) - self.assertGreaterEqual(size["height"], self.max_height) - - def test_that_we_can_maximise_the_window(self): - # valid size - width = self.max_width - 100 - height = self.max_height - 100 - self.marionette.set_window_size(width, height) - - # event handler - self.marionette.execute_script(""" - window.wrappedJSObject.rcvd_event = false; - window.onresize = function() { - window.wrappedJSObject.rcvd_event = true; - }; - """) - self.marionette.maximize_window() - self.wait_for_condition(lambda m: m.execute_script("return window.wrappedJSObject.rcvd_event;")) - - size = self.marionette.window_size - self.assertGreaterEqual(size['width'], self.max_width, - "Window width does not use availWidth, current width: {0}, max width: {1}".format(size['width'], self.max_width)) - self.assertGreaterEqual(size['height'], self.max_height, - "Window height does not use availHeight. current width: {0}, max width: {1}".format(size['height'], self.max_height)) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_shadow_dom.py b/testing/marionette/harness/marionette_harness/tests/unit/test_shadow_dom.py deleted file mode 100644 index 3f91d7cc0..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_shadow_dom.py +++ /dev/null @@ -1,80 +0,0 @@ -# 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.by import By -from marionette_driver.errors import ( - NoSuchElementException, - StaleElementException, - UnsupportedOperationException, -) - -from marionette_harness import MarionetteTestCase - - -class TestShadowDom(MarionetteTestCase): - - def setUp(self): - super(TestShadowDom, self).setUp() - self.marionette.set_pref("dom.webcomponents.enabled", True) - self.marionette.navigate(self.marionette.absolute_url("test_shadow_dom.html")) - - self.host = self.marionette.find_element(By.ID, "host") - self.marionette.switch_to_shadow_root(self.host) - self.button = self.marionette.find_element(By.ID, "button") - - def tearDown(self): - self.marionette.clear_pref("dom.webcomponents.enabled") - super(TestShadowDom, self).tearDown() - - def test_chrome_error(self): - with self.marionette.using_context("chrome"): - self.assertRaises(UnsupportedOperationException, - self.marionette.switch_to_shadow_root) - - def test_shadow_dom(self): - # Button in shadow root should be actionable - self.button.click() - - def test_shadow_dom_after_switch_away_from_shadow_root(self): - # Button in shadow root should be actionable - self.button.click() - self.marionette.switch_to_shadow_root() - # After switching back to top content, button should be stale - self.assertRaises(StaleElementException, self.button.click) - - def test_shadow_dom_raises_stale_element_exception_when_button_remove(self): - self.marionette.execute_script( - 'document.getElementById("host").shadowRoot.getElementById("button").remove();') - # After removing button from shadow DOM, button should be stale - self.assertRaises(StaleElementException, self.button.click) - - def test_shadow_dom_raises_stale_element_exception_when_host_removed(self): - self.marionette.execute_script('document.getElementById("host").remove();') - # After removing shadow DOM host element, button should be stale - self.assertRaises(StaleElementException, self.button.click) - - def test_non_existent_shadow_dom(self): - # Jump back to top level content - self.marionette.switch_to_shadow_root() - # When no ShadowRoot is found, switch_to_shadow_root throws NoSuchElementException - self.assertRaises(NoSuchElementException, self.marionette.switch_to_shadow_root, - self.marionette.find_element(By.ID, "empty-host")) - - def test_inner_shadow_dom(self): - # Button in shadow root should be actionable - self.button.click() - self.inner_host = self.marionette.find_element(By.ID, "inner-host") - self.marionette.switch_to_shadow_root(self.inner_host) - self.inner_button = self.marionette.find_element(By.ID, "inner-button") - # Nested nutton in nested shadow root should be actionable - self.inner_button.click() - self.marionette.switch_to_shadow_root() - # After jumping back to parent shadow root, button should again be actionable but inner - # button should now be stale - self.button.click() - self.assertRaises(StaleElementException, self.inner_button.click) - self.marionette.switch_to_shadow_root() - # After switching back to top content, both buttons should now be stale - self.assertRaises(StaleElementException, self.button.click) - self.assertRaises(StaleElementException, self.inner_button.click) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_simpletest_chrome.js b/testing/marionette/harness/marionette_harness/tests/unit/test_simpletest_chrome.js deleted file mode 100644 index d5edffa67..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_simpletest_chrome.js +++ /dev/null @@ -1,12 +0,0 @@ -/* 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/. */ - -MARIONETTE_TIMEOUT = 1000; -MARIONETTE_CONTEXT = 'chrome'; - -is(2, 2, "test for is()"); -isnot(2, 3, "test for isnot()"); -ok(2 == 2, "test for ok()"); -finish(); - diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_simpletest_fail.js b/testing/marionette/harness/marionette_harness/tests/unit/test_simpletest_fail.js deleted file mode 100644 index 16d9aea59..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_simpletest_fail.js +++ /dev/null @@ -1,16 +0,0 @@ -/* 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/. */ - -MARIONETTE_TIMEOUT = 1000; - -/* this test will fail */ - -setTimeout(function() { - is(1, 2, "is(1,2) should fail", TEST_UNEXPECTED_FAIL, TEST_PASS); - finish(); -}, 100); -isnot(1, 1, "isnot(1,1) should fail", TEST_UNEXPECTED_FAIL, TEST_PASS); -ok(1 == 2, "ok(1==2) should fail", TEST_UNEXPECTED_FAIL, TEST_PASS); - - diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_simpletest_pass.js b/testing/marionette/harness/marionette_harness/tests/unit/test_simpletest_pass.js deleted file mode 100644 index 93ee67619..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_simpletest_pass.js +++ /dev/null @@ -1,12 +0,0 @@ -/* 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/. */ - -MARIONETTE_TIMEOUT = 1000; - -is(2, 2, "test for is()"); -isnot(2, 3, "test for isnot()"); -ok(2 == 2, "test for ok()"); - -setTimeout(finish, 100); - diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_simpletest_sanity.py b/testing/marionette/harness/marionette_harness/tests/unit/test_simpletest_sanity.py deleted file mode 100644 index 8f9728561..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_simpletest_sanity.py +++ /dev/null @@ -1,107 +0,0 @@ -# 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_harness import MarionetteTestCase - - -class SimpletestSanityTest(MarionetteTestCase): - callFinish = "return finish();" - - def run_sync(self, test): - return self.marionette.execute_js_script(test, async=False) - - def run_async(self, test): - return self.marionette.execute_js_script(test) - - def test_is(self): - def runtests(): - sentFail1 = "is(true, false, 'isTest1', TEST_UNEXPECTED_FAIL, TEST_PASS);" + self.callFinish - sentFail2 = "is(true, false, 'isTest2', TEST_UNEXPECTED_FAIL, TEST_PASS);" + self.callFinish - sentPass1 = "is(true, true, 'isTest3');" + self.callFinish - sentPass2 = "is(true, true, 'isTest4');" + self.callFinish - - self.assertEqual(1, len(self.run_sync(sentFail1)["failures"])) - self.assertEqual(0, self.run_sync(sentFail2)["passed"]) - self.assertEqual(1, self.run_sync(sentPass1)["passed"]) - self.assertEqual(0, len(self.run_sync(sentPass2)["failures"])) - - self.marionette.timeout.script = 1 - self.assertEqual(1, len(self.run_async(sentFail1)["failures"])) - self.assertEqual(0, self.run_async(sentFail2)["passed"]) - self.assertEqual(1, self.run_async(sentPass1)["passed"]) - self.assertEqual(0, len(self.run_async(sentPass2)["failures"])) - - self.marionette.set_context("content") - runtests() - self.marionette.set_context("chrome") - runtests() - - def test_isnot(self): - def runtests(): - sentFail1 = "isnot(true, true, 'isnotTest3', TEST_UNEXPECTED_FAIL, TEST_PASS);" + self.callFinish - sentFail2 = "isnot(true, true, 'isnotTest4', TEST_UNEXPECTED_FAIL, TEST_PASS);" + self.callFinish - sentPass1 = "isnot(true, false, 'isnotTest1');" + self.callFinish - sentPass2 = "isnot(true, false, 'isnotTest2');" + self.callFinish - - self.assertEqual(1, len(self.run_sync(sentFail1)["failures"])); - self.assertEqual(0, self.run_sync(sentFail2)["passed"]); - self.assertEqual(0, len(self.run_sync(sentPass1)["failures"])); - self.assertEqual(1, self.run_sync(sentPass2)["passed"]); - - self.marionette.timeout.script = 1 - self.assertEqual(1, len(self.run_async(sentFail1)["failures"])); - self.assertEqual(0, self.run_async(sentFail2)["passed"]); - self.assertEqual(0, len(self.run_async(sentPass1)["failures"])); - self.assertEqual(1, self.run_async(sentPass2)["passed"]); - - self.marionette.set_context("content") - runtests() - self.marionette.set_context("chrome") - runtests() - - def test_ok(self): - def runtests(): - sentFail1 = "ok(1==2, 'testOk1', TEST_UNEXPECTED_FAIL, TEST_PASS);" + self.callFinish - sentFail2 = "ok(1==2, 'testOk2', TEST_UNEXPECTED_FAIL, TEST_PASS);" + self.callFinish - sentPass1 = "ok(1==1, 'testOk3');" + self.callFinish - sentPass2 = "ok(1==1, 'testOk4');" + self.callFinish - - self.assertEqual(1, len(self.run_sync(sentFail1)["failures"])); - self.assertEqual(0, self.run_sync(sentFail2)["passed"]); - self.assertEqual(0, len(self.run_sync(sentPass1)["failures"])); - self.assertEqual(1, self.run_sync(sentPass2)["passed"]); - - self.marionette.timeout.script = 1 - self.assertEqual(1, len(self.run_async(sentFail1)["failures"])); - self.assertEqual(0, self.run_async(sentFail2)["passed"]); - self.assertEqual(0, len(self.run_async(sentPass1)["failures"])); - self.assertEqual(1, self.run_async(sentPass2)["passed"]); - - self.marionette.set_context("content") - runtests() - self.marionette.set_context("chrome") - runtests() - - def test_todo(self): - def runtests(): - sentFail1 = "todo(1==1, 'testTodo1', TEST_UNEXPECTED_PASS, TEST_KNOWN_FAIL);" + self.callFinish - sentFail2 = "todo(1==1, 'testTodo2', TEST_UNEXPECTED_PASS, TEST_KNOWN_FAIL);" + self.callFinish - sentPass1 = "todo(1==2, 'testTodo3');" + self.callFinish - sentPass2 = "todo(1==2, 'testTodo4');" + self.callFinish - - self.assertEqual(1, len(self.run_sync(sentFail1)["unexpectedSuccesses"])); - self.assertEqual(0, len(self.run_sync(sentFail2)["expectedFailures"])); - self.assertEqual(0, len(self.run_sync(sentPass1)["unexpectedSuccesses"])); - self.assertEqual(1, len(self.run_sync(sentPass2)["expectedFailures"])); - - self.marionette.timeout.script = 1 - self.assertEqual(1, len(self.run_async(sentFail1)["unexpectedSuccesses"])); - self.assertEqual(0, len(self.run_async(sentFail2)["expectedFailures"])); - self.assertEqual(0, len(self.run_async(sentPass1)["unexpectedSuccesses"])); - self.assertEqual(1, len(self.run_async(sentPass2)["expectedFailures"])); - - self.marionette.set_context("content") - runtests() - self.marionette.set_context("chrome") - runtests() diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_simpletest_timeout.js b/testing/marionette/harness/marionette_harness/tests/unit/test_simpletest_timeout.js deleted file mode 100644 index 9792a936a..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_simpletest_timeout.js +++ /dev/null @@ -1,16 +0,0 @@ -/* 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/. */ - -MARIONETTE_TIMEOUT = 100; - -/* this test will timeout */ - -function do_test() { - is(1, 1); - isnot(1, 2); - ok(1 == 1); - finish(); -} - -setTimeout(do_test, 1000); diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_single_finger_desktop.py b/testing/marionette/harness/marionette_harness/tests/unit/test_single_finger_desktop.py deleted file mode 100644 index 8ac80c3c5..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_single_finger_desktop.py +++ /dev/null @@ -1,123 +0,0 @@ -# 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/. - -import os -import sys - -from marionette_driver.errors import MarionetteException -from marionette_driver import Actions, By - -from marionette_harness import MarionetteTestCase, skip - -# add this directory to the path -sys.path.append(os.path.dirname(__file__)) - -from single_finger_functions import ( - chain, chain_flick, context_menu, double_tap, - long_press_action, long_press_on_xy_action, - move_element, move_element_offset, press_release, single_tap, wait, - wait_with_value -) - - -class testSingleFingerMouse(MarionetteTestCase): - def setUp(self): - super(MarionetteTestCase, self).setUp() - # set context menu related preferences needed for some tests - self.marionette.set_context("chrome") - self.enabled = self.marionette.execute_script(""" -let prefs = Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefBranch); -let value = false; -try { - value = prefs.getBoolPref("ui.click_hold_context_menus"); -} -catch (e) {} -prefs.setBoolPref("ui.click_hold_context_menus", true); -return value; -""") - self.wait_time = self.marionette.execute_script(""" -let prefs = Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefBranch); -let value = 750; -try { - value = prefs.getIntPref("ui.click_hold_context_menus.delay"); -} -catch (e) {} -prefs.setIntPref("ui.click_hold_context_menus.delay", value); -return value; -""") - self.marionette.set_context("content") - - def tearDown(self): - self.marionette.set_context("chrome") - self.marionette.execute_script( - """ -let prefs = Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefBranch); -prefs.setBoolPref("ui.click_hold_context_menus", arguments[0]); -""", [self.enabled]) - self.marionette.execute_script( - """ -let prefs = Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefBranch); -prefs.setIntPref("ui.click_hold_context_menus.delay", arguments[0]); -""", [self.wait_time]) - self.marionette.set_context("content") - super(MarionetteTestCase, self).tearDown() - - def test_press_release(self): - press_release(self.marionette, 1, self.wait_for_condition, "button1-mousemove-mousedown-mouseup-click") - - def test_press_release_twice(self): - press_release(self.marionette, 2, self.wait_for_condition, "button1-mousemove-mousedown-mouseup-click-mousemove-mousedown-mouseup-click") - - def test_move_element(self): - move_element(self.marionette, self.wait_for_condition, "button1-mousemove-mousedown", "button2-mousemove-mouseup") - - def test_move_by_offset(self): - move_element_offset(self.marionette, self.wait_for_condition, "button1-mousemove-mousedown", "button2-mousemove-mouseup") - - def test_wait(self): - wait(self.marionette, self.wait_for_condition, "button1-mousemove-mousedown-mouseup-click") - - def test_wait_with_value(self): - wait_with_value(self.marionette, self.wait_for_condition, "button1-mousemove-mousedown-mouseup-click") - - @skip("Bug 1191066") - def test_context_menu(self): - context_menu(self.marionette, self.wait_for_condition, - "button1-mousemove-mousedown-contextmenu", - "button1-mousemove-mousedown-contextmenu-mouseup-click") - - @skip("Bug 1191066") - def test_long_press_action(self): - long_press_action(self.marionette, self.wait_for_condition, - "button1-mousemove-mousedown-contextmenu-mouseup-click") - - @skip("Bug 1191066") - def test_long_press_on_xy_action(self): - long_press_on_xy_action(self.marionette, self.wait_for_condition, - "button1-mousemove-mousedown-contextmenu-mouseup-click") - - @skip("Bug 865334") - def test_long_press_fail(self): - testAction = self.marionette.absolute_url("testAction.html") - self.marionette.navigate(testAction) - button = self.marionette.find_element(By.ID, "button1Copy") - action = Actions(self.marionette) - action.press(button).long_press(button, 5) - self.assertRaises(MarionetteException, action.perform) - - def test_chain(self): - chain(self.marionette, self.wait_for_condition, "button1-mousemove-mousedown", "delayed-mousemove-mouseup") - - def test_chain_flick(self): - chain_flick(self.marionette, self.wait_for_condition, "button1-mousemove-mousedown-mousemove", "buttonFlick-mousemove-mouseup") - - def test_single_tap(self): - single_tap(self.marionette, self.wait_for_condition, "button1-mousemove-mousedown-mouseup-click") - - def test_double_tap(self): - double_tap(self.marionette, self.wait_for_condition, "button1-mousemove-mousedown-mouseup-click-mousemove-mousedown-mouseup-click") diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_skip_setup.py b/testing/marionette/harness/marionette_harness/tests/unit/test_skip_setup.py deleted file mode 100644 index 9a0432fb7..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_skip_setup.py +++ /dev/null @@ -1,35 +0,0 @@ -# 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_harness import MarionetteTestCase, SkipTest - - -class TestSetUpSkipped(MarionetteTestCase): - - testVar = {'test':'SkipTest'} - - def setUp(self): - try: - self.testVar['email'] - except KeyError: - raise SkipTest('email key not present in dict, skip ...') - MarionetteTestCase.setUp(self) - - def test_assert(self): - assert True - -class TestSetUpNotSkipped(MarionetteTestCase): - - testVar = {'test':'SkipTest'} - - def setUp(self): - try: - self.testVar['test'] - except KeyError: - raise SkipTest('email key not present in dict, skip ...') - MarionetteTestCase.setUp(self) - - def test_assert(self): - assert True - diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_switch_frame.py b/testing/marionette/harness/marionette_harness/tests/unit/test_switch_frame.py deleted file mode 100644 index 18eb34169..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_switch_frame.py +++ /dev/null @@ -1,183 +0,0 @@ -# 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.by import By -from marionette_driver.errors import ( - JavascriptException, - NoSuchFrameException, -) - -from marionette_harness import MarionetteTestCase - - -class TestSwitchFrame(MarionetteTestCase): - def test_switch_simple(self): - start_url = "test_iframe.html" - verify_title = "Marionette IFrame Test" - test_html = self.marionette.absolute_url(start_url) - self.marionette.navigate(test_html) - self.assertEqual(self.marionette.get_active_frame(), None) - frame = self.marionette.find_element(By.ID, "test_iframe") - self.marionette.switch_to_frame(frame) - self.assertTrue(start_url in self.marionette.get_url()) - inner_frame_element = self.marionette.get_active_frame() - # test that we can switch back to main frame, then switch back to the - # inner frame with the value we got from get_active_frame - self.marionette.switch_to_frame() - self.assertEqual(verify_title, self.marionette.title) - self.marionette.switch_to_frame(inner_frame_element) - self.assertTrue(start_url in self.marionette.get_url()) - - def test_switch_nested(self): - start_url = "test_nested_iframe.html" - verify_title = "Marionette IFrame Test" - test_html = self.marionette.absolute_url(start_url) - self.marionette.navigate(test_html) - frame = self.marionette.find_element(By.ID, "test_iframe") - self.assertEqual(self.marionette.get_active_frame(), None) - self.marionette.switch_to_frame(frame) - self.assertTrue(start_url in self.marionette.get_url()) - inner_frame_element = self.marionette.get_active_frame() - # test that we can switch back to main frame, then switch back to the - # inner frame with the value we got from get_active_frame - self.marionette.switch_to_frame() - self.assertEqual(verify_title, self.marionette.title) - self.marionette.switch_to_frame(inner_frame_element) - self.assertTrue(start_url in self.marionette.get_url()) - inner_frame = self.marionette.find_element(By.ID, 'inner_frame') - self.marionette.switch_to_frame(inner_frame) - self.assertTrue(start_url in self.marionette.get_url()) - self.marionette.switch_to_frame() # go back to main frame - self.assertTrue(start_url in self.marionette.get_url()) - #test that we're using the right window object server-side - self.assertTrue("test_nested_iframe.html" in self.marionette.execute_script("return window.location.href;")) - - def test_stack_trace(self): - start_url = "test_iframe.html" - verify_title = "Marionette IFrame Test" - test_html = self.marionette.absolute_url(start_url) - self.marionette.navigate(test_html) - frame = self.marionette.find_element(By.ID, "test_iframe") - self.assertEqual(self.marionette.get_active_frame(), None) - self.marionette.switch_to_frame(frame) - self.assertTrue(start_url in self.marionette.get_url()) - inner_frame_element = self.marionette.get_active_frame() - # test that we can switch back to main frame, then switch back to the - # inner frame with the value we got from get_active_frame - self.marionette.switch_to_frame() - self.assertEqual(verify_title, self.marionette.title) - self.marionette.switch_to_frame(inner_frame_element) - self.assertTrue(start_url in self.marionette.get_url()) - - try: - self.marionette.execute_async_script("foo();") - except JavascriptException as e: - self.assertTrue("foo" in e.message) - - def test_should_be_able_to_carry_on_working_if_the_frame_is_deleted_from_under_us(self): - test_html = self.marionette.absolute_url("deletingFrame.html") - self.marionette.navigate(test_html) - - self.marionette.switch_to_frame(self.marionette.find_element(By.ID, - 'iframe1')) - killIframe = self.marionette.find_element(By.ID, "killIframe") - killIframe.click() - self.marionette.switch_to_frame() - - self.assertEqual(0, len(self.marionette.find_elements(By.ID, "iframe1"))) - - addIFrame = self.marionette.find_element(By.ID, "addBackFrame") - addIFrame.click() - self.marionette.find_element(By.ID, "iframe1") - - self.marionette.switch_to_frame(self.marionette.find_element(By.ID, - "iframe1")) - - self.marionette.find_element(By.ID, "checkbox") - - def test_should_allow_a_user_to_switch_from_an_iframe_back_to_the_main_content_of_the_page(self): - test_iframe = self.marionette.absolute_url("test_iframe.html") - self.marionette.navigate(test_iframe) - self.marionette.switch_to_frame(0) - self.marionette.switch_to_default_content() - header = self.marionette.find_element(By.ID, "iframe_page_heading") - self.assertEqual(header.text, "This is the heading") - - def test_should_be_able_to_switch_to_a_frame_by_its_index(self): - test_html = self.marionette.absolute_url("frameset.html") - self.marionette.navigate(test_html) - self.marionette.switch_to_frame(2) - element = self.marionette.find_element(By.ID, "email") - self.assertEquals("email", element.get_attribute("type")) - - def test_should_be_able_to_switch_to_a_frame_using_a_previously_located_element(self): - test_html = self.marionette.absolute_url("frameset.html") - self.marionette.navigate(test_html) - frame = self.marionette.find_element(By.NAME, "third") - self.marionette.switch_to_frame(frame) - - element = self.marionette.find_element(By.ID, "email") - self.assertEquals("email", element.get_attribute("type")) - - def test_switch_to_frame_with_out_of_bounds_index(self): - self.marionette.navigate(self.marionette.absolute_url("test_iframe.html")) - count = self.marionette.execute_script("return window.frames.length;") - self.assertRaises(NoSuchFrameException, self.marionette.switch_to_frame, count) - - def test_switch_to_frame_with_negative_index(self): - self.marionette.navigate(self.marionette.absolute_url("test_iframe.html")) - self.assertRaises(NoSuchFrameException, self.marionette.switch_to_frame, -1) - - def test_switch_to_parent_frame(self): - frame_html = self.marionette.absolute_url("frameset.html") - self.marionette.navigate(frame_html) - frame = self.marionette.find_element(By.NAME, "third") - self.marionette.switch_to_frame(frame) - - # If we don't find the following element we aren't on the right page - self.marionette.find_element(By.ID, "checky") - form_page_title = self.marionette.execute_script("return document.title") - self.assertEqual("We Leave From Here", form_page_title) - - self.marionette.switch_to_parent_frame() - - current_page_title = self.marionette.execute_script("return document.title") - self.assertEqual("Unique title", current_page_title) - - def test_switch_to_parent_frame_from_default_context_is_a_noop(self): - formpage = self.marionette.absolute_url("formPage.html") - self.marionette.navigate(formpage) - - self.marionette.switch_to_parent_frame() - - form_page_title = self.marionette.execute_script("return document.title") - self.assertEqual("We Leave From Here", form_page_title) - - def test_should_be_able_to_switch_to_parent_from_second_level(self): - frame_html = self.marionette.absolute_url("frameset.html") - self.marionette.navigate(frame_html) - frame = self.marionette.find_element(By.NAME, "fourth") - self.marionette.switch_to_frame(frame) - - second_level = self.marionette.find_element(By.NAME, "child1") - self.marionette.switch_to_frame(second_level) - self.marionette.find_element(By.NAME, "myCheckBox") - - self.marionette.switch_to_parent_frame() - - second_level = self.marionette.find_element(By.NAME, "child1") - - def test_should_be_able_to_switch_to_parent_from_iframe(self): - frame_html = self.marionette.absolute_url("test_iframe.html") - self.marionette.navigate(frame_html) - frame = self.marionette.find_element(By.ID, "test_iframe") - self.marionette.switch_to_frame(frame) - - current_page_title = self.marionette.execute_script("return document.title") - self.assertEqual("Marionette Test", current_page_title) - - self.marionette.switch_to_parent_frame() - - parent_page_title = self.marionette.execute_script("return document.title") - self.assertEqual("Marionette IFrame Test", parent_page_title) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_switch_frame_chrome.py b/testing/marionette/harness/marionette_harness/tests/unit/test_switch_frame_chrome.py deleted file mode 100644 index 03c13026e..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_switch_frame_chrome.py +++ /dev/null @@ -1,56 +0,0 @@ -# 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.errors import JavascriptException - -from marionette_harness import MarionetteTestCase, WindowManagerMixin - - -class TestSwitchFrameChrome(WindowManagerMixin, MarionetteTestCase): - - def setUp(self): - super(TestSwitchFrameChrome, self).setUp() - self.marionette.set_context("chrome") - - def open_window_with_js(): - self.marionette.execute_script(""" - window.open('chrome://marionette/content/test.xul', - 'foo', 'chrome,centerscreen'); - """) - - new_window = self.open_window(trigger=open_window_with_js) - self.marionette.switch_to_window(new_window) - self.assertNotEqual(self.start_window, self.marionette.current_chrome_window_handle) - - def tearDown(self): - self.close_all_windows() - super(TestSwitchFrameChrome, self).tearDown() - - def test_switch_simple(self): - self.assertIn("test.xul", self.marionette.get_url(), "Initial navigation has failed") - self.marionette.switch_to_frame(0) - self.assertIn("test2.xul", self.marionette.get_url(),"Switching by index failed") - self.marionette.switch_to_frame() - self.assertEqual(None, self.marionette.get_active_frame(), "Switiching by null failed") - self.assertIn("test.xul", self.marionette.get_url(), "Switching by null failed") - self.marionette.switch_to_frame("iframe") - self.assertIn("test2.xul", self.marionette.get_url(), "Switching by name failed") - self.marionette.switch_to_frame() - self.assertIn("test.xul", self.marionette.get_url(), "Switching by null failed") - self.marionette.switch_to_frame("iframename") - self.assertIn("test2.xul", self.marionette.get_url(), "Switching by name failed") - iframe_element = self.marionette.get_active_frame() - self.marionette.switch_to_frame() - self.assertIn("test.xul", self.marionette.get_url(), "Switching by null failed") - self.marionette.switch_to_frame(iframe_element) - self.assertIn("test2.xul", self.marionette.get_url(), "Switching by element failed") - - def test_stack_trace(self): - self.assertIn("test.xul", self.marionette.get_url(), "Initial navigation has failed") - self.marionette.switch_to_frame(0) - self.assertRaises(JavascriptException, self.marionette.execute_async_script, "foo();") - try: - self.marionette.execute_async_script("foo();") - except JavascriptException as e: - self.assertIn("foo", e.message) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_switch_remote_frame.py b/testing/marionette/harness/marionette_harness/tests/unit/test_switch_remote_frame.py deleted file mode 100644 index 07ddeef2a..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_switch_remote_frame.py +++ /dev/null @@ -1,118 +0,0 @@ -# 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.by import By - -from marionette_harness import MarionetteTestCase - - -OOP_BY_DEFAULT = "dom.ipc.browser_frames.oop_by_default" -BROWSER_FRAMES_ENABLED = "dom.mozBrowserFramesEnabled" - - -class TestSwitchRemoteFrame(MarionetteTestCase): - def setUp(self): - super(TestSwitchRemoteFrame, self).setUp() - with self.marionette.using_context('chrome'): - self.oop_by_default = self.marionette.get_pref(OOP_BY_DEFAULT) - self.mozBrowserFramesEnabled = self.marionette.get_pref(BROWSER_FRAMES_ENABLED) - self.marionette.set_pref(OOP_BY_DEFAULT, True) - self.marionette.set_pref(BROWSER_FRAMES_ENABLED, True) - - self.multi_process_browser = self.marionette.execute_script(""" - try { - return Services.appinfo.browserTabsRemoteAutostart; - } catch (e) { - return false; - }""") - - def tearDown(self): - with self.marionette.using_context("chrome"): - if self.oop_by_default is None: - self.marionette.clear_pref(OOP_BY_DEFAULT) - else: - self.marionette.set_pref(OOP_BY_DEFAULT, self.oop_by_default) - - if self.mozBrowserFramesEnabled is None: - self.marionette.clear_pref(BROWSER_FRAMES_ENABLED) - else: - self.marionette.set_pref(BROWSER_FRAMES_ENABLED, self.mozBrowserFramesEnabled) - - @property - def is_main_process(self): - return self.marionette.execute_script(""" - return Components.classes["@mozilla.org/xre/app-info;1"]. - getService(Components.interfaces.nsIXULRuntime). - processType == Components.interfaces.nsIXULRuntime.PROCESS_TYPE_DEFAULT; - """, sandbox="system") - - def test_remote_frame(self): - self.marionette.navigate(self.marionette.absolute_url("test.html")) - self.marionette.push_permission('browser', True) - self.marionette.execute_script(""" - let iframe = document.createElement("iframe"); - iframe.setAttribute('mozbrowser', true); - iframe.setAttribute('remote', true); - iframe.id = "remote_iframe"; - iframe.style.height = "100px"; - iframe.style.width = "100%%"; - iframe.src = "{}"; - document.body.appendChild(iframe); - """.format(self.marionette.absolute_url("test.html"))) - remote_iframe = self.marionette.find_element(By.ID, "remote_iframe") - self.marionette.switch_to_frame(remote_iframe) - main_process = self.is_main_process - self.assertFalse(main_process) - - def test_remote_frame_revisit(self): - # test if we can revisit a remote frame (this takes a different codepath) - self.marionette.navigate(self.marionette.absolute_url("test.html")) - self.marionette.push_permission('browser', True) - self.marionette.execute_script(""" - let iframe = document.createElement("iframe"); - iframe.setAttribute('mozbrowser', true); - iframe.setAttribute('remote', true); - iframe.id = "remote_iframe"; - iframe.style.height = "100px"; - iframe.style.width = "100%%"; - iframe.src = "{}"; - document.body.appendChild(iframe); - """.format(self.marionette.absolute_url("test.html"))) - self.marionette.switch_to_frame(self.marionette.find_element(By.ID, - "remote_iframe")) - main_process = self.is_main_process - self.assertFalse(main_process) - self.marionette.switch_to_frame() - main_process = self.is_main_process - should_be_main_process = not self.multi_process_browser - self.assertEqual(main_process, should_be_main_process) - self.marionette.switch_to_frame(self.marionette.find_element(By.ID, - "remote_iframe")) - main_process = self.is_main_process - self.assertFalse(main_process) - - def test_we_can_switch_to_a_remote_frame_by_index(self): - # test if we can revisit a remote frame (this takes a different codepath) - self.marionette.navigate(self.marionette.absolute_url("test.html")) - self.marionette.push_permission('browser', True) - self.marionette.execute_script(""" - let iframe = document.createElement("iframe"); - iframe.setAttribute('mozbrowser', true); - iframe.setAttribute('remote', true); - iframe.id = "remote_iframe"; - iframe.style.height = "100px"; - iframe.style.width = "100%%"; - iframe.src = "{}"; - document.body.appendChild(iframe); - """.format(self.marionette.absolute_url("test.html"))) - self.marionette.switch_to_frame(0) - main_process = self.is_main_process - self.assertFalse(main_process) - self.marionette.switch_to_frame() - main_process = self.is_main_process - should_be_main_process = not self.multi_process_browser - self.assertEqual(main_process, should_be_main_process) - self.marionette.switch_to_frame(0) - main_process = self.is_main_process - self.assertFalse(main_process) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_switch_window_chrome.py b/testing/marionette/harness/marionette_harness/tests/unit/test_switch_window_chrome.py deleted file mode 100644 index 0ad63b6ce..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_switch_window_chrome.py +++ /dev/null @@ -1,124 +0,0 @@ -# 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/. - -import os -import sys -from unittest import skipIf - -from marionette_driver import By - -# add this directory to the path -sys.path.append(os.path.dirname(__file__)) - -from test_switch_window_content import TestSwitchToWindowContent - - -class TestSwitchWindowChrome(TestSwitchToWindowContent): - - def setUp(self): - super(TestSwitchWindowChrome, self).setUp() - - self.marionette.set_context("chrome") - - def tearDown(self): - self.close_all_windows() - - super(TestSwitchWindowChrome, self).tearDown() - - def open_window_in_background(self): - with self.marionette.using_context("chrome"): - self.marionette.execute_script(""" - window.open("about:blank", null, "location=1,toolbar=1"); - window.focus(); - """) - - def open_window_in_foreground(self): - with self.marionette.using_context("content"): - self.marionette.navigate(self.test_page) - link = self.marionette.find_element(By.ID, "new-window") - link.click() - - @skipIf(sys.platform.startswith("linux"), - "Bug 1335457 - Fails to open a background window on Linux") - def test_switch_tabs_for_new_background_window_without_focus_change(self): - # Bug 1334981 - with testmode enabled getMostRecentWindow detects the wrong window - with self.marionette.using_prefs({"focusmanager.testmode": False}): - # Open an addition tab in the original window so we can better check - # the selected index in thew new window to be opened. - second_tab = self.open_tab(trigger=self.open_tab_in_foreground) - self.marionette.switch_to_window(second_tab, focus=True) - second_tab_index = self.get_selected_tab_index() - self.assertNotEqual(second_tab_index, self.selected_tab_index) - - # Opens a new background window, but we are interested in the tab - tab_in_new_window = self.open_tab(trigger=self.open_window_in_background) - self.assertEqual(self.marionette.current_window_handle, second_tab) - self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window) - self.assertEqual(self.get_selected_tab_index(), second_tab_index) - with self.marionette.using_context("content"): - self.assertEqual(self.marionette.get_url(), self.empty_page) - - # Switch to the tab in the new window but don't focus it - self.marionette.switch_to_window(tab_in_new_window, focus=False) - self.assertEqual(self.marionette.current_window_handle, tab_in_new_window) - self.assertNotEqual(self.marionette.current_chrome_window_handle, self.start_window) - self.assertEqual(self.get_selected_tab_index(), second_tab_index) - with self.marionette.using_context("content"): - self.assertEqual(self.marionette.get_url(), "about:blank") - - def test_switch_tabs_for_new_foreground_window_with_focus_change(self): - # Open an addition tab in the original window so we can better check - # the selected index in thew new window to be opened. - second_tab = self.open_tab(trigger=self.open_tab_in_foreground) - self.marionette.switch_to_window(second_tab, focus=True) - second_tab_index = self.get_selected_tab_index() - self.assertNotEqual(second_tab_index, self.selected_tab_index) - - # Opens a new window, but we are interested in the tab - tab_in_new_window = self.open_tab(trigger=self.open_window_in_foreground) - self.assertEqual(self.marionette.current_window_handle, second_tab) - self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window) - self.assertNotEqual(self.get_selected_tab_index(), second_tab_index) - with self.marionette.using_context("content"): - self.assertEqual(self.marionette.get_url(), self.test_page) - - self.marionette.switch_to_window(tab_in_new_window) - self.assertEqual(self.marionette.current_window_handle, tab_in_new_window) - self.assertNotEqual(self.marionette.current_chrome_window_handle, self.start_window) - self.assertNotEqual(self.get_selected_tab_index(), second_tab_index) - with self.marionette.using_context("content"): - self.assertEqual(self.marionette.get_url(), self.empty_page) - - self.marionette.switch_to_window(second_tab, focus=True) - self.assertEqual(self.marionette.current_window_handle, second_tab) - self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window) - # Bug 1335085 - The focus doesn't change even as requested so. - # self.assertEqual(self.get_selected_tab_index(), second_tab_index) - with self.marionette.using_context("content"): - self.assertEqual(self.marionette.get_url(), self.test_page) - - def test_switch_tabs_for_new_foreground_window_without_focus_change(self): - # Open an addition tab in the original window so we can better check - # the selected index in thew new window to be opened. - second_tab = self.open_tab(trigger=self.open_tab_in_foreground) - self.marionette.switch_to_window(second_tab, focus=True) - second_tab_index = self.get_selected_tab_index() - self.assertNotEqual(second_tab_index, self.selected_tab_index) - - # Opens a new window, but we are interested in the tab which automatically - # gets the focus. - self.open_tab(trigger=self.open_window_in_foreground) - self.assertEqual(self.marionette.current_window_handle, second_tab) - self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window) - self.assertNotEqual(self.get_selected_tab_index(), second_tab_index) - with self.marionette.using_context("content"): - self.assertEqual(self.marionette.get_url(), self.test_page) - - # Switch to the second tab in the first window, but don't focus it. - self.marionette.switch_to_window(second_tab, focus=False) - self.assertEqual(self.marionette.current_window_handle, second_tab) - self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window) - self.assertNotEqual(self.get_selected_tab_index(), second_tab_index) - with self.marionette.using_context("content"): - self.assertEqual(self.marionette.get_url(), self.test_page) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_switch_window_content.py b/testing/marionette/harness/marionette_harness/tests/unit/test_switch_window_content.py deleted file mode 100644 index fbab1898f..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_switch_window_content.py +++ /dev/null @@ -1,171 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla ublic -# 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 Actions, By, Wait -from marionette_driver.keys import Keys - -from marionette_harness import MarionetteTestCase, skip_if_mobile, WindowManagerMixin - - -class TestSwitchToWindowContent(WindowManagerMixin, MarionetteTestCase): - - def setUp(self): - super(TestSwitchToWindowContent, self).setUp() - - if self.marionette.session_capabilities["platformName"] == "darwin": - self.mod_key = Keys.META - else: - self.mod_key = Keys.CONTROL - - self.empty_page = self.marionette.absolute_url("empty.html") - self.test_page = self.marionette.absolute_url("windowHandles.html") - - self.selected_tab_index = self.get_selected_tab_index() - - with self.marionette.using_context("content"): - self.marionette.navigate(self.test_page) - - def tearDown(self): - self.close_all_tabs() - - super(TestSwitchToWindowContent, self).tearDown() - - def get_selected_tab_index(self): - with self.marionette.using_context("chrome"): - return self.marionette.execute_script(""" - Components.utils.import("resource://gre/modules/AppConstants.jsm"); - - let win = null; - - if (AppConstants.MOZ_APP_NAME == "fennec") { - Components.utils.import("resource://gre/modules/Services.jsm"); - win = Services.wm.getMostRecentWindow("navigator:browser"); - } else { - Components.utils.import("resource:///modules/RecentWindow.jsm"); - win = RecentWindow.getMostRecentBrowserWindow(); - } - - let tabBrowser = null; - - // Fennec - if (win.BrowserApp) { - tabBrowser = win.BrowserApp; - - // Firefox - } else if (win.gBrowser) { - tabBrowser = win.gBrowser; - - } else { - return null; - } - - for (let i = 0; i < tabBrowser.tabs.length; i++) { - if (tabBrowser.tabs[i] == tabBrowser.selectedTab) { - return i; - } - } - """) - - def open_tab_in_background(self): - with self.marionette.using_context("content"): - link = self.marionette.find_element(By.ID, "new-tab") - - action = Actions(self.marionette) - action.key_down(self.mod_key).click(link).perform() - - def open_tab_in_foreground(self): - with self.marionette.using_context("content"): - link = self.marionette.find_element(By.ID, "new-tab") - link.click() - - def test_switch_tabs_with_focus_change(self): - new_tab = self.open_tab(self.open_tab_in_foreground) - self.assertEqual(self.marionette.current_window_handle, self.start_tab) - self.assertNotEqual(self.get_selected_tab_index(), self.selected_tab_index) - with self.marionette.using_context("content"): - self.assertEqual(self.marionette.get_url(), self.test_page) - - self.marionette.switch_to_window(new_tab) - self.assertEqual(self.marionette.current_window_handle, new_tab) - self.assertNotEqual(self.get_selected_tab_index(), self.selected_tab_index) - - with self.marionette.using_context("content"): - Wait(self.marionette).until( - lambda _: self.marionette.get_url() == self.empty_page, - message="{} has been loaded in the newly opened tab.".format(self.empty_page)) - - self.marionette.switch_to_window(self.start_tab, focus=True) - self.assertEqual(self.marionette.current_window_handle, self.start_tab) - self.assertEqual(self.get_selected_tab_index(), self.selected_tab_index) - with self.marionette.using_context("content"): - self.assertEqual(self.marionette.get_url(), self.test_page) - - self.marionette.switch_to_window(new_tab) - self.marionette.close() - self.marionette.switch_to_window(self.start_tab) - - self.assertEqual(self.marionette.current_window_handle, self.start_tab) - self.assertEqual(self.get_selected_tab_index(), self.selected_tab_index) - with self.marionette.using_context("content"): - self.assertEqual(self.marionette.get_url(), self.test_page) - - def test_switch_tabs_without_focus_change(self): - new_tab = self.open_tab(self.open_tab_in_foreground) - self.assertEqual(self.marionette.current_window_handle, self.start_tab) - self.assertNotEqual(self.get_selected_tab_index(), self.selected_tab_index) - with self.marionette.using_context("content"): - self.assertEqual(self.marionette.get_url(), self.test_page) - - # Switch to new tab first because it is already selected - self.marionette.switch_to_window(new_tab) - self.assertEqual(self.marionette.current_window_handle, new_tab) - - self.marionette.switch_to_window(self.start_tab, focus=False) - self.assertEqual(self.marionette.current_window_handle, self.start_tab) - self.assertNotEqual(self.get_selected_tab_index(), self.selected_tab_index) - - with self.marionette.using_context("content"): - self.assertEqual(self.marionette.get_url(), self.test_page) - - self.marionette.switch_to_window(new_tab) - self.marionette.close() - - self.marionette.switch_to_window(self.start_tab) - self.assertEqual(self.marionette.current_window_handle, self.start_tab) - self.assertEqual(self.get_selected_tab_index(), self.selected_tab_index) - with self.marionette.using_context("content"): - self.assertEqual(self.marionette.get_url(), self.test_page) - - def test_switch_from_content_to_chrome_window_should_not_change_selected_tab(self): - new_tab = self.open_tab(self.open_tab_in_foreground) - - self.marionette.switch_to_window(new_tab) - self.assertEqual(self.marionette.current_window_handle, new_tab) - new_tab_index = self.get_selected_tab_index() - - self.marionette.switch_to_window(self.start_window) - self.assertEqual(self.marionette.current_window_handle, new_tab) - self.assertEqual(self.get_selected_tab_index(), new_tab_index) - - @skip_if_mobile("New windows not supported in Fennec") - def test_switch_to_new_private_browsing_window_has_to_register_browsers(self): - # Test that tabs (browsers) are correctly registered for a newly opened - # private browsing window. This has to also happen without explicitely - # switching to the tab itself before using any commands in content scope. - # - # Note: Not sure why this only affects private browsing windows only. - - def open_private_browsing_window(): - with self.marionette.using_context("content"): - self.marionette.navigate("about:privatebrowsing") - button = self.marionette.find_element(By.ID, "startPrivateBrowsing") - button.click() - - new_window = self.open_window(open_private_browsing_window) - self.marionette.switch_to_window(new_window) - self.assertEqual(self.marionette.current_chrome_window_handle, new_window) - self.assertNotEqual(self.marionette.current_window_handle, self.start_tab) - - with self.marionette.using_context("content"): - self.marionette.execute_script(" return true; ") diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_teardown_context_preserved.py b/testing/marionette/harness/marionette_harness/tests/unit/test_teardown_context_preserved.py deleted file mode 100644 index 843152bc5..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_teardown_context_preserved.py +++ /dev/null @@ -1,21 +0,0 @@ -# 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_harness import MarionetteTestCase, SkipTest - - -class TestTearDownContext(MarionetteTestCase): - def setUp(self): - MarionetteTestCase.setUp(self) - self.marionette.set_context(self.marionette.CONTEXT_CHROME) - - def tearDown(self): - self.assertEqual(self.get_context(), self.marionette.CONTEXT_CHROME) - MarionetteTestCase.tearDown(self) - - def get_context(self): - return self.marionette._send_message("getContext", key="value") - - def test_skipped_teardown_ok(self): - raise SkipTest("This should leave our teardown method in chrome context") diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_text.py b/testing/marionette/harness/marionette_harness/tests/unit/test_text.py deleted file mode 100644 index e2025e9b6..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_text.py +++ /dev/null @@ -1,224 +0,0 @@ -# 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.by import By -from marionette_driver.keys import Keys - -from marionette_harness import MarionetteTestCase, skip_if_mobile - - -class TestText(MarionetteTestCase): - def test_getText(self): - test_html = self.marionette.absolute_url("test.html") - self.marionette.navigate(test_html) - l = self.marionette.find_element(By.ID, "mozLink") - self.assertEqual("Click me!", l.text) - - def test_clearText(self): - test_html = self.marionette.absolute_url("test.html") - self.marionette.navigate(test_html) - l = self.marionette.find_element(By.NAME, "myInput") - self.assertEqual("asdf", self.marionette.execute_script("return arguments[0].value;", [l])) - l.clear() - self.assertEqual("", self.marionette.execute_script("return arguments[0].value;", [l])) - - def test_sendKeys(self): - test_html = self.marionette.absolute_url("test.html") - self.marionette.navigate(test_html) - l = self.marionette.find_element(By.NAME, "myInput") - self.assertEqual("asdf", self.marionette.execute_script("return arguments[0].value;", [l])) - - # Set caret position to the middle of the input text. - self.marionette.execute_script( - """var el = arguments[0]; - el.selectionStart = el.selectionEnd = el.value.length / 2;""", - script_args=[l]) - - l.send_keys("o") - self.assertEqual("asodf", self.marionette.execute_script("return arguments[0].value;", [l])) - - def test_send_keys_to_type_input(self): - test_html = self.marionette.absolute_url("html5/test_html_inputs.html") - self.marionette.navigate(test_html) - num_input = self.marionette.find_element(By.ID, 'number') - self.assertEqual("", self.marionette.execute_script("return arguments[0].value", [num_input])) - num_input.send_keys("1234") - self.assertEqual('1234', self.marionette.execute_script("return arguments[0].value", [num_input])) - - def test_should_fire_key_press_events(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - key_reporter = self.marionette.find_element(By.ID, "keyReporter") - key_reporter.send_keys("a") - - result = self.marionette.find_element(By.ID, "result") - self.assertIn("press:", result.text) - - def test_should_fire_key_down_events(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - key_reporter = self.marionette.find_element(By.ID, "keyReporter") - key_reporter.send_keys("a") - - result = self.marionette.find_element(By.ID, "result") - self.assertIn("down:", result.text) - - def test_should_fire_key_up_events(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - key_reporter = self.marionette.find_element(By.ID, "keyReporter") - key_reporter.send_keys("a") - - result = self.marionette.find_element(By.ID, "result") - self.assertIn("up:", result.text) - - def test_should_type_lowercase_characters(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - key_reporter = self.marionette.find_element(By.ID, "keyReporter") - key_reporter.send_keys("abc def") - - self.assertEqual("abc def", key_reporter.get_property("value")) - - def test_should_type_uppercase_characters(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - key_reporter = self.marionette.find_element(By.ID, "keyReporter") - key_reporter.send_keys("ABC DEF") - - self.assertEqual("ABC DEF", key_reporter.get_property("value")) - - def test_should_type_a_quote_characters(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - key_reporter = self.marionette.find_element(By.ID, "keyReporter") - key_reporter.send_keys('"') - - self.assertEqual('"', key_reporter.get_property("value")) - - def test_should_type_an_at_character(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - key_reporter = self.marionette.find_element(By.ID, "keyReporter") - key_reporter.send_keys('@') - - self.assertEqual("@", key_reporter.get_property("value")) - - def test_should_type_a_mix_of_upper_and_lower_case_character(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - key_reporter = self.marionette.find_element(By.ID, "keyReporter") - key_reporter.send_keys("me@EXampLe.com") - - self.assertEqual("me@EXampLe.com", key_reporter.get_property("value")) - - def test_arrow_keys_are_not_printable(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - key_reporter = self.marionette.find_element(By.ID, "keyReporter") - key_reporter.send_keys(Keys.ARROW_LEFT) - - self.assertEqual("", key_reporter.get_property("value")) - - def test_will_simulate_a_key_up_when_entering_text_into_input_elements(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - element = self.marionette.find_element(By.ID, "keyUp") - element.send_keys("I like cheese") - - result = self.marionette.find_element(By.ID, "result") - self.assertEqual(result.text, "I like cheese") - - def test_will_simulate_a_key_down_when_entering_text_into_input_elements(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - element = self.marionette.find_element(By.ID, "keyDown") - element.send_keys("I like cheese") - - result = self.marionette.find_element(By.ID, "result") - # Because the key down gets the result before the input element is - # filled, we're a letter short here - self.assertEqual(result.text, "I like chees") - - def test_will_simulate_a_key_press_when_entering_text_into_input_elements(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - element = self.marionette.find_element(By.ID, "keyPress") - element.send_keys("I like cheese") - - result = self.marionette.find_element(By.ID, "result") - # Because the key down gets the result before the input element is - # filled, we're a letter short here - self.assertEqual(result.text, "I like chees") - - def test_will_simulate_a_keyup_when_entering_text_into_textareas(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - element = self.marionette.find_element(By.ID, "keyUpArea") - element.send_keys("I like cheese") - - result = self.marionette.find_element(By.ID, "result") - self.assertEqual(result.text, "I like cheese") - - def test_will_simulate_a_keydown_when_entering_text_into_textareas(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - element = self.marionette.find_element(By.ID, "keyDownArea") - element.send_keys("I like cheese") - - result = self.marionette.find_element(By.ID, "result") - # Because the key down gets the result before the input element is - # filled, we're a letter short here - self.assertEqual(result.text, "I like chees") - - def test_will_simulate_a_keypress_when_entering_text_into_textareas(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - element = self.marionette.find_element(By.ID, "keyPressArea") - element.send_keys("I like cheese") - - result = self.marionette.find_element(By.ID, "result") - # Because the key down gets the result before the input element is - # filled, we're a letter short here - self.assertEqual(result.text, "I like chees") - - @skip_if_mobile("Bug 1333069 - Assertion: 'down: 40' not found in u''") - def test_should_report_key_code_of_arrow_keys_up_down_events(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - result = self.marionette.find_element(By.ID, "result") - element = self.marionette.find_element(By.ID, "keyReporter") - element.send_keys(Keys.ARROW_DOWN) - self.assertIn("down: 40", result.text.strip()) - self.assertIn("up: 40", result.text.strip()) - - element.send_keys(Keys.ARROW_UP) - self.assertIn("down: 38", result.text.strip()) - self.assertIn("up: 38", result.text.strip()) - - element.send_keys(Keys.ARROW_LEFT) - self.assertIn("down: 37", result.text.strip()) - self.assertIn("up: 37", result.text.strip()) - - element.send_keys(Keys.ARROW_RIGHT) - self.assertIn("down: 39", result.text.strip()) - self.assertIn("up: 39", result.text.strip()) - - # And leave no rubbish/printable keys in the "keyReporter" - self.assertEqual("", element.get_property("value")) - - def testNumericNonShiftKeys(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - element = self.marionette.find_element(By.ID, "keyReporter") - numericLineCharsNonShifted = "`1234567890-=[]\\,.'/42" - element.send_keys(numericLineCharsNonShifted) - self.assertEqual(numericLineCharsNonShifted, element.get_property("value")) - - def testShouldTypeAnInteger(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - element = self.marionette.find_element(By.ID, "keyReporter") - element.send_keys(1234) - self.assertEqual("1234", element.get_property("value")) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_text_chrome.py b/testing/marionette/harness/marionette_harness/tests/unit/test_text_chrome.py deleted file mode 100644 index e0b63de16..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_text_chrome.py +++ /dev/null @@ -1,44 +0,0 @@ -# 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.by import By - -from marionette_harness import MarionetteTestCase, skip, WindowManagerMixin - - -@skip("Disabled in bug 896043 and when working on Chrome code re-enable for bug 896046") -class TestTextChrome(WindowManagerMixin, MarionetteTestCase): - - def setUp(self): - super(TestTextChrome, self).setUp() - self.marionette.set_context("chrome") - - def open_window_with_js(): - self.marionette.execute_script(""" - window.open('chrome://marionette/content/test.xul', - 'foo', 'chrome,centerscreen'); - """) - - new_window = self.open_window(trigger=open_window_with_js) - self.marionette.switch_to_window(new_window) - - def tearDown(self): - self.close_all_windows() - super(TestTextChrome, self).tearDown() - - def test_getText(self): - box = self.marionette.find_element(By.ID, "textInput") - self.assertEqual("test", box.text) - - def test_clearText(self): - box = self.marionette.find_element(By.ID, "textInput") - self.assertEqual("test", box.text) - box.clear() - self.assertEqual("", box.text) - - def test_sendKeys(self): - box = self.marionette.find_element(By.ID, "textInput") - self.assertEqual("test", box.text) - box.send_keys("at") - self.assertEqual("attest", box.text) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_timeouts.py b/testing/marionette/harness/marionette_harness/tests/unit/test_timeouts.py deleted file mode 100644 index 354e6a7eb..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_timeouts.py +++ /dev/null @@ -1,115 +0,0 @@ -# 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.by import By -from marionette_driver.errors import ( - MarionetteException, - NoSuchElementException, - ScriptTimeoutException, -) -from marionette_driver.marionette import HTMLElement - -from marionette_harness import MarionetteTestCase, run_if_manage_instance, skip_if_mobile - - -class TestTimeouts(MarionetteTestCase): - def tearDown(self): - self.marionette.timeout.reset() - MarionetteTestCase.tearDown(self) - - def test_page_timeout_notdefinetimeout_pass(self): - test_html = self.marionette.absolute_url("test.html") - self.marionette.navigate(test_html) - - def test_page_timeout_fail(self): - self.marionette.timeout.page_load = 0 - test_html = self.marionette.absolute_url("test.html") - self.assertRaises(MarionetteException, self.marionette.navigate, test_html) - - def test_page_timeout_pass(self): - self.marionette.timeout.page_load = 60 - test_html = self.marionette.absolute_url("test.html") - self.marionette.navigate(test_html) - - def test_search_timeout_notfound_settimeout(self): - test_html = self.marionette.absolute_url("test.html") - self.marionette.navigate(test_html) - self.marionette.timeout.implicit = 1 - self.assertRaises(NoSuchElementException, self.marionette.find_element, By.ID, "I'm not on the page") - self.marionette.timeout.implicit = 0 - self.assertRaises(NoSuchElementException, self.marionette.find_element, By.ID, "I'm not on the page") - - def test_search_timeout_found_settimeout(self): - test_html = self.marionette.absolute_url("test.html") - self.marionette.navigate(test_html) - button = self.marionette.find_element(By.ID, "createDivButton") - button.click() - self.marionette.timeout.implicit = 8 - self.assertEqual(HTMLElement, type(self.marionette.find_element(By.ID, "newDiv"))) - - def test_search_timeout_found(self): - test_html = self.marionette.absolute_url("test.html") - self.marionette.navigate(test_html) - button = self.marionette.find_element(By.ID, "createDivButton") - button.click() - self.assertRaises(NoSuchElementException, self.marionette.find_element, By.ID, "newDiv") - - @run_if_manage_instance("Only runnable if Marionette manages the instance") - @skip_if_mobile("Bug 1322993 - Missing temporary folder") - def test_reset_timeout(self): - timeouts = [getattr(self.marionette.timeout, f) for f in ( - 'implicit', 'page_load', 'script',)] - - def do_check(callback): - for timeout in timeouts: - timeout = 10000 - self.assertEqual(timeout, 10000) - callback() - for timeout in timeouts: - self.assertNotEqual(timeout, 10000) - - def callback_quit(): - self.marionette.quit() - self.marionette.start_session() - - do_check(self.marionette.restart) - do_check(callback_quit) - - def test_execute_async_timeout_settimeout(self): - test_html = self.marionette.absolute_url("test.html") - self.marionette.navigate(test_html) - self.marionette.timeout.script = 1 - self.assertRaises(ScriptTimeoutException, self.marionette.execute_async_script, "var x = 1;") - - def test_no_timeout_settimeout(self): - test_html = self.marionette.absolute_url("test.html") - self.marionette.navigate(test_html) - self.marionette.timeout.script = 1 - self.assertTrue(self.marionette.execute_async_script(""" - var callback = arguments[arguments.length - 1]; - setTimeout(function() { callback(true); }, 500); - """)) - - def test_compat_input_types(self): - # When using the spec-incompatible input format which we have - # for backwards compatibility, it should be possible to send ms - # as a string type and have the server parseInt it to an integer. - body = {"type": "script", "ms": "30000"} - self.marionette._send_message("setTimeouts", body) - - def test_deprecated_set_timeouts_command(self): - body = {"implicit": 3000} - self.marionette._send_message("timeouts", body) - - def test_deprecated_set_search_timeout(self): - self.marionette.set_search_timeout(1000) - self.assertEqual(1, self.marionette.timeout.implicit) - - def test_deprecated_set_script_timeout(self): - self.marionette.set_script_timeout(2000) - self.assertEqual(2, self.marionette.timeout.script) - - def test_deprecated_set_page_load_timeout(self): - self.marionette.set_page_load_timeout(3000) - self.assertEqual(3, self.marionette.timeout.page_load) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_transport.py b/testing/marionette/harness/marionette_harness/tests/unit/test_transport.py deleted file mode 100644 index 39e36a9b2..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_transport.py +++ /dev/null @@ -1,172 +0,0 @@ -# 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/. - -import json - -from marionette_driver.transport import ( - Command, - Proto2Command, - Proto2Response, - Response, -) - -from marionette_harness import MarionetteTestCase, skip_unless_protocol - - -get_current_url = ("getCurrentUrl", None) -execute_script = ("executeScript", {"script": "return 42"}) - - -class TestMessageSequencing(MarionetteTestCase): - @property - def last_id(self): - return self.marionette.client.last_id - - @last_id.setter - def last_id(self, new_id): - self.marionette.client.last_id = new_id - - def send(self, name, params): - self.last_id = self.last_id + 1 - cmd = Command(self.last_id, name, params) - self.marionette.client.send(cmd) - return self.last_id - - @skip_unless_protocol("Skip for level < 3", lambda level: level >= 3) - def test_discard_older_messages(self): - first = self.send(*get_current_url) - second = self.send(*execute_script) - resp = self.marionette.client.receive() - self.assertEqual(second, resp.id) - - @skip_unless_protocol("Skip for level < 3", lambda level: level >= 3) - def test_last_id_incremented(self): - before = self.last_id - self.send(*get_current_url) - self.assertGreater(self.last_id, before) - - -class MessageTestCase(MarionetteTestCase): - def assert_attr(self, obj, attr): - self.assertTrue(hasattr(obj, attr), - "object does not have attribute {}".format(attr)) - - -class TestCommand(MessageTestCase): - def create(self, msgid="msgid", name="name", params="params"): - return Command(msgid, name, params) - - def test_initialise(self): - cmd = self.create() - self.assert_attr(cmd, "id") - self.assert_attr(cmd, "name") - self.assert_attr(cmd, "params") - self.assertEqual("msgid", cmd.id) - self.assertEqual("name", cmd.name) - self.assertEqual("params", cmd.params) - - def test_stringify(self): - cmd = self.create() - string = str(cmd) - self.assertIn("Command", string) - self.assertIn("id=msgid", string) - self.assertIn("name=name", string) - self.assertIn("params=params", string) - - def test_to_msg(self): - cmd = self.create() - msg = json.loads(cmd.to_msg()) - self.assertEquals(msg[0], Command.TYPE) - self.assertEquals(msg[1], "msgid") - self.assertEquals(msg[2], "name") - self.assertEquals(msg[3], "params") - - def test_from_msg(self): - msg = [Command.TYPE, "msgid", "name", "params"] - payload = json.dumps(msg) - cmd = Command.from_msg(payload) - self.assertEquals(msg[1], cmd.id) - self.assertEquals(msg[2], cmd.name) - self.assertEquals(msg[3], cmd.params) - - -class TestResponse(MessageTestCase): - def create(self, msgid="msgid", error="error", result="result"): - return Response(msgid, error, result) - - def test_initialise(self): - resp = self.create() - self.assert_attr(resp, "id") - self.assert_attr(resp, "error") - self.assert_attr(resp, "result") - self.assertEqual("msgid", resp.id) - self.assertEqual("error", resp.error) - self.assertEqual("result", resp.result) - - def test_stringify(self): - resp = self.create() - string = str(resp) - self.assertIn("Response", string) - self.assertIn("id=msgid", string) - self.assertIn("error=error", string) - self.assertIn("result=result", string) - - def test_to_msg(self): - resp = self.create() - msg = json.loads(resp.to_msg()) - self.assertEquals(msg[0], Response.TYPE) - self.assertEquals(msg[1], "msgid") - self.assertEquals(msg[2], "error") - self.assertEquals(msg[3], "result") - - def test_from_msg(self): - msg = [Response.TYPE, "msgid", "error", "result"] - payload = json.dumps(msg) - resp = Response.from_msg(payload) - self.assertEquals(msg[1], resp.id) - self.assertEquals(msg[2], resp.error) - self.assertEquals(msg[3], resp.result) - - -class TestProto2Command(MessageTestCase): - def create(self, name="name", params="params"): - return Proto2Command(name, params) - - def test_initialise(self): - cmd = self.create() - self.assert_attr(cmd, "id") - self.assert_attr(cmd, "name") - self.assert_attr(cmd, "params") - self.assertEqual(None, cmd.id) - self.assertEqual("name", cmd.name) - self.assertEqual("params", cmd.params) - - def test_from_data_unknown(self): - with self.assertRaises(ValueError): - cmd = Proto2Command.from_data({}) - - -class TestProto2Response(MessageTestCase): - def create(self, error="error", result="result"): - return Proto2Response(error, result) - - def test_initialise(self): - resp = self.create() - self.assert_attr(resp, "id") - self.assert_attr(resp, "error") - self.assert_attr(resp, "result") - self.assertEqual(None, resp.id) - self.assertEqual("error", resp.error) - self.assertEqual("result", resp.result) - - def test_from_data_error(self): - data = {"error": "error"} - resp = Proto2Response.from_data(data) - self.assertEqual(data, resp.error) - self.assertEqual(None, resp.result) - - def test_from_data_result(self): - resp = Proto2Response.from_data("result") - self.assertEqual(None, resp.error) - self.assertEqual("result", resp.result) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_typing.py b/testing/marionette/harness/marionette_harness/tests/unit/test_typing.py deleted file mode 100644 index ca63a0dc7..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_typing.py +++ /dev/null @@ -1,332 +0,0 @@ -# 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/. - -import urllib - -from marionette_driver.by import By -from marionette_driver.errors import ElementNotInteractableException -from marionette_driver.keys import Keys - -from marionette_harness import MarionetteTestCase, skip, skip_if_mobile - - -def inline(doc): - return "data:text/html;charset=utf-8,{}".format(urllib.quote(doc)) - - -class TypingTestCase(MarionetteTestCase): - - def setUp(self): - super(TypingTestCase, self).setUp() - - if self.marionette.session_capabilities["platformName"] == "darwin": - self.mod_key = Keys.META - else: - self.mod_key = Keys.CONTROL - - -class TestTypingChrome(TypingTestCase): - - def setUp(self): - super(TestTypingChrome, self).setUp() - self.marionette.set_context("chrome") - - @skip_if_mobile("Interacting with chrome elements not available for Fennec") - def test_cut_and_paste_shortcuts(self): - with self.marionette.using_context("content"): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - - keyReporter = self.marionette.find_element(By.ID, "keyReporter") - self.assertEqual("", keyReporter.get_property("value")) - keyReporter.send_keys("zyxwvutsr") - self.assertEqual("zyxwvutsr", keyReporter.get_property("value")) - - # select all and cut - keyReporter.send_keys(self.mod_key, "a") - keyReporter.send_keys(self.mod_key, "x") - self.assertEqual("", keyReporter.get_property("value")) - - url_bar = self.marionette.find_element(By.ID, "urlbar") - - # Clear contents first - url_bar.send_keys(self.mod_key, "a") - url_bar.send_keys(Keys.BACK_SPACE) - self.assertEqual("", url_bar.get_attribute("value")) - - url_bar.send_keys(self.mod_key, "v") - self.assertEqual("zyxwvutsr", url_bar.get_property("value")) - - -class TestTypingContent(TypingTestCase): - - def testShouldFireKeyPressEvents(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - keyReporter = self.marionette.find_element(By.ID, "keyReporter") - keyReporter.send_keys("a") - result = self.marionette.find_element(By.ID, "result") - self.assertTrue("press:" in result.text) - - def testShouldFireKeyDownEvents(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - keyReporter = self.marionette.find_element(By.ID, "keyReporter") - keyReporter.send_keys("I") - result = self.marionette.find_element(By.ID, "result") - self.assertTrue("down" in result.text) - - def testShouldFireKeyUpEvents(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - - keyReporter = self.marionette.find_element(By.ID, "keyReporter") - keyReporter.send_keys("a") - result = self.marionette.find_element(By.ID, "result") - self.assertTrue("up:" in result.text) - - def testShouldTypeLowerCaseLetters(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - - keyReporter = self.marionette.find_element(By.ID, "keyReporter") - keyReporter.send_keys("abc def") - self.assertEqual("abc def", keyReporter.get_property("value")) - - def testShouldBeAbleToTypeCapitalLetters(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - - keyReporter = self.marionette.find_element(By.ID, "keyReporter") - keyReporter.send_keys("ABC DEF") - self.assertEqual("ABC DEF", keyReporter.get_property("value")) - - def testCutAndPasteShortcuts(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - - keyReporter = self.marionette.find_element(By.ID, "keyReporter") - self.assertEqual("", keyReporter.get_property("value")) - keyReporter.send_keys("zyxwvutsr") - self.assertEqual("zyxwvutsr", keyReporter.get_property("value")) - - # select all and cut - keyReporter.send_keys(self.mod_key, "a") - keyReporter.send_keys(self.mod_key, "x") - self.assertEqual("", keyReporter.get_property("value")) - - keyReporter.send_keys(self.mod_key, "v") - self.assertEqual("zyxwvutsr", keyReporter.get_property("value")) - - def testShouldBeAbleToTypeQuoteMarks(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - - keyReporter = self.marionette.find_element(By.ID, "keyReporter") - keyReporter.send_keys("\"") - self.assertEqual("\"", keyReporter.get_property("value")) - - def testShouldBeAbleToTypeTheAtCharacter(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - - keyReporter = self.marionette.find_element(By.ID, "keyReporter") - keyReporter.send_keys("@") - self.assertEqual("@", keyReporter.get_property("value")) - - def testShouldBeAbleToMixUpperAndLowerCaseLetters(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - - keyReporter = self.marionette.find_element(By.ID, "keyReporter") - keyReporter.send_keys("me@eXample.com") - self.assertEqual("me@eXample.com", keyReporter.get_property("value")) - - def testArrowKeysShouldNotBePrintable(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - - keyReporter = self.marionette.find_element(By.ID, "keyReporter") - keyReporter.send_keys(Keys.ARROW_LEFT) - self.assertEqual("", keyReporter.get_property("value")) - - def testWillSimulateAKeyUpWhenEnteringTextIntoInputElements(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - - element = self.marionette.find_element(By.ID, "keyUp") - element.send_keys("I like cheese") - result = self.marionette.find_element(By.ID, "result") - self.assertEqual(result.text, "I like cheese") - - def testWillSimulateAKeyDownWhenEnteringTextIntoInputElements(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - - element = self.marionette.find_element(By.ID, "keyDown") - element.send_keys("I like cheese") - result = self.marionette.find_element(By.ID, "result") - # Because the key down gets the result before the input element is - # filled, we're a letter short here - self.assertEqual(result.text, "I like chees") - - def testWillSimulateAKeyPressWhenEnteringTextIntoInputElements(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - - element = self.marionette.find_element(By.ID, "keyPress") - element.send_keys("I like cheese") - result = self.marionette.find_element(By.ID, "result") - # Because the key down gets the result before the input element is - # filled, we're a letter short here - self.assertEqual(result.text, "I like chees") - - def testWillSimulateAKeyUpWhenEnteringTextIntoTextAreas(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - - element = self.marionette.find_element(By.ID, "keyUpArea") - element.send_keys("I like cheese") - result = self.marionette.find_element(By.ID, "result") - self.assertEqual("I like cheese", result.text) - - def testWillSimulateAKeyDownWhenEnteringTextIntoTextAreas(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - - element = self.marionette.find_element(By.ID, "keyDownArea") - element.send_keys("I like cheese") - result = self.marionette.find_element(By.ID, "result") - # Because the key down gets the result before the input element is - # filled, we're a letter short here - self.assertEqual(result.text, "I like chees") - - def testWillSimulateAKeyPressWhenEnteringTextIntoTextAreas(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - - element = self.marionette.find_element(By.ID, "keyPressArea") - element.send_keys("I like cheese") - result = self.marionette.find_element(By.ID, "result") - # Because the key down gets the result before the input element is - # filled, we're a letter short here - self.assertEqual(result.text, "I like chees") - - @skip_if_mobile("Bug 1324752 - Arrow keys cannot be sent in Fennec") - def testShouldReportKeyCodeOfArrowKeysUpDownEvents(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - - result = self.marionette.find_element(By.ID, "result") - element = self.marionette.find_element(By.ID, "keyReporter") - - element.send_keys(Keys.ARROW_DOWN) - - self.assertIn("down: 40", result.text.strip()) - self.assertIn("up: 40", result.text.strip()) - - element.send_keys(Keys.ARROW_UP) - self.assertIn("down: 38", result.text.strip()) - self.assertIn("up: 38", result.text.strip()) - - element.send_keys(Keys.ARROW_LEFT) - self.assertIn("down: 37", result.text.strip()) - self.assertIn("up: 37", result.text.strip()) - - element.send_keys(Keys.ARROW_RIGHT) - self.assertIn("down: 39", result.text.strip()) - self.assertIn("up: 39", result.text.strip()) - - # And leave no rubbish/printable keys in the "keyReporter" - self.assertEqual("", element.get_property("value")) - - @skip("Reenable in Bug 1068728") - def testNumericShiftKeys(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - - result = self.marionette.find_element(By.ID, "result") - element = self.marionette.find_element(By.ID, "keyReporter") - numericShiftsEtc = "~!@#$%^&*()_+{}:i\"<>?|END~" - element.send_keys(numericShiftsEtc) - self.assertEqual(numericShiftsEtc, element.get_property("value")) - self.assertIn(" up: 16", result.text.strip()) - - def testLowerCaseAlphaKeys(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - - element = self.marionette.find_element(By.ID, "keyReporter") - lowerAlphas = "abcdefghijklmnopqrstuvwxyz" - element.send_keys(lowerAlphas) - self.assertEqual(lowerAlphas, element.get_property("value")) - - @skip("Reenable in Bug 1068735") - def testUppercaseAlphaKeys(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - - result = self.marionette.find_element(By.ID, "result") - element = self.marionette.find_element(By.ID, "keyReporter") - upperAlphas = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - element.send_keys(upperAlphas) - self.assertEqual(upperAlphas, element.get_property("value")) - self.assertIn(" up: 16", result.text.strip()) - - @skip("Reenable in Bug 1068726") - def testAllPrintableKeys(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - - result = self.marionette.find_element(By.ID, "result") - element = self.marionette.find_element(By.ID, "keyReporter") - allPrintable = "!\"#$%&'()*+,-./0123456789:<=>?@ ABCDEFGHIJKLMNOPQRSTUVWXYZ [\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" - element.send_keys(allPrintable) - - self.assertTrue(allPrintable, element.get_property("value")) - self.assertIn(" up: 16", result.text.strip()) - - @skip("Reenable in Bug 1068733") - def testSpecialSpaceKeys(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - - element = self.marionette.find_element(By.ID, "keyReporter") - element.send_keys("abcd" + Keys.SPACE + "fgh" + Keys.SPACE + "ij") - self.assertEqual("abcd fgh ij", element.get_property("value")) - - def testShouldTypeAnInteger(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - - element = self.marionette.find_element(By.ID, "keyReporter") - element.send_keys(1234) - self.assertEqual("1234", element.get_property("value")) - - def testShouldSendKeysToElementsWithoutTheValueAttribute(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - - # If we don't get an error below we are good - self.marionette.find_element(By.TAG_NAME, "body").send_keys("foo") - - def test_not_interactable_if_hidden(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - not_displayed = self.marionette.find_element(By.ID, "notDisplayed") - self.assertRaises(ElementNotInteractableException, not_displayed.send_keys, "foo") - - def test_appends_to_input_text(self): - self.marionette.navigate(inline("<input>")) - el = self.marionette.find_element(By.TAG_NAME, "input") - el.send_keys("foo") - el.send_keys("bar") - self.assertEqual("foobar", el.get_property("value")) - - def test_appends_to_textarea(self): - self.marionette.navigate(inline("<textarea></textarea>")) - textarea = self.marionette.find_element(By.TAG_NAME, "textarea") - textarea.send_keys("foo") - textarea.send_keys("bar") - self.assertEqual("foobar", textarea.get_property("value")) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_using_permissions.py b/testing/marionette/harness/marionette_harness/tests/unit/test_using_permissions.py deleted file mode 100644 index 71e271dd4..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_using_permissions.py +++ /dev/null @@ -1,46 +0,0 @@ -# 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.errors import JavascriptException - -from marionette_harness import MarionetteTestCase - - -class TestUsingPermssions(MarionetteTestCase): - - def test_using_permissions(self): - # Test that multiple permissions can be set with 'using_permissions', - # and that they are set correctly and unset correctly after leaving - # the context manager. - original_perm = self.marionette.get_permission('systemXHR') - original_alarm = self.marionette.get_permission('alarms') - new_perm = True if original_perm != 1 else False - new_alarm = True if original_alarm != 1 else False - with self.marionette.using_permissions({'systemXHR': new_perm, - 'alarms': new_alarm}): - now_perm = self.marionette.get_permission('systemXHR') - now_alarm = self.marionette.get_permission('alarms') - self.assertEquals(new_perm, now_perm) - self.assertNotEquals(now_perm, original_perm) - self.assertEquals(new_alarm, now_alarm) - self.assertNotEquals(now_alarm, original_alarm) - self.assertEquals(original_perm, - self.marionette.get_permission('systemXHR')) - self.assertEquals(original_alarm, - self.marionette.get_permission('alarms')) - - def test_exception_using_permissions(self): - # Test that throwing an exception inside the context manager doesn't - # prevent the permissions from being restored at context manager exit. - original_perm = self.marionette.get_permission('systemXHR') - new_perm = True if original_perm != 1 else False - with self.marionette.using_permissions({'systemXHR': new_perm}): - now_perm = self.marionette.get_permission('systemXHR') - self.assertEquals(new_perm, now_perm) - self.assertNotEquals(now_perm, original_perm) - self.assertRaises(JavascriptException, - self.marionette.execute_script, - "return foo.bar.baz;") - self.assertEquals(original_perm, - self.marionette.get_permission('systemXHR')) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_visibility.py b/testing/marionette/harness/marionette_harness/tests/unit/test_visibility.py deleted file mode 100644 index 750ecf20a..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_visibility.py +++ /dev/null @@ -1,121 +0,0 @@ -# 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.by import By - -from marionette_harness import MarionetteTestCase - - -class TestVisibility(MarionetteTestCase): - - def testShouldAllowTheUserToTellIfAnElementIsDisplayedOrNot(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - - self.assertTrue(self.marionette.find_element(By.ID, "displayed").is_displayed()) - self.assertFalse(self.marionette.find_element(By.ID, "none").is_displayed()) - self.assertFalse(self.marionette.find_element(By.ID, - "suppressedParagraph").is_displayed()) - self.assertFalse(self.marionette.find_element(By.ID, "hidden").is_displayed()) - - def testVisibilityShouldTakeIntoAccountParentVisibility(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - - childDiv = self.marionette.find_element(By.ID, "hiddenchild") - hiddenLink = self.marionette.find_element(By.ID, "hiddenlink") - - self.assertFalse(childDiv.is_displayed()) - self.assertFalse(hiddenLink.is_displayed()) - - def testShouldCountElementsAsVisibleIfStylePropertyHasBeenSet(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - shown = self.marionette.find_element(By.ID, "visibleSubElement") - self.assertTrue(shown.is_displayed()) - - def testShouldModifyTheVisibilityOfAnElementDynamically(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - element = self.marionette.find_element(By.ID, "hideMe") - self.assertTrue(element.is_displayed()) - element.click() - self.assertFalse(element.is_displayed()) - - def testHiddenInputElementsAreNeverVisible(self): - test_html = self.marionette.absolute_url("javascriptPage.html") - self.marionette.navigate(test_html) - - shown = self.marionette.find_element(By.NAME, "hidden") - - self.assertFalse(shown.is_displayed()) - - def testShouldSayElementsWithNegativeTransformAreNotDisplayed(self): - test_html = self.marionette.absolute_url("cssTransform.html") - self.marionette.navigate(test_html) - - elementX = self.marionette.find_element(By.ID, 'parentX') - self.assertFalse(elementX.is_displayed()) - elementY = self.marionette.find_element(By.ID, 'parentY') - self.assertFalse(elementY.is_displayed()) - - def testShouldSayElementsWithParentWithNegativeTransformAreNotDisplayed(self): - test_html = self.marionette.absolute_url("cssTransform.html") - self.marionette.navigate(test_html) - - elementX = self.marionette.find_element(By.ID, 'childX') - self.assertFalse(elementX.is_displayed()) - elementY = self.marionette.find_element(By.ID, 'childY') - self.assertFalse(elementY.is_displayed()) - - def testShouldSayElementWithZeroTransformIsVisible(self): - test_html = self.marionette.absolute_url("cssTransform.html") - self.marionette.navigate(test_html) - - zero_tranform = self.marionette.find_element(By.ID, 'zero-tranform') - self.assertTrue(zero_tranform.is_displayed()) - - def testShouldSayElementIsVisibleWhenItHasNegativeTransformButElementisntInANegativeSpace(self): - test_html = self.marionette.absolute_url("cssTransform2.html") - self.marionette.navigate(test_html) - negative_percent__tranform = self.marionette.find_element(By.ID, 'negative-percentage-transformY') - self.assertTrue(negative_percent__tranform.is_displayed()) - - def testShouldSayElementIsInvisibleWhenOverflowXIsHiddenAndOutOfViewport(self): - test_html = self.marionette.absolute_url("bug814037.html") - self.marionette.navigate(test_html) - overflow_x = self.marionette.find_element(By.ID, "assertMe2") - self.assertFalse(overflow_x.is_displayed()) - - def testShouldShowElementNotVisibleWithHiddenAttribute(self): - test_html = self.marionette.absolute_url("hidden.html") - self.marionette.navigate(test_html) - singleHidden = self.marionette.find_element(By.ID, 'singleHidden') - self.assertFalse(singleHidden.is_displayed()) - - def testShouldShowElementNotVisibleWhenParentElementHasHiddenAttribute(self): - test_html = self.marionette.absolute_url("hidden.html") - self.marionette.navigate(test_html) - child = self.marionette.find_element(By.ID, 'child') - self.assertFalse(child.is_displayed()) - - def testShouldClickOnELementPartiallyOffLeft(self): - test_html = self.marionette.absolute_url("element_left.html") - self.marionette.navigate(test_html) - self.marionette.find_element(By.CSS_SELECTOR, '.element').click() - - def testShouldClickOnELementPartiallyOffRight(self): - test_html = self.marionette.absolute_url("element_right.html") - self.marionette.navigate(test_html) - self.marionette.find_element(By.CSS_SELECTOR, '.element').click() - - def testShouldClickOnELementPartiallyOffTop(self): - test_html = self.marionette.absolute_url("element_top.html") - self.marionette.navigate(test_html) - self.marionette.find_element(By.CSS_SELECTOR, '.element').click() - - def testShouldClickOnELementPartiallyOffBottom(self): - test_html = self.marionette.absolute_url("element_bottom.html") - self.marionette.navigate(test_html) - self.marionette.find_element(By.CSS_SELECTOR, '.element').click() diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_wait.py b/testing/marionette/harness/marionette_harness/tests/unit/test_wait.py deleted file mode 100644 index 6a4872773..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_wait.py +++ /dev/null @@ -1,347 +0,0 @@ -# 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/. - -import sys -import time - -from marionette_driver import errors, wait -from marionette_driver.wait import Wait - -from marionette_harness import MarionetteTestCase - - -class TickingClock(object): - - def __init__(self, incr=1): - self.ticks = 0 - self.increment = incr - - def sleep(self, dur=None): - dur = dur if dur is not None else self.increment - self.ticks += dur - - @property - def now(self): - return self.ticks - - -class SequenceClock(object): - - def __init__(self, times): - self.times = times - self.i = 0 - - @property - def now(self): - if len(self.times) > self.i: - self.i += 1 - return self.times[self.i - 1] - - def sleep(self, dur): - pass - - -class MockMarionette(object): - - def __init__(self): - self.waited = 0 - - def exception(self, e=None, wait=1): - self.wait() - if self.waited == wait: - if e is None: - e = Exception - raise e - - def true(self, wait=1): - self.wait() - if self.waited == wait: - return True - return None - - def false(self, wait=1): - self.wait() - return False - - def none(self, wait=1): - self.wait() - return None - - def value(self, value, wait=1): - self.wait() - if self.waited == wait: - return value - return None - - def wait(self): - self.waited += 1 - - -def at_third_attempt(clock, end): - return clock.now == 2 - - -def now(clock, end): - return True - - -class SystemClockTest(MarionetteTestCase): - - def setUp(self): - super(SystemClockTest, self).setUp() - self.clock = wait.SystemClock() - - def test_construction_initializes_time(self): - self.assertEqual(self.clock._time, time) - - def test_sleep(self): - start = time.time() - self.clock.sleep(0.1) - end = time.time() - start - self.assertGreater(end, 0) - - def test_time_now(self): - self.assertIsNotNone(self.clock.now) - - -class FormalWaitTest(MarionetteTestCase): - - def setUp(self): - super(FormalWaitTest, self).setUp() - self.m = MockMarionette() - self.m.timeout = 123 - - def test_construction_with_custom_timeout(self): - wt = Wait(self.m, timeout=42) - self.assertEqual(wt.timeout, 42) - - def test_construction_with_custom_interval(self): - wt = Wait(self.m, interval=42) - self.assertEqual(wt.interval, 42) - - def test_construction_with_custom_clock(self): - c = TickingClock(1) - wt = Wait(self.m, clock=c) - self.assertEqual(wt.clock, c) - - def test_construction_with_custom_exception(self): - wt = Wait(self.m, ignored_exceptions=Exception) - self.assertIn(Exception, wt.exceptions) - self.assertEqual(len(wt.exceptions), 1) - - def test_construction_with_custom_exception_list(self): - exc = [Exception, ValueError] - wt = Wait(self.m, ignored_exceptions=exc) - for e in exc: - self.assertIn(e, wt.exceptions) - self.assertEqual(len(wt.exceptions), len(exc)) - - def test_construction_with_custom_exception_tuple(self): - exc = (Exception, ValueError) - wt = Wait(self.m, ignored_exceptions=exc) - for e in exc: - self.assertIn(e, wt.exceptions) - self.assertEqual(len(wt.exceptions), len(exc)) - - def test_duplicate_exceptions(self): - wt = Wait(self.m, ignored_exceptions=[Exception, Exception]) - self.assertIn(Exception, wt.exceptions) - self.assertEqual(len(wt.exceptions), 1) - - def test_default_timeout(self): - self.assertEqual(wait.DEFAULT_TIMEOUT, 5) - - def test_default_interval(self): - self.assertEqual(wait.DEFAULT_INTERVAL, 0.1) - - def test_end_property(self): - wt = Wait(self.m) - self.assertIsNotNone(wt.end) - - def test_marionette_property(self): - wt = Wait(self.m) - self.assertEqual(wt.marionette, self.m) - - def test_clock_property(self): - wt = Wait(self.m) - self.assertIsInstance(wt.clock, wait.SystemClock) - - def test_timeout_uses_default_if_marionette_timeout_is_none(self): - self.m.timeout = None - wt = Wait(self.m) - self.assertEqual(wt.timeout, wait.DEFAULT_TIMEOUT) - - -class PredicatesTest(MarionetteTestCase): - - def test_until(self): - c = wait.SystemClock() - self.assertFalse(wait.until_pred(c, sys.maxint)) - self.assertTrue(wait.until_pred(c, 0)) - - -class WaitUntilTest(MarionetteTestCase): - - def setUp(self): - super(WaitUntilTest, self).setUp() - - self.m = MockMarionette() - self.clock = TickingClock() - self.wt = Wait(self.m, timeout=10, interval=1, clock=self.clock) - - def test_true(self): - r = self.wt.until(lambda x: x.true()) - self.assertTrue(r) - self.assertEqual(self.clock.ticks, 0) - - def test_true_within_timeout(self): - r = self.wt.until(lambda x: x.true(wait=5)) - self.assertTrue(r) - self.assertEqual(self.clock.ticks, 4) - - def test_timeout(self): - with self.assertRaises(errors.TimeoutException): - r = self.wt.until(lambda x: x.true(wait=15)) - self.assertEqual(self.clock.ticks, 10) - - def test_exception_raises_immediately(self): - with self.assertRaises(TypeError): - self.wt.until(lambda x: x.exception(e=TypeError)) - self.assertEqual(self.clock.ticks, 0) - - def test_ignored_exception(self): - self.wt.exceptions = (TypeError,) - with self.assertRaises(errors.TimeoutException): - self.wt.until(lambda x: x.exception(e=TypeError)) - - def test_ignored_exception_wrapped_in_timeoutexception(self): - self.wt.exceptions = (TypeError,) - - exc = None - try: - self.wt.until(lambda x: x.exception(e=TypeError)) - except Exception as e: - exc = e - - s = str(exc) - self.assertIsNotNone(exc) - self.assertIsInstance(exc, errors.TimeoutException) - self.assertIn(", caused by {0!r}".format(TypeError), s) - self.assertIn("self.wt.until(lambda x: x.exception(e=TypeError))", s) - - def test_ignored_exception_after_timeout_is_not_raised(self): - with self.assertRaises(errors.TimeoutException): - r = self.wt.until(lambda x: x.exception(wait=15)) - self.assertEqual(self.clock.ticks, 10) - - def test_keyboard_interrupt(self): - with self.assertRaises(KeyboardInterrupt): - self.wt.until(lambda x: x.exception(e=KeyboardInterrupt)) - - def test_system_exit(self): - with self.assertRaises(SystemExit): - self.wt.until(lambda x: x.exception(SystemExit)) - - def test_true_condition_returns_immediately(self): - r = self.wt.until(lambda x: x.true()) - self.assertIsInstance(r, bool) - self.assertTrue(r) - self.assertEqual(self.clock.ticks, 0) - - def test_value(self): - r = self.wt.until(lambda x: "foo") - self.assertEqual(r, "foo") - self.assertEqual(self.clock.ticks, 0) - - def test_custom_predicate(self): - r = self.wt.until(lambda x: x.true(wait=2), is_true=at_third_attempt) - self.assertTrue(r) - self.assertEqual(self.clock.ticks, 1) - - def test_custom_predicate_times_out(self): - with self.assertRaises(errors.TimeoutException): - self.wt.until(lambda x: x.true(wait=4), is_true=at_third_attempt) - - self.assertEqual(self.clock.ticks, 2) - - def test_timeout_elapsed_duration(self): - with self.assertRaisesRegexp(errors.TimeoutException, - "Timed out after 2.0 seconds"): - self.wt.until(lambda x: x.true(wait=4), is_true=at_third_attempt) - - def test_timeout_elapsed_rounding(self): - wt = Wait(self.m, clock=SequenceClock([1, 0.01, 1]), timeout=0) - with self.assertRaisesRegexp(errors.TimeoutException, - "Timed out after 1.0 seconds"): - wt.until(lambda x: x.true(), is_true=now) - - def test_timeout_elapsed_interval_by_delayed_condition_return(self): - def callback(mn): - self.clock.sleep(11) - return mn.false() - - with self.assertRaisesRegexp(errors.TimeoutException, - "Timed out after 11.0 seconds"): - self.wt.until(callback) - # With a delayed conditional return > timeout, only 1 iteration is - # possible - self.assertEqual(self.m.waited, 1) - - def test_timeout_with_delayed_condition_return(self): - def callback(mn): - self.clock.sleep(.5) - return mn.false() - - with self.assertRaisesRegexp(errors.TimeoutException, - "Timed out after 10.0 seconds"): - self.wt.until(callback) - # With a delayed conditional return < interval, 10 iterations should be - # possible - self.assertEqual(self.m.waited, 10) - - def test_timeout_interval_shorter_than_delayed_condition_return(self): - def callback(mn): - self.clock.sleep(2) - return mn.false() - - with self.assertRaisesRegexp(errors.TimeoutException, - "Timed out after 10.0 seconds"): - self.wt.until(callback) - # With a delayed return of the conditional which takes twice that long than the interval, - # half of the iterations should be possible - self.assertEqual(self.m.waited, 5) - - def test_message(self): - self.wt.exceptions = (TypeError,) - exc = None - try: - self.wt.until(lambda x: x.exception(e=TypeError), message="hooba") - except errors.TimeoutException as e: - exc = e - - result = str(exc) - self.assertIn("seconds with message: hooba, caused by", result) - - def test_no_message(self): - self.wt.exceptions = (TypeError,) - exc = None - try: - self.wt.until(lambda x: x.exception(e=TypeError), message="") - except errors.TimeoutException as e: - exc = e - - result = str(exc) - self.assertIn("seconds, caused by", result) - - def test_message_has_none_as_its_value(self): - self.wt.exceptions = (TypeError,) - exc = None - try: - self.wt.until(False, None, None) - except errors.TimeoutException as e: - exc = e - - result = str(exc) - self.assertNotIn("with message:", result) - self.assertNotIn("secondsNone", result) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_window_close_chrome.py b/testing/marionette/harness/marionette_harness/tests/unit/test_window_close_chrome.py deleted file mode 100644 index 18ae191c1..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_window_close_chrome.py +++ /dev/null @@ -1,80 +0,0 @@ -# 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_harness import MarionetteTestCase, WindowManagerMixin - - -class TestCloseWindow(WindowManagerMixin, MarionetteTestCase): - - def setUp(self): - super(TestCloseWindow, self).setUp() - - self.marionette.set_context("chrome") - - def tearDown(self): - self.close_all_windows() - self.close_all_tabs() - - super(TestCloseWindow, self).tearDown() - - def test_close_chrome_window_for_browser_window(self): - win = self.open_window() - self.marionette.switch_to_window(win) - - self.assertNotIn(win, self.marionette.window_handles) - chrome_window_handles = self.marionette.close_chrome_window() - self.assertNotIn(win, chrome_window_handles) - self.assertListEqual(self.start_windows, chrome_window_handles) - self.assertNotIn(win, self.marionette.window_handles) - - def test_close_chrome_window_for_non_browser_window(self): - - def open_window_with_js(): - self.marionette.execute_script(""" - window.open('chrome://marionette/content/test.xul', - 'foo', 'chrome,centerscreen'); - """) - - win = self.open_window(trigger=open_window_with_js) - self.marionette.switch_to_window(win) - - self.assertIn(win, self.marionette.window_handles) - chrome_window_handles = self.marionette.close_chrome_window() - self.assertNotIn(win, chrome_window_handles) - self.assertListEqual(self.start_windows, chrome_window_handles) - self.assertNotIn(win, self.marionette.window_handles) - - def test_close_chrome_window_for_last_open_window(self): - self.close_all_windows() - - self.assertListEqual([], self.marionette.close_chrome_window()) - self.assertListEqual([self.start_tab], self.marionette.window_handles) - self.assertListEqual([self.start_window], self.marionette.chrome_window_handles) - self.assertIsNotNone(self.marionette.session) - - def test_close_window_for_browser_tab(self): - tab = self.open_tab() - self.marionette.switch_to_window(tab) - - window_handles = self.marionette.close() - self.assertNotIn(tab, window_handles) - self.assertListEqual(self.start_tabs, window_handles) - - def test_close_window_for_browser_window_with_single_tab(self): - win = self.open_window() - self.marionette.switch_to_window(win) - - self.assertEqual(len(self.start_tabs) + 1, len(self.marionette.window_handles)) - window_handles = self.marionette.close() - self.assertNotIn(win, window_handles) - self.assertListEqual(self.start_tabs, window_handles) - self.assertListEqual(self.start_windows, self.marionette.chrome_window_handles) - - def test_close_window_for_last_open_tab(self): - self.close_all_tabs() - - self.assertListEqual([], self.marionette.close()) - self.assertListEqual([self.start_tab], self.marionette.window_handles) - self.assertListEqual([self.start_window], self.marionette.chrome_window_handles) - self.assertIsNotNone(self.marionette.session) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_window_close_content.py b/testing/marionette/harness/marionette_harness/tests/unit/test_window_close_content.py deleted file mode 100644 index 8e6485e54..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_window_close_content.py +++ /dev/null @@ -1,81 +0,0 @@ -# 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_harness import MarionetteTestCase, skip_if_mobile, WindowManagerMixin - - -class TestCloseWindow(WindowManagerMixin, MarionetteTestCase): - - def tearDown(self): - self.close_all_windows() - self.close_all_tabs() - - super(TestCloseWindow, self).tearDown() - - @skip_if_mobile("Interacting with chrome windows not available for Fennec") - def test_close_chrome_window_for_browser_window(self): - win = self.open_window() - self.marionette.switch_to_window(win) - - self.assertNotIn(win, self.marionette.window_handles) - chrome_window_handles = self.marionette.close_chrome_window() - self.assertNotIn(win, chrome_window_handles) - self.assertListEqual(self.start_windows, chrome_window_handles) - self.assertNotIn(win, self.marionette.window_handles) - - @skip_if_mobile("Interacting with chrome windows not available for Fennec") - def test_close_chrome_window_for_non_browser_window(self): - - def open_window_with_js(): - with self.marionette.using_context("chrome"): - self.marionette.execute_script(""" - window.open('chrome://marionette/content/test.xul', - 'foo', 'chrome,centerscreen'); - """) - - win = self.open_window(trigger=open_window_with_js) - self.marionette.switch_to_window(win) - - self.assertIn(win, self.marionette.window_handles) - chrome_window_handles = self.marionette.close_chrome_window() - self.assertNotIn(win, chrome_window_handles) - self.assertListEqual(self.start_windows, chrome_window_handles) - self.assertNotIn(win, self.marionette.window_handles) - - @skip_if_mobile("Interacting with chrome windows not available for Fennec") - def test_close_chrome_window_for_last_open_window(self): - self.close_all_windows() - - self.assertListEqual([], self.marionette.close_chrome_window()) - self.assertListEqual([self.start_tab], self.marionette.window_handles) - self.assertListEqual([self.start_window], self.marionette.chrome_window_handles) - self.assertIsNotNone(self.marionette.session) - - @skip_if_mobile("Needs application independent method to open a new tab") - def test_close_window_for_browser_tab(self): - tab = self.open_tab() - self.marionette.switch_to_window(tab) - - window_handles = self.marionette.close() - self.assertNotIn(tab, window_handles) - self.assertListEqual(self.start_tabs, window_handles) - - @skip_if_mobile("Interacting with chrome windows not available for Fennec") - def test_close_window_for_browser_window_with_single_tab(self): - win = self.open_window() - self.marionette.switch_to_window(win) - - self.assertEqual(len(self.start_tabs) + 1, len(self.marionette.window_handles)) - window_handles = self.marionette.close() - self.assertNotIn(win, window_handles) - self.assertListEqual(self.start_tabs, window_handles) - self.assertListEqual(self.start_windows, self.marionette.chrome_window_handles) - - def test_close_window_for_last_open_tab(self): - self.close_all_tabs() - - self.assertListEqual([], self.marionette.close()) - self.assertListEqual([self.start_tab], self.marionette.window_handles) - self.assertListEqual([self.start_window], self.marionette.chrome_window_handles) - self.assertIsNotNone(self.marionette.session) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_window_handles_chrome.py b/testing/marionette/harness/marionette_harness/tests/unit/test_window_handles_chrome.py deleted file mode 100644 index 7260d6324..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_window_handles_chrome.py +++ /dev/null @@ -1,207 +0,0 @@ -# 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_harness import MarionetteTestCase, WindowManagerMixin - - -class TestWindowHandles(WindowManagerMixin, MarionetteTestCase): - - def setUp(self): - super(TestWindowHandles, self).setUp() - - self.empty_page = self.marionette.absolute_url("empty.html") - self.test_page = self.marionette.absolute_url("windowHandles.html") - self.marionette.navigate(self.test_page) - - self.marionette.set_context("chrome") - - def tearDown(self): - self.close_all_windows() - self.close_all_tabs() - - super(TestWindowHandles, self).tearDown() - - def test_chrome_window_handles_with_scopes(self): - # Open a browser and a non-browser (about window) chrome window - self.open_window( - trigger=lambda: self.marionette.execute_script("window.open();")) - self.assertEqual(len(self.marionette.chrome_window_handles), len(self.start_windows) + 1) - self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window) - - self.open_window( - trigger=lambda: self.marionette.find_element(By.ID, "aboutName").click()) - self.assertEqual(len(self.marionette.chrome_window_handles), len(self.start_windows) + 2) - self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window) - - chrome_window_handles_in_chrome_scope = self.marionette.chrome_window_handles - window_handles_in_chrome_scope = self.marionette.window_handles - - with self.marionette.using_context("content"): - self.assertEqual(self.marionette.chrome_window_handles, - chrome_window_handles_in_chrome_scope) - self.assertEqual(self.marionette.window_handles, - window_handles_in_chrome_scope) - - def test_chrome_window_handles_after_opening_new_window(self): - def open_with_link(): - with self.marionette.using_context("content"): - link = self.marionette.find_element(By.ID, "new-window") - link.click() - - # We open a new window but are actually interested in the new tab - new_win = self.open_window(trigger=open_with_link) - self.assertEqual(len(self.marionette.chrome_window_handles), len(self.start_windows) + 1) - self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window) - - # Check that the new tab has the correct page loaded - self.marionette.switch_to_window(new_win) - self.assertEqual(self.marionette.current_chrome_window_handle, new_win) - with self.marionette.using_context("content"): - Wait(self.marionette, timeout=self.marionette.timeout.page_load).until( - lambda mn: mn.get_url() == self.empty_page, - message="{} did not load after opening a new tab".format(self.empty_page)) - - # Ensure navigate works in our current window - other_page = self.marionette.absolute_url("test.html") - with self.marionette.using_context("content"): - self.marionette.navigate(other_page) - self.assertEqual(self.marionette.get_url(), other_page) - - # Close the opened window and carry on in our original tab. - self.marionette.close() - self.assertEqual(len(self.marionette.chrome_window_handles), len(self.start_windows)) - - self.marionette.switch_to_window(self.start_window) - self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window) - with self.marionette.using_context("content"): - self.assertEqual(self.marionette.get_url(), self.test_page) - - def test_window_handles_after_opening_new_tab(self): - def open_with_link(): - with self.marionette.using_context("content"): - link = self.marionette.find_element(By.ID, "new-tab") - link.click() - - new_tab = self.open_tab(trigger=open_with_link) - self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs) + 1) - self.assertEqual(self.marionette.current_window_handle, self.start_tab) - - self.marionette.switch_to_window(new_tab) - self.assertEqual(self.marionette.current_window_handle, new_tab) - with self.marionette.using_context("content"): - Wait(self.marionette, timeout=self.marionette.timeout.page_load).until( - lambda mn: mn.get_url() == self.empty_page, - message="{} did not load after opening a new tab".format(self.empty_page)) - - # Ensure navigate works in our current tab - other_page = self.marionette.absolute_url("test.html") - with self.marionette.using_context("content"): - self.marionette.navigate(other_page) - self.assertEqual(self.marionette.get_url(), other_page) - - self.marionette.switch_to_window(self.start_tab) - self.assertEqual(self.marionette.current_window_handle, self.start_tab) - with self.marionette.using_context("content"): - self.assertEqual(self.marionette.get_url(), self.test_page) - - self.marionette.switch_to_window(new_tab) - self.marionette.close() - self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs)) - - self.marionette.switch_to_window(self.start_tab) - self.assertEqual(self.marionette.current_window_handle, self.start_tab) - - def test_window_handles_after_opening_new_window(self): - def open_with_link(): - with self.marionette.using_context("content"): - link = self.marionette.find_element(By.ID, "new-window") - link.click() - - # We open a new window but are actually interested in the new tab - new_tab = self.open_tab(trigger=open_with_link) - self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs) + 1) - self.assertEqual(self.marionette.current_window_handle, self.start_tab) - - # Check that the new tab has the correct page loaded - self.marionette.switch_to_window(new_tab) - self.assertEqual(self.marionette.current_window_handle, new_tab) - with self.marionette.using_context("content"): - Wait(self.marionette, timeout=self.marionette.timeout.page_load).until( - lambda mn: mn.get_url() == self.empty_page, - message="{} did not load after opening a new tab".format(self.empty_page)) - - # Ensure navigate works in our current window - other_page = self.marionette.absolute_url("test.html") - with self.marionette.using_context("content"): - self.marionette.navigate(other_page) - self.assertEqual(self.marionette.get_url(), other_page) - - # Close the opened window and carry on in our original tab. - self.marionette.close() - self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs)) - - self.marionette.switch_to_window(self.start_tab) - self.assertEqual(self.marionette.current_window_handle, self.start_tab) - with self.marionette.using_context("content"): - self.assertEqual(self.marionette.get_url(), self.test_page) - - def test_window_handles_after_closing_original_tab(self): - def open_with_link(): - with self.marionette.using_context("content"): - link = self.marionette.find_element(By.ID, "new-tab") - link.click() - - new_tab = self.open_tab(trigger=open_with_link) - self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs) + 1) - self.assertEqual(self.marionette.current_window_handle, self.start_tab) - - self.marionette.close() - self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs)) - - self.marionette.switch_to_window(new_tab) - self.assertEqual(self.marionette.current_window_handle, new_tab) - with self.marionette.using_context("content"): - Wait(self.marionette, timeout=self.marionette.timeout.page_load).until( - lambda mn: mn.get_url() == self.empty_page, - message="{} did not load after opening a new tab".format(self.empty_page)) - - def test_window_handles_no_switch(self): - """Regression test for bug 1294456. - This test is testing the case where Marionette attempts to send a - command to a window handle when the browser has opened and selected - a new tab. Before bug 1294456 landed, the Marionette driver was getting - confused about which window handle the client cared about, and assumed - it was the window handle for the newly opened and selected tab. - - This caused Marionette to think that the browser needed to do a remoteness - flip in the e10s case, since the tab opened by menu_newNavigatorTab is - about:newtab (which is currently non-remote). This meant that commands - sent to what should have been the original window handle would be - queued and never sent, since the remoteness flip in the new tab was - never going to happen. - """ - def open_with_menu(): - menu_new_tab = self.marionette.find_element(By.ID, 'menu_newNavigatorTab') - menu_new_tab.click() - - new_tab = self.open_tab(trigger=open_with_menu) - - # We still have the default tab set as our window handle. This - # get_url command should be sent immediately, and not be forever-queued. - with self.marionette.using_context("content"): - self.assertEqual(self.marionette.get_url(), self.test_page) - - self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs) + 1) - self.assertEqual(self.marionette.current_window_handle, self.start_tab) - - self.marionette.switch_to_window(new_tab) - self.assertEqual(self.marionette.current_window_handle, new_tab) - - self.marionette.close() - self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs)) - - self.marionette.switch_to_window(self.start_tab) - self.assertEqual(self.marionette.current_window_handle, self.start_tab) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_window_handles_content.py b/testing/marionette/harness/marionette_harness/tests/unit/test_window_handles_content.py deleted file mode 100644 index b6ad3a6c3..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_window_handles_content.py +++ /dev/null @@ -1,96 +0,0 @@ -# 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_harness import MarionetteTestCase, WindowManagerMixin - - -class TestWindowHandles(WindowManagerMixin, MarionetteTestCase): - - def setUp(self): - super(TestWindowHandles, self).setUp() - - self.empty_page = self.marionette.absolute_url("empty.html") - self.test_page = self.marionette.absolute_url("windowHandles.html") - self.marionette.navigate(self.test_page) - - def tearDown(self): - self.close_all_tabs() - - super(TestWindowHandles, self).tearDown() - - def test_window_handles_after_opening_new_tab(self): - def open_with_link(): - link = self.marionette.find_element(By.ID, "new-tab") - link.click() - - new_tab = self.open_tab(trigger=open_with_link) - self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs) + 1) - self.assertEqual(self.marionette.current_window_handle, self.start_tab) - - self.marionette.switch_to_window(new_tab) - self.assertEqual(self.marionette.current_window_handle, new_tab) - Wait(self.marionette, timeout=self.marionette.timeout.page_load).until( - lambda mn: mn.get_url() == self.empty_page, - message="{} did not load after opening a new tab".format(self.empty_page)) - - self.marionette.switch_to_window(self.start_tab) - self.assertEqual(self.marionette.current_window_handle, self.start_tab) - self.assertEqual(self.marionette.get_url(), self.test_page) - - self.marionette.switch_to_window(new_tab) - self.marionette.close() - self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs)) - - self.marionette.switch_to_window(self.start_tab) - self.assertEqual(self.marionette.current_window_handle, self.start_tab) - - def test_window_handles_after_opening_new_window(self): - def open_with_link(): - link = self.marionette.find_element(By.ID, "new-window") - link.click() - - # We open a new window but are actually interested in the new tab - new_tab = self.open_tab(trigger=open_with_link) - self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs) + 1) - self.assertEqual(self.marionette.current_window_handle, self.start_tab) - - # Check that the new tab has the correct page loaded - self.marionette.switch_to_window(new_tab) - self.assertEqual(self.marionette.current_window_handle, new_tab) - Wait(self.marionette, self.marionette.timeout.page_load).until( - lambda _: self.marionette.get_url() == self.empty_page, - message="The expected page '{}' has not been loaded".format(self.empty_page)) - - # Ensure navigate works in our current window - other_page = self.marionette.absolute_url("test.html") - self.marionette.navigate(other_page) - self.assertEqual(self.marionette.get_url(), other_page) - - # Close the opened window and carry on in our original tab. - self.marionette.close() - self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs)) - - self.marionette.switch_to_window(self.start_tab) - self.assertEqual(self.marionette.current_window_handle, self.start_tab) - self.assertEqual(self.marionette.get_url(), self.test_page) - - def test_window_handles_after_closing_original_tab(self): - def open_with_link(): - link = self.marionette.find_element(By.ID, "new-tab") - link.click() - - new_tab = self.open_tab(trigger=open_with_link) - self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs) + 1) - self.assertEqual(self.marionette.current_window_handle, self.start_tab) - - self.marionette.close() - self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs)) - - self.marionette.switch_to_window(new_tab) - self.assertEqual(self.marionette.current_window_handle, new_tab) - Wait(self.marionette, self.marionette.timeout.page_load).until( - lambda _: self.marionette.get_url() == self.empty_page, - message="The expected page '{}' has not been loaded".format(self.empty_page)) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_window_position.py b/testing/marionette/harness/marionette_harness/tests/unit/test_window_position.py deleted file mode 100644 index ac5365806..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_window_position.py +++ /dev/null @@ -1,42 +0,0 @@ -#Copyright 2007-2009 WebDriver committers -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. - -from marionette_driver.errors import InvalidArgumentException - -from marionette_harness import MarionetteTestCase - - -class TestWindowPosition(MarionetteTestCase): - def test_get_types(self): - position = self.marionette.get_window_position() - self.assertTrue(isinstance(position["x"], int)) - self.assertTrue(isinstance(position["y"], int)) - - def test_set_types(self): - for x, y in (["a", "b"], [1.2, 3.4], [True, False], [[], []], [{}, {}]): - with self.assertRaises(InvalidArgumentException): - self.marionette.set_window_position(x, y) - - def test_out_of_bounds_arguments(self): - with self.assertRaises(InvalidArgumentException): - self.marionette.set_window_position(-1, 0) - with self.assertRaises(InvalidArgumentException): - self.marionette.set_window_position(0, -1) - - def test_move(self): - old_position = self.marionette.get_window_position() - new_position = {"x": old_position["x"] + 10, "y": old_position["y"] + 10} - self.marionette.set_window_position(new_position["x"], new_position["y"]) - self.assertNotEqual(old_position['x'], new_position["x"]) - self.assertNotEqual(old_position['y'], new_position["y"]) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_window_title.py b/testing/marionette/harness/marionette_harness/tests/unit/test_window_title.py deleted file mode 100644 index 4f6e3ccf7..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_window_title.py +++ /dev/null @@ -1,12 +0,0 @@ -# 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_harness import MarionetteTestCase - - -class TestTitle(MarionetteTestCase): - def test_get_html_title(self): - test_html = self.marionette.absolute_url("test.html") - self.marionette.navigate(test_html) - self.assertEqual('Marionette Test', self.marionette.title) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_window_title_chrome.py b/testing/marionette/harness/marionette_harness/tests/unit/test_window_title_chrome.py deleted file mode 100644 index 7bee682fd..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_window_title_chrome.py +++ /dev/null @@ -1,26 +0,0 @@ -# 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_harness import MarionetteTestCase - - -class TestTitleChrome(MarionetteTestCase): - def setUp(self): - MarionetteTestCase.setUp(self) - self.marionette.set_context("chrome") - self.win = self.marionette.current_window_handle - self.marionette.execute_script("window.open('chrome://marionette/content/test.xul', 'foo', 'chrome,centerscreen');") - self.marionette.switch_to_window('foo') - self.assertNotEqual(self.win, self.marionette.current_window_handle) - - def tearDown(self): - self.assertNotEqual(self.win, self.marionette.current_window_handle) - self.marionette.execute_script("window.close();") - self.marionette.switch_to_window(self.win) - MarionetteTestCase.tearDown(self) - - def test_get_chrome_title(self): - title = self.marionette.execute_script("return window.document.documentElement.getAttribute('title');") - self.assertEqual(title, self.marionette.title) - self.assertEqual('Title Test', self.marionette.title) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_window_type.py b/testing/marionette/harness/marionette_harness/tests/unit/test_window_type.py deleted file mode 100644 index 6c5e75f51..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_window_type.py +++ /dev/null @@ -1,27 +0,0 @@ -# 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_harness import MarionetteTestCase - - -class TestWindowTypeChrome(MarionetteTestCase): - def setUp(self): - MarionetteTestCase.setUp(self) - self.marionette.set_context("chrome") - self.win = self.marionette.current_window_handle - self.marionette.execute_script("window.open('chrome://marionette/content/test.xul', 'foo', 'chrome,centerscreen');") - self.marionette.switch_to_window('foo') - self.assertNotEqual(self.win, self.marionette.current_window_handle) - - def tearDown(self): - self.assertNotEqual(self.win, self.marionette.current_window_handle) - self.marionette.execute_script("window.close();") - self.marionette.switch_to_window(self.win) - MarionetteTestCase.tearDown(self) - - def test_get_window_type(self): - window_type = self.marionette.execute_script("return window.document.documentElement.getAttribute('windowtype');") - self.assertEqual(window_type, self.marionette.get_window_type()) - self.assertEqual('Test Type', self.marionette.get_window_type()) - diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_with_using_context.py b/testing/marionette/harness/marionette_harness/tests/unit/test_with_using_context.py deleted file mode 100644 index 1b2d60d2d..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_with_using_context.py +++ /dev/null @@ -1,66 +0,0 @@ -# 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.decorators import using_context -from marionette_driver.errors import MarionetteException - -from marionette_harness import MarionetteTestCase - - -class TestSetContext(MarionetteTestCase): - def setUp(self): - MarionetteTestCase.setUp(self) - - # shortcuts to improve readability of these tests - self.chrome = self.marionette.CONTEXT_CHROME - self.content = self.marionette.CONTEXT_CONTENT - - test_url = self.marionette.absolute_url("empty.html") - self.marionette.navigate(test_url) - self.marionette.set_context(self.content) - self.assertEquals(self.get_context(), self.content) - - def get_context(self): - return self.marionette._send_message("getContext", key="value") - - def test_set_different_context_using_with_block(self): - with self.marionette.using_context(self.chrome): - self.assertEquals(self.get_context(), self.chrome) - self.assertEquals(self.get_context(), self.content) - - def test_set_same_context_using_with_block(self): - with self.marionette.using_context(self.content): - self.assertEquals(self.get_context(), self.content) - self.assertEquals(self.get_context(), self.content) - - def test_nested_with_blocks(self): - with self.marionette.using_context(self.chrome): - self.assertEquals(self.get_context(), self.chrome) - with self.marionette.using_context(self.content): - self.assertEquals(self.get_context(), self.content) - self.assertEquals(self.get_context(), self.chrome) - self.assertEquals(self.get_context(), self.content) - - def test_set_scope_while_in_with_block(self): - with self.marionette.using_context(self.chrome): - self.assertEquals(self.get_context(), self.chrome) - self.marionette.set_context(self.content) - self.assertEquals(self.get_context(), self.content) - self.assertEquals(self.get_context(), self.content) - - def test_exception_raised_while_in_with_block_is_propagated(self): - with self.assertRaises(MarionetteException): - with self.marionette.using_context(self.chrome): - raise MarionetteException - self.assertEquals(self.get_context(), self.content) - - def test_with_using_context_decorator(self): - @using_context('content') - def inner_content(m): - self.assertEquals(self.get_context(), 'content') - @using_context('chrome') - def inner_chrome(m): - self.assertEquals(self.get_context(), 'chrome') - inner_content(self.marionette) - inner_chrome(self.marionette) diff --git a/testing/marionette/harness/marionette_harness/tests/unit/unit-tests.ini b/testing/marionette/harness/marionette_harness/tests/unit/unit-tests.ini deleted file mode 100644 index 573096378..000000000 --- a/testing/marionette/harness/marionette_harness/tests/unit/unit-tests.ini +++ /dev/null @@ -1,132 +0,0 @@ -[test_marionette.py] -[test_geckoinstance.py] -[test_data_driven.py] -[test_session.py] -[test_capabilities.py] -[test_accessibility.py] -[test_expectedfail.py] -expected = fail -[test_import_script.py] -[test_click.py] -[test_click_chrome.py] -skip-if = appname == 'fennec' -[test_checkbox.py] -[test_checkbox_chrome.py] -skip-if = appname == 'fennec' -[test_elementsize.py] -[test_elementsize_chrome.py] -skip-if = appname == 'fennec' -[test_position.py] -[test_rendered_element.py] -[test_chrome_element_css.py] -skip-if = appname == 'fennec' -[test_element_state.py] -[test_element_state_chrome.py] -skip-if = appname == 'fennec' -[test_text.py] -[test_text_chrome.py] -skip-if = true # "Bug 896046" - -[test_clearing.py] -[test_typing.py] - -[test_log.py] - -[test_about_pages.py] - -[test_execute_async_script.py] -[test_execute_script.py] -[test_simpletest_fail.js] -[test_element_retrieval.py] -[test_findelement_chrome.py] -skip-if = appname == 'fennec' - -[test_navigation.py] - -[test_timeouts.py] - -[test_single_finger_desktop.py] -skip-if = appname == 'fennec' || os == "win" # Bug 1025040 - -[test_simpletest_pass.js] -[test_simpletest_sanity.py] -[test_simpletest_chrome.js] -[test_simpletest_timeout.js] -[test_anonymous_content.py] -skip-if = appname == 'fennec' -[test_switch_frame.py] -skip-if = os == "win" # Bug 1078237 -[test_switch_frame_chrome.py] -skip-if = appname == 'fennec' -[test_switch_remote_frame.py] -skip-if = appname == 'fennec' -[test_switch_window_chrome.py] -skip-if = appname == 'fennec' -[test_switch_window_content.py] - -[test_pagesource.py] -[test_pagesource_chrome.py] -skip-if = appname == 'fennec' - -[test_visibility.py] -[test_window_handles_chrome.py] -skip-if = appname == 'fennec' -[test_window_handles_content.py] -[test_window_close_chrome.py] -skip-if = appname == 'fennec' -[test_window_close_content.py] -[test_window_position.py] -skip-if = appname == 'fennec' - -[test_screenshot.py] -[test_cookies.py] -[test_window_title.py] -[test_window_title_chrome.py] -skip-if = appname == 'fennec' -[test_window_type.py] -skip-if = appname == 'fennec' -[test_implicit_waits.py] -[test_wait.py] -[test_expected.py] -[test_date_time_value.py] -[test_getactiveframe_oop.py] -skip-if = true # Bug 925688 -[test_chrome_async_finish.js] -[test_screen_orientation.py] -[test_errors.py] - -[test_execute_isolate.py] -[test_click_scrolling.py] -[test_profile_management.py] -skip-if = manage_instance == false || appname == 'fennec' # Bug 1298921 and bug 1322993 -[test_quit_restart.py] -skip-if = manage_instance == false || appname == 'fennec' # Bug 1298921 and bug 1322993 -[test_set_window_size.py] -skip-if = os == "linux" || appname == 'fennec' # Bug 1085717 -[test_with_using_context.py] - -[test_modal_dialogs.py] -skip-if = appname == 'fennec' # Bug 1325738 -[test_key_actions.py] -[test_mouse_action.py] -skip-if = appname == 'fennec' -[test_teardown_context_preserved.py] -[test_file_upload.py] -skip-if = appname == 'fennec' || os == "win" # http://bugs.python.org/issue14574 - -[test_execute_sandboxes.py] -[test_using_permissions.py] -[test_prefs.py] - -[test_shadow_dom.py] - -[test_chrome.py] -skip-if = appname == 'fennec' - -[test_addons.py] -skip-if = appname == 'fennec' # Bug 1330598 - -[test_select.py] -[test_crash.py] -skip-if = manage_instance == false || appname == 'fennec' # Bug 1298921 and bug 1322993 -[test_localization.py] diff --git a/testing/marionette/harness/marionette_harness/tests/webapi-tests.ini b/testing/marionette/harness/marionette_harness/tests/webapi-tests.ini deleted file mode 100644 index 5c94220af..000000000 --- a/testing/marionette/harness/marionette_harness/tests/webapi-tests.ini +++ /dev/null @@ -1,5 +0,0 @@ -[include:../../../../../dom/system/tests/marionette/manifest.ini] -skip-if = android_version > '15' # Bug 1203072 -[include:../../../../../dom/events/test/marionette/manifest.ini] -skip-if = android_version > '15' # Bug 1203075 -[include:../../../../../dom/network/tests/marionette/manifest.ini] diff --git a/testing/marionette/harness/marionette_harness/www/black.png b/testing/marionette/harness/marionette_harness/www/black.png Binary files differdeleted file mode 100644 index b62a3a7bc..000000000 --- a/testing/marionette/harness/marionette_harness/www/black.png +++ /dev/null diff --git a/testing/marionette/harness/marionette_harness/www/bug814037.html b/testing/marionette/harness/marionette_harness/www/bug814037.html deleted file mode 100644 index 22950134e..000000000 --- a/testing/marionette/harness/marionette_harness/www/bug814037.html +++ /dev/null @@ -1,55 +0,0 @@ -<html>
-<head>
-<style>
-body {
- width: 100%;
- margin: 0px;
- transition: transform 300ms ease;
- overflow-x: hidden;
-}
-
-body.section1 {
- transform: translateX(0%);
-}
-
-body.section2 {
- transform: translateX(-100%);
-}
-
-section {
- width: 100%;
- height: 100%;
- position: absolute;
-}
-
-#section1 {
- left: 0px;
-}
-
-#section2 {
- left: 100%;
-}
-.mypossie {
- position:absolute;
- left: -1000px;
-}
-</style>
-
-</head>
- <body class="section1">
- <section id="section1">
- <div id="assertMe1">
- <p>Section 1</p>
- </div>
- <button id="b1" onclick="var sect = document.getElementsByTagName('body')[0]; sect.classList.add('section2'); sect.classList.remove('section1');">Show section 2</button>
- </section>
-
- <section id="section2">
- <div id="assertMe2">
- <p>Section 2</p>
- </div>
- <button id="b2" onclick="var sect = document.getElementsByTagName('body')[0]; sect.classList.add('section1'); sect.classList.remove('section2'); ">Show section 1</button>
- </section>
- <section class='mypossie'>out in left field!</section>
- </body>
-</html>
diff --git a/testing/marionette/harness/marionette_harness/www/click_out_of_bounds_overflow.html b/testing/marionette/harness/marionette_harness/www/click_out_of_bounds_overflow.html deleted file mode 100644 index f0bee9b46..000000000 --- a/testing/marionette/harness/marionette_harness/www/click_out_of_bounds_overflow.html +++ /dev/null @@ -1,90 +0,0 @@ -<!-- 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/. --> - -<!DOCTYPE html> -<html><head> - <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> -<body> -<div style="height: 100px; overflow: auto;"> - <table> - <tbody> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td>data</td></tr> - <tr><td><a href="#clicked" id="link">click me</a></td></tr> - </tbody> - </table> -</div> -</body> -</html> diff --git a/testing/marionette/harness/marionette_harness/www/clicks.html b/testing/marionette/harness/marionette_harness/www/clicks.html deleted file mode 100644 index e48cb1b2f..000000000 --- a/testing/marionette/harness/marionette_harness/www/clicks.html +++ /dev/null @@ -1,39 +0,0 @@ -<html> -<head> - <!-- 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/. --> - <title>clicks</title> -</head> -<body> -<h1>Testing Clicks</h1> - -<iframe id="source" src="click_source.html"> - -</iframe> - -<iframe id="target" name="target"> - -</iframe> - -<a href="xhtmlTest.html" id="normal">I'm a normal link</a> -<a href="#" id="anchor">I go to an anchor</a> -<a href="javascript:window.open('xhtmlTest.html', '_blank')" id="new-window">I open a window with javascript</a> -<a href="xhtmlTest.html" id="twoClientRects"><span></span><span>Click me</span></a> -<a href="xhtmlTest.html" id="link-with-enclosed-image"><img id="enclosed-image" src="./icon.gif"/></a> -<a href="xhtmlTest.html" id="link-with-enclosed-span"><span id="enclosed-span" style="color: rgb(0, 255, 0)">I'm a green link</span></a> -<p style="background: none repeat scroll 0% 0% rgb(0, 255, 0); width: 5em;"><a id="overflowLink" href="xhtmlTest.html">looooooooooong short looooooooooong</a> - </p> -<div id="bubblesTo" onclick="window.bubbledClick = true;"> - <a id="bubblesFrom">I bubble</a> -</div> -<div id="addbuttonlistener" onclick="var el = document.getElementById('showbutton');el.addEventListener('mousedown', function (evt) { document.getElementById('showbutton').innerHTML = evt.button; }, false);"> - click to add an event listener -</div> -<div id="showbutton"> - this will show the button clicked -</div> -<a href="xhtmlTest.html">333333</a> -<p><a href="xhtmlTest.html" id="embeddedBlock"><span style="display: block;">I have a span</span><div>And a div</div><span>And another span</span></a></p> -</body> -</html> diff --git a/testing/marionette/harness/marionette_harness/www/cssTransform.html b/testing/marionette/harness/marionette_harness/www/cssTransform.html deleted file mode 100644 index c3b99648a..000000000 --- a/testing/marionette/harness/marionette_harness/www/cssTransform.html +++ /dev/null @@ -1,61 +0,0 @@ -<!DOCTYPE html> -<style> -#parentY { - transform: translateY(-10000px); - -webkit-transform: translateY(-10000px); - -o-transform: translateY(-10000px); - -ms-transform: translateY(-10000px); - -moz-transform: translateY(-10000px); -} -#parentX { - transform: translateX(-10000px); - -webkit-transform: translateX(-10000px); - -o-transform: translateX(-10000px); - -ms-transform: translateX(-10000px); - -moz-transform: translateX(-10000px); -} -#transformX { - transform: translateX(-10000px); - -webkit-transform: translateX(-10000px); - -o-transform: translateX(-10000px); - -ms-transform: translateX(-10000px); - -moz-transform: translateX(-10000px); -} -#transformY { - transform: translateY(-10000px); - -webkit-transform: translateY(-10000px); - -o-transform: translateY(-10000px); - -ms-transform: translateY(-10000px); - -moz-transform: translateY(-10000px); -} - -#zero-transform { - transform: translateY(0px); - -webkit-transform: translateY(0px); - -o-transform: translateY(0px); - -ms-transform: translateY(0px); - -moz-transform: translateY(0px); - transform: translateX(0px); - -webkit-transform: translateX(0px); - -o-transform: translateX(0px); - -ms-transform: translateX(0px); - -moz-transform: translateX(0px); -} -</style> -<div id='zero-tranform'> -You shouldn't see anything other than this sentence on the page -</div> -<div id='parentY'> - I have a hidden child - <div id='childY'> - I am a hidden child - </div> -</div> -<div id='parentX'> - I have a hidden child - <div id='childX'> - I am a hidden child - </div> -</div> -<div id='transformX'>I am a hidden element </div> -<div id='transformY'>I am a hidden element </div> diff --git a/testing/marionette/harness/marionette_harness/www/cssTransform2.html b/testing/marionette/harness/marionette_harness/www/cssTransform2.html deleted file mode 100644 index 602924bfb..000000000 --- a/testing/marionette/harness/marionette_harness/www/cssTransform2.html +++ /dev/null @@ -1,20 +0,0 @@ -<!DOCTYPE html> -<style> -#negative-percentage-transformY{ - transform: translateY(-75px); - -webkit-transform: translateY(-75%); - -o-transform: translateY(-75%); - -ms-transform: translateY(-75%); - -moz-transform: translateY(-75%); -} -.block { - display = block; -} -</style> -<div class='block'> - <br/> -</div> - <br/> -<div class='block'> -</div> -<div id='negative-percentage-transformY'>I am not a hidden element </div> diff --git a/testing/marionette/harness/marionette_harness/www/datetimePage.html b/testing/marionette/harness/marionette_harness/www/datetimePage.html deleted file mode 100644 index 87a05f6d2..000000000 --- a/testing/marionette/harness/marionette_harness/www/datetimePage.html +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0"?> -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> - <!-- 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/. --> -<head> - <title>Testing date and time inputs</title> -</head> -<body> - <form> - <input id="date-test" type="date"/> - <input id="time-test" type="time"/> - </form> -</body> -</html> diff --git a/testing/marionette/harness/marionette_harness/www/deletingFrame.html b/testing/marionette/harness/marionette_harness/www/deletingFrame.html deleted file mode 100644 index d891605af..000000000 --- a/testing/marionette/harness/marionette_harness/www/deletingFrame.html +++ /dev/null @@ -1,29 +0,0 @@ -<html>
-<head>
- <title></title>
-</head>
- <script type="text/javascript">
- function remove() {
- var iframe = document.getElementById("iframe1");
- var myDiv = document.getElementById("myDiv");
- myDiv.removeChild(iframe);
- }
- function addBack() {
- var iframe = '<iframe src="javascriptPage.html" marginheight="0" marginwidth="0" topmargin="0" leftmargin="600" allowtransparency="true" frameborder="0" height="600" scrolling="no" width="120" id="iframe1"></iframe>';
- var myDiv2 = document.getElementById("myDiv2");
- myDiv2.innerHTML = iframe;
- }
- </script>
-<body>
-
-<div id='myDiv'>
-<iframe src="formPage.html" marginheight="0" marginwidth="0" topmargin="0" leftmargin="0" allowtransparency="true"
- frameborder="1" height="900" scrolling="no" width="500" id="iframe1"></iframe>
-
- </div>
-<div id='myDiv2'>
-
-</div>
-<input type='button' id='addBackFrame' value='Add back frame' onclick='addBack();' />
-</body>
-</html>
diff --git a/testing/marionette/harness/marionette_harness/www/double_click.html b/testing/marionette/harness/marionette_harness/www/double_click.html deleted file mode 100644 index fb3ec217a..000000000 --- a/testing/marionette/harness/marionette_harness/www/double_click.html +++ /dev/null @@ -1,18 +0,0 @@ -<?xml version="1.0"?> -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> - <!-- 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/. --> - <head> - <title>Testing Double Click</title> - </head> - <div> - <p id="one-word-div">zyxw</p> - </div> - - <div> - <form> - <input type="text" id="input-field" size="80"/> - </form> - </div> -</html> diff --git a/testing/marionette/harness/marionette_harness/www/element_bottom.html b/testing/marionette/harness/marionette_harness/www/element_bottom.html deleted file mode 100644 index 470199a07..000000000 --- a/testing/marionette/harness/marionette_harness/www/element_bottom.html +++ /dev/null @@ -1,12 +0,0 @@ -<!DOCTYPE html> -<style> - .element{ - position: absolute; - bottom: -50px; - background-color: red; - width: 100px; - height: 100px; - } -</style> -<div class='element'></div> - diff --git a/testing/marionette/harness/marionette_harness/www/element_left.html b/testing/marionette/harness/marionette_harness/www/element_left.html deleted file mode 100644 index c98b945f9..000000000 --- a/testing/marionette/harness/marionette_harness/www/element_left.html +++ /dev/null @@ -1,12 +0,0 @@ -<!DOCTYPE html> -<style> - .element { - position: absolute; - left: -50px; - background-color: red; - width: 100px; - height: 100px; - } -</style> -<div class='element'></div> - diff --git a/testing/marionette/harness/marionette_harness/www/element_outside_viewport.html b/testing/marionette/harness/marionette_harness/www/element_outside_viewport.html deleted file mode 100644 index 69b66b875..000000000 --- a/testing/marionette/harness/marionette_harness/www/element_outside_viewport.html +++ /dev/null @@ -1,41 +0,0 @@ -<!DOCTYPE html> -<style> - div { - position: absolute; - width: 100px; - height: 100px; - } - .top { background-color: red; } - #top-70 { left: 80px; top: 0; } - #top-50 { left: 190px; top: 20px; } - #top-30 { left: 300px; top: 40px; } - - .right { background-color: black; } - #right-70 { top: 80px; right: -140px;} - #right-50 { top: 190px; right: -120px;} - #right-30 { top: 300px; right: -100px;} - - .bottom { background-color: blue; } - #bottom-70 { right: -50px; bottom: -140px; } - #bottom-50 { right: 60px; bottom: -120px; } - #bottom-30 { right: 170px; bottom: -100px; } - - .left { background-color: green; } - #left-70 { bottom: -50px; left: 0; } - #left-50 { bottom: 60px; left: 20px; } - #left-30 { bottom: 170px; left: 40px; } -</style> -<body onload="window.scrollTo(70, 70);"> - <div id="top-70" class="top"></div> - <div id="top-50" class="top"></div> - <div id="top-30" class="top"></div> - <div id="right-70" class="right"></div> - <div id="right-50" class="right"></div> - <div id="right-30" class="right"></div> - <div id="bottom-70" class="bottom"></div> - <div id="bottom-50" class="bottom"></div> - <div id="bottom-30" class="bottom"></div> - <div id="left-70" class="left"></div> - <div id="left-50" class="left"></div> - <div id="left-30" class="left"></div> -</body> diff --git a/testing/marionette/harness/marionette_harness/www/element_right.html b/testing/marionette/harness/marionette_harness/www/element_right.html deleted file mode 100644 index ff8774ab6..000000000 --- a/testing/marionette/harness/marionette_harness/www/element_right.html +++ /dev/null @@ -1,12 +0,0 @@ -<!DOCTYPE html> -<style> - .element{ - position: absolute; - right: -50px; - background-color: red; - width: 100px; - height: 100px; - } -</style> -<div class='element'></div> - diff --git a/testing/marionette/harness/marionette_harness/www/element_top.html b/testing/marionette/harness/marionette_harness/www/element_top.html deleted file mode 100644 index b975e34c4..000000000 --- a/testing/marionette/harness/marionette_harness/www/element_top.html +++ /dev/null @@ -1,12 +0,0 @@ -<!DOCTYPE html> -<style> - .element{ - position: absolute; - top: -50px; - background-color: red; - width: 100px; - height: 100px; - } -</style> -<div class='element'></div> - diff --git a/testing/marionette/harness/marionette_harness/www/empty.html b/testing/marionette/harness/marionette_harness/www/empty.html deleted file mode 100644 index 0a9d08cd6..000000000 --- a/testing/marionette/harness/marionette_harness/www/empty.html +++ /dev/null @@ -1,12 +0,0 @@ -<!-- 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/. --> - -<!DOCTYPE html> -<html> -<head> -<title>Marionette Test</title> -</head> -<body> -</body> -</html> diff --git a/testing/marionette/harness/marionette_harness/www/formPage.html b/testing/marionette/harness/marionette_harness/www/formPage.html deleted file mode 100644 index ee1ff9f2b..000000000 --- a/testing/marionette/harness/marionette_harness/www/formPage.html +++ /dev/null @@ -1,116 +0,0 @@ -<html> -<head> - <title>We Leave From Here</title> - - <script type="text/javascript"> - function changePage() { - var newLocation = '/common/page/3'; - window.location = newLocation; - } - </script> -</head> -<body> -There should be a form here: - -<form method="get" action="resultPage.html" name="login"> - <input type="email" id="email"/> - <input type="submit" id="submitButton" value="Hello there"/> -</form> - -<form method="get" action="resultPage.html" name="optional" style="display: block"> - Here's a checkbox: - <input type="checkbox" id="checky" name="checky" value="furrfu"/> - <input type="checkbox" id="checkedchecky" name="checkedchecky" checked="checked" /> - <input type="checkbox" id="disabledchecky" disabled="disabled" name="disabledchecky" /> - <input type="checkbox" id="randomly_disabled_checky" disabled="somerandomstring" checked="checked" name="randomlydisabledchecky" /> - <br/> - <select name="selectomatic"> - <option selected="selected" id="non_multi_option" value="one">One</option> - <option value="two">Two</option> - <option value="four">Four</option> - <option value="still learning how to count, apparently">Still learning how to count, apparently</option> - </select> - - <select name="multi" id="multi" multiple="multiple"> - <option selected="selected" value="eggs">Eggs</option> - <option value="ham">Ham</option> - <option selected="selected" value="sausages">Sausages</option> - <option value="onion gravy">Onion gravy</option> - </select> - - <select name="no-select" disabled="disabled"> - <option value="foo">Foo</option> - </select> - - <select name="select_empty_multiple" multiple> - <option id="multi_1" value="select_1">select_1</option> - <option id="multi_2" value="select_2">select_2</option> - <option id="multi_3" value="select_3">select_3</option> - <option id="multi_4" value="select_4">select_4</option> - </select> - - <select name="multi_true" multiple="true"> - <option id="multi_true_1" value="select_1">select_1</option> - <option id="multi_true_2" value="select_2">select_2</option> - </select> - - <select name="multi_false" multiple="false"> - <option id="multi_false_1" value="select_1">select_1</option> - <option id="multi_false_2" value="select_2">select_2</option> - </select> - - <select id="invisi_select" style="opacity:0;"> - <option selected value="apples">Apples</option> - <option value="oranges">Oranges</option> - </select> - - <select name="select-default"> - <option>One</option> - <option>Two</option> - <option>Four</option> - <option>Still learning how to count, apparently</option> - </select> - - <select name="select_with_spaces"> - <option>One</option> - <option> Two </option> - <option> - Four - </option> - <option> - Still learning how to count, - apparently - </option> - </select> - - <select> - <option id="blankOption"></option> - <option id="optionEmptyValueSet" value="">nothing</option> - </select> - - <br/> - - <input type="radio" id="cheese" name="snack" value="cheese"/>Cheese<br/> - <input type="radio" id="peas" name="snack" value="peas"/>Peas<br/> - <input type="radio" id="cheese_and_peas" name="snack" value="cheese and peas" checked/>Cheese and peas<br/> - <input type="radio" id="nothing" name="snack" value="nowt" disabled="disabled"/>Not a sausage<br/> - <input type="radio" id="randomly_disabled_nothing" name="snack" value="funny nowt" disabled="somedisablingstring"/>Not another sausage - - <input type="hidden" name="hidden" value="fromage" /> - - <p id="cheeseLiker">I like cheese</p> - <input type="submit" value="Click!"/> - - <input type="radio" id="lone_disabled_selected_radio" name="not_a_snack" value="cumberland" checked="checked" disabled="disabled" />Cumberland sausage -</form> - -<input type='button' id='killIframe' onclick='top.remove();' value="Kill containing iframe" /> - -<form method="get" action="formPage.html"> - <p> - <label for="checkbox-with-label" id="label-for-checkbox-with-label">Label</label><input type="checkbox" id="checkbox-with-label" /> - </p> -</form> -<input id="vsearchGadget" name="SearchableText" type="text" size="18" value="" title="Hvad søger du?" accesskey="4" class="inputLabel" /> -</body> -</html> diff --git a/testing/marionette/harness/marionette_harness/www/frameset.html b/testing/marionette/harness/marionette_harness/www/frameset.html deleted file mode 100644 index 209d705b5..000000000 --- a/testing/marionette/harness/marionette_harness/www/frameset.html +++ /dev/null @@ -1,14 +0,0 @@ -<html> - <head> - <title>Unique title</title> - </head> -<frameset cols="*, *, *, *, *, *, *"> - <frame name="first" src="page/1"/> - <frame name="second" src="page/2?title=Fish"/> - <frame name="third" src="formPage.html"/> - <frame name="fourth" src="framesetPage2.html"/> - <frame id="fifth" src="xhtmlTest.html"/> - <frame id="sixth" src="test_iframe.html"/> - <frame id="sixth.iframe1" src="page/3"/> -</frameset> -</html>
\ No newline at end of file diff --git a/testing/marionette/harness/marionette_harness/www/framesetPage2.html b/testing/marionette/harness/marionette_harness/www/framesetPage2.html deleted file mode 100644 index 5190ceb6c..000000000 --- a/testing/marionette/harness/marionette_harness/www/framesetPage2.html +++ /dev/null @@ -1,7 +0,0 @@ -<html> -<head></head> -<frameset cols="*, *"> - <frame name="child1" src="test.html"/> - <frame name="child2" src="test.html"/> -</frameset> -</html> diff --git a/testing/marionette/harness/marionette_harness/www/hidden.html b/testing/marionette/harness/marionette_harness/www/hidden.html deleted file mode 100644 index 0e8097e97..000000000 --- a/testing/marionette/harness/marionette_harness/www/hidden.html +++ /dev/null @@ -1,5 +0,0 @@ -<!DOCTYPE html> -<div id='singleHidden' hidden>This will not be visible</div> -<div id='parent' hidden> - <div id='child'>My parent is hidden so you can't see me</div> -</div>
\ No newline at end of file diff --git a/testing/marionette/harness/marionette_harness/www/html5/blue.jpg b/testing/marionette/harness/marionette_harness/www/html5/blue.jpg Binary files differdeleted file mode 100644 index 8ea27c42f..000000000 --- a/testing/marionette/harness/marionette_harness/www/html5/blue.jpg +++ /dev/null diff --git a/testing/marionette/harness/marionette_harness/www/html5/boolean_attributes.html b/testing/marionette/harness/marionette_harness/www/html5/boolean_attributes.html deleted file mode 100644 index eb0d458b8..000000000 --- a/testing/marionette/harness/marionette_harness/www/html5/boolean_attributes.html +++ /dev/null @@ -1,2 +0,0 @@ -<!DOCTYPE html> -<input id='disabled' disabled>
\ No newline at end of file diff --git a/testing/marionette/harness/marionette_harness/www/html5/geolocation.js b/testing/marionette/harness/marionette_harness/www/html5/geolocation.js deleted file mode 100644 index f07af148e..000000000 --- a/testing/marionette/harness/marionette_harness/www/html5/geolocation.js +++ /dev/null @@ -1,18 +0,0 @@ -function success(position) { - var message = document.getElementById("status"); - message.innerHTML ="<img src='http://maps.google.com/maps/api/staticmap?center=" + position.coords.latitude + "," + position.coords.longitude + "&size=300x200&maptype=roadmap&zoom=12&&markers=size:mid|color:red|" + position.coords.latitude + "," + position.coords.longitude + "&sensor=false' />"; - message.innerHTML += "<p>Longitude: " + position.coords.longitude + "</p>"; - message.innerHTML += "<p>Latitude: " + position.coords.latitude + "</p>"; - message.innerHTML += "<p>Altitude: " + position.coords.altitude + "</p>"; -} - -function error(msg) { - var message = document.getElementById("status"); - message.innerHTML = "Failed to get geolocation."; -} - -if (navigator.geolocation) { - navigator.geolocation.getCurrentPosition(success, error); -} else { - error('Geolocation is not supported.'); -}
\ No newline at end of file diff --git a/testing/marionette/harness/marionette_harness/www/html5/green.jpg b/testing/marionette/harness/marionette_harness/www/html5/green.jpg Binary files differdeleted file mode 100644 index 6a0d3bea4..000000000 --- a/testing/marionette/harness/marionette_harness/www/html5/green.jpg +++ /dev/null diff --git a/testing/marionette/harness/marionette_harness/www/html5/offline.html b/testing/marionette/harness/marionette_harness/www/html5/offline.html deleted file mode 100644 index c24178b5f..000000000 --- a/testing/marionette/harness/marionette_harness/www/html5/offline.html +++ /dev/null @@ -1 +0,0 @@ -<html><head><title>Offline</title></head><body></body></html> diff --git a/testing/marionette/harness/marionette_harness/www/html5/red.jpg b/testing/marionette/harness/marionette_harness/www/html5/red.jpg Binary files differdeleted file mode 100644 index f296e2719..000000000 --- a/testing/marionette/harness/marionette_harness/www/html5/red.jpg +++ /dev/null diff --git a/testing/marionette/harness/marionette_harness/www/html5/status.html b/testing/marionette/harness/marionette_harness/www/html5/status.html deleted file mode 100644 index 394116a52..000000000 --- a/testing/marionette/harness/marionette_harness/www/html5/status.html +++ /dev/null @@ -1 +0,0 @@ -<html><head><title>Online</title></head><body></body></html> diff --git a/testing/marionette/harness/marionette_harness/www/html5/test.appcache b/testing/marionette/harness/marionette_harness/www/html5/test.appcache deleted file mode 100644 index 3bc4e0025..000000000 --- a/testing/marionette/harness/marionette_harness/www/html5/test.appcache +++ /dev/null @@ -1,11 +0,0 @@ -CACHE MANIFEST - -CACHE: -# Additional items to cache. -yellow.jpg -red.jpg -blue.jpg -green.jpg - -FALLBACK: -status.html offline.html diff --git a/testing/marionette/harness/marionette_harness/www/html5/test_html_inputs.html b/testing/marionette/harness/marionette_harness/www/html5/test_html_inputs.html deleted file mode 100644 index 96c9b97e3..000000000 --- a/testing/marionette/harness/marionette_harness/www/html5/test_html_inputs.html +++ /dev/null @@ -1,2 +0,0 @@ -<!DOCTYPE html> -<input id='number' type=number>
\ No newline at end of file diff --git a/testing/marionette/harness/marionette_harness/www/html5/yellow.jpg b/testing/marionette/harness/marionette_harness/www/html5/yellow.jpg Binary files differdeleted file mode 100644 index 7c609b371..000000000 --- a/testing/marionette/harness/marionette_harness/www/html5/yellow.jpg +++ /dev/null diff --git a/testing/marionette/harness/marionette_harness/www/html5Page.html b/testing/marionette/harness/marionette_harness/www/html5Page.html deleted file mode 100644 index 4197cd122..000000000 --- a/testing/marionette/harness/marionette_harness/www/html5Page.html +++ /dev/null @@ -1,45 +0,0 @@ -<html manifest="html5/test.appcache"> -<!-- -Copyright 2011 Software Freedom Conservancy. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. ---> - - -<head> -<title>HTML5</title> -</head> -<body> - -<h3>Geolocation Test</h3> -<div id="status">Location unknown</div> -<script language="javascript" type="text/javascript" src="html5/geolocation.js"></script> - -<h3>Application Cache Test</h3> -<div id="images"> - <p>Current network status: <span id="state"></span></p> - <script> - var state = document.getElementById('state') - setInterval(function () { - state.className = navigator.onLine ? 'online' : 'offline'; - state.innerHTML = navigator.onLine ? 'online' : 'offline'; - }, 250); - </script> - <img id="red" src="html5/red.jpg"> - <img id="blue" src="html5/blue.jpg"> - <img id="green" src="html5/green.jpg"> - <img id="yellow" src="html5/yellow.jpg"> -</div> - -</body> -</html> diff --git a/testing/marionette/harness/marionette_harness/www/javascriptPage.html b/testing/marionette/harness/marionette_harness/www/javascriptPage.html deleted file mode 100644 index b47c902c5..000000000 --- a/testing/marionette/harness/marionette_harness/www/javascriptPage.html +++ /dev/null @@ -1,278 +0,0 @@ -<?xml version="1.0"?> -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> - <!-- 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/. --> -<head> - <title>Testing Javascript</title> - <script type="text/javascript"> - var seen = {}; - - function updateContent(input) { - document.getElementById('result').innerHTML = "<p>" + input.value + "</p>"; - } - - function displayMessage(message) { - document.getElementById('result').innerHTML = "<p>" + message + "</p>"; - } - - function appendMessage(message) { - document.getElementById('result').innerHTML += message + " "; - } - - function register(message) { - if (!seen[message]) { - appendMessage(message); - seen[message] = true; - } - } - - function delayedShowHide(delay, show) { - var blackBox = document.getElementById('clickToHide'); - window.setTimeout(function() { - blackBox.style.display = show ? '' : 'none'; - }, delay); - } - </script> - <script type="text/javascript"> - var startList = function() { - // Ugh. Let's hope no-one is faking their user agent when running the tests - if (navigator.userAgent.indexOf("MSIE") != -1) { - var navRoot = document.getElementById("nav"); - for (var i = 0; i < navRoot.childNodes.length; i++) { - var node = navRoot.childNodes[i]; - if (node.nodeName == "LI") { - node.onmouseover = function() { - this.className += " over"; - }; - node.onmouseout = function() { - this.className = this.className.replace(" over", ""); - }; - } - } - } - }; - window.onload=startList; - </script> - <style type="text/css"> - #nav { - padding: 0; margin: 0; list-style: none; - } - #nav li { - float: left; position: relative; width: 10em; - } - #nav li ul { - display: none; position: absolute; top: 1em; left: 0; - } - #nav li > ul { top: auto; left: auto; } - #nav li:hover ul, #nav li.over ul{ display: block; background: white; } - </style> -</head> -<body> -<h1>Type Stuff</h1> - -<div> - <ul id="nav"> - <li id="menu1">Menu 1 - <ul> - <li id="item1" onclick="displayMessage('item 1');">Item 1</li> - <li>Item 2</li> - </ul> - </li> - </ul> -</div> - -<div id="resultContainer" height="30"> - <div id="result" style="width:300;height:60"> - <p> </p> - </div> - -</div> - -<div id="formageddon"> - <form action="#"> - Key Up: <input type="text" id="keyUp" onkeyup="javascript:updateContent(this)"/><br/> - Key Down: <input type="text" id="keyDown" onkeydown="javascript:updateContent(this)"/><br/> - Key Press: <input type="text" id="keyPress" onkeypress="javascript:updateContent(this)"/><br/> - Change: <input type="text" id="change" onkeypress="javascript:displayMessage('change')"/><br/> - <textarea id="keyDownArea" onkeydown="javascript:updateContent(this)" rows="2" cols="15"></textarea> - <textarea id="keyPressArea" onkeypress="javascript:updateContent(this)" rows="2" cols="15"></textarea> - <textarea id="keyUpArea" onkeyup="javascript:updateContent(this)" rows="2" cols="15"></textarea> - <select id="selector" onchange="javascript:updateContent(this)"> - <option value="foo">Foo</option> - <option value="bar">Bar</option> - </select> - <input type="checkbox" id="checkbox" value="checkbox thing" onchange="javascript:updateContent(this)"/> - <input id="clickField" type="text" onclick="document.getElementById('clickField').value='Clicked';" value="Hello"/> - <input id="doubleClickField" type="text" onclick="document.getElementById('doubleClickField').value='Clicked';" ondblclick="document.getElementById('doubleClickField').value='DoubleClicked';" oncontextmenu="document.getElementById('doubleClickField').value='ContextClicked'; return false;" value="DoubleHello"/> - <input id="clearMe" value="Something" onchange="displayMessage('Cleared')"/> - </form> -</div> - -<div> - <p><a href="#" onclick="javascript:document.title='Changed'">Change the page title!</a></p> - - <p><a onclick="javascript:document.title='Changed'" id="nohref">No href</a></p> - - <p><a id="updatediv" href="#" onclick="javascript:document.getElementById('dynamo').innerHTML = 'Fish and chips!';">Update a - div</a></p> -</div> - -<div id="dynamo">What's for dinner?</div> - -<div id="mousedown" onmousedown="javascript:displayMessage('mouse down');"> - <p>Click for the mouse down event</p> - <span><p id="child">Here's some text</p></span> -</div> - -<div id="mouseup" onmouseup="javascript:displayMessage('mouse up');"> - <p>Click for the mouse up event</p> -</div> - -<div id="mouseclick" onclick="javascript:displayMessage('mouse click');"> - <p>Click for the mouse click event</p> -</div> - -<div id="mousebutton"> - <p>Click to show button</p> -</div> - -<div id="error" onclick="document.getElementById('doesnotexist').innerHTML = 'cheese';"> - Clicking this causes a JS exception in the click handler -</div> - -<div> - <form action="resultPage.html" id="on-form"> - <input id="theworks" - onfocus="appendMessage('focus')" - onkeydown="appendMessage('keydown')" - onkeypress="appendMessage('keypress')" - onkeyup="appendMessage('keyup')" - onblur="appendMessage('blur')" - onchange="appendMessage('change')" - /> - - <input id="changeable" name="changeable" onfocus="appendMessage('focus')" onchange="appendMessage('change')" onblur="appendMessage('blur')"/> - - <button type="button" id="plainButton" - onfocus="appendMessage('focus')" - onkeydown="appendMessage('keydown')" - onkeypress="appendMessage('keypress')" - onkeyup="appendMessage('keyup')" - onblur="appendMessage('blur')" - onclick="appendMessage('click')" - onmousedown="appendMessage('mousedown ')" - onmouseup="appendMessage('mouseup ')" - onmouseover="register('mouseover ')" - onmousemove="register('mousemove ')" - > - <b>Go somewhere</b> - </button> - <button type="submit" id="submittingButton"><emph>submit</emph></button> - <button type="button" id="jsSubmitButton" onclick="javascript:document.getElementById('on-form').submit();">Submitomatic</button> - - <button type="button" id="switchFocus" onclick="document.getElementById('theworks').focus();">Switch focus</button> - <button type="button" onclick="var element = document.getElementById('switchFocus'); var clickEvent = document.createEvent('MouseEvents'); clickEvent.initMouseEvent('click', true, true, null, 0, 0, 0, 0, 0,false, false, false, false, 0, element);element.dispatchEvent(clickEvent);">Do magic</button><br/> - <label id="labelForCheckbox" for="labeledCheckbox" onclick="appendMessage('labelclick')">Toggle checkbox</label><input type="checkbox" id="labeledCheckbox" onclick="appendMessage('chboxclick')"/> - </form> - - <form action="javascriptPage.html" id="submitListeningForm" onsubmit="appendMessage('form-onsubmit '); return false;"> - <p> - <input id="submitListeningForm-text" type="text" onsubmit="appendMessage('text-onsubmit ')" onclick="appendMessage('text-onclick ');" /> - <input id="submitListeningForm-submit" type="submit" onsubmit="appendMessage('submit-onsubmit ')" onclick="appendMessage('submit-onclick ');" /> - </p> - </form> -</div> - -<p id="suppressedParagraph" style="display: none">A paragraph suppressed using CSS display=none</p> - -<div> - <p id="displayed">Displayed</p> - - <form action="#"><input type="hidden" name="hidden" /> </form> - - <p id="none" style="display: none;">Display set to none</p> - - <p id="hidden" style="visibility: hidden;">Hidden</p> - - <div id="hiddenparent" style="height: 2em; display: none;"> - <div id="hiddenchild"> - <a href="#" id="hiddenlink">ok</a> - </div> - </div> - - <div style="visibility: hidden;"> - <span> - <input id="unclickable" /> - <input type="checkbox" id="untogglable" checked="checked" />Check box you can't see - </span> - </div> - - <p id="outer" style="visibility: hidden">A <b id="visibleSubElement" style="visibility: visible">sub-element that is explicitly visible</b> using CSS visibility=visible</p> -</div> - -<div> - <form> - <input type="text" id="keyReporter" size="80" - onkeyup="appendMessage('up: ' + event.keyCode)" - onkeypress="appendMessage('press: ' + event.keyCode)" - onkeydown="displayMessage(''); appendMessage('down: ' + event.keyCode)" /> - <input name="suppress" onkeydown="if (event.preventDefault) event.preventDefault(); event.returnValue = false; return false;" onkeypress="appendMessage('press');"/> - </form> -</div> - -<!-- Used for testing styles --> -<div style="background-color: green;" id="green-parent"> - <p id="style1">This should be greenish</p> - <ul> - <li id="green-item">So should this</li> - <li id="red-item" style="background-color: red;">But this is red</li> - </ul> -</div> - -<a href="#" id="close" onclick="window.close();">Close window</a> - -<div id="delete" onclick="var d = document.getElementById('deleted'); this.removeChild(d);"> - <p id="deleted">I should be deleted when you click my containing div</p> - <p>Whereas, I should not</p> -</div> - -<div> - <span id="hideMe" onclick="this.style.display = 'none';">Click to hide me.</span> -</div> - -<div style="margin-top: 10px;"> - Click actions delayed by 3000ms: - <div id="clickToShow" onclick="delayedShowHide(3000, true);" - style="float: left;width: 100px;height:100px;border: 1px solid black;"> - Click to show black box - </div> - <div id="clickToHide" onclick="delayedShowHide(3000, false);" - style="float: left;width: 100px;height:100px;border: 1px solid black; - background-color: black; color: white; display: none;"> - Click to hide black box - </div> - <div style="clear: both"></div> -</div> - -<a id="new_window" onmouseup="window.open('closeable_window.html', 'close_me')" href="#">Click me to open a new window</a> - -<a id="throwing-mouseover" onmouseover="throw new Error()" href="#throwing-mouseover">Mouse over me will throw a JS error</a> - -<div id="parent"> - <span id="movable" onmouseover="var p = document.getElementById('movable'); displayMessage('parent matches? ' + (p != event.relatedTarget));"> - Click on me to show the related target - </span> -</div> - -<div id="zero" style="width:0;height:0"> - <div> - <img src="map.png"> - </div> -</div> - -<input type='text' id='notDisplayed' style='display:none'> -</body> -</html> - - diff --git a/testing/marionette/harness/marionette_harness/www/macbeth.html b/testing/marionette/harness/marionette_harness/www/macbeth.html deleted file mode 100644 index 2404f1d72..000000000 --- a/testing/marionette/harness/marionette_harness/www/macbeth.html +++ /dev/null @@ -1,5254 +0,0 @@ -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"> - <html> - <head> - <title>Macbeth: Entire Play - </title> - <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> - </HEAD> - <body bgcolor="#ffffff" text="#000000"> - - <!-- Originally from http://shakespeare.mit.edu/macbeth/full.html --> - -<a href="#5.8.86">Quick link to last speech</a> - -<H3>ACT I</h3> -<h3>SCENE I. A desert place.</h3> -<p><blockquote> -<i>Thunder and lightning. Enter three Witches</i> -</blockquote> - -<A NAME=speech1><b>First Witch</b></a> -<blockquote> -<A NAME=1.1.1>When shall we three meet again</A><br> -<A NAME=1.1.2>In thunder, lightning, or in rain?</A><br> -</blockquote> - -<A NAME=speech2><b>Second Witch</b></a> -<blockquote> -<A NAME=1.1.3>When the hurlyburly's done,</A><br> -<A NAME=1.1.4>When the battle's lost and won.</A><br> -</blockquote> - -<A NAME=speech3><b>Third Witch</b></a> -<blockquote> -<A NAME=1.1.5>That will be ere the set of sun.</A><br> -</blockquote> - -<A NAME=speech4><b>First Witch</b></a> -<blockquote> -<A NAME=1.1.6>Where the place?</A><br> -</blockquote> - -<A NAME=speech5><b>Second Witch</b></a> -<blockquote> -<A NAME=1.1.7> Upon the heath.</A><br> -</blockquote> - -<A NAME=speech6><b>Third Witch</b></a> -<blockquote> -<A NAME=1.1.8>There to meet with Macbeth.</A><br> -</blockquote> - -<A NAME=speech7><b>First Witch</b></a> -<blockquote> -<A NAME=1.1.9>I come, Graymalkin!</A><br> -</blockquote> - -<A NAME=speech8><b>Second Witch</b></a> -<blockquote> -<A NAME=1.1.10>Paddock calls.</A><br> -</blockquote> - -<A NAME=speech9><b>Third Witch</b></a> -<blockquote> -<A NAME=1.1.11>Anon.</A><br> -</blockquote> - -<A NAME=speech10><b>ALL</b></a> -<blockquote> -<A NAME=1.1.12>Fair is foul, and foul is fair:</A><br> -<A NAME=1.1.13>Hover through the fog and filthy air.</A><br> -<p><i>Exeunt</i></p> -</blockquote> -<h3>SCENE II. A camp near Forres.</h3> -<p><blockquote> -<i>Alarum within. Enter DUNCAN, MALCOLM, DONALBAIN, LENNOX, with Attendants, meeting a bleeding Sergeant</i> -</blockquote> - -<A NAME=speech1><b>DUNCAN</b></a> -<blockquote> -<A NAME=1.2.1>What bloody man is that? He can report,</A><br> -<A NAME=1.2.2>As seemeth by his plight, of the revolt</A><br> -<A NAME=1.2.3>The newest state.</A><br> -</blockquote> - -<A NAME=speech2><b>MALCOLM</b></a> -<blockquote> -<A NAME=1.2.4> This is the sergeant</A><br> -<A NAME=1.2.5>Who like a good and hardy soldier fought</A><br> -<A NAME=1.2.6>'Gainst my captivity. Hail, brave friend!</A><br> -<A NAME=1.2.7>Say to the king the knowledge of the broil</A><br> -<A NAME=1.2.8>As thou didst leave it.</A><br> -</blockquote> - -<A NAME=speech3><b>Sergeant</b></a> -<blockquote> -<A NAME=1.2.9>Doubtful it stood;</A><br> -<A NAME=1.2.10>As two spent swimmers, that do cling together</A><br> -<A NAME=1.2.11>And choke their art. The merciless Macdonwald--</A><br> -<A NAME=1.2.12>Worthy to be a rebel, for to that</A><br> -<A NAME=1.2.13>The multiplying villanies of nature</A><br> -<A NAME=1.2.14>Do swarm upon him--from the western isles</A><br> -<A NAME=1.2.15>Of kerns and gallowglasses is supplied;</A><br> -<A NAME=1.2.16>And fortune, on his damned quarrel smiling,</A><br> -<A NAME=1.2.17>Show'd like a rebel's whore: but all's too weak:</A><br> -<A NAME=1.2.18>For brave Macbeth--well he deserves that name--</A><br> -<A NAME=1.2.19>Disdaining fortune, with his brandish'd steel,</A><br> -<A NAME=1.2.20>Which smoked with bloody execution,</A><br> -<A NAME=1.2.21>Like valour's minion carved out his passage</A><br> -<A NAME=1.2.22>Till he faced the slave;</A><br> -<A NAME=1.2.23>Which ne'er shook hands, nor bade farewell to him,</A><br> -<A NAME=1.2.24>Till he unseam'd him from the nave to the chaps,</A><br> -<A NAME=1.2.25>And fix'd his head upon our battlements.</A><br> -</blockquote> - -<A NAME=speech4><b>DUNCAN</b></a> -<blockquote> -<A NAME=1.2.26>O valiant cousin! worthy gentleman!</A><br> -</blockquote> - -<A NAME=speech5><b>Sergeant</b></a> -<blockquote> -<A NAME=1.2.27>As whence the sun 'gins his reflection</A><br> -<A NAME=1.2.28>Shipwrecking storms and direful thunders break,</A><br> -<A NAME=1.2.29>So from that spring whence comfort seem'd to come</A><br> -<A NAME=1.2.30>Discomfort swells. Mark, king of Scotland, mark:</A><br> -<A NAME=1.2.31>No sooner justice had with valour arm'd</A><br> -<A NAME=1.2.32>Compell'd these skipping kerns to trust their heels,</A><br> -<A NAME=1.2.33>But the Norweyan lord surveying vantage,</A><br> -<A NAME=1.2.34>With furbish'd arms and new supplies of men</A><br> -<A NAME=1.2.35>Began a fresh assault.</A><br> -</blockquote> - -<A NAME=speech6><b>DUNCAN</b></a> -<blockquote> -<A NAME=1.2.36>Dismay'd not this</A><br> -<A NAME=1.2.37>Our captains, Macbeth and Banquo?</A><br> -</blockquote> - -<A NAME=speech7><b>Sergeant</b></a> -<blockquote> -<A NAME=1.2.38>Yes;</A><br> -<A NAME=1.2.39>As sparrows eagles, or the hare the lion.</A><br> -<A NAME=1.2.40>If I say sooth, I must report they were</A><br> -<A NAME=1.2.41>As cannons overcharged with double cracks, so they</A><br> -<A NAME=1.2.42>Doubly redoubled strokes upon the foe:</A><br> -<A NAME=1.2.43>Except they meant to bathe in reeking wounds,</A><br> -<A NAME=1.2.44>Or memorise another Golgotha,</A><br> -<A NAME=1.2.45>I cannot tell.</A><br> -<A NAME=1.2.46>But I am faint, my gashes cry for help.</A><br> -</blockquote> - -<A NAME=speech8><b>DUNCAN</b></a> -<blockquote> -<A NAME=1.2.47>So well thy words become thee as thy wounds;</A><br> -<A NAME=1.2.48>They smack of honour both. Go get him surgeons.</A><br> -<p><i>Exit Sergeant, attended</i></p> -<A NAME=1.2.49>Who comes here?</A><br> -<p><i>Enter ROSS</i></p> -</blockquote> - -<A NAME=speech9><b>MALCOLM</b></a> -<blockquote> -<A NAME=1.2.50> The worthy thane of Ross.</A><br> -</blockquote> - -<A NAME=speech10><b>LENNOX</b></a> -<blockquote> -<A NAME=1.2.51>What a haste looks through his eyes! So should he look</A><br> -<A NAME=1.2.52>That seems to speak things strange.</A><br> -</blockquote> - -<A NAME=speech11><b>ROSS</b></a> -<blockquote> -<A NAME=1.2.53>God save the king!</A><br> -</blockquote> - -<A NAME=speech12><b>DUNCAN</b></a> -<blockquote> -<A NAME=1.2.54>Whence camest thou, worthy thane?</A><br> -</blockquote> - -<A NAME=speech13><b>ROSS</b></a> -<blockquote> -<A NAME=1.2.55>From Fife, great king;</A><br> -<A NAME=1.2.56>Where the Norweyan banners flout the sky</A><br> -<A NAME=1.2.57>And fan our people cold. Norway himself,</A><br> -<A NAME=1.2.58>With terrible numbers,</A><br> -<A NAME=1.2.59>Assisted by that most disloyal traitor</A><br> -<A NAME=1.2.60>The thane of Cawdor, began a dismal conflict;</A><br> -<A NAME=1.2.61>Till that Bellona's bridegroom, lapp'd in proof,</A><br> -<A NAME=1.2.62>Confronted him with self-comparisons,</A><br> -<A NAME=1.2.63>Point against point rebellious, arm 'gainst arm.</A><br> -<A NAME=1.2.64>Curbing his lavish spirit: and, to conclude,</A><br> -<A NAME=1.2.65>The victory fell on us.</A><br> -</blockquote> - -<A NAME=speech14><b>DUNCAN</b></a> -<blockquote> -<A NAME=1.2.66>Great happiness!</A><br> -</blockquote> - -<A NAME=speech15><b>ROSS</b></a> -<blockquote> -<A NAME=1.2.67>That now</A><br> -<A NAME=1.2.68>Sweno, the Norways' king, craves composition:</A><br> -<A NAME=1.2.69>Nor would we deign him burial of his men</A><br> -<A NAME=1.2.70>Till he disbursed at Saint Colme's inch</A><br> -<A NAME=1.2.71>Ten thousand dollars to our general use.</A><br> -</blockquote> - -<A NAME=speech16><b>DUNCAN</b></a> -<blockquote> -<A NAME=1.2.72>No more that thane of Cawdor shall deceive</A><br> -<A NAME=1.2.73>Our bosom interest: go pronounce his present death,</A><br> -<A NAME=1.2.74>And with his former title greet Macbeth.</A><br> -</blockquote> - -<A NAME=speech17><b>ROSS</b></a> -<blockquote> -<A NAME=1.2.75>I'll see it done.</A><br> -</blockquote> - -<A NAME=speech18><b>DUNCAN</b></a> -<blockquote> -<A NAME=1.2.76>What he hath lost noble Macbeth hath won.</A><br> -<p><i>Exeunt</i></p> -</blockquote> -<h3>SCENE III. A heath near Forres.</h3> -<p><blockquote> -<i>Thunder. Enter the three Witches</i> -</blockquote> - -<A NAME=speech1><b>First Witch</b></a> -<blockquote> -<A NAME=1.3.1>Where hast thou been, sister?</A><br> -</blockquote> - -<A NAME=speech2><b>Second Witch</b></a> -<blockquote> -<A NAME=1.3.2>Killing swine.</A><br> -</blockquote> - -<A NAME=speech3><b>Third Witch</b></a> -<blockquote> -<A NAME=1.3.3>Sister, where thou?</A><br> -</blockquote> - -<A NAME=speech4><b>First Witch</b></a> -<blockquote> -<A NAME=1.3.4>A sailor's wife had chestnuts in her lap,</A><br> -<A NAME=1.3.5>And munch'd, and munch'd, and munch'd:--</A><br> -<A NAME=1.3.6>'Give me,' quoth I:</A><br> -<A NAME=1.3.7>'Aroint thee, witch!' the rump-fed ronyon cries.</A><br> -<A NAME=1.3.8>Her husband's to Aleppo gone, master o' the Tiger:</A><br> -<A NAME=1.3.9>But in a sieve I'll thither sail,</A><br> -<A NAME=1.3.10>And, like a rat without a tail,</A><br> -<A NAME=1.3.11>I'll do, I'll do, and I'll do.</A><br> -</blockquote> - -<A NAME=speech5><b>Second Witch</b></a> -<blockquote> -<A NAME=1.3.12>I'll give thee a wind.</A><br> -</blockquote> - -<A NAME=speech6><b>First Witch</b></a> -<blockquote> -<A NAME=1.3.13>Thou'rt kind.</A><br> -</blockquote> - -<A NAME=speech7><b>Third Witch</b></a> -<blockquote> -<A NAME=1.3.14>And I another.</A><br> -</blockquote> - -<A NAME=speech8><b>First Witch</b></a> -<blockquote> -<A NAME=1.3.15>I myself have all the other,</A><br> -<A NAME=1.3.16>And the very ports they blow,</A><br> -<A NAME=1.3.17>All the quarters that they know</A><br> -<A NAME=1.3.18>I' the shipman's card.</A><br> -<A NAME=1.3.19>I will drain him dry as hay:</A><br> -<A NAME=1.3.20>Sleep shall neither night nor day</A><br> -<A NAME=1.3.21>Hang upon his pent-house lid;</A><br> -<A NAME=1.3.22>He shall live a man forbid:</A><br> -<A NAME=1.3.23>Weary se'nnights nine times nine</A><br> -<A NAME=1.3.24>Shall he dwindle, peak and pine:</A><br> -<A NAME=1.3.25>Though his bark cannot be lost,</A><br> -<A NAME=1.3.26>Yet it shall be tempest-tost.</A><br> -<A NAME=1.3.27>Look what I have.</A><br> -</blockquote> - -<A NAME=speech9><b>Second Witch</b></a> -<blockquote> -<A NAME=1.3.28>Show me, show me.</A><br> -</blockquote> - -<A NAME=speech10><b>First Witch</b></a> -<blockquote> -<A NAME=1.3.29>Here I have a pilot's thumb,</A><br> -<A NAME=1.3.30>Wreck'd as homeward he did come.</A><br> -<p><i>Drum within</i></p> -</blockquote> - -<A NAME=speech11><b>Third Witch</b></a> -<blockquote> -<A NAME=1.3.31>A drum, a drum!</A><br> -<A NAME=1.3.32>Macbeth doth come.</A><br> -</blockquote> - -<A NAME=speech12><b>ALL</b></a> -<blockquote> -<A NAME=1.3.33>The weird sisters, hand in hand,</A><br> -<A NAME=1.3.34>Posters of the sea and land,</A><br> -<A NAME=1.3.35>Thus do go about, about:</A><br> -<A NAME=1.3.36>Thrice to thine and thrice to mine</A><br> -<A NAME=1.3.37>And thrice again, to make up nine.</A><br> -<A NAME=1.3.38>Peace! the charm's wound up.</A><br> -<p><i>Enter MACBETH and BANQUO</i></p> -</blockquote> - -<A NAME=speech13><b>MACBETH</b></a> -<blockquote> -<A NAME=1.3.39>So foul and fair a day I have not seen.</A><br> -</blockquote> - -<A NAME=speech14><b>BANQUO</b></a> -<blockquote> -<A NAME=1.3.40>How far is't call'd to Forres? What are these</A><br> -<A NAME=1.3.41>So wither'd and so wild in their attire,</A><br> -<A NAME=1.3.42>That look not like the inhabitants o' the earth,</A><br> -<A NAME=1.3.43>And yet are on't? Live you? or are you aught</A><br> -<A NAME=1.3.44>That man may question? You seem to understand me,</A><br> -<A NAME=1.3.45>By each at once her chappy finger laying</A><br> -<A NAME=1.3.46>Upon her skinny lips: you should be women,</A><br> -<A NAME=1.3.47>And yet your beards forbid me to interpret</A><br> -<A NAME=1.3.48>That you are so.</A><br> -</blockquote> - -<A NAME=speech15><b>MACBETH</b></a> -<blockquote> -<A NAME=1.3.49> Speak, if you can: what are you?</A><br> -</blockquote> - -<A NAME=speech16><b>First Witch</b></a> -<blockquote> -<A NAME=1.3.50>All hail, Macbeth! hail to thee, thane of Glamis!</A><br> -</blockquote> - -<A NAME=speech17><b>Second Witch</b></a> -<blockquote> -<A NAME=1.3.51>All hail, Macbeth, hail to thee, thane of Cawdor!</A><br> -</blockquote> - -<A NAME=speech18><b>Third Witch</b></a> -<blockquote> -<A NAME=1.3.52>All hail, Macbeth, thou shalt be king hereafter!</A><br> -</blockquote> - -<A NAME=speech19><b>BANQUO</b></a> -<blockquote> -<A NAME=1.3.53>Good sir, why do you start; and seem to fear</A><br> -<A NAME=1.3.54>Things that do sound so fair? I' the name of truth,</A><br> -<A NAME=1.3.55>Are ye fantastical, or that indeed</A><br> -<A NAME=1.3.56>Which outwardly ye show? My noble partner</A><br> -<A NAME=1.3.57>You greet with present grace and great prediction</A><br> -<A NAME=1.3.58>Of noble having and of royal hope,</A><br> -<A NAME=1.3.59>That he seems rapt withal: to me you speak not.</A><br> -<A NAME=1.3.60>If you can look into the seeds of time,</A><br> -<A NAME=1.3.61>And say which grain will grow and which will not,</A><br> -<A NAME=1.3.62>Speak then to me, who neither beg nor fear</A><br> -<A NAME=1.3.63>Your favours nor your hate.</A><br> -</blockquote> - -<A NAME=speech20><b>First Witch</b></a> -<blockquote> -<A NAME=1.3.64>Hail!</A><br> -</blockquote> - -<A NAME=speech21><b>Second Witch</b></a> -<blockquote> -<A NAME=1.3.65>Hail!</A><br> -</blockquote> - -<A NAME=speech22><b>Third Witch</b></a> -<blockquote> -<A NAME=1.3.66>Hail!</A><br> -</blockquote> - -<A NAME=speech23><b>First Witch</b></a> -<blockquote> -<A NAME=1.3.67>Lesser than Macbeth, and greater.</A><br> -</blockquote> - -<A NAME=speech24><b>Second Witch</b></a> -<blockquote> -<A NAME=1.3.68>Not so happy, yet much happier.</A><br> -</blockquote> - -<A NAME=speech25><b>Third Witch</b></a> -<blockquote> -<A NAME=1.3.69>Thou shalt get kings, though thou be none:</A><br> -<A NAME=1.3.70>So all hail, Macbeth and Banquo!</A><br> -</blockquote> - -<A NAME=speech26><b>First Witch</b></a> -<blockquote> -<A NAME=1.3.71>Banquo and Macbeth, all hail!</A><br> -</blockquote> - -<A NAME=speech27><b>MACBETH</b></a> -<blockquote> -<A NAME=1.3.72>Stay, you imperfect speakers, tell me more:</A><br> -<A NAME=1.3.73>By Sinel's death I know I am thane of Glamis;</A><br> -<A NAME=1.3.74>But how of Cawdor? the thane of Cawdor lives,</A><br> -<A NAME=1.3.75>A prosperous gentleman; and to be king</A><br> -<A NAME=1.3.76>Stands not within the prospect of belief,</A><br> -<A NAME=1.3.77>No more than to be Cawdor. Say from whence</A><br> -<A NAME=1.3.78>You owe this strange intelligence? or why</A><br> -<A NAME=1.3.79>Upon this blasted heath you stop our way</A><br> -<A NAME=1.3.80>With such prophetic greeting? Speak, I charge you.</A><br> -<p><i>Witches vanish</i></p> -</blockquote> - -<A NAME=speech28><b>BANQUO</b></a> -<blockquote> -<A NAME=1.3.81>The earth hath bubbles, as the water has,</A><br> -<A NAME=1.3.82>And these are of them. Whither are they vanish'd?</A><br> -</blockquote> - -<A NAME=speech29><b>MACBETH</b></a> -<blockquote> -<A NAME=1.3.83>Into the air; and what seem'd corporal melted</A><br> -<A NAME=1.3.84>As breath into the wind. Would they had stay'd!</A><br> -</blockquote> - -<A NAME=speech30><b>BANQUO</b></a> -<blockquote> -<A NAME=1.3.85>Were such things here as we do speak about?</A><br> -<A NAME=1.3.86>Or have we eaten on the insane root</A><br> -<A NAME=1.3.87>That takes the reason prisoner?</A><br> -</blockquote> - -<A NAME=speech31><b>MACBETH</b></a> -<blockquote> -<A NAME=1.3.88>Your children shall be kings.</A><br> -</blockquote> - -<A NAME=speech32><b>BANQUO</b></a> -<blockquote> -<A NAME=1.3.89>You shall be king.</A><br> -</blockquote> - -<A NAME=speech33><b>MACBETH</b></a> -<blockquote> -<A NAME=1.3.90>And thane of Cawdor too: went it not so?</A><br> -</blockquote> - -<A NAME=speech34><b>BANQUO</b></a> -<blockquote> -<A NAME=1.3.91>To the selfsame tune and words. Who's here?</A><br> -<p><i>Enter ROSS and ANGUS</i></p> -</blockquote> - -<A NAME=speech35><b>ROSS</b></a> -<blockquote> -<A NAME=1.3.92>The king hath happily received, Macbeth,</A><br> -<A NAME=1.3.93>The news of thy success; and when he reads</A><br> -<A NAME=1.3.94>Thy personal venture in the rebels' fight,</A><br> -<A NAME=1.3.95>His wonders and his praises do contend</A><br> -<A NAME=1.3.96>Which should be thine or his: silenced with that,</A><br> -<A NAME=1.3.97>In viewing o'er the rest o' the selfsame day,</A><br> -<A NAME=1.3.98>He finds thee in the stout Norweyan ranks,</A><br> -<A NAME=1.3.99>Nothing afeard of what thyself didst make,</A><br> -<A NAME=1.3.100>Strange images of death. As thick as hail</A><br> -<A NAME=1.3.101>Came post with post; and every one did bear</A><br> -<A NAME=1.3.102>Thy praises in his kingdom's great defence,</A><br> -<A NAME=1.3.103>And pour'd them down before him.</A><br> -</blockquote> - -<A NAME=speech36><b>ANGUS</b></a> -<blockquote> -<A NAME=1.3.104>We are sent</A><br> -<A NAME=1.3.105>To give thee from our royal master thanks;</A><br> -<A NAME=1.3.106>Only to herald thee into his sight,</A><br> -<A NAME=1.3.107>Not pay thee.</A><br> -</blockquote> - -<A NAME=speech37><b>ROSS</b></a> -<blockquote> -<A NAME=1.3.108>And, for an earnest of a greater honour,</A><br> -<A NAME=1.3.109>He bade me, from him, call thee thane of Cawdor:</A><br> -<A NAME=1.3.110>In which addition, hail, most worthy thane!</A><br> -<A NAME=1.3.111>For it is thine.</A><br> -</blockquote> - -<A NAME=speech38><b>BANQUO</b></a> -<blockquote> -<A NAME=1.3.112> What, can the devil speak true?</A><br> -</blockquote> - -<A NAME=speech39><b>MACBETH</b></a> -<blockquote> -<A NAME=1.3.113>The thane of Cawdor lives: why do you dress me</A><br> -<A NAME=1.3.114>In borrow'd robes?</A><br> -</blockquote> - -<A NAME=speech40><b>ANGUS</b></a> -<blockquote> -<A NAME=1.3.115> Who was the thane lives yet;</A><br> -<A NAME=1.3.116>But under heavy judgment bears that life</A><br> -<A NAME=1.3.117>Which he deserves to lose. Whether he was combined</A><br> -<A NAME=1.3.118>With those of Norway, or did line the rebel</A><br> -<A NAME=1.3.119>With hidden help and vantage, or that with both</A><br> -<A NAME=1.3.120>He labour'd in his country's wreck, I know not;</A><br> -<A NAME=1.3.121>But treasons capital, confess'd and proved,</A><br> -<A NAME=1.3.122>Have overthrown him.</A><br> -</blockquote> - -<A NAME=speech41><b>MACBETH</b></a> -<blockquote> -<A NAME=1.3.123>[Aside] Glamis, and thane of Cawdor!</A><br> -<A NAME=1.3.124>The greatest is behind.</A><br> -<p><i>To ROSS and ANGUS</i></p> -<A NAME=1.3.125>Thanks for your pains.</A><br> -<p><i>To BANQUO</i></p> -<A NAME=1.3.126>Do you not hope your children shall be kings,</A><br> -<A NAME=1.3.127>When those that gave the thane of Cawdor to me</A><br> -<A NAME=1.3.128>Promised no less to them?</A><br> -</blockquote> - -<A NAME=speech42><b>BANQUO</b></a> -<blockquote> -<A NAME=1.3.129>That trusted home</A><br> -<A NAME=1.3.130>Might yet enkindle you unto the crown,</A><br> -<A NAME=1.3.131>Besides the thane of Cawdor. But 'tis strange:</A><br> -<A NAME=1.3.132>And oftentimes, to win us to our harm,</A><br> -<A NAME=1.3.133>The instruments of darkness tell us truths,</A><br> -<A NAME=1.3.134>Win us with honest trifles, to betray's</A><br> -<A NAME=1.3.135>In deepest consequence.</A><br> -<A NAME=1.3.136>Cousins, a word, I pray you.</A><br> -</blockquote> - -<A NAME=speech43><b>MACBETH</b></a> -<blockquote> -<A NAME=1.3.137>[Aside] Two truths are told,</A><br> -<A NAME=1.3.138>As happy prologues to the swelling act</A><br> -<A NAME=1.3.139>Of the imperial theme.--I thank you, gentlemen.</A><br> -<p><i>Aside</i></p> -<A NAME=1.3.140>Cannot be ill, cannot be good: if ill,</A><br> -<A NAME=1.3.141>Why hath it given me earnest of success,</A><br> -<A NAME=1.3.142>Commencing in a truth? I am thane of Cawdor:</A><br> -<A NAME=1.3.143>If good, why do I yield to that suggestion</A><br> -<A NAME=1.3.144>Whose horrid image doth unfix my hair</A><br> -<A NAME=1.3.145>And make my seated heart knock at my ribs,</A><br> -<A NAME=1.3.146>Against the use of nature? Present fears</A><br> -<A NAME=1.3.147>Are less than horrible imaginings:</A><br> -<A NAME=1.3.148>My thought, whose murder yet is but fantastical,</A><br> -<A NAME=1.3.149>Shakes so my single state of man that function</A><br> -<A NAME=1.3.150>Is smother'd in surmise, and nothing is</A><br> -<A NAME=1.3.151>But what is not.</A><br> -</blockquote> - -<A NAME=speech44><b>BANQUO</b></a> -<blockquote> -<A NAME=1.3.152> Look, how our partner's rapt.</A><br> -</blockquote> - -<A NAME=speech45><b>MACBETH</b></a> -<blockquote> -<A NAME=1.3.153>[Aside] If chance will have me king, why, chance may crown me,</A><br> -<A NAME=1.3.154>Without my stir.</A><br> -</blockquote> - -<A NAME=speech46><b>BANQUO</b></a> -<blockquote> -<A NAME=1.3.155> New horrors come upon him,</A><br> -<A NAME=1.3.156>Like our strange garments, cleave not to their mould</A><br> -<A NAME=1.3.157>But with the aid of use.</A><br> -</blockquote> - -<A NAME=speech47><b>MACBETH</b></a> -<blockquote> -<A NAME=1.3.158>[Aside] Come what come may,</A><br> -<A NAME=1.3.159>Time and the hour runs through the roughest day.</A><br> -</blockquote> - -<A NAME=speech48><b>BANQUO</b></a> -<blockquote> -<A NAME=1.3.160>Worthy Macbeth, we stay upon your leisure.</A><br> -</blockquote> - -<A NAME=speech49><b>MACBETH</b></a> -<blockquote> -<A NAME=1.3.161>Give me your favour: my dull brain was wrought</A><br> -<A NAME=1.3.162>With things forgotten. Kind gentlemen, your pains</A><br> -<A NAME=1.3.163>Are register'd where every day I turn</A><br> -<A NAME=1.3.164>The leaf to read them. Let us toward the king.</A><br> -<A NAME=1.3.165>Think upon what hath chanced, and, at more time,</A><br> -<A NAME=1.3.166>The interim having weigh'd it, let us speak</A><br> -<A NAME=1.3.167>Our free hearts each to other.</A><br> -</blockquote> - -<A NAME=speech50><b>BANQUO</b></a> -<blockquote> -<A NAME=1.3.168>Very gladly.</A><br> -</blockquote> - -<A NAME=speech51><b>MACBETH</b></a> -<blockquote> -<A NAME=1.3.169>Till then, enough. Come, friends.</A><br> -<p><i>Exeunt</i></p> -</blockquote> -<h3>SCENE IV. Forres. The palace.</h3> -<p><blockquote> -<i>Flourish. Enter DUNCAN, MALCOLM, DONALBAIN, LENNOX, and Attendants</i> -</blockquote> - -<A NAME=speech1><b>DUNCAN</b></a> -<blockquote> -<A NAME=1.4.1>Is execution done on Cawdor? Are not</A><br> -<A NAME=1.4.2>Those in commission yet return'd?</A><br> -</blockquote> - -<A NAME=speech2><b>MALCOLM</b></a> -<blockquote> -<A NAME=1.4.3>My liege,</A><br> -<A NAME=1.4.4>They are not yet come back. But I have spoke</A><br> -<A NAME=1.4.5>With one that saw him die: who did report</A><br> -<A NAME=1.4.6>That very frankly he confess'd his treasons,</A><br> -<A NAME=1.4.7>Implored your highness' pardon and set forth</A><br> -<A NAME=1.4.8>A deep repentance: nothing in his life</A><br> -<A NAME=1.4.9>Became him like the leaving it; he died</A><br> -<A NAME=1.4.10>As one that had been studied in his death</A><br> -<A NAME=1.4.11>To throw away the dearest thing he owed,</A><br> -<A NAME=1.4.12>As 'twere a careless trifle.</A><br> -</blockquote> - -<A NAME=speech3><b>DUNCAN</b></a> -<blockquote> -<A NAME=1.4.13>There's no art</A><br> -<A NAME=1.4.14>To find the mind's construction in the face:</A><br> -<A NAME=1.4.15>He was a gentleman on whom I built</A><br> -<A NAME=1.4.16>An absolute trust.</A><br> -<p><i>Enter MACBETH, BANQUO, ROSS, and ANGUS</i></p> -<A NAME=1.4.17>O worthiest cousin!</A><br> -<A NAME=1.4.18>The sin of my ingratitude even now</A><br> -<A NAME=1.4.19>Was heavy on me: thou art so far before</A><br> -<A NAME=1.4.20>That swiftest wing of recompense is slow</A><br> -<A NAME=1.4.21>To overtake thee. Would thou hadst less deserved,</A><br> -<A NAME=1.4.22>That the proportion both of thanks and payment</A><br> -<A NAME=1.4.23>Might have been mine! only I have left to say,</A><br> -<A NAME=1.4.24>More is thy due than more than all can pay.</A><br> -</blockquote> - -<A NAME=speech4><b>MACBETH</b></a> -<blockquote> -<A NAME=1.4.25>The service and the loyalty I owe,</A><br> -<A NAME=1.4.26>In doing it, pays itself. Your highness' part</A><br> -<A NAME=1.4.27>Is to receive our duties; and our duties</A><br> -<A NAME=1.4.28>Are to your throne and state children and servants,</A><br> -<A NAME=1.4.29>Which do but what they should, by doing every thing</A><br> -<A NAME=1.4.30>Safe toward your love and honour.</A><br> -</blockquote> - -<A NAME=speech5><b>DUNCAN</b></a> -<blockquote> -<A NAME=1.4.31>Welcome hither:</A><br> -<A NAME=1.4.32>I have begun to plant thee, and will labour</A><br> -<A NAME=1.4.33>To make thee full of growing. Noble Banquo,</A><br> -<A NAME=1.4.34>That hast no less deserved, nor must be known</A><br> -<A NAME=1.4.35>No less to have done so, let me enfold thee</A><br> -<A NAME=1.4.36>And hold thee to my heart.</A><br> -</blockquote> - -<A NAME=speech6><b>BANQUO</b></a> -<blockquote> -<A NAME=1.4.37>There if I grow,</A><br> -<A NAME=1.4.38>The harvest is your own.</A><br> -</blockquote> - -<A NAME=speech7><b>DUNCAN</b></a> -<blockquote> -<A NAME=1.4.39>My plenteous joys,</A><br> -<A NAME=1.4.40>Wanton in fulness, seek to hide themselves</A><br> -<A NAME=1.4.41>In drops of sorrow. Sons, kinsmen, thanes,</A><br> -<A NAME=1.4.42>And you whose places are the nearest, know</A><br> -<A NAME=1.4.43>We will establish our estate upon</A><br> -<A NAME=1.4.44>Our eldest, Malcolm, whom we name hereafter</A><br> -<A NAME=1.4.45>The Prince of Cumberland; which honour must</A><br> -<A NAME=1.4.46>Not unaccompanied invest him only,</A><br> -<A NAME=1.4.47>But signs of nobleness, like stars, shall shine</A><br> -<A NAME=1.4.48>On all deservers. From hence to Inverness,</A><br> -<A NAME=1.4.49>And bind us further to you.</A><br> -</blockquote> - -<A NAME=speech8><b>MACBETH</b></a> -<blockquote> -<A NAME=1.4.50>The rest is labour, which is not used for you:</A><br> -<A NAME=1.4.51>I'll be myself the harbinger and make joyful</A><br> -<A NAME=1.4.52>The hearing of my wife with your approach;</A><br> -<A NAME=1.4.53>So humbly take my leave.</A><br> -</blockquote> - -<A NAME=speech9><b>DUNCAN</b></a> -<blockquote> -<A NAME=1.4.54>My worthy Cawdor!</A><br> -</blockquote> - -<A NAME=speech10><b>MACBETH</b></a> -<blockquote> -<A NAME=1.4.55>[Aside] The Prince of Cumberland! that is a step</A><br> -<A NAME=1.4.56>On which I must fall down, or else o'erleap,</A><br> -<A NAME=1.4.57>For in my way it lies. Stars, hide your fires;</A><br> -<A NAME=1.4.58>Let not light see my black and deep desires:</A><br> -<A NAME=1.4.59>The eye wink at the hand; yet let that be,</A><br> -<A NAME=1.4.60>Which the eye fears, when it is done, to see.</A><br> -<p><i>Exit</i></p> -</blockquote> - -<A NAME=speech11><b>DUNCAN</b></a> -<blockquote> -<A NAME=1.4.61>True, worthy Banquo; he is full so valiant,</A><br> -<A NAME=1.4.62>And in his commendations I am fed;</A><br> -<A NAME=1.4.63>It is a banquet to me. Let's after him,</A><br> -<A NAME=1.4.64>Whose care is gone before to bid us welcome:</A><br> -<A NAME=1.4.65>It is a peerless kinsman.</A><br> -<p><i>Flourish. Exeunt</i></p> -</blockquote> -<h3>SCENE V. Inverness. Macbeth's castle.</h3> -<p><blockquote> -<i>Enter LADY MACBETH, reading a letter</i> -</blockquote> - -<A NAME=speech1><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=1.5.1>'They met me in the day of success: and I have</A><br> -<A NAME=1.5.2>learned by the perfectest report, they have more in</A><br> -<A NAME=1.5.3>them than mortal knowledge. When I burned in desire</A><br> -<A NAME=1.5.4>to question them further, they made themselves air,</A><br> -<A NAME=1.5.5>into which they vanished. Whiles I stood rapt in</A><br> -<A NAME=1.5.6>the wonder of it, came missives from the king, who</A><br> -<A NAME=1.5.7>all-hailed me 'Thane of Cawdor;' by which title,</A><br> -<A NAME=1.5.8>before, these weird sisters saluted me, and referred</A><br> -<A NAME=1.5.9>me to the coming on of time, with 'Hail, king that</A><br> -<A NAME=1.5.10>shalt be!' This have I thought good to deliver</A><br> -<A NAME=1.5.11>thee, my dearest partner of greatness, that thou</A><br> -<A NAME=1.5.12>mightst not lose the dues of rejoicing, by being</A><br> -<A NAME=1.5.13>ignorant of what greatness is promised thee. Lay it</A><br> -<A NAME=1.5.14>to thy heart, and farewell.'</A><br> -<A NAME=1.5.15>Glamis thou art, and Cawdor; and shalt be</A><br> -<A NAME=1.5.16>What thou art promised: yet do I fear thy nature;</A><br> -<A NAME=1.5.17>It is too full o' the milk of human kindness</A><br> -<A NAME=1.5.18>To catch the nearest way: thou wouldst be great;</A><br> -<A NAME=1.5.19>Art not without ambition, but without</A><br> -<A NAME=1.5.20>The illness should attend it: what thou wouldst highly,</A><br> -<A NAME=1.5.21>That wouldst thou holily; wouldst not play false,</A><br> -<A NAME=1.5.22>And yet wouldst wrongly win: thou'ldst have, great Glamis,</A><br> -<A NAME=1.5.23>That which cries 'Thus thou must do, if thou have it;</A><br> -<A NAME=1.5.24>And that which rather thou dost fear to do</A><br> -<A NAME=1.5.25>Than wishest should be undone.' Hie thee hither,</A><br> -<A NAME=1.5.26>That I may pour my spirits in thine ear;</A><br> -<A NAME=1.5.27>And chastise with the valour of my tongue</A><br> -<A NAME=1.5.28>All that impedes thee from the golden round,</A><br> -<A NAME=1.5.29>Which fate and metaphysical aid doth seem</A><br> -<A NAME=1.5.30>To have thee crown'd withal.</A><br> -<p><i>Enter a Messenger</i></p> -<A NAME=1.5.31>What is your tidings?</A><br> -</blockquote> - -<A NAME=speech2><b>Messenger</b></a> -<blockquote> -<A NAME=1.5.32>The king comes here to-night.</A><br> -</blockquote> - -<A NAME=speech3><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=1.5.33>Thou'rt mad to say it:</A><br> -<A NAME=1.5.34>Is not thy master with him? who, were't so,</A><br> -<A NAME=1.5.35>Would have inform'd for preparation.</A><br> -</blockquote> - -<A NAME=speech4><b>Messenger</b></a> -<blockquote> -<A NAME=1.5.36>So please you, it is true: our thane is coming:</A><br> -<A NAME=1.5.37>One of my fellows had the speed of him,</A><br> -<A NAME=1.5.38>Who, almost dead for breath, had scarcely more</A><br> -<A NAME=1.5.39>Than would make up his message.</A><br> -</blockquote> - -<A NAME=speech5><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=1.5.40>Give him tending;</A><br> -<A NAME=1.5.41>He brings great news.</A><br> -<p><i>Exit Messenger</i></p> -<A NAME=1.5.42>The raven himself is hoarse</A><br> -<A NAME=1.5.43>That croaks the fatal entrance of Duncan</A><br> -<A NAME=1.5.44>Under my battlements. Come, you spirits</A><br> -<A NAME=1.5.45>That tend on mortal thoughts, unsex me here,</A><br> -<A NAME=1.5.46>And fill me from the crown to the toe top-full</A><br> -<A NAME=1.5.47>Of direst cruelty! make thick my blood;</A><br> -<A NAME=1.5.48>Stop up the access and passage to remorse,</A><br> -<A NAME=1.5.49>That no compunctious visitings of nature</A><br> -<A NAME=1.5.50>Shake my fell purpose, nor keep peace between</A><br> -<A NAME=1.5.51>The effect and it! Come to my woman's breasts,</A><br> -<A NAME=1.5.52>And take my milk for gall, you murdering ministers,</A><br> -<A NAME=1.5.53>Wherever in your sightless substances</A><br> -<A NAME=1.5.54>You wait on nature's mischief! Come, thick night,</A><br> -<A NAME=1.5.55>And pall thee in the dunnest smoke of hell,</A><br> -<A NAME=1.5.56>That my keen knife see not the wound it makes,</A><br> -<A NAME=1.5.57>Nor heaven peep through the blanket of the dark,</A><br> -<A NAME=1.5.58>To cry 'Hold, hold!'</A><br> -<p><i>Enter MACBETH</i></p> -<A NAME=1.5.59>Great Glamis! worthy Cawdor!</A><br> -<A NAME=1.5.60>Greater than both, by the all-hail hereafter!</A><br> -<A NAME=1.5.61>Thy letters have transported me beyond</A><br> -<A NAME=1.5.62>This ignorant present, and I feel now</A><br> -<A NAME=1.5.63>The future in the instant.</A><br> -</blockquote> - -<A NAME=speech6><b>MACBETH</b></a> -<blockquote> -<A NAME=1.5.64>My dearest love,</A><br> -<A NAME=1.5.65>Duncan comes here to-night.</A><br> -</blockquote> - -<A NAME=speech7><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=1.5.66>And when goes hence?</A><br> -</blockquote> - -<A NAME=speech8><b>MACBETH</b></a> -<blockquote> -<A NAME=1.5.67>To-morrow, as he purposes.</A><br> -</blockquote> - -<A NAME=speech9><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=1.5.68>O, never</A><br> -<A NAME=1.5.69>Shall sun that morrow see!</A><br> -<A NAME=1.5.70>Your face, my thane, is as a book where men</A><br> -<A NAME=1.5.71>May read strange matters. To beguile the time,</A><br> -<A NAME=1.5.72>Look like the time; bear welcome in your eye,</A><br> -<A NAME=1.5.73>Your hand, your tongue: look like the innocent flower,</A><br> -<A NAME=1.5.74>But be the serpent under't. He that's coming</A><br> -<A NAME=1.5.75>Must be provided for: and you shall put</A><br> -<A NAME=1.5.76>This night's great business into my dispatch;</A><br> -<A NAME=1.5.77>Which shall to all our nights and days to come</A><br> -<A NAME=1.5.78>Give solely sovereign sway and masterdom.</A><br> -</blockquote> - -<A NAME=speech10><b>MACBETH</b></a> -<blockquote> -<A NAME=1.5.79>We will speak further.</A><br> -</blockquote> - -<A NAME=speech11><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=1.5.80>Only look up clear;</A><br> -<A NAME=1.5.81>To alter favour ever is to fear:</A><br> -<A NAME=1.5.82>Leave all the rest to me.</A><br> -<p><i>Exeunt</i></p> -</blockquote> -<h3>SCENE VI. Before Macbeth's castle.</h3> -<p><blockquote> -<i>Hautboys and torches. Enter DUNCAN, MALCOLM, DONALBAIN, BANQUO, LENNOX, MACDUFF, ROSS, ANGUS, and Attendants</i> -</blockquote> - -<A NAME=speech1><b>DUNCAN</b></a> -<blockquote> -<A NAME=1.6.1>This castle hath a pleasant seat; the air</A><br> -<A NAME=1.6.2>Nimbly and sweetly recommends itself</A><br> -<A NAME=1.6.3>Unto our gentle senses.</A><br> -</blockquote> - -<A NAME=speech2><b>BANQUO</b></a> -<blockquote> -<A NAME=1.6.4>This guest of summer,</A><br> -<A NAME=1.6.5>The temple-haunting martlet, does approve,</A><br> -<A NAME=1.6.6>By his loved mansionry, that the heaven's breath</A><br> -<A NAME=1.6.7>Smells wooingly here: no jutty, frieze,</A><br> -<A NAME=1.6.8>Buttress, nor coign of vantage, but this bird</A><br> -<A NAME=1.6.9>Hath made his pendent bed and procreant cradle:</A><br> -<A NAME=1.6.10>Where they most breed and haunt, I have observed,</A><br> -<A NAME=1.6.11>The air is delicate.</A><br> -<p><i>Enter LADY MACBETH</i></p> -</blockquote> - -<A NAME=speech3><b>DUNCAN</b></a> -<blockquote> -<A NAME=1.6.12>See, see, our honour'd hostess!</A><br> -<A NAME=1.6.13>The love that follows us sometime is our trouble,</A><br> -<A NAME=1.6.14>Which still we thank as love. Herein I teach you</A><br> -<A NAME=1.6.15>How you shall bid God 'ild us for your pains,</A><br> -<A NAME=1.6.16>And thank us for your trouble.</A><br> -</blockquote> - -<A NAME=speech4><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=1.6.17>All our service</A><br> -<A NAME=1.6.18>In every point twice done and then done double</A><br> -<A NAME=1.6.19>Were poor and single business to contend</A><br> -<A NAME=1.6.20>Against those honours deep and broad wherewith</A><br> -<A NAME=1.6.21>Your majesty loads our house: for those of old,</A><br> -<A NAME=1.6.22>And the late dignities heap'd up to them,</A><br> -<A NAME=1.6.23>We rest your hermits.</A><br> -</blockquote> - -<A NAME=speech5><b>DUNCAN</b></a> -<blockquote> -<A NAME=1.6.24>Where's the thane of Cawdor?</A><br> -<A NAME=1.6.25>We coursed him at the heels, and had a purpose</A><br> -<A NAME=1.6.26>To be his purveyor: but he rides well;</A><br> -<A NAME=1.6.27>And his great love, sharp as his spur, hath holp him</A><br> -<A NAME=1.6.28>To his home before us. Fair and noble hostess,</A><br> -<A NAME=1.6.29>We are your guest to-night.</A><br> -</blockquote> - -<A NAME=speech6><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=1.6.30>Your servants ever</A><br> -<A NAME=1.6.31>Have theirs, themselves and what is theirs, in compt,</A><br> -<A NAME=1.6.32>To make their audit at your highness' pleasure,</A><br> -<A NAME=1.6.33>Still to return your own.</A><br> -</blockquote> - -<A NAME=speech7><b>DUNCAN</b></a> -<blockquote> -<A NAME=1.6.34>Give me your hand;</A><br> -<A NAME=1.6.35>Conduct me to mine host: we love him highly,</A><br> -<A NAME=1.6.36>And shall continue our graces towards him.</A><br> -<A NAME=1.6.37>By your leave, hostess.</A><br> -<p><i>Exeunt</i></p> -</blockquote> -<h3>SCENE VII. Macbeth's castle.</h3> -<p><blockquote> -<i>Hautboys and torches. Enter a Sewer, and divers Servants with dishes and service, and pass over the stage. Then enter MACBETH</i> -</blockquote> - -<A NAME=speech1><b>MACBETH</b></a> -<blockquote> -<A NAME=1.7.1>If it were done when 'tis done, then 'twere well</A><br> -<A NAME=1.7.2>It were done quickly: if the assassination</A><br> -<A NAME=1.7.3>Could trammel up the consequence, and catch</A><br> -<A NAME=1.7.4>With his surcease success; that but this blow</A><br> -<A NAME=1.7.5>Might be the be-all and the end-all here,</A><br> -<A NAME=1.7.6>But here, upon this bank and shoal of time,</A><br> -<A NAME=1.7.7>We'ld jump the life to come. But in these cases</A><br> -<A NAME=1.7.8>We still have judgment here; that we but teach</A><br> -<A NAME=1.7.9>Bloody instructions, which, being taught, return</A><br> -<A NAME=1.7.10>To plague the inventor: this even-handed justice</A><br> -<A NAME=1.7.11>Commends the ingredients of our poison'd chalice</A><br> -<A NAME=1.7.12>To our own lips. He's here in double trust;</A><br> -<A NAME=1.7.13>First, as I am his kinsman and his subject,</A><br> -<A NAME=1.7.14>Strong both against the deed; then, as his host,</A><br> -<A NAME=1.7.15>Who should against his murderer shut the door,</A><br> -<A NAME=1.7.16>Not bear the knife myself. Besides, this Duncan</A><br> -<A NAME=1.7.17>Hath borne his faculties so meek, hath been</A><br> -<A NAME=1.7.18>So clear in his great office, that his virtues</A><br> -<A NAME=1.7.19>Will plead like angels, trumpet-tongued, against</A><br> -<A NAME=1.7.20>The deep damnation of his taking-off;</A><br> -<A NAME=1.7.21>And pity, like a naked new-born babe,</A><br> -<A NAME=1.7.22>Striding the blast, or heaven's cherubim, horsed</A><br> -<A NAME=1.7.23>Upon the sightless couriers of the air,</A><br> -<A NAME=1.7.24>Shall blow the horrid deed in every eye,</A><br> -<A NAME=1.7.25>That tears shall drown the wind. I have no spur</A><br> -<A NAME=1.7.26>To prick the sides of my intent, but only</A><br> -<A NAME=1.7.27>Vaulting ambition, which o'erleaps itself</A><br> -<A NAME=1.7.28>And falls on the other.</A><br> -<p><i>Enter LADY MACBETH</i></p> -<A NAME=1.7.29>How now! what news?</A><br> -</blockquote> - -<A NAME=speech2><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=1.7.30>He has almost supp'd: why have you left the chamber?</A><br> -</blockquote> - -<A NAME=speech3><b>MACBETH</b></a> -<blockquote> -<A NAME=1.7.31>Hath he ask'd for me?</A><br> -</blockquote> - -<A NAME=speech4><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=1.7.32>Know you not he has?</A><br> -</blockquote> - -<A NAME=speech5><b>MACBETH</b></a> -<blockquote> -<A NAME=1.7.33>We will proceed no further in this business:</A><br> -<A NAME=1.7.34>He hath honour'd me of late; and I have bought</A><br> -<A NAME=1.7.35>Golden opinions from all sorts of people,</A><br> -<A NAME=1.7.36>Which would be worn now in their newest gloss,</A><br> -<A NAME=1.7.37>Not cast aside so soon.</A><br> -</blockquote> - -<A NAME=speech6><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=1.7.38>Was the hope drunk</A><br> -<A NAME=1.7.39>Wherein you dress'd yourself? hath it slept since?</A><br> -<A NAME=1.7.40>And wakes it now, to look so green and pale</A><br> -<A NAME=1.7.41>At what it did so freely? From this time</A><br> -<A NAME=1.7.42>Such I account thy love. Art thou afeard</A><br> -<A NAME=1.7.43>To be the same in thine own act and valour</A><br> -<A NAME=1.7.44>As thou art in desire? Wouldst thou have that</A><br> -<A NAME=1.7.45>Which thou esteem'st the ornament of life,</A><br> -<A NAME=1.7.46>And live a coward in thine own esteem,</A><br> -<A NAME=1.7.47>Letting 'I dare not' wait upon 'I would,'</A><br> -<A NAME=1.7.48>Like the poor cat i' the adage?</A><br> -</blockquote> - -<A NAME=speech7><b>MACBETH</b></a> -<blockquote> -<A NAME=1.7.49>Prithee, peace:</A><br> -<A NAME=1.7.50>I dare do all that may become a man;</A><br> -<A NAME=1.7.51>Who dares do more is none.</A><br> -</blockquote> - -<A NAME=speech8><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=1.7.52>What beast was't, then,</A><br> -<A NAME=1.7.53>That made you break this enterprise to me?</A><br> -<A NAME=1.7.54>When you durst do it, then you were a man;</A><br> -<A NAME=1.7.55>And, to be more than what you were, you would</A><br> -<A NAME=1.7.56>Be so much more the man. Nor time nor place</A><br> -<A NAME=1.7.57>Did then adhere, and yet you would make both:</A><br> -<A NAME=1.7.58>They have made themselves, and that their fitness now</A><br> -<A NAME=1.7.59>Does unmake you. I have given suck, and know</A><br> -<A NAME=1.7.60>How tender 'tis to love the babe that milks me:</A><br> -<A NAME=1.7.61>I would, while it was smiling in my face,</A><br> -<A NAME=1.7.62>Have pluck'd my nipple from his boneless gums,</A><br> -<A NAME=1.7.63>And dash'd the brains out, had I so sworn as you</A><br> -<A NAME=1.7.64>Have done to this.</A><br> -</blockquote> - -<A NAME=speech9><b>MACBETH</b></a> -<blockquote> -<A NAME=1.7.65> If we should fail?</A><br> -</blockquote> - -<A NAME=speech10><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=1.7.66>We fail!</A><br> -<A NAME=1.7.67>But screw your courage to the sticking-place,</A><br> -<A NAME=1.7.68>And we'll not fail. When Duncan is asleep--</A><br> -<A NAME=1.7.69>Whereto the rather shall his day's hard journey</A><br> -<A NAME=1.7.70>Soundly invite him--his two chamberlains</A><br> -<A NAME=1.7.71>Will I with wine and wassail so convince</A><br> -<A NAME=1.7.72>That memory, the warder of the brain,</A><br> -<A NAME=1.7.73>Shall be a fume, and the receipt of reason</A><br> -<A NAME=1.7.74>A limbeck only: when in swinish sleep</A><br> -<A NAME=1.7.75>Their drenched natures lie as in a death,</A><br> -<A NAME=1.7.76>What cannot you and I perform upon</A><br> -<A NAME=1.7.77>The unguarded Duncan? what not put upon</A><br> -<A NAME=1.7.78>His spongy officers, who shall bear the guilt</A><br> -<A NAME=1.7.79>Of our great quell?</A><br> -</blockquote> - -<A NAME=speech11><b>MACBETH</b></a> -<blockquote> -<A NAME=1.7.80>Bring forth men-children only;</A><br> -<A NAME=1.7.81>For thy undaunted mettle should compose</A><br> -<A NAME=1.7.82>Nothing but males. Will it not be received,</A><br> -<A NAME=1.7.83>When we have mark'd with blood those sleepy two</A><br> -<A NAME=1.7.84>Of his own chamber and used their very daggers,</A><br> -<A NAME=1.7.85>That they have done't?</A><br> -</blockquote> - -<A NAME=speech12><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=1.7.86>Who dares receive it other,</A><br> -<A NAME=1.7.87>As we shall make our griefs and clamour roar</A><br> -<A NAME=1.7.88>Upon his death?</A><br> -</blockquote> - -<A NAME=speech13><b>MACBETH</b></a> -<blockquote> -<A NAME=1.7.89> I am settled, and bend up</A><br> -<A NAME=1.7.90>Each corporal agent to this terrible feat.</A><br> -<A NAME=1.7.91>Away, and mock the time with fairest show:</A><br> -<A NAME=1.7.92>False face must hide what the false heart doth know.</A><br> -<p><i>Exeunt</i></p> -</blockquote><p> -<H3>ACT II</h3> -<h3>SCENE I. Court of Macbeth's castle.</h3> -<p><blockquote> -<i>Enter BANQUO, and FLEANCE bearing a torch before him</i> -</blockquote> - -<A NAME=speech1><b>BANQUO</b></a> -<blockquote> -<A NAME=2.1.1>How goes the night, boy?</A><br> -</blockquote> - -<A NAME=speech2><b>FLEANCE</b></a> -<blockquote> -<A NAME=2.1.2>The moon is down; I have not heard the clock.</A><br> -</blockquote> - -<A NAME=speech3><b>BANQUO</b></a> -<blockquote> -<A NAME=2.1.3>And she goes down at twelve.</A><br> -</blockquote> - -<A NAME=speech4><b>FLEANCE</b></a> -<blockquote> -<A NAME=2.1.4>I take't, 'tis later, sir.</A><br> -</blockquote> - -<A NAME=speech5><b>BANQUO</b></a> -<blockquote> -<A NAME=2.1.5>Hold, take my sword. There's husbandry in heaven;</A><br> -<A NAME=2.1.6>Their candles are all out. Take thee that too.</A><br> -<A NAME=2.1.7>A heavy summons lies like lead upon me,</A><br> -<A NAME=2.1.8>And yet I would not sleep: merciful powers,</A><br> -<A NAME=2.1.9>Restrain in me the cursed thoughts that nature</A><br> -<A NAME=2.1.10>Gives way to in repose!</A><br> -<p><i>Enter MACBETH, and a Servant with a torch</i></p> -<A NAME=2.1.11>Give me my sword.</A><br> -<A NAME=2.1.12>Who's there?</A><br> -</blockquote> - -<A NAME=speech6><b>MACBETH</b></a> -<blockquote> -<A NAME=2.1.13>A friend.</A><br> -</blockquote> - -<A NAME=speech7><b>BANQUO</b></a> -<blockquote> -<A NAME=2.1.14>What, sir, not yet at rest? The king's a-bed:</A><br> -<A NAME=2.1.15>He hath been in unusual pleasure, and</A><br> -<A NAME=2.1.16>Sent forth great largess to your offices.</A><br> -<A NAME=2.1.17>This diamond he greets your wife withal,</A><br> -<A NAME=2.1.18>By the name of most kind hostess; and shut up</A><br> -<A NAME=2.1.19>In measureless content.</A><br> -</blockquote> - -<A NAME=speech8><b>MACBETH</b></a> -<blockquote> -<A NAME=2.1.20>Being unprepared,</A><br> -<A NAME=2.1.21>Our will became the servant to defect;</A><br> -<A NAME=2.1.22>Which else should free have wrought.</A><br> -</blockquote> - -<A NAME=speech9><b>BANQUO</b></a> -<blockquote> -<A NAME=2.1.23>All's well.</A><br> -<A NAME=2.1.24>I dreamt last night of the three weird sisters:</A><br> -<A NAME=2.1.25>To you they have show'd some truth.</A><br> -</blockquote> - -<A NAME=speech10><b>MACBETH</b></a> -<blockquote> -<A NAME=2.1.26>I think not of them:</A><br> -<A NAME=2.1.27>Yet, when we can entreat an hour to serve,</A><br> -<A NAME=2.1.28>We would spend it in some words upon that business,</A><br> -<A NAME=2.1.29>If you would grant the time.</A><br> -</blockquote> - -<A NAME=speech11><b>BANQUO</b></a> -<blockquote> -<A NAME=2.1.30>At your kind'st leisure.</A><br> -</blockquote> - -<A NAME=speech12><b>MACBETH</b></a> -<blockquote> -<A NAME=2.1.31>If you shall cleave to my consent, when 'tis,</A><br> -<A NAME=2.1.32>It shall make honour for you.</A><br> -</blockquote> - -<A NAME=speech13><b>BANQUO</b></a> -<blockquote> -<A NAME=2.1.33>So I lose none</A><br> -<A NAME=2.1.34>In seeking to augment it, but still keep</A><br> -<A NAME=2.1.35>My bosom franchised and allegiance clear,</A><br> -<A NAME=2.1.36>I shall be counsell'd.</A><br> -</blockquote> - -<A NAME=speech14><b>MACBETH</b></a> -<blockquote> -<A NAME=2.1.37>Good repose the while!</A><br> -</blockquote> - -<A NAME=speech15><b>BANQUO</b></a> -<blockquote> -<A NAME=2.1.38>Thanks, sir: the like to you!</A><br> -<p><i>Exeunt BANQUO and FLEANCE</i></p> -</blockquote> - -<A NAME=speech16><b>MACBETH</b></a> -<blockquote> -<A NAME=2.1.39>Go bid thy mistress, when my drink is ready,</A><br> -<A NAME=2.1.40>She strike upon the bell. Get thee to bed.</A><br> -<p><i>Exit Servant</i></p> -<A NAME=2.1.41>Is this a dagger which I see before me,</A><br> -<A NAME=2.1.42>The handle toward my hand? Come, let me clutch thee.</A><br> -<A NAME=2.1.43>I have thee not, and yet I see thee still.</A><br> -<A NAME=2.1.44>Art thou not, fatal vision, sensible</A><br> -<A NAME=2.1.45>To feeling as to sight? or art thou but</A><br> -<A NAME=2.1.46>A dagger of the mind, a false creation,</A><br> -<A NAME=2.1.47>Proceeding from the heat-oppressed brain?</A><br> -<A NAME=2.1.48>I see thee yet, in form as palpable</A><br> -<A NAME=2.1.49>As this which now I draw.</A><br> -<A NAME=2.1.50>Thou marshall'st me the way that I was going;</A><br> -<A NAME=2.1.51>And such an instrument I was to use.</A><br> -<A NAME=2.1.52>Mine eyes are made the fools o' the other senses,</A><br> -<A NAME=2.1.53>Or else worth all the rest; I see thee still,</A><br> -<A NAME=2.1.54>And on thy blade and dudgeon gouts of blood,</A><br> -<A NAME=2.1.55>Which was not so before. There's no such thing:</A><br> -<A NAME=2.1.56>It is the bloody business which informs</A><br> -<A NAME=2.1.57>Thus to mine eyes. Now o'er the one halfworld</A><br> -<A NAME=2.1.58>Nature seems dead, and wicked dreams abuse</A><br> -<A NAME=2.1.59>The curtain'd sleep; witchcraft celebrates</A><br> -<A NAME=2.1.60>Pale Hecate's offerings, and wither'd murder,</A><br> -<A NAME=2.1.61>Alarum'd by his sentinel, the wolf,</A><br> -<A NAME=2.1.62>Whose howl's his watch, thus with his stealthy pace.</A><br> -<A NAME=2.1.63>With Tarquin's ravishing strides, towards his design</A><br> -<A NAME=2.1.64>Moves like a ghost. Thou sure and firm-set earth,</A><br> -<A NAME=2.1.65>Hear not my steps, which way they walk, for fear</A><br> -<A NAME=2.1.66>Thy very stones prate of my whereabout,</A><br> -<A NAME=2.1.67>And take the present horror from the time,</A><br> -<A NAME=2.1.68>Which now suits with it. Whiles I threat, he lives:</A><br> -<A NAME=2.1.69>Words to the heat of deeds too cold breath gives.</A><br> -<p><i>A bell rings</i></p> -<A NAME=2.1.70>I go, and it is done; the bell invites me.</A><br> -<A NAME=2.1.71>Hear it not, Duncan; for it is a knell</A><br> -<A NAME=2.1.72>That summons thee to heaven or to hell.</A><br> -<p><i>Exit</i></p> -</blockquote> -<h3>SCENE II. The same.</h3> -<p><blockquote> -<i>Enter LADY MACBETH</i> -</blockquote> - -<A NAME=speech1><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=2.2.1>That which hath made them drunk hath made me bold;</A><br> -<A NAME=2.2.2>What hath quench'd them hath given me fire.</A><br> -<A NAME=2.2.3>Hark! Peace!</A><br> -<A NAME=2.2.4>It was the owl that shriek'd, the fatal bellman,</A><br> -<A NAME=2.2.5>Which gives the stern'st good-night. He is about it:</A><br> -<A NAME=2.2.6>The doors are open; and the surfeited grooms</A><br> -<A NAME=2.2.7>Do mock their charge with snores: I have drugg'd</A><br> -<A NAME=2.2.8>their possets,</A><br> -<A NAME=2.2.9>That death and nature do contend about them,</A><br> -<A NAME=2.2.10>Whether they live or die.</A><br> -</blockquote> - -<A NAME=speech2><b>MACBETH</b></a> -<blockquote> -<A NAME=2.2.11>[Within] Who's there? what, ho!</A><br> -</blockquote> - -<A NAME=speech3><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=2.2.12>Alack, I am afraid they have awaked,</A><br> -<A NAME=2.2.13>And 'tis not done. The attempt and not the deed</A><br> -<A NAME=2.2.14>Confounds us. Hark! I laid their daggers ready;</A><br> -<A NAME=2.2.15>He could not miss 'em. Had he not resembled</A><br> -<A NAME=2.2.16>My father as he slept, I had done't.</A><br> -<p><i>Enter MACBETH</i></p> -<A NAME=2.2.17>My husband!</A><br> -</blockquote> - -<A NAME=speech4><b>MACBETH</b></a> -<blockquote> -<A NAME=2.2.18>I have done the deed. Didst thou not hear a noise?</A><br> -</blockquote> - -<A NAME=speech5><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=2.2.19>I heard the owl scream and the crickets cry.</A><br> -<A NAME=2.2.20>Did not you speak?</A><br> -</blockquote> - -<A NAME=speech6><b>MACBETH</b></a> -<blockquote> -<A NAME=2.2.21> When?</A><br> -</blockquote> - -<A NAME=speech7><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=2.2.22>Now.</A><br> -</blockquote> - -<A NAME=speech8><b>MACBETH</b></a> -<blockquote> -<A NAME=2.2.23>As I descended?</A><br> -</blockquote> - -<A NAME=speech9><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=2.2.24>Ay.</A><br> -</blockquote> - -<A NAME=speech10><b>MACBETH</b></a> -<blockquote> -<A NAME=2.2.25>Hark!</A><br> -<A NAME=2.2.26>Who lies i' the second chamber?</A><br> -</blockquote> - -<A NAME=speech11><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=2.2.27>Donalbain.</A><br> -</blockquote> - -<A NAME=speech12><b>MACBETH</b></a> -<blockquote> -<A NAME=2.2.28>This is a sorry sight.</A><br> -<p><i>Looking on his hands</i></p> -</blockquote> - -<A NAME=speech13><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=2.2.29>A foolish thought, to say a sorry sight.</A><br> -</blockquote> - -<A NAME=speech14><b>MACBETH</b></a> -<blockquote> -<A NAME=2.2.30>There's one did laugh in's sleep, and one cried</A><br> -<A NAME=2.2.31>'Murder!'</A><br> -<A NAME=2.2.32>That they did wake each other: I stood and heard them:</A><br> -<A NAME=2.2.33>But they did say their prayers, and address'd them</A><br> -<A NAME=2.2.34>Again to sleep.</A><br> -</blockquote> - -<A NAME=speech15><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=2.2.35> There are two lodged together.</A><br> -</blockquote> - -<A NAME=speech16><b>MACBETH</b></a> -<blockquote> -<A NAME=2.2.36>One cried 'God bless us!' and 'Amen' the other;</A><br> -<A NAME=2.2.37>As they had seen me with these hangman's hands.</A><br> -<A NAME=2.2.38>Listening their fear, I could not say 'Amen,'</A><br> -<A NAME=2.2.39>When they did say 'God bless us!'</A><br> -</blockquote> - -<A NAME=speech17><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=2.2.40>Consider it not so deeply.</A><br> -</blockquote> - -<A NAME=speech18><b>MACBETH</b></a> -<blockquote> -<A NAME=2.2.41>But wherefore could not I pronounce 'Amen'?</A><br> -<A NAME=2.2.42>I had most need of blessing, and 'Amen'</A><br> -<A NAME=2.2.43>Stuck in my throat.</A><br> -</blockquote> - -<A NAME=speech19><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=2.2.44>These deeds must not be thought</A><br> -<A NAME=2.2.45>After these ways; so, it will make us mad.</A><br> -</blockquote> - -<A NAME=speech20><b>MACBETH</b></a> -<blockquote> -<A NAME=2.2.46>Methought I heard a voice cry 'Sleep no more!</A><br> -<A NAME=2.2.47>Macbeth does murder sleep', the innocent sleep,</A><br> -<A NAME=2.2.48>Sleep that knits up the ravell'd sleeve of care,</A><br> -<A NAME=2.2.49>The death of each day's life, sore labour's bath,</A><br> -<A NAME=2.2.50>Balm of hurt minds, great nature's second course,</A><br> -<A NAME=2.2.51>Chief nourisher in life's feast,--</A><br> -</blockquote> - -<A NAME=speech21><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=2.2.52>What do you mean?</A><br> -</blockquote> - -<A NAME=speech22><b>MACBETH</b></a> -<blockquote> -<A NAME=2.2.53>Still it cried 'Sleep no more!' to all the house:</A><br> -<A NAME=2.2.54>'Glamis hath murder'd sleep, and therefore Cawdor</A><br> -<A NAME=2.2.55>Shall sleep no more; Macbeth shall sleep no more.'</A><br> -</blockquote> - -<A NAME=speech23><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=2.2.56>Who was it that thus cried? Why, worthy thane,</A><br> -<A NAME=2.2.57>You do unbend your noble strength, to think</A><br> -<A NAME=2.2.58>So brainsickly of things. Go get some water,</A><br> -<A NAME=2.2.59>And wash this filthy witness from your hand.</A><br> -<A NAME=2.2.60>Why did you bring these daggers from the place?</A><br> -<A NAME=2.2.61>They must lie there: go carry them; and smear</A><br> -<A NAME=2.2.62>The sleepy grooms with blood.</A><br> -</blockquote> - -<A NAME=speech24><b>MACBETH</b></a> -<blockquote> -<A NAME=2.2.63>I'll go no more:</A><br> -<A NAME=2.2.64>I am afraid to think what I have done;</A><br> -<A NAME=2.2.65>Look on't again I dare not.</A><br> -</blockquote> - -<A NAME=speech25><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=2.2.66>Infirm of purpose!</A><br> -<A NAME=2.2.67>Give me the daggers: the sleeping and the dead</A><br> -<A NAME=2.2.68>Are but as pictures: 'tis the eye of childhood</A><br> -<A NAME=2.2.69>That fears a painted devil. If he do bleed,</A><br> -<A NAME=2.2.70>I'll gild the faces of the grooms withal;</A><br> -<A NAME=2.2.71>For it must seem their guilt.</A><br> -<p><i>Exit. Knocking within</i></p> -</blockquote> - -<A NAME=speech26><b>MACBETH</b></a> -<blockquote> -<A NAME=2.2.72>Whence is that knocking?</A><br> -<A NAME=2.2.73>How is't with me, when every noise appals me?</A><br> -<A NAME=2.2.74>What hands are here? ha! they pluck out mine eyes.</A><br> -<A NAME=2.2.75>Will all great Neptune's ocean wash this blood</A><br> -<A NAME=2.2.76>Clean from my hand? No, this my hand will rather</A><br> -<A NAME=2.2.77>The multitudinous seas in incarnadine,</A><br> -<A NAME=2.2.78>Making the green one red.</A><br> -<p><i>Re-enter LADY MACBETH</i></p> -</blockquote> - -<A NAME=speech27><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=2.2.79>My hands are of your colour; but I shame</A><br> -<A NAME=2.2.80>To wear a heart so white.</A><br> -<p><i>Knocking within</i></p> -<A NAME=2.2.81>I hear a knocking</A><br> -<A NAME=2.2.82>At the south entry: retire we to our chamber;</A><br> -<A NAME=2.2.83>A little water clears us of this deed:</A><br> -<A NAME=2.2.84>How easy is it, then! Your constancy</A><br> -<A NAME=2.2.85>Hath left you unattended.</A><br> -<p><i>Knocking within</i></p> -<A NAME=2.2.86>Hark! more knocking.</A><br> -<A NAME=2.2.87>Get on your nightgown, lest occasion call us,</A><br> -<A NAME=2.2.88>And show us to be watchers. Be not lost</A><br> -<A NAME=2.2.89>So poorly in your thoughts.</A><br> -</blockquote> - -<A NAME=speech28><b>MACBETH</b></a> -<blockquote> -<A NAME=2.2.90>To know my deed, 'twere best not know myself.</A><br> -<p><i>Knocking within</i></p> -<A NAME=2.2.91>Wake Duncan with thy knocking! I would thou couldst!</A><br> -<p><i>Exeunt</i></p> -</blockquote> -<h3>SCENE III. The same.</h3> -<p><blockquote> -<i>Knocking within. Enter a Porter</i> -</blockquote> - -<A NAME=speech1><b>Porter</b></a> -<blockquote> -<A NAME=2.3.1>Here's a knocking indeed! If a</A><br> -<A NAME=2.3.2>man were porter of hell-gate, he should have</A><br> -<A NAME=2.3.3>old turning the key.</A><br> -<p><i>Knocking within</i></p> -<A NAME=2.3.4>Knock,</A><br> -<A NAME=2.3.5>knock, knock! Who's there, i' the name of</A><br> -<A NAME=2.3.6>Beelzebub? Here's a farmer, that hanged</A><br> -<A NAME=2.3.7>himself on the expectation of plenty: come in</A><br> -<A NAME=2.3.8>time; have napkins enow about you; here</A><br> -<A NAME=2.3.9>you'll sweat for't.</A><br> -<p><i>Knocking within</i></p> -<A NAME=2.3.10>Knock,</A><br> -<A NAME=2.3.11>knock! Who's there, in the other devil's</A><br> -<A NAME=2.3.12>name? Faith, here's an equivocator, that could</A><br> -<A NAME=2.3.13>swear in both the scales against either scale;</A><br> -<A NAME=2.3.14>who committed treason enough for God's sake,</A><br> -<A NAME=2.3.15>yet could not equivocate to heaven: O, come</A><br> -<A NAME=2.3.16>in, equivocator.</A><br> -<p><i>Knocking within</i></p> -<A NAME=2.3.17>Knock,</A><br> -<A NAME=2.3.18>knock, knock! Who's there? Faith, here's an</A><br> -<A NAME=2.3.19>English tailor come hither, for stealing out of</A><br> -<A NAME=2.3.20>a French hose: come in, tailor; here you may</A><br> -<A NAME=2.3.21>roast your goose.</A><br> -<p><i>Knocking within</i></p> -<A NAME=2.3.22>Knock,</A><br> -<A NAME=2.3.23>knock; never at quiet! What are you? But</A><br> -<A NAME=2.3.24>this place is too cold for hell. I'll devil-porter</A><br> -<A NAME=2.3.25>it no further: I had thought to have let in</A><br> -<A NAME=2.3.26>some of all professions that go the primrose</A><br> -<A NAME=2.3.27>way to the everlasting bonfire.</A><br> -<p><i>Knocking within</i></p> -<A NAME=2.3.28>Anon, anon! I pray you, remember the porter.</A><br> -<p><i>Opens the gate</i></p> -<p><i>Enter MACDUFF and LENNOX</i></p> -</blockquote> - -<A NAME=speech2><b>MACDUFF</b></a> -<blockquote> -<A NAME=2.3.29>Was it so late, friend, ere you went to bed,</A><br> -<A NAME=2.3.30>That you do lie so late?</A><br> -</blockquote> - -<A NAME=speech3><b>Porter</b></a> -<blockquote> -<A NAME=2.3.31>'Faith sir, we were carousing till the</A><br> -<A NAME=2.3.32>second cock: and drink, sir, is a great</A><br> -<A NAME=2.3.33>provoker of three things.</A><br> -</blockquote> - -<A NAME=speech4><b>MACDUFF</b></a> -<blockquote> -<A NAME=2.3.34>What three things does drink especially provoke?</A><br> -</blockquote> - -<A NAME=speech5><b>Porter</b></a> -<blockquote> -<A NAME=2.3.35>Marry, sir, nose-painting, sleep, and</A><br> -<A NAME=2.3.36>urine. Lechery, sir, it provokes, and unprovokes;</A><br> -<A NAME=2.3.37>it provokes the desire, but it takes</A><br> -<A NAME=2.3.38>away the performance: therefore, much drink</A><br> -<A NAME=2.3.39>may be said to be an equivocator with lechery:</A><br> -<A NAME=2.3.40>it makes him, and it mars him; it sets</A><br> -<A NAME=2.3.41>him on, and it takes him off; it persuades him,</A><br> -<A NAME=2.3.42>and disheartens him; makes him stand to, and</A><br> -<A NAME=2.3.43>not stand to; in conclusion, equivocates him</A><br> -<A NAME=2.3.44>in a sleep, and, giving him the lie, leaves him.</A><br> -</blockquote> - -<A NAME=speech6><b>MACDUFF</b></a> -<blockquote> -<A NAME=2.3.45>I believe drink gave thee the lie last night.</A><br> -</blockquote> - -<A NAME=speech7><b>Porter</b></a> -<blockquote> -<A NAME=2.3.46>That it did, sir, i' the very throat on</A><br> -<A NAME=2.3.47>me: but I requited him for his lie; and, I</A><br> -<A NAME=2.3.48>think, being too strong for him, though he took</A><br> -<A NAME=2.3.49>up my legs sometime, yet I made a shift to cast</A><br> -<A NAME=2.3.50>him.</A><br> -</blockquote> - -<A NAME=speech8><b>MACDUFF</b></a> -<blockquote> -<A NAME=2.3.51>Is thy master stirring?</A><br> -<p><i>Enter MACBETH</i></p> -<A NAME=2.3.52>Our knocking has awaked him; here he comes.</A><br> -</blockquote> - -<A NAME=speech9><b>LENNOX</b></a> -<blockquote> -<A NAME=2.3.53>Good morrow, noble sir.</A><br> -</blockquote> - -<A NAME=speech10><b>MACBETH</b></a> -<blockquote> -<A NAME=2.3.54>Good morrow, both.</A><br> -</blockquote> - -<A NAME=speech11><b>MACDUFF</b></a> -<blockquote> -<A NAME=2.3.55>Is the king stirring, worthy thane?</A><br> -</blockquote> - -<A NAME=speech12><b>MACBETH</b></a> -<blockquote> -<A NAME=2.3.56>Not yet.</A><br> -</blockquote> - -<A NAME=speech13><b>MACDUFF</b></a> -<blockquote> -<A NAME=2.3.57>He did command me to call timely on him:</A><br> -<A NAME=2.3.58>I have almost slipp'd the hour.</A><br> -</blockquote> - -<A NAME=speech14><b>MACBETH</b></a> -<blockquote> -<A NAME=2.3.59>I'll bring you to him.</A><br> -</blockquote> - -<A NAME=speech15><b>MACDUFF</b></a> -<blockquote> -<A NAME=2.3.60>I know this is a joyful trouble to you;</A><br> -<A NAME=2.3.61>But yet 'tis one.</A><br> -</blockquote> - -<A NAME=speech16><b>MACBETH</b></a> -<blockquote> -<A NAME=2.3.62>The labour we delight in physics pain.</A><br> -<A NAME=2.3.63>This is the door.</A><br> -</blockquote> - -<A NAME=speech17><b>MACDUFF</b></a> -<blockquote> -<A NAME=2.3.64> I'll make so bold to call,</A><br> -<A NAME=2.3.65>For 'tis my limited service.</A><br> -<p><i>Exit</i></p> -</blockquote> - -<A NAME=speech18><b>LENNOX</b></a> -<blockquote> -<A NAME=2.3.66>Goes the king hence to-day?</A><br> -</blockquote> - -<A NAME=speech19><b>MACBETH</b></a> -<blockquote> -<A NAME=2.3.67>He does: he did appoint so.</A><br> -</blockquote> - -<A NAME=speech20><b>LENNOX</b></a> -<blockquote> -<A NAME=2.3.68>The night has been unruly: where we lay,</A><br> -<A NAME=2.3.69>Our chimneys were blown down; and, as they say,</A><br> -<A NAME=2.3.70>Lamentings heard i' the air; strange screams of death,</A><br> -<A NAME=2.3.71>And prophesying with accents terrible</A><br> -<A NAME=2.3.72>Of dire combustion and confused events</A><br> -<A NAME=2.3.73>New hatch'd to the woeful time: the obscure bird</A><br> -<A NAME=2.3.74>Clamour'd the livelong night: some say, the earth</A><br> -<A NAME=2.3.75>Was feverous and did shake.</A><br> -</blockquote> - -<A NAME=speech21><b>MACBETH</b></a> -<blockquote> -<A NAME=2.3.76>'Twas a rough night.</A><br> -</blockquote> - -<A NAME=speech22><b>LENNOX</b></a> -<blockquote> -<A NAME=2.3.77>My young remembrance cannot parallel</A><br> -<A NAME=2.3.78>A fellow to it.</A><br> -<p><i>Re-enter MACDUFF</i></p> -</blockquote> - -<A NAME=speech23><b>MACDUFF</b></a> -<blockquote> -<A NAME=2.3.79>O horror, horror, horror! Tongue nor heart</A><br> -<A NAME=2.3.80>Cannot conceive nor name thee!</A><br> -</blockquote> - -<A NAME=speech24><b>MACBETH</b></a> - -<A NAME=speech25><b>LENNOX</b></a> -<blockquote> -<A NAME=2.3.81>What's the matter.</A><br> -</blockquote> - -<A NAME=speech26><b>MACDUFF</b></a> -<blockquote> -<A NAME=2.3.82>Confusion now hath made his masterpiece!</A><br> -<A NAME=2.3.83>Most sacrilegious murder hath broke ope</A><br> -<A NAME=2.3.84>The Lord's anointed temple, and stole thence</A><br> -<A NAME=2.3.85>The life o' the building!</A><br> -</blockquote> - -<A NAME=speech27><b>MACBETH</b></a> -<blockquote> -<A NAME=2.3.86>What is 't you say? the life?</A><br> -</blockquote> - -<A NAME=speech28><b>LENNOX</b></a> -<blockquote> -<A NAME=2.3.87>Mean you his majesty?</A><br> -</blockquote> - -<A NAME=speech29><b>MACDUFF</b></a> -<blockquote> -<A NAME=2.3.88>Approach the chamber, and destroy your sight</A><br> -<A NAME=2.3.89>With a new Gorgon: do not bid me speak;</A><br> -<A NAME=2.3.90>See, and then speak yourselves.</A><br> -<p><i>Exeunt MACBETH and LENNOX</i></p> -<A NAME=2.3.91>Awake, awake!</A><br> -<A NAME=2.3.92>Ring the alarum-bell. Murder and treason!</A><br> -<A NAME=2.3.93>Banquo and Donalbain! Malcolm! awake!</A><br> -<A NAME=2.3.94>Shake off this downy sleep, death's counterfeit,</A><br> -<A NAME=2.3.95>And look on death itself! up, up, and see</A><br> -<A NAME=2.3.96>The great doom's image! Malcolm! Banquo!</A><br> -<A NAME=2.3.97>As from your graves rise up, and walk like sprites,</A><br> -<A NAME=2.3.98>To countenance this horror! Ring the bell.</A><br> -<p><i>Bell rings</i></p> -<p><i>Enter LADY MACBETH</i></p> -</blockquote> - -<A NAME=speech30><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=2.3.99>What's the business,</A><br> -<A NAME=2.3.100>That such a hideous trumpet calls to parley</A><br> -<A NAME=2.3.101>The sleepers of the house? speak, speak!</A><br> -</blockquote> - -<A NAME=speech31><b>MACDUFF</b></a> -<blockquote> -<A NAME=2.3.102>O gentle lady,</A><br> -<A NAME=2.3.103>'Tis not for you to hear what I can speak:</A><br> -<A NAME=2.3.104>The repetition, in a woman's ear,</A><br> -<A NAME=2.3.105>Would murder as it fell.</A><br> -<p><i>Enter BANQUO</i></p> -<A NAME=2.3.106>O Banquo, Banquo,</A><br> -<A NAME=2.3.107>Our royal master 's murder'd!</A><br> -</blockquote> - -<A NAME=speech32><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=2.3.108>Woe, alas!</A><br> -<A NAME=2.3.109>What, in our house?</A><br> -</blockquote> - -<A NAME=speech33><b>BANQUO</b></a> -<blockquote> -<A NAME=2.3.110>Too cruel any where.</A><br> -<A NAME=2.3.111>Dear Duff, I prithee, contradict thyself,</A><br> -<A NAME=2.3.112>And say it is not so.</A><br> -<p><i>Re-enter MACBETH and LENNOX, with ROSS</i></p> -</blockquote> - -<A NAME=speech34><b>MACBETH</b></a> -<blockquote> -<A NAME=2.3.113>Had I but died an hour before this chance,</A><br> -<A NAME=2.3.114>I had lived a blessed time; for, from this instant,</A><br> -<A NAME=2.3.115>There 's nothing serious in mortality:</A><br> -<A NAME=2.3.116>All is but toys: renown and grace is dead;</A><br> -<A NAME=2.3.117>The wine of life is drawn, and the mere lees</A><br> -<A NAME=2.3.118>Is left this vault to brag of.</A><br> -<p><i>Enter MALCOLM and DONALBAIN</i></p> -</blockquote> - -<A NAME=speech35><b>DONALBAIN</b></a> -<blockquote> -<A NAME=2.3.119>What is amiss?</A><br> -</blockquote> - -<A NAME=speech36><b>MACBETH</b></a> -<blockquote> -<A NAME=2.3.120> You are, and do not know't:</A><br> -<A NAME=2.3.121>The spring, the head, the fountain of your blood</A><br> -<A NAME=2.3.122>Is stopp'd; the very source of it is stopp'd.</A><br> -</blockquote> - -<A NAME=speech37><b>MACDUFF</b></a> -<blockquote> -<A NAME=2.3.123>Your royal father 's murder'd.</A><br> -</blockquote> - -<A NAME=speech38><b>MALCOLM</b></a> -<blockquote> -<A NAME=2.3.124>O, by whom?</A><br> -</blockquote> - -<A NAME=speech39><b>LENNOX</b></a> -<blockquote> -<A NAME=2.3.125>Those of his chamber, as it seem'd, had done 't:</A><br> -<A NAME=2.3.126>Their hands and faces were an badged with blood;</A><br> -<A NAME=2.3.127>So were their daggers, which unwiped we found</A><br> -<A NAME=2.3.128>Upon their pillows:</A><br> -<A NAME=2.3.129>They stared, and were distracted; no man's life</A><br> -<A NAME=2.3.130>Was to be trusted with them.</A><br> -</blockquote> - -<A NAME=speech40><b>MACBETH</b></a> -<blockquote> -<A NAME=2.3.131>O, yet I do repent me of my fury,</A><br> -<A NAME=2.3.132>That I did kill them.</A><br> -</blockquote> - -<A NAME=speech41><b>MACDUFF</b></a> -<blockquote> -<A NAME=2.3.133>Wherefore did you so?</A><br> -</blockquote> - -<A NAME=speech42><b>MACBETH</b></a> -<blockquote> -<A NAME=2.3.134>Who can be wise, amazed, temperate and furious,</A><br> -<A NAME=2.3.135>Loyal and neutral, in a moment? No man:</A><br> -<A NAME=2.3.136>The expedition my violent love</A><br> -<A NAME=2.3.137>Outrun the pauser, reason. Here lay Duncan,</A><br> -<A NAME=2.3.138>His silver skin laced with his golden blood;</A><br> -<A NAME=2.3.139>And his gash'd stabs look'd like a breach in nature</A><br> -<A NAME=2.3.140>For ruin's wasteful entrance: there, the murderers,</A><br> -<A NAME=2.3.141>Steep'd in the colours of their trade, their daggers</A><br> -<A NAME=2.3.142>Unmannerly breech'd with gore: who could refrain,</A><br> -<A NAME=2.3.143>That had a heart to love, and in that heart</A><br> -<A NAME=2.3.144>Courage to make 's love kno wn?</A><br> -</blockquote> - -<A NAME=speech43><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=2.3.145>Help me hence, ho!</A><br> -</blockquote> - -<A NAME=speech44><b>MACDUFF</b></a> -<blockquote> -<A NAME=2.3.146>Look to the lady.</A><br> -</blockquote> - -<A NAME=speech45><b>MALCOLM</b></a> -<blockquote> -<A NAME=2.3.147>[Aside to DONALBAIN] Why do we hold our tongues,</A><br> -<A NAME=2.3.148>That most may claim this argument for ours?</A><br> -</blockquote> - -<A NAME=speech46><b>DONALBAIN</b></a> -<blockquote> -<A NAME=2.3.149>[Aside to MALCOLM] What should be spoken here,</A><br> -<A NAME=2.3.150>where our fate,</A><br> -<A NAME=2.3.151>Hid in an auger-hole, may rush, and seize us?</A><br> -<A NAME=2.3.152>Let 's away;</A><br> -<A NAME=2.3.153>Our tears are not yet brew'd.</A><br> -</blockquote> - -<A NAME=speech47><b>MALCOLM</b></a> -<blockquote> -<A NAME=2.3.154>[Aside to DONALBAIN] Nor our strong sorrow</A><br> -<A NAME=2.3.155>Upon the foot of motion.</A><br> -</blockquote> - -<A NAME=speech48><b>BANQUO</b></a> -<blockquote> -<A NAME=2.3.156>Look to the lady:</A><br> -<p><i>LADY MACBETH is carried out</i></p> -<A NAME=2.3.157>And when we have our naked frailties hid,</A><br> -<A NAME=2.3.158>That suffer in exposure, let us meet,</A><br> -<A NAME=2.3.159>And question this most bloody piece of work,</A><br> -<A NAME=2.3.160>To know it further. Fears and scruples shake us:</A><br> -<A NAME=2.3.161>In the great hand of God I stand; and thence</A><br> -<A NAME=2.3.162>Against the undivulged pretence I fight</A><br> -<A NAME=2.3.163>Of treasonous malice.</A><br> -</blockquote> - -<A NAME=speech49><b>MACDUFF</b></a> -<blockquote> -<A NAME=2.3.164>And so do I.</A><br> -</blockquote> - -<A NAME=speech50><b>ALL</b></a> -<blockquote> -<A NAME=2.3.165>So all.</A><br> -</blockquote> - -<A NAME=speech51><b>MACBETH</b></a> -<blockquote> -<A NAME=2.3.166>Let's briefly put on manly readiness,</A><br> -<A NAME=2.3.167>And meet i' the hall together.</A><br> -</blockquote> - -<A NAME=speech52><b>ALL</b></a> -<blockquote> -<A NAME=2.3.168>Well contented.</A><br> -<p><i>Exeunt all but Malcolm and Donalbain.</i></p> -</blockquote> - -<A NAME=speech53><b>MALCOLM</b></a> -<blockquote> -<A NAME=2.3.169>What will you do? Let's not consort with them:</A><br> -<A NAME=2.3.170>To show an unfelt sorrow is an office</A><br> -<A NAME=2.3.171>Which the false man does easy. I'll to England.</A><br> -</blockquote> - -<A NAME=speech54><b>DONALBAIN</b></a> -<blockquote> -<A NAME=2.3.172>To Ireland, I; our separated fortune</A><br> -<A NAME=2.3.173>Shall keep us both the safer: where we are,</A><br> -<A NAME=2.3.174>There's daggers in men's smiles: the near in blood,</A><br> -<A NAME=2.3.175>The nearer bloody.</A><br> -</blockquote> - -<A NAME=speech55><b>MALCOLM</b></a> -<blockquote> -<A NAME=2.3.176> This murderous shaft that's shot</A><br> -<A NAME=2.3.177>Hath not yet lighted, and our safest way</A><br> -<A NAME=2.3.178>Is to avoid the aim. Therefore, to horse;</A><br> -<A NAME=2.3.179>And let us not be dainty of leave-taking,</A><br> -<A NAME=2.3.180>But shift away: there's warrant in that theft</A><br> -<A NAME=2.3.181>Which steals itself, when there's no mercy left.</A><br> -<p><i>Exeunt</i></p> -</blockquote> -<h3>SCENE IV. Outside Macbeth's castle.</h3> -<p><blockquote> -<i>Enter ROSS and an old Man</i> -</blockquote> - -<A NAME=speech1><b>Old Man</b></a> -<blockquote> -<A NAME=2.4.1>Threescore and ten I can remember well:</A><br> -<A NAME=2.4.2>Within the volume of which time I have seen</A><br> -<A NAME=2.4.3>Hours dreadful and things strange; but this sore night</A><br> -<A NAME=2.4.4>Hath trifled former knowings.</A><br> -</blockquote> - -<A NAME=speech2><b>ROSS</b></a> -<blockquote> -<A NAME=2.4.5>Ah, good father,</A><br> -<A NAME=2.4.6>Thou seest, the heavens, as troubled with man's act,</A><br> -<A NAME=2.4.7>Threaten his bloody stage: by the clock, 'tis day,</A><br> -<A NAME=2.4.8>And yet dark night strangles the travelling lamp:</A><br> -<A NAME=2.4.9>Is't night's predominance, or the day's shame,</A><br> -<A NAME=2.4.10>That darkness does the face of earth entomb,</A><br> -<A NAME=2.4.11>When living light should kiss it?</A><br> -</blockquote> - -<A NAME=speech3><b>Old Man</b></a> -<blockquote> -<A NAME=2.4.12>'Tis unnatural,</A><br> -<A NAME=2.4.13>Even like the deed that's done. On Tuesday last,</A><br> -<A NAME=2.4.14>A falcon, towering in her pride of place,</A><br> -<A NAME=2.4.15>Was by a mousing owl hawk'd at and kill'd.</A><br> -</blockquote> - -<A NAME=speech4><b>ROSS</b></a> -<blockquote> -<A NAME=2.4.16>And Duncan's horses--a thing most strange and certain--</A><br> -<A NAME=2.4.17>Beauteous and swift, the minions of their race,</A><br> -<A NAME=2.4.18>Turn'd wild in nature, broke their stalls, flung out,</A><br> -<A NAME=2.4.19>Contending 'gainst obedience, as they would make</A><br> -<A NAME=2.4.20>War with mankind.</A><br> -</blockquote> - -<A NAME=speech5><b>Old Man</b></a> -<blockquote> -<A NAME=2.4.21>'Tis said they eat each other.</A><br> -</blockquote> - -<A NAME=speech6><b>ROSS</b></a> -<blockquote> -<A NAME=2.4.22>They did so, to the amazement of mine eyes</A><br> -<A NAME=2.4.23>That look'd upon't. Here comes the good Macduff.</A><br> -<p><i>Enter MACDUFF</i></p> -<A NAME=2.4.24>How goes the world, sir, now?</A><br> -</blockquote> - -<A NAME=speech7><b>MACDUFF</b></a> -<blockquote> -<A NAME=2.4.25>Why, see you not?</A><br> -</blockquote> - -<A NAME=speech8><b>ROSS</b></a> -<blockquote> -<A NAME=2.4.26>Is't known who did this more than bloody deed?</A><br> -</blockquote> - -<A NAME=speech9><b>MACDUFF</b></a> -<blockquote> -<A NAME=2.4.27>Those that Macbeth hath slain.</A><br> -</blockquote> - -<A NAME=speech10><b>ROSS</b></a> -<blockquote> -<A NAME=2.4.28>Alas, the day!</A><br> -<A NAME=2.4.29>What good could they pretend?</A><br> -</blockquote> - -<A NAME=speech11><b>MACDUFF</b></a> -<blockquote> -<A NAME=2.4.30>They were suborn'd:</A><br> -<A NAME=2.4.31>Malcolm and Donalbain, the king's two sons,</A><br> -<A NAME=2.4.32>Are stol'n away and fled; which puts upon them</A><br> -<A NAME=2.4.33>Suspicion of the deed.</A><br> -</blockquote> - -<A NAME=speech12><b>ROSS</b></a> -<blockquote> -<A NAME=2.4.34>'Gainst nature still!</A><br> -<A NAME=2.4.35>Thriftless ambition, that wilt ravin up</A><br> -<A NAME=2.4.36>Thine own life's means! Then 'tis most like</A><br> -<A NAME=2.4.37>The sovereignty will fall upon Macbeth.</A><br> -</blockquote> - -<A NAME=speech13><b>MACDUFF</b></a> -<blockquote> -<A NAME=2.4.38>He is already named, and gone to Scone</A><br> -<A NAME=2.4.39>To be invested.</A><br> -</blockquote> - -<A NAME=speech14><b>ROSS</b></a> -<blockquote> -<A NAME=2.4.40> Where is Duncan's body?</A><br> -</blockquote> - -<A NAME=speech15><b>MACDUFF</b></a> -<blockquote> -<A NAME=2.4.41>Carried to Colmekill,</A><br> -<A NAME=2.4.42>The sacred storehouse of his predecessors,</A><br> -<A NAME=2.4.43>And guardian of their bones.</A><br> -</blockquote> - -<A NAME=speech16><b>ROSS</b></a> -<blockquote> -<A NAME=2.4.44>Will you to Scone?</A><br> -</blockquote> - -<A NAME=speech17><b>MACDUFF</b></a> -<blockquote> -<A NAME=2.4.45>No, cousin, I'll to Fife.</A><br> -</blockquote> - -<A NAME=speech18><b>ROSS</b></a> -<blockquote> -<A NAME=2.4.46>Well, I will thither.</A><br> -</blockquote> - -<A NAME=speech19><b>MACDUFF</b></a> -<blockquote> -<A NAME=2.4.47>Well, may you see things well done there: adieu!</A><br> -<A NAME=2.4.48>Lest our old robes sit easier than our new!</A><br> -</blockquote> - -<A NAME=speech20><b>ROSS</b></a> -<blockquote> -<A NAME=2.4.49>Farewell, father.</A><br> -</blockquote> - -<A NAME=speech21><b>Old Man</b></a> -<blockquote> -<A NAME=2.4.50>God's benison go with you; and with those</A><br> -<A NAME=2.4.51>That would make good of bad, and friends of foes!</A><br> -<p><i>Exeunt</i></p> -</blockquote><p> -<H3>ACT III</h3> -<h3>SCENE I. Forres. The palace.</h3> -<p><blockquote> -<i>Enter BANQUO</i> -</blockquote> - -<A NAME=speech1><b>BANQUO</b></a> -<blockquote> -<A NAME=3.1.1>Thou hast it now: king, Cawdor, Glamis, all,</A><br> -<A NAME=3.1.2>As the weird women promised, and, I fear,</A><br> -<A NAME=3.1.3>Thou play'dst most foully for't: yet it was said</A><br> -<A NAME=3.1.4>It should not stand in thy posterity,</A><br> -<A NAME=3.1.5>But that myself should be the root and father</A><br> -<A NAME=3.1.6>Of many kings. If there come truth from them--</A><br> -<A NAME=3.1.7>As upon thee, Macbeth, their speeches shine--</A><br> -<A NAME=3.1.8>Why, by the verities on thee made good,</A><br> -<A NAME=3.1.9>May they not be my oracles as well,</A><br> -<A NAME=3.1.10>And set me up in hope? But hush! no more.</A><br> -<p><i>Sennet sounded. Enter MACBETH, as king, LADY MACBETH, as queen, LENNOX, ROSS, Lords, Ladies, and Attendants</i></p> -</blockquote> - -<A NAME=speech2><b>MACBETH</b></a> -<blockquote> -<A NAME=3.1.11>Here's our chief guest.</A><br> -</blockquote> - -<A NAME=speech3><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=3.1.12>If he had been forgotten,</A><br> -<A NAME=3.1.13>It had been as a gap in our great feast,</A><br> -<A NAME=3.1.14>And all-thing unbecoming.</A><br> -</blockquote> - -<A NAME=speech4><b>MACBETH</b></a> -<blockquote> -<A NAME=3.1.15>To-night we hold a solemn supper sir,</A><br> -<A NAME=3.1.16>And I'll request your presence.</A><br> -</blockquote> - -<A NAME=speech5><b>BANQUO</b></a> -<blockquote> -<A NAME=3.1.17>Let your highness</A><br> -<A NAME=3.1.18>Command upon me; to the which my duties</A><br> -<A NAME=3.1.19>Are with a most indissoluble tie</A><br> -<A NAME=3.1.20>For ever knit.</A><br> -</blockquote> - -<A NAME=speech6><b>MACBETH</b></a> -<blockquote> -<A NAME=3.1.21> Ride you this afternoon?</A><br> -</blockquote> - -<A NAME=speech7><b>BANQUO</b></a> -<blockquote> -<A NAME=3.1.22>Ay, my good lord.</A><br> -</blockquote> - -<A NAME=speech8><b>MACBETH</b></a> -<blockquote> -<A NAME=3.1.23>We should have else desired your good advice,</A><br> -<A NAME=3.1.24>Which still hath been both grave and prosperous,</A><br> -<A NAME=3.1.25>In this day's council; but we'll take to-morrow.</A><br> -<A NAME=3.1.26>Is't far you ride?</A><br> -</blockquote> - -<A NAME=speech9><b>BANQUO</b></a> -<blockquote> -<A NAME=3.1.27>As far, my lord, as will fill up the time</A><br> -<A NAME=3.1.28>'Twixt this and supper: go not my horse the better,</A><br> -<A NAME=3.1.29>I must become a borrower of the night</A><br> -<A NAME=3.1.30>For a dark hour or twain.</A><br> -</blockquote> - -<A NAME=speech10><b>MACBETH</b></a> -<blockquote> -<A NAME=3.1.31>Fail not our feast.</A><br> -</blockquote> - -<A NAME=speech11><b>BANQUO</b></a> -<blockquote> -<A NAME=3.1.32>My lord, I will not.</A><br> -</blockquote> - -<A NAME=speech12><b>MACBETH</b></a> -<blockquote> -<A NAME=3.1.33>We hear, our bloody cousins are bestow'd</A><br> -<A NAME=3.1.34>In England and in Ireland, not confessing</A><br> -<A NAME=3.1.35>Their cruel parricide, filling their hearers</A><br> -<A NAME=3.1.36>With strange invention: but of that to-morrow,</A><br> -<A NAME=3.1.37>When therewithal we shall have cause of state</A><br> -<A NAME=3.1.38>Craving us jointly. Hie you to horse: adieu,</A><br> -<A NAME=3.1.39>Till you return at night. Goes Fleance with you?</A><br> -</blockquote> - -<A NAME=speech13><b>BANQUO</b></a> -<blockquote> -<A NAME=3.1.40>Ay, my good lord: our time does call upon 's.</A><br> -</blockquote> - -<A NAME=speech14><b>MACBETH</b></a> -<blockquote> -<A NAME=3.1.41>I wish your horses swift and sure of foot;</A><br> -<A NAME=3.1.42>And so I do commend you to their backs. Farewell.</A><br> -<p><i>Exit BANQUO</i></p> -<A NAME=3.1.43>Let every man be master of his time</A><br> -<A NAME=3.1.44>Till seven at night: to make society</A><br> -<A NAME=3.1.45>The sweeter welcome, we will keep ourself</A><br> -<A NAME=3.1.46>Till supper-time alone: while then, God be with you!</A><br> -<p><i>Exeunt all but MACBETH, and an attendant</i></p> -<A NAME=3.1.47>Sirrah, a word with you: attend those men</A><br> -<A NAME=3.1.48>Our pleasure?</A><br> -</blockquote> - -<A NAME=speech15><b>ATTENDANT</b></a> -<blockquote> -<A NAME=3.1.49>They are, my lord, without the palace gate.</A><br> -</blockquote> - -<A NAME=speech16><b>MACBETH</b></a> -<blockquote> -<A NAME=3.1.50>Bring them before us.</A><br> -<p><i>Exit Attendant</i></p> -<A NAME=3.1.51>To be thus is nothing;</A><br> -<A NAME=3.1.52>But to be safely thus.--Our fears in Banquo</A><br> -<A NAME=3.1.53>Stick deep; and in his royalty of nature</A><br> -<A NAME=3.1.54>Reigns that which would be fear'd: 'tis much he dares;</A><br> -<A NAME=3.1.55>And, to that dauntless temper of his mind,</A><br> -<A NAME=3.1.56>He hath a wisdom that doth guide his valour</A><br> -<A NAME=3.1.57>To act in safety. There is none but he</A><br> -<A NAME=3.1.58>Whose being I do fear: and, under him,</A><br> -<A NAME=3.1.59>My Genius is rebuked; as, it is said,</A><br> -<A NAME=3.1.60>Mark Antony's was by Caesar. He chid the sisters</A><br> -<A NAME=3.1.61>When first they put the name of king upon me,</A><br> -<A NAME=3.1.62>And bade them speak to him: then prophet-like</A><br> -<A NAME=3.1.63>They hail'd him father to a line of kings:</A><br> -<A NAME=3.1.64>Upon my head they placed a fruitless crown,</A><br> -<A NAME=3.1.65>And put a barren sceptre in my gripe,</A><br> -<A NAME=3.1.66>Thence to be wrench'd with an unlineal hand,</A><br> -<A NAME=3.1.67>No son of mine succeeding. If 't be so,</A><br> -<A NAME=3.1.68>For Banquo's issue have I filed my mind;</A><br> -<A NAME=3.1.69>For them the gracious Duncan have I murder'd;</A><br> -<A NAME=3.1.70>Put rancours in the vessel of my peace</A><br> -<A NAME=3.1.71>Only for them; and mine eternal jewel</A><br> -<A NAME=3.1.72>Given to the common enemy of man,</A><br> -<A NAME=3.1.73>To make them kings, the seed of Banquo kings!</A><br> -<A NAME=3.1.74>Rather than so, come fate into the list.</A><br> -<A NAME=3.1.75>And champion me to the utterance! Who's there!</A><br> -<p><i>Re-enter Attendant, with two Murderers</i></p> -<A NAME=3.1.76>Now go to the door, and stay there till we call.</A><br> -<p><i>Exit Attendant</i></p> -<A NAME=3.1.77>Was it not yesterday we spoke together?</A><br> -</blockquote> - -<A NAME=speech17><b>First Murderer</b></a> -<blockquote> -<A NAME=3.1.78>It was, so please your highness.</A><br> -</blockquote> - -<A NAME=speech18><b>MACBETH</b></a> -<blockquote> -<A NAME=3.1.79>Well then, now</A><br> -<A NAME=3.1.80>Have you consider'd of my speeches? Know</A><br> -<A NAME=3.1.81>That it was he in the times past which held you</A><br> -<A NAME=3.1.82>So under fortune, which you thought had been</A><br> -<A NAME=3.1.83>Our innocent self: this I made good to you</A><br> -<A NAME=3.1.84>In our last conference, pass'd in probation with you,</A><br> -<A NAME=3.1.85>How you were borne in hand, how cross'd,</A><br> -<A NAME=3.1.86>the instruments,</A><br> -<A NAME=3.1.87>Who wrought with them, and all things else that might</A><br> -<A NAME=3.1.88>To half a soul and to a notion crazed</A><br> -<A NAME=3.1.89>Say 'Thus did Banquo.'</A><br> -</blockquote> - -<A NAME=speech19><b>First Murderer</b></a> -<blockquote> -<A NAME=3.1.90>You made it known to us.</A><br> -</blockquote> - -<A NAME=speech20><b>MACBETH</b></a> -<blockquote> -<A NAME=3.1.91>I did so, and went further, which is now</A><br> -<A NAME=3.1.92>Our point of second meeting. Do you find</A><br> -<A NAME=3.1.93>Your patience so predominant in your nature</A><br> -<A NAME=3.1.94>That you can let this go? Are you so gospell'd</A><br> -<A NAME=3.1.95>To pray for this good man and for his issue,</A><br> -<A NAME=3.1.96>Whose heavy hand hath bow'd you to the grave</A><br> -<A NAME=3.1.97>And beggar'd yours for ever?</A><br> -</blockquote> - -<A NAME=speech21><b>First Murderer</b></a> -<blockquote> -<A NAME=3.1.98>We are men, my liege.</A><br> -</blockquote> - -<A NAME=speech22><b>MACBETH</b></a> -<blockquote> -<A NAME=3.1.99>Ay, in the catalogue ye go for men;</A><br> -<A NAME=3.1.100>As hounds and greyhounds, mongrels, spaniels, curs,</A><br> -<A NAME=3.1.101>Shoughs, water-rugs and demi-wolves, are clept</A><br> -<A NAME=3.1.102>All by the name of dogs: the valued file</A><br> -<A NAME=3.1.103>Distinguishes the swift, the slow, the subtle,</A><br> -<A NAME=3.1.104>The housekeeper, the hunter, every one</A><br> -<A NAME=3.1.105>According to the gift which bounteous nature</A><br> -<A NAME=3.1.106>Hath in him closed; whereby he does receive</A><br> -<A NAME=3.1.107>Particular addition. from the bill</A><br> -<A NAME=3.1.108>That writes them all alike: and so of men.</A><br> -<A NAME=3.1.109>Now, if you have a station in the file,</A><br> -<A NAME=3.1.110>Not i' the worst rank of manhood, say 't;</A><br> -<A NAME=3.1.111>And I will put that business in your bosoms,</A><br> -<A NAME=3.1.112>Whose execution takes your enemy off,</A><br> -<A NAME=3.1.113>Grapples you to the heart and love of us,</A><br> -<A NAME=3.1.114>Who wear our health but sickly in his life,</A><br> -<A NAME=3.1.115>Which in his death were perfect.</A><br> -</blockquote> - -<A NAME=speech23><b>Second Murderer</b></a> -<blockquote> -<A NAME=3.1.116>I am one, my liege,</A><br> -<A NAME=3.1.117>Whom the vile blows and buffets of the world</A><br> -<A NAME=3.1.118>Have so incensed that I am reckless what</A><br> -<A NAME=3.1.119>I do to spite the world.</A><br> -</blockquote> - -<A NAME=speech24><b>First Murderer</b></a> -<blockquote> -<A NAME=3.1.120>And I another</A><br> -<A NAME=3.1.121>So weary with disasters, tugg'd with fortune,</A><br> -<A NAME=3.1.122>That I would set my lie on any chance,</A><br> -<A NAME=3.1.123>To mend it, or be rid on't.</A><br> -</blockquote> - -<A NAME=speech25><b>MACBETH</b></a> -<blockquote> -<A NAME=3.1.124>Both of you</A><br> -<A NAME=3.1.125>Know Banquo was your enemy.</A><br> -</blockquote> - -<A NAME=speech26><b>Both Murderers</b></a> -<blockquote> -<A NAME=3.1.126>True, my lord.</A><br> -</blockquote> - -<A NAME=speech27><b>MACBETH</b></a> -<blockquote> -<A NAME=3.1.127>So is he mine; and in such bloody distance,</A><br> -<A NAME=3.1.128>That every minute of his being thrusts</A><br> -<A NAME=3.1.129>Against my near'st of life: and though I could</A><br> -<A NAME=3.1.130>With barefaced power sweep him from my sight</A><br> -<A NAME=3.1.131>And bid my will avouch it, yet I must not,</A><br> -<A NAME=3.1.132>For certain friends that are both his and mine,</A><br> -<A NAME=3.1.133>Whose loves I may not drop, but wail his fall</A><br> -<A NAME=3.1.134>Who I myself struck down; and thence it is,</A><br> -<A NAME=3.1.135>That I to your assistance do make love,</A><br> -<A NAME=3.1.136>Masking the business from the common eye</A><br> -<A NAME=3.1.137>For sundry weighty reasons.</A><br> -</blockquote> - -<A NAME=speech28><b>Second Murderer</b></a> -<blockquote> -<A NAME=3.1.138>We shall, my lord,</A><br> -<A NAME=3.1.139>Perform what you command us.</A><br> -</blockquote> - -<A NAME=speech29><b>First Murderer</b></a> -<blockquote> -<A NAME=3.1.140>Though our lives--</A><br> -</blockquote> - -<A NAME=speech30><b>MACBETH</b></a> -<blockquote> -<A NAME=3.1.141>Your spirits shine through you. Within this hour at most</A><br> -<A NAME=3.1.142>I will advise you where to plant yourselves;</A><br> -<A NAME=3.1.143>Acquaint you with the perfect spy o' the time,</A><br> -<A NAME=3.1.144>The moment on't; for't must be done to-night,</A><br> -<A NAME=3.1.145>And something from the palace; always thought</A><br> -<A NAME=3.1.146>That I require a clearness: and with him--</A><br> -<A NAME=3.1.147>To leave no rubs nor botches in the work--</A><br> -<A NAME=3.1.148>Fleance his son, that keeps him company,</A><br> -<A NAME=3.1.149>Whose absence is no less material to me</A><br> -<A NAME=3.1.150>Than is his father's, must embrace the fate</A><br> -<A NAME=3.1.151>Of that dark hour. Resolve yourselves apart:</A><br> -<A NAME=3.1.152>I'll come to you anon.</A><br> -</blockquote> - -<A NAME=speech31><b>Both Murderers</b></a> -<blockquote> -<A NAME=3.1.153>We are resolved, my lord.</A><br> -</blockquote> - -<A NAME=speech32><b>MACBETH</b></a> -<blockquote> -<A NAME=3.1.154>I'll call upon you straight: abide within.</A><br> -<p><i>Exeunt Murderers</i></p> -<A NAME=3.1.155>It is concluded. Banquo, thy soul's flight,</A><br> -<A NAME=3.1.156>If it find heaven, must find it out to-night.</A><br> -<p><i>Exit</i></p> -</blockquote> -<h3>SCENE II. The palace.</h3> -<p><blockquote> -<i>Enter LADY MACBETH and a Servant</i> -</blockquote> - -<A NAME=speech1><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=3.2.1>Is Banquo gone from court?</A><br> -</blockquote> - -<A NAME=speech2><b>Servant</b></a> -<blockquote> -<A NAME=3.2.2>Ay, madam, but returns again to-night.</A><br> -</blockquote> - -<A NAME=speech3><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=3.2.3>Say to the king, I would attend his leisure</A><br> -<A NAME=3.2.4>For a few words.</A><br> -</blockquote> - -<A NAME=speech4><b>Servant</b></a> -<blockquote> -<A NAME=3.2.5> Madam, I will.</A><br> -<p><i>Exit</i></p> -</blockquote> - -<A NAME=speech5><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=3.2.6>Nought's had, all's spent,</A><br> -<A NAME=3.2.7>Where our desire is got without content:</A><br> -<A NAME=3.2.8>'Tis safer to be that which we destroy</A><br> -<A NAME=3.2.9>Than by destruction dwell in doubtful joy.</A><br> -<p><i>Enter MACBETH</i></p> -<A NAME=3.2.10>How now, my lord! why do you keep alone,</A><br> -<A NAME=3.2.11>Of sorriest fancies your companions making,</A><br> -<A NAME=3.2.12>Using those thoughts which should indeed have died</A><br> -<A NAME=3.2.13>With them they think on? Things without all remedy</A><br> -<A NAME=3.2.14>Should be without regard: what's done is done.</A><br> -</blockquote> - -<A NAME=speech6><b>MACBETH</b></a> -<blockquote> -<A NAME=3.2.15>We have scotch'd the snake, not kill'd it:</A><br> -<A NAME=3.2.16>She'll close and be herself, whilst our poor malice</A><br> -<A NAME=3.2.17>Remains in danger of her former tooth.</A><br> -<A NAME=3.2.18>But let the frame of things disjoint, both the</A><br> -<A NAME=3.2.19>worlds suffer,</A><br> -<A NAME=3.2.20>Ere we will eat our meal in fear and sleep</A><br> -<A NAME=3.2.21>In the affliction of these terrible dreams</A><br> -<A NAME=3.2.22>That shake us nightly: better be with the dead,</A><br> -<A NAME=3.2.23>Whom we, to gain our peace, have sent to peace,</A><br> -<A NAME=3.2.24>Than on the torture of the mind to lie</A><br> -<A NAME=3.2.25>In restless ecstasy. Duncan is in his grave;</A><br> -<A NAME=3.2.26>After life's fitful fever he sleeps well;</A><br> -<A NAME=3.2.27>Treason has done his worst: nor steel, nor poison,</A><br> -<A NAME=3.2.28>Malice domestic, foreign levy, nothing,</A><br> -<A NAME=3.2.29>Can touch him further.</A><br> -</blockquote> - -<A NAME=speech7><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=3.2.30>Come on;</A><br> -<A NAME=3.2.31>Gentle my lord, sleek o'er your rugged looks;</A><br> -<A NAME=3.2.32>Be bright and jovial among your guests to-night.</A><br> -</blockquote> - -<A NAME=speech8><b>MACBETH</b></a> -<blockquote> -<A NAME=3.2.33>So shall I, love; and so, I pray, be you:</A><br> -<A NAME=3.2.34>Let your remembrance apply to Banquo;</A><br> -<A NAME=3.2.35>Present him eminence, both with eye and tongue:</A><br> -<A NAME=3.2.36>Unsafe the while, that we</A><br> -<A NAME=3.2.37>Must lave our honours in these flattering streams,</A><br> -<A NAME=3.2.38>And make our faces vizards to our hearts,</A><br> -<A NAME=3.2.39>Disguising what they are.</A><br> -</blockquote> - -<A NAME=speech9><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=3.2.40>You must leave this.</A><br> -</blockquote> - -<A NAME=speech10><b>MACBETH</b></a> -<blockquote> -<A NAME=3.2.41>O, full of scorpions is my mind, dear wife!</A><br> -<A NAME=3.2.42>Thou know'st that Banquo, and his Fleance, lives.</A><br> -</blockquote> - -<A NAME=speech11><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=3.2.43>But in them nature's copy's not eterne.</A><br> -</blockquote> - -<A NAME=speech12><b>MACBETH</b></a> -<blockquote> -<A NAME=3.2.44>There's comfort yet; they are assailable;</A><br> -<A NAME=3.2.45>Then be thou jocund: ere the bat hath flown</A><br> -<A NAME=3.2.46>His cloister'd flight, ere to black Hecate's summons</A><br> -<A NAME=3.2.47>The shard-borne beetle with his drowsy hums</A><br> -<A NAME=3.2.48>Hath rung night's yawning peal, there shall be done</A><br> -<A NAME=3.2.49>A deed of dreadful note.</A><br> -</blockquote> - -<A NAME=speech13><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=3.2.50>What's to be done?</A><br> -</blockquote> - -<A NAME=speech14><b>MACBETH</b></a> -<blockquote> -<A NAME=3.2.51>Be innocent of the knowledge, dearest chuck,</A><br> -<A NAME=3.2.52>Till thou applaud the deed. Come, seeling night,</A><br> -<A NAME=3.2.53>Scarf up the tender eye of pitiful day;</A><br> -<A NAME=3.2.54>And with thy bloody and invisible hand</A><br> -<A NAME=3.2.55>Cancel and tear to pieces that great bond</A><br> -<A NAME=3.2.56>Which keeps me pale! Light thickens; and the crow</A><br> -<A NAME=3.2.57>Makes wing to the rooky wood:</A><br> -<A NAME=3.2.58>Good things of day begin to droop and drowse;</A><br> -<A NAME=3.2.59>While night's black agents to their preys do rouse.</A><br> -<A NAME=3.2.60>Thou marvell'st at my words: but hold thee still;</A><br> -<A NAME=3.2.61>Things bad begun make strong themselves by ill.</A><br> -<A NAME=3.2.62>So, prithee, go with me.</A><br> -<p><i>Exeunt</i></p> -</blockquote> -<h3>SCENE III. A park near the palace.</h3> -<p><blockquote> -<i>Enter three Murderers</i> -</blockquote> - -<A NAME=speech1><b>First Murderer</b></a> -<blockquote> -<A NAME=3.3.1>But who did bid thee join with us?</A><br> -</blockquote> - -<A NAME=speech2><b>Third Murderer</b></a> -<blockquote> -<A NAME=3.3.2>Macbeth.</A><br> -</blockquote> - -<A NAME=speech3><b>Second Murderer</b></a> -<blockquote> -<A NAME=3.3.3>He needs not our mistrust, since he delivers</A><br> -<A NAME=3.3.4>Our offices and what we have to do</A><br> -<A NAME=3.3.5>To the direction just.</A><br> -</blockquote> - -<A NAME=speech4><b>First Murderer</b></a> -<blockquote> -<A NAME=3.3.6>Then stand with us.</A><br> -<A NAME=3.3.7>The west yet glimmers with some streaks of day:</A><br> -<A NAME=3.3.8>Now spurs the lated traveller apace</A><br> -<A NAME=3.3.9>To gain the timely inn; and near approaches</A><br> -<A NAME=3.3.10>The subject of our watch.</A><br> -</blockquote> - -<A NAME=speech5><b>Third Murderer</b></a> -<blockquote> -<A NAME=3.3.11>Hark! I hear horses.</A><br> -</blockquote> - -<A NAME=speech6><b>BANQUO</b></a> -<blockquote> -<A NAME=3.3.12>[Within] Give us a light there, ho!</A><br> -</blockquote> - -<A NAME=speech7><b>Second Murderer</b></a> -<blockquote> -<A NAME=3.3.13>Then 'tis he: the rest</A><br> -<A NAME=3.3.14>That are within the note of expectation</A><br> -<A NAME=3.3.15>Already are i' the court.</A><br> -</blockquote> - -<A NAME=speech8><b>First Murderer</b></a> -<blockquote> -<A NAME=3.3.16>His horses go about.</A><br> -</blockquote> - -<A NAME=speech9><b>Third Murderer</b></a> -<blockquote> -<A NAME=3.3.17>Almost a mile: but he does usually,</A><br> -<A NAME=3.3.18>So all men do, from hence to the palace gate</A><br> -<A NAME=3.3.19>Make it their walk.</A><br> -</blockquote> - -<A NAME=speech10><b>Second Murderer</b></a> -<blockquote> -<A NAME=3.3.20>A light, a light!</A><br> -<p><i>Enter BANQUO, and FLEANCE with a torch</i></p> -</blockquote> - -<A NAME=speech11><b>Third Murderer</b></a> -<blockquote> -<A NAME=3.3.21>'Tis he.</A><br> -</blockquote> - -<A NAME=speech12><b>First Murderer</b></a> -<blockquote> -<A NAME=3.3.22>Stand to't.</A><br> -</blockquote> - -<A NAME=speech13><b>BANQUO</b></a> -<blockquote> -<A NAME=3.3.23>It will be rain to-night.</A><br> -</blockquote> - -<A NAME=speech14><b>First Murderer</b></a> -<blockquote> -<A NAME=3.3.24>Let it come down.</A><br> -<p><i>They set upon BANQUO</i></p> -</blockquote> - -<A NAME=speech15><b>BANQUO</b></a> -<blockquote> -<A NAME=3.3.25>O, treachery! Fly, good Fleance, fly, fly, fly!</A><br> -<A NAME=3.3.26>Thou mayst revenge. O slave!</A><br> -<p><i>Dies. FLEANCE escapes</i></p> -</blockquote> - -<A NAME=speech16><b>Third Murderer</b></a> -<blockquote> -<A NAME=3.3.27>Who did strike out the light?</A><br> -</blockquote> - -<A NAME=speech17><b>First Murderer</b></a> -<blockquote> -<A NAME=3.3.28>Wast not the way?</A><br> -</blockquote> - -<A NAME=speech18><b>Third Murderer</b></a> -<blockquote> -<A NAME=3.3.29>There's but one down; the son is fled.</A><br> -</blockquote> - -<A NAME=speech19><b>Second Murderer</b></a> -<blockquote> -<A NAME=3.3.30>We have lost</A><br> -<A NAME=3.3.31>Best half of our affair.</A><br> -</blockquote> - -<A NAME=speech20><b>First Murderer</b></a> -<blockquote> -<A NAME=3.3.32>Well, let's away, and say how much is done.</A><br> -<p><i>Exeunt</i></p> -</blockquote> -<h3>SCENE IV. The same. Hall in the palace.</h3> -<p><blockquote> -<i>A banquet prepared. Enter MACBETH, LADY MACBETH, ROSS, LENNOX, Lords, and Attendants</i> -</blockquote> - -<A NAME=speech1><b>MACBETH</b></a> -<blockquote> -<A NAME=3.4.1>You know your own degrees; sit down: at first</A><br> -<A NAME=3.4.2>And last the hearty welcome.</A><br> -</blockquote> - -<A NAME=speech2><b>Lords</b></a> -<blockquote> -<A NAME=3.4.3>Thanks to your majesty.</A><br> -</blockquote> - -<A NAME=speech3><b>MACBETH</b></a> -<blockquote> -<A NAME=3.4.4>Ourself will mingle with society,</A><br> -<A NAME=3.4.5>And play the humble host.</A><br> -<A NAME=3.4.6>Our hostess keeps her state, but in best time</A><br> -<A NAME=3.4.7>We will require her welcome.</A><br> -</blockquote> - -<A NAME=speech4><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=3.4.8>Pronounce it for me, sir, to all our friends;</A><br> -<A NAME=3.4.9>For my heart speaks they are welcome.</A><br> -<p><i>First Murderer appears at the door</i></p> -</blockquote> - -<A NAME=speech5><b>MACBETH</b></a> -<blockquote> -<A NAME=3.4.10>See, they encounter thee with their hearts' thanks.</A><br> -<A NAME=3.4.11>Both sides are even: here I'll sit i' the midst:</A><br> -<A NAME=3.4.12>Be large in mirth; anon we'll drink a measure</A><br> -<A NAME=3.4.13>The table round.</A><br> -<p><i>Approaching the door</i></p> -<A NAME=3.4.14>There's blood on thy face.</A><br> -</blockquote> - -<A NAME=speech6><b>First Murderer</b></a> -<blockquote> -<A NAME=3.4.15>'Tis Banquo's then.</A><br> -</blockquote> - -<A NAME=speech7><b>MACBETH</b></a> -<blockquote> -<A NAME=3.4.16>'Tis better thee without than he within.</A><br> -<A NAME=3.4.17>Is he dispatch'd?</A><br> -</blockquote> - -<A NAME=speech8><b>First Murderer</b></a> -<blockquote> -<A NAME=3.4.18>My lord, his throat is cut; that I did for him.</A><br> -</blockquote> - -<A NAME=speech9><b>MACBETH</b></a> -<blockquote> -<A NAME=3.4.19>Thou art the best o' the cut-throats: yet he's good</A><br> -<A NAME=3.4.20>That did the like for Fleance: if thou didst it,</A><br> -<A NAME=3.4.21>Thou art the nonpareil.</A><br> -</blockquote> - -<A NAME=speech10><b>First Murderer</b></a> -<blockquote> -<A NAME=3.4.22>Most royal sir,</A><br> -<A NAME=3.4.23>Fleance is 'scaped.</A><br> -</blockquote> - -<A NAME=speech11><b>MACBETH</b></a> -<blockquote> -<A NAME=3.4.24>Then comes my fit again: I had else been perfect,</A><br> -<A NAME=3.4.25>Whole as the marble, founded as the rock,</A><br> -<A NAME=3.4.26>As broad and general as the casing air:</A><br> -<A NAME=3.4.27>But now I am cabin'd, cribb'd, confined, bound in</A><br> -<A NAME=3.4.28>To saucy doubts and fears. But Banquo's safe?</A><br> -</blockquote> - -<A NAME=speech12><b>First Murderer</b></a> -<blockquote> -<A NAME=3.4.29>Ay, my good lord: safe in a ditch he bides,</A><br> -<A NAME=3.4.30>With twenty trenched gashes on his head;</A><br> -<A NAME=3.4.31>The least a death to nature.</A><br> -</blockquote> - -<A NAME=speech13><b>MACBETH</b></a> -<blockquote> -<A NAME=3.4.32>Thanks for that:</A><br> -<A NAME=3.4.33>There the grown serpent lies; the worm that's fled</A><br> -<A NAME=3.4.34>Hath nature that in time will venom breed,</A><br> -<A NAME=3.4.35>No teeth for the present. Get thee gone: to-morrow</A><br> -<A NAME=3.4.36>We'll hear, ourselves, again.</A><br> -<p><i>Exit Murderer</i></p> -</blockquote> - -<A NAME=speech14><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=3.4.37>My royal lord,</A><br> -<A NAME=3.4.38>You do not give the cheer: the feast is sold</A><br> -<A NAME=3.4.39>That is not often vouch'd, while 'tis a-making,</A><br> -<A NAME=3.4.40>'Tis given with welcome: to feed were best at home;</A><br> -<A NAME=3.4.41>From thence the sauce to meat is ceremony;</A><br> -<A NAME=3.4.42>Meeting were bare without it.</A><br> -</blockquote> - -<A NAME=speech15><b>MACBETH</b></a> -<blockquote> -<A NAME=3.4.43>Sweet remembrancer!</A><br> -<A NAME=3.4.44>Now, good digestion wait on appetite,</A><br> -<A NAME=3.4.45>And health on both!</A><br> -</blockquote> - -<A NAME=speech16><b>LENNOX</b></a> -<blockquote> -<A NAME=3.4.46>May't please your highness sit.</A><br> -<p><i>The GHOST OF BANQUO enters, and sits in MACBETH's place</i></p> -</blockquote> - -<A NAME=speech17><b>MACBETH</b></a> -<blockquote> -<A NAME=3.4.47>Here had we now our country's honour roof'd,</A><br> -<A NAME=3.4.48>Were the graced person of our Banquo present;</A><br> -<A NAME=3.4.49>Who may I rather challenge for unkindness</A><br> -<A NAME=3.4.50>Than pity for mischance!</A><br> -</blockquote> - -<A NAME=speech18><b>ROSS</b></a> -<blockquote> -<A NAME=3.4.51>His absence, sir,</A><br> -<A NAME=3.4.52>Lays blame upon his promise. Please't your highness</A><br> -<A NAME=3.4.53>To grace us with your royal company.</A><br> -</blockquote> - -<A NAME=speech19><b>MACBETH</b></a> -<blockquote> -<A NAME=3.4.54>The table's full.</A><br> -</blockquote> - -<A NAME=speech20><b>LENNOX</b></a> -<blockquote> -<A NAME=3.4.55> Here is a place reserved, sir.</A><br> -</blockquote> - -<A NAME=speech21><b>MACBETH</b></a> -<blockquote> -<A NAME=3.4.56>Where?</A><br> -</blockquote> - -<A NAME=speech22><b>LENNOX</b></a> -<blockquote> -<A NAME=3.4.57>Here, my good lord. What is't that moves your highness?</A><br> -</blockquote> - -<A NAME=speech23><b>MACBETH</b></a> -<blockquote> -<A NAME=3.4.58>Which of you have done this?</A><br> -</blockquote> - -<A NAME=speech24><b>Lords</b></a> -<blockquote> -<A NAME=3.4.59>What, my good lord?</A><br> -</blockquote> - -<A NAME=speech25><b>MACBETH</b></a> -<blockquote> -<A NAME=3.4.60>Thou canst not say I did it: never shake</A><br> -<A NAME=3.4.61>Thy gory locks at me.</A><br> -</blockquote> - -<A NAME=speech26><b>ROSS</b></a> -<blockquote> -<A NAME=3.4.62>Gentlemen, rise: his highness is not well.</A><br> -</blockquote> - -<A NAME=speech27><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=3.4.63>Sit, worthy friends: my lord is often thus,</A><br> -<A NAME=3.4.64>And hath been from his youth: pray you, keep seat;</A><br> -<A NAME=3.4.65>The fit is momentary; upon a thought</A><br> -<A NAME=3.4.66>He will again be well: if much you note him,</A><br> -<A NAME=3.4.67>You shall offend him and extend his passion:</A><br> -<A NAME=3.4.68>Feed, and regard him not. Are you a man?</A><br> -</blockquote> - -<A NAME=speech28><b>MACBETH</b></a> -<blockquote> -<A NAME=3.4.69>Ay, and a bold one, that dare look on that</A><br> -<A NAME=3.4.70>Which might appal the devil.</A><br> -</blockquote> - -<A NAME=speech29><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=3.4.71>O proper stuff!</A><br> -<A NAME=3.4.72>This is the very painting of your fear:</A><br> -<A NAME=3.4.73>This is the air-drawn dagger which, you said,</A><br> -<A NAME=3.4.74>Led you to Duncan. O, these flaws and starts,</A><br> -<A NAME=3.4.75>Impostors to true fear, would well become</A><br> -<A NAME=3.4.76>A woman's story at a winter's fire,</A><br> -<A NAME=3.4.77>Authorized by her grandam. Shame itself!</A><br> -<A NAME=3.4.78>Why do you make such faces? When all's done,</A><br> -<A NAME=3.4.79>You look but on a stool.</A><br> -</blockquote> - -<A NAME=speech30><b>MACBETH</b></a> -<blockquote> -<A NAME=3.4.80>Prithee, see there! behold! look! lo!</A><br> -<A NAME=3.4.81>how say you?</A><br> -<A NAME=3.4.82>Why, what care I? If thou canst nod, speak too.</A><br> -<A NAME=3.4.83>If charnel-houses and our graves must send</A><br> -<A NAME=3.4.84>Those that we bury back, our monuments</A><br> -<A NAME=3.4.85>Shall be the maws of kites.</A><br> -<p><i>GHOST OF BANQUO vanishes</i></p> -</blockquote> - -<A NAME=speech31><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=3.4.86>What, quite unmann'd in folly?</A><br> -</blockquote> - -<A NAME=speech32><b>MACBETH</b></a> -<blockquote> -<A NAME=3.4.87>If I stand here, I saw him.</A><br> -</blockquote> - -<A NAME=speech33><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=3.4.88>Fie, for shame!</A><br> -</blockquote> - -<A NAME=speech34><b>MACBETH</b></a> -<blockquote> -<A NAME=3.4.89>Blood hath been shed ere now, i' the olden time,</A><br> -<A NAME=3.4.90>Ere human statute purged the gentle weal;</A><br> -<A NAME=3.4.91>Ay, and since too, murders have been perform'd</A><br> -<A NAME=3.4.92>Too terrible for the ear: the times have been,</A><br> -<A NAME=3.4.93>That, when the brains were out, the man would die,</A><br> -<A NAME=3.4.94>And there an end; but now they rise again,</A><br> -<A NAME=3.4.95>With twenty mortal murders on their crowns,</A><br> -<A NAME=3.4.96>And push us from our stools: this is more strange</A><br> -<A NAME=3.4.97>Than such a murder is.</A><br> -</blockquote> - -<A NAME=speech35><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=3.4.98>My worthy lord,</A><br> -<A NAME=3.4.99>Your noble friends do lack you.</A><br> -</blockquote> - -<A NAME=speech36><b>MACBETH</b></a> -<blockquote> -<A NAME=3.4.100>I do forget.</A><br> -<A NAME=3.4.101>Do not muse at me, my most worthy friends,</A><br> -<A NAME=3.4.102>I have a strange infirmity, which is nothing</A><br> -<A NAME=3.4.103>To those that know me. Come, love and health to all;</A><br> -<A NAME=3.4.104>Then I'll sit down. Give me some wine; fill full.</A><br> -<A NAME=3.4.105>I drink to the general joy o' the whole table,</A><br> -<A NAME=3.4.106>And to our dear friend Banquo, whom we miss;</A><br> -<A NAME=3.4.107>Would he were here! to all, and him, we thirst,</A><br> -<A NAME=3.4.108>And all to all.</A><br> -</blockquote> - -<A NAME=speech37><b>Lords</b></a> -<blockquote> -<A NAME=3.4.109> Our duties, and the pledge.</A><br> -<p><i>Re-enter GHOST OF BANQUO</i></p> -</blockquote> - -<A NAME=speech38><b>MACBETH</b></a> -<blockquote> -<A NAME=3.4.110>Avaunt! and quit my sight! let the earth hide thee!</A><br> -<A NAME=3.4.111>Thy bones are marrowless, thy blood is cold;</A><br> -<A NAME=3.4.112>Thou hast no speculation in those eyes</A><br> -<A NAME=3.4.113>Which thou dost glare with!</A><br> -</blockquote> - -<A NAME=speech39><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=3.4.114>Think of this, good peers,</A><br> -<A NAME=3.4.115>But as a thing of custom: 'tis no other;</A><br> -<A NAME=3.4.116>Only it spoils the pleasure of the time.</A><br> -</blockquote> - -<A NAME=speech40><b>MACBETH</b></a> -<blockquote> -<A NAME=3.4.117>What man dare, I dare:</A><br> -<A NAME=3.4.118>Approach thou like the rugged Russian bear,</A><br> -<A NAME=3.4.119>The arm'd rhinoceros, or the Hyrcan tiger;</A><br> -<A NAME=3.4.120>Take any shape but that, and my firm nerves</A><br> -<A NAME=3.4.121>Shall never tremble: or be alive again,</A><br> -<A NAME=3.4.122>And dare me to the desert with thy sword;</A><br> -<A NAME=3.4.123>If trembling I inhabit then, protest me</A><br> -<A NAME=3.4.124>The baby of a girl. Hence, horrible shadow!</A><br> -<A NAME=3.4.125>Unreal mockery, hence!</A><br> -<p><i>GHOST OF BANQUO vanishes</i></p> -<A NAME=3.4.126>Why, so: being gone,</A><br> -<A NAME=3.4.127>I am a man again. Pray you, sit still.</A><br> -</blockquote> - -<A NAME=speech41><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=3.4.128>You have displaced the mirth, broke the good meeting,</A><br> -<A NAME=3.4.129>With most admired disorder.</A><br> -</blockquote> - -<A NAME=speech42><b>MACBETH</b></a> -<blockquote> -<A NAME=3.4.130>Can such things be,</A><br> -<A NAME=3.4.131>And overcome us like a summer's cloud,</A><br> -<A NAME=3.4.132>Without our special wonder? You make me strange</A><br> -<A NAME=3.4.133>Even to the disposition that I owe,</A><br> -<A NAME=3.4.134>When now I think you can behold such sights,</A><br> -<A NAME=3.4.135>And keep the natural ruby of your cheeks,</A><br> -<A NAME=3.4.136>When mine is blanched with fear.</A><br> -</blockquote> - -<A NAME=speech43><b>ROSS</b></a> -<blockquote> -<A NAME=3.4.137>What sights, my lord?</A><br> -</blockquote> - -<A NAME=speech44><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=3.4.138>I pray you, speak not; he grows worse and worse;</A><br> -<A NAME=3.4.139>Question enrages him. At once, good night:</A><br> -<A NAME=3.4.140>Stand not upon the order of your going,</A><br> -<A NAME=3.4.141>But go at once.</A><br> -</blockquote> - -<A NAME=speech45><b>LENNOX</b></a> -<blockquote> -<A NAME=3.4.142> Good night; and better health</A><br> -<A NAME=3.4.143>Attend his majesty!</A><br> -</blockquote> - -<A NAME=speech46><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=3.4.144>A kind good night to all!</A><br> -<p><i>Exeunt all but MACBETH and LADY MACBETH</i></p> -</blockquote> - -<A NAME=speech47><b>MACBETH</b></a> -<blockquote> -<A NAME=3.4.145>It will have blood; they say, blood will have blood:</A><br> -<A NAME=3.4.146>Stones have been known to move and trees to speak;</A><br> -<A NAME=3.4.147>Augurs and understood relations have</A><br> -<A NAME=3.4.148>By magot-pies and choughs and rooks brought forth</A><br> -<A NAME=3.4.149>The secret'st man of blood. What is the night?</A><br> -</blockquote> - -<A NAME=speech48><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=3.4.150>Almost at odds with morning, which is which.</A><br> -</blockquote> - -<A NAME=speech49><b>MACBETH</b></a> -<blockquote> -<A NAME=3.4.151>How say'st thou, that Macduff denies his person</A><br> -<A NAME=3.4.152>At our great bidding?</A><br> -</blockquote> - -<A NAME=speech50><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=3.4.153>Did you send to him, sir?</A><br> -</blockquote> - -<A NAME=speech51><b>MACBETH</b></a> -<blockquote> -<A NAME=3.4.154>I hear it by the way; but I will send:</A><br> -<A NAME=3.4.155>There's not a one of them but in his house</A><br> -<A NAME=3.4.156>I keep a servant fee'd. I will to-morrow,</A><br> -<A NAME=3.4.157>And betimes I will, to the weird sisters:</A><br> -<A NAME=3.4.158>More shall they speak; for now I am bent to know,</A><br> -<A NAME=3.4.159>By the worst means, the worst. For mine own good,</A><br> -<A NAME=3.4.160>All causes shall give way: I am in blood</A><br> -<A NAME=3.4.161>Stepp'd in so far that, should I wade no more,</A><br> -<A NAME=3.4.162>Returning were as tedious as go o'er:</A><br> -<A NAME=3.4.163>Strange things I have in head, that will to hand;</A><br> -<A NAME=3.4.164>Which must be acted ere they may be scann'd.</A><br> -</blockquote> - -<A NAME=speech52><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=3.4.165>You lack the season of all natures, sleep.</A><br> -</blockquote> - -<A NAME=speech53><b>MACBETH</b></a> -<blockquote> -<A NAME=3.4.166>Come, we'll to sleep. My strange and self-abuse</A><br> -<A NAME=3.4.167>Is the initiate fear that wants hard use:</A><br> -<A NAME=3.4.168>We are yet but young in deed.</A><br> -<p><i>Exeunt</i></p> -</blockquote> -<h3>SCENE V. A Heath.</h3> -<p><blockquote> -<i>Thunder. Enter the three Witches meeting HECATE</i> -</blockquote> - -<A NAME=speech1><b>First Witch</b></a> -<blockquote> -<A NAME=3.5.1>Why, how now, Hecate! you look angerly.</A><br> -</blockquote> - -<A NAME=speech2><b>HECATE</b></a> -<blockquote> -<A NAME=3.5.2>Have I not reason, beldams as you are,</A><br> -<A NAME=3.5.3>Saucy and overbold? How did you dare</A><br> -<A NAME=3.5.4>To trade and traffic with Macbeth</A><br> -<A NAME=3.5.5>In riddles and affairs of death;</A><br> -<A NAME=3.5.6>And I, the mistress of your charms,</A><br> -<A NAME=3.5.7>The close contriver of all harms,</A><br> -<A NAME=3.5.8>Was never call'd to bear my part,</A><br> -<A NAME=3.5.9>Or show the glory of our art?</A><br> -<A NAME=3.5.10>And, which is worse, all you have done</A><br> -<A NAME=3.5.11>Hath been but for a wayward son,</A><br> -<A NAME=3.5.12>Spiteful and wrathful, who, as others do,</A><br> -<A NAME=3.5.13>Loves for his own ends, not for you.</A><br> -<A NAME=3.5.14>But make amends now: get you gone,</A><br> -<A NAME=3.5.15>And at the pit of Acheron</A><br> -<A NAME=3.5.16>Meet me i' the morning: thither he</A><br> -<A NAME=3.5.17>Will come to know his destiny:</A><br> -<A NAME=3.5.18>Your vessels and your spells provide,</A><br> -<A NAME=3.5.19>Your charms and every thing beside.</A><br> -<A NAME=3.5.20>I am for the air; this night I'll spend</A><br> -<A NAME=3.5.21>Unto a dismal and a fatal end:</A><br> -<A NAME=3.5.22>Great business must be wrought ere noon:</A><br> -<A NAME=3.5.23>Upon the corner of the moon</A><br> -<A NAME=3.5.24>There hangs a vaporous drop profound;</A><br> -<A NAME=3.5.25>I'll catch it ere it come to ground:</A><br> -<A NAME=3.5.26>And that distill'd by magic sleights</A><br> -<A NAME=3.5.27>Shall raise such artificial sprites</A><br> -<A NAME=3.5.28>As by the strength of their illusion</A><br> -<A NAME=3.5.29>Shall draw him on to his confusion:</A><br> -<A NAME=3.5.30>He shall spurn fate, scorn death, and bear</A><br> -<A NAME=3.5.31>He hopes 'bove wisdom, grace and fear:</A><br> -<A NAME=3.5.32>And you all know, security</A><br> -<A NAME=3.5.33>Is mortals' chiefest enemy.</A><br> -<p><i>Music and a song within: 'Come away, come away,' & c</i></p> -<A NAME=3.5.34>Hark! I am call'd; my little spirit, see,</A><br> -<A NAME=3.5.35>Sits in a foggy cloud, and stays for me.</A><br> -<p><i>Exit</i></p> -</blockquote> - -<A NAME=speech3><b>First Witch</b></a> -<blockquote> -<A NAME=3.5.36>Come, let's make haste; she'll soon be back again.</A><br> -<p><i>Exeunt</i></p> -</blockquote> -<h3>SCENE VI. Forres. The palace.</h3> -<p><blockquote> -<i>Enter LENNOX and another Lord</i> -</blockquote> - -<A NAME=speech1><b>LENNOX</b></a> -<blockquote> -<A NAME=3.6.1>My former speeches have but hit your thoughts,</A><br> -<A NAME=3.6.2>Which can interpret further: only, I say,</A><br> -<A NAME=3.6.3>Things have been strangely borne. The</A><br> -<A NAME=3.6.4>gracious Duncan</A><br> -<A NAME=3.6.5>Was pitied of Macbeth: marry, he was dead:</A><br> -<A NAME=3.6.6>And the right-valiant Banquo walk'd too late;</A><br> -<A NAME=3.6.7>Whom, you may say, if't please you, Fleance kill'd,</A><br> -<A NAME=3.6.8>For Fleance fled: men must not walk too late.</A><br> -<A NAME=3.6.9>Who cannot want the thought how monstrous</A><br> -<A NAME=3.6.10>It was for Malcolm and for Donalbain</A><br> -<A NAME=3.6.11>To kill their gracious father? damned fact!</A><br> -<A NAME=3.6.12>How it did grieve Macbeth! did he not straight</A><br> -<A NAME=3.6.13>In pious rage the two delinquents tear,</A><br> -<A NAME=3.6.14>That were the slaves of drink and thralls of sleep?</A><br> -<A NAME=3.6.15>Was not that nobly done? Ay, and wisely too;</A><br> -<A NAME=3.6.16>For 'twould have anger'd any heart alive</A><br> -<A NAME=3.6.17>To hear the men deny't. So that, I say,</A><br> -<A NAME=3.6.18>He has borne all things well: and I do think</A><br> -<A NAME=3.6.19>That had he Duncan's sons under his key--</A><br> -<A NAME=3.6.20>As, an't please heaven, he shall not--they</A><br> -<A NAME=3.6.21>should find</A><br> -<A NAME=3.6.22>What 'twere to kill a father; so should Fleance.</A><br> -<A NAME=3.6.23>But, peace! for from broad words and 'cause he fail'd</A><br> -<A NAME=3.6.24>His presence at the tyrant's feast, I hear</A><br> -<A NAME=3.6.25>Macduff lives in disgrace: sir, can you tell</A><br> -<A NAME=3.6.26>Where he bestows himself?</A><br> -</blockquote> - -<A NAME=speech2><b>Lord</b></a> -<blockquote> -<A NAME=3.6.27>The son of Duncan,</A><br> -<A NAME=3.6.28>From whom this tyrant holds the due of birth</A><br> -<A NAME=3.6.29>Lives in the English court, and is received</A><br> -<A NAME=3.6.30>Of the most pious Edward with such grace</A><br> -<A NAME=3.6.31>That the malevolence of fortune nothing</A><br> -<A NAME=3.6.32>Takes from his high respect: thither Macduff</A><br> -<A NAME=3.6.33>Is gone to pray the holy king, upon his aid</A><br> -<A NAME=3.6.34>To wake Northumberland and warlike Siward:</A><br> -<A NAME=3.6.35>That, by the help of these--with Him above</A><br> -<A NAME=3.6.36>To ratify the work--we may again</A><br> -<A NAME=3.6.37>Give to our tables meat, sleep to our nights,</A><br> -<A NAME=3.6.38>Free from our feasts and banquets bloody knives,</A><br> -<A NAME=3.6.39>Do faithful homage and receive free honours:</A><br> -<A NAME=3.6.40>All which we pine for now: and this report</A><br> -<A NAME=3.6.41>Hath so exasperate the king that he</A><br> -<A NAME=3.6.42>Prepares for some attempt of war.</A><br> -</blockquote> - -<A NAME=speech3><b>LENNOX</b></a> -<blockquote> -<A NAME=3.6.43>Sent he to Macduff?</A><br> -</blockquote> - -<A NAME=speech4><b>Lord</b></a> -<blockquote> -<A NAME=3.6.44>He did: and with an absolute 'Sir, not I,'</A><br> -<A NAME=3.6.45>The cloudy messenger turns me his back,</A><br> -<A NAME=3.6.46>And hums, as who should say 'You'll rue the time</A><br> -<A NAME=3.6.47>That clogs me with this answer.'</A><br> -</blockquote> - -<A NAME=speech5><b>LENNOX</b></a> -<blockquote> -<A NAME=3.6.48>And that well might</A><br> -<A NAME=3.6.49>Advise him to a caution, to hold what distance</A><br> -<A NAME=3.6.50>His wisdom can provide. Some holy angel</A><br> -<A NAME=3.6.51>Fly to the court of England and unfold</A><br> -<A NAME=3.6.52>His message ere he come, that a swift blessing</A><br> -<A NAME=3.6.53>May soon return to this our suffering country</A><br> -<A NAME=3.6.54>Under a hand accursed!</A><br> -</blockquote> - -<A NAME=speech6><b>Lord</b></a> -<blockquote> -<A NAME=3.6.55>I'll send my prayers with him.</A><br> -<p><i>Exeunt</i></p> -</blockquote><p> -<H3>ACT IV</h3> -<h3>SCENE I. A cavern. In the middle, a boiling cauldron.</h3> -<p><blockquote> -<i>Thunder. Enter the three Witches</i> -</blockquote> - -<A NAME=speech1><b>First Witch</b></a> -<blockquote> -<A NAME=4.1.1>Thrice the brinded cat hath mew'd.</A><br> -</blockquote> - -<A NAME=speech2><b>Second Witch</b></a> -<blockquote> -<A NAME=4.1.2>Thrice and once the hedge-pig whined.</A><br> -</blockquote> - -<A NAME=speech3><b>Third Witch</b></a> -<blockquote> -<A NAME=4.1.3>Harpier cries 'Tis time, 'tis time.</A><br> -</blockquote> - -<A NAME=speech4><b>First Witch</b></a> -<blockquote> -<A NAME=4.1.4>Round about the cauldron go;</A><br> -<A NAME=4.1.5>In the poison'd entrails throw.</A><br> -<A NAME=4.1.6>Toad, that under cold stone</A><br> -<A NAME=4.1.7>Days and nights has thirty-one</A><br> -<A NAME=4.1.8>Swelter'd venom sleeping got,</A><br> -<A NAME=4.1.9>Boil thou first i' the charmed pot.</A><br> -</blockquote> - -<A NAME=speech5><b>ALL</b></a> -<blockquote> -<A NAME=4.1.10>Double, double toil and trouble;</A><br> -<A NAME=4.1.11>Fire burn, and cauldron bubble.</A><br> -</blockquote> - -<A NAME=speech6><b>Second Witch</b></a> -<blockquote> -<A NAME=4.1.12>Fillet of a fenny snake,</A><br> -<A NAME=4.1.13>In the cauldron boil and bake;</A><br> -<A NAME=4.1.14>Eye of newt and toe of frog,</A><br> -<A NAME=4.1.15>Wool of bat and tongue of dog,</A><br> -<A NAME=4.1.16>Adder's fork and blind-worm's sting,</A><br> -<A NAME=4.1.17>Lizard's leg and owlet's wing,</A><br> -<A NAME=4.1.18>For a charm of powerful trouble,</A><br> -<A NAME=4.1.19>Like a hell-broth boil and bubble.</A><br> -</blockquote> - -<A NAME=speech7><b>ALL</b></a> -<blockquote> -<A NAME=4.1.20>Double, double toil and trouble;</A><br> -<A NAME=4.1.21>Fire burn and cauldron bubble.</A><br> -</blockquote> - -<A NAME=speech8><b>Third Witch</b></a> -<blockquote> -<A NAME=4.1.22>Scale of dragon, tooth of wolf,</A><br> -<A NAME=4.1.23>Witches' mummy, maw and gulf</A><br> -<A NAME=4.1.24>Of the ravin'd salt-sea shark,</A><br> -<A NAME=4.1.25>Root of hemlock digg'd i' the dark,</A><br> -<A NAME=4.1.26>Liver of blaspheming Jew,</A><br> -<A NAME=4.1.27>Gall of goat, and slips of yew</A><br> -<A NAME=4.1.28>Silver'd in the moon's eclipse,</A><br> -<A NAME=4.1.29>Nose of Turk and Tartar's lips,</A><br> -<A NAME=4.1.30>Finger of birth-strangled babe</A><br> -<A NAME=4.1.31>Ditch-deliver'd by a drab,</A><br> -<A NAME=4.1.32>Make the gruel thick and slab:</A><br> -<A NAME=4.1.33>Add thereto a tiger's chaudron,</A><br> -<A NAME=4.1.34>For the ingredients of our cauldron.</A><br> -</blockquote> - -<A NAME=speech9><b>ALL</b></a> -<blockquote> -<A NAME=4.1.35>Double, double toil and trouble;</A><br> -<A NAME=4.1.36>Fire burn and cauldron bubble.</A><br> -</blockquote> - -<A NAME=speech10><b>Second Witch</b></a> -<blockquote> -<A NAME=4.1.37>Cool it with a baboon's blood,</A><br> -<A NAME=4.1.38>Then the charm is firm and good.</A><br> -<p><i>Enter HECATE to the other three Witches</i></p> -</blockquote> - -<A NAME=speech11><b>HECATE</b></a> -<blockquote> -<A NAME=4.1.39>O well done! I commend your pains;</A><br> -<A NAME=4.1.40>And every one shall share i' the gains;</A><br> -<A NAME=4.1.41>And now about the cauldron sing,</A><br> -<A NAME=4.1.42>Live elves and fairies in a ring,</A><br> -<A NAME=4.1.43>Enchanting all that you put in.</A><br> -<p><i>Music and a song: 'Black spirits,' & c</i></p> -<p><i>HECATE retires</i></p> -</blockquote> - -<A NAME=speech12><b>Second Witch</b></a> -<blockquote> -<A NAME=4.1.44>By the pricking of my thumbs,</A><br> -<A NAME=4.1.45>Something wicked this way comes.</A><br> -<A NAME=4.1.46>Open, locks,</A><br> -<A NAME=4.1.47>Whoever knocks!</A><br> -<p><i>Enter MACBETH</i></p> -</blockquote> - -<A NAME=speech13><b>MACBETH</b></a> -<blockquote> -<A NAME=4.1.48>How now, you secret, black, and midnight hags!</A><br> -<A NAME=4.1.49>What is't you do?</A><br> -</blockquote> - -<A NAME=speech14><b>ALL</b></a> -<blockquote> -<A NAME=4.1.50> A deed without a name.</A><br> -</blockquote> - -<A NAME=speech15><b>MACBETH</b></a> -<blockquote> -<A NAME=4.1.51>I conjure you, by that which you profess,</A><br> -<A NAME=4.1.52>Howe'er you come to know it, answer me:</A><br> -<A NAME=4.1.53>Though you untie the winds and let them fight</A><br> -<A NAME=4.1.54>Against the churches; though the yesty waves</A><br> -<A NAME=4.1.55>Confound and swallow navigation up;</A><br> -<A NAME=4.1.56>Though bladed corn be lodged and trees blown down;</A><br> -<A NAME=4.1.57>Though castles topple on their warders' heads;</A><br> -<A NAME=4.1.58>Though palaces and pyramids do slope</A><br> -<A NAME=4.1.59>Their heads to their foundations; though the treasure</A><br> -<A NAME=4.1.60>Of nature's germens tumble all together,</A><br> -<A NAME=4.1.61>Even till destruction sicken; answer me</A><br> -<A NAME=4.1.62>To what I ask you.</A><br> -</blockquote> - -<A NAME=speech16><b>First Witch</b></a> -<blockquote> -<A NAME=4.1.63> Speak.</A><br> -</blockquote> - -<A NAME=speech17><b>Second Witch</b></a> -<blockquote> -<A NAME=4.1.64>Demand.</A><br> -</blockquote> - -<A NAME=speech18><b>Third Witch</b></a> -<blockquote> -<A NAME=4.1.65>We'll answer.</A><br> -</blockquote> - -<A NAME=speech19><b>First Witch</b></a> -<blockquote> -<A NAME=4.1.66>Say, if thou'dst rather hear it from our mouths,</A><br> -<A NAME=4.1.67>Or from our masters?</A><br> -</blockquote> - -<A NAME=speech20><b>MACBETH</b></a> -<blockquote> -<A NAME=4.1.68>Call 'em; let me see 'em.</A><br> -</blockquote> - -<A NAME=speech21><b>First Witch</b></a> -<blockquote> -<A NAME=4.1.69>Pour in sow's blood, that hath eaten</A><br> -<A NAME=4.1.70>Her nine farrow; grease that's sweaten</A><br> -<A NAME=4.1.71>From the murderer's gibbet throw</A><br> -<A NAME=4.1.72>Into the flame.</A><br> -</blockquote> - -<A NAME=speech22><b>ALL</b></a> -<blockquote> -<A NAME=4.1.73> Come, high or low;</A><br> -<A NAME=4.1.74>Thyself and office deftly show!</A><br> -<p><i>Thunder. First Apparition: an armed Head</i></p> -</blockquote> - -<A NAME=speech23><b>MACBETH</b></a> -<blockquote> -<A NAME=4.1.75>Tell me, thou unknown power,--</A><br> -</blockquote> - -<A NAME=speech24><b>First Witch</b></a> -<blockquote> -<A NAME=4.1.76>He knows thy thought:</A><br> -<A NAME=4.1.77>Hear his speech, but say thou nought.</A><br> -</blockquote> - -<A NAME=speech25><b>First Apparition</b></a> -<blockquote> -<A NAME=4.1.78>Macbeth! Macbeth! Macbeth! beware Macduff;</A><br> -<A NAME=4.1.79>Beware the thane of Fife. Dismiss me. Enough.</A><br> -<p><i>Descends</i></p> -</blockquote> - -<A NAME=speech26><b>MACBETH</b></a> -<blockquote> -<A NAME=4.1.80>Whate'er thou art, for thy good caution, thanks;</A><br> -<A NAME=4.1.81>Thou hast harp'd my fear aright: but one</A><br> -<A NAME=4.1.82>word more,--</A><br> -</blockquote> - -<A NAME=speech27><b>First Witch</b></a> -<blockquote> -<A NAME=4.1.83>He will not be commanded: here's another,</A><br> -<A NAME=4.1.84>More potent than the first.</A><br> -<p><i>Thunder. Second Apparition: A bloody Child</i></p> -</blockquote> - -<A NAME=speech28><b>Second Apparition</b></a> -<blockquote> -<A NAME=4.1.85>Macbeth! Macbeth! Macbeth!</A><br> -</blockquote> - -<A NAME=speech29><b>MACBETH</b></a> -<blockquote> -<A NAME=4.1.86>Had I three ears, I'ld hear thee.</A><br> -</blockquote> - -<A NAME=speech30><b>Second Apparition</b></a> -<blockquote> -<A NAME=4.1.87>Be bloody, bold, and resolute; laugh to scorn</A><br> -<A NAME=4.1.88>The power of man, for none of woman born</A><br> -<A NAME=4.1.89>Shall harm Macbeth.</A><br> -<p><i>Descends</i></p> -</blockquote> - -<A NAME=speech31><b>MACBETH</b></a> -<blockquote> -<A NAME=4.1.90>Then live, Macduff: what need I fear of thee?</A><br> -<A NAME=4.1.91>But yet I'll make assurance double sure,</A><br> -<A NAME=4.1.92>And take a bond of fate: thou shalt not live;</A><br> -<A NAME=4.1.93>That I may tell pale-hearted fear it lies,</A><br> -<A NAME=4.1.94>And sleep in spite of thunder.</A><br> -<p><i>Thunder. Third Apparition: a Child crowned, with a tree in his hand</i></p> -<A NAME=4.1.95>What is this</A><br> -<A NAME=4.1.96>That rises like the issue of a king,</A><br> -<A NAME=4.1.97>And wears upon his baby-brow the round</A><br> -<A NAME=4.1.98>And top of sovereignty?</A><br> -</blockquote> - -<A NAME=speech32><b>ALL</b></a> -<blockquote> -<A NAME=4.1.99>Listen, but speak not to't.</A><br> -</blockquote> - -<A NAME=speech33><b>Third Apparition</b></a> -<blockquote> -<A NAME=4.1.100>Be lion-mettled, proud; and take no care</A><br> -<A NAME=4.1.101>Who chafes, who frets, or where conspirers are:</A><br> -<A NAME=4.1.102>Macbeth shall never vanquish'd be until</A><br> -<A NAME=4.1.103>Great Birnam wood to high Dunsinane hill</A><br> -<A NAME=4.1.104>Shall come against him.</A><br> -<p><i>Descends</i></p> -</blockquote> - -<A NAME=speech34><b>MACBETH</b></a> -<blockquote> -<A NAME=4.1.105>That will never be</A><br> -<A NAME=4.1.106>Who can impress the forest, bid the tree</A><br> -<A NAME=4.1.107>Unfix his earth-bound root? Sweet bodements! good!</A><br> -<A NAME=4.1.108>Rebellion's head, rise never till the wood</A><br> -<A NAME=4.1.109>Of Birnam rise, and our high-placed Macbeth</A><br> -<A NAME=4.1.110>Shall live the lease of nature, pay his breath</A><br> -<A NAME=4.1.111>To time and mortal custom. Yet my heart</A><br> -<A NAME=4.1.112>Throbs to know one thing: tell me, if your art</A><br> -<A NAME=4.1.113>Can tell so much: shall Banquo's issue ever</A><br> -<A NAME=4.1.114>Reign in this kingdom?</A><br> -</blockquote> - -<A NAME=speech35><b>ALL</b></a> -<blockquote> -<A NAME=4.1.115>Seek to know no more.</A><br> -</blockquote> - -<A NAME=speech36><b>MACBETH</b></a> -<blockquote> -<A NAME=4.1.116>I will be satisfied: deny me this,</A><br> -<A NAME=4.1.117>And an eternal curse fall on you! Let me know.</A><br> -<A NAME=4.1.118>Why sinks that cauldron? and what noise is this?</A><br> -<p><i>Hautboys</i></p> -</blockquote> - -<A NAME=speech37><b>First Witch</b></a> -<blockquote> -<A NAME=4.1.119>Show!</A><br> -</blockquote> - -<A NAME=speech38><b>Second Witch</b></a> -<blockquote> -<A NAME=4.1.120>Show!</A><br> -</blockquote> - -<A NAME=speech39><b>Third Witch</b></a> -<blockquote> -<A NAME=4.1.121>Show!</A><br> -</blockquote> - -<A NAME=speech40><b>ALL</b></a> -<blockquote> -<A NAME=4.1.122>Show his eyes, and grieve his heart;</A><br> -<A NAME=4.1.123>Come like shadows, so depart!</A><br> -<p><i>A show of Eight Kings, the last with a glass in his hand; GHOST OF BANQUO following</i></p> -</blockquote> - -<A NAME=speech41><b>MACBETH</b></a> -<blockquote> -<A NAME=4.1.124>Thou art too like the spirit of Banquo: down!</A><br> -<A NAME=4.1.125>Thy crown does sear mine eye-balls. And thy hair,</A><br> -<A NAME=4.1.126>Thou other gold-bound brow, is like the first.</A><br> -<A NAME=4.1.127>A third is like the former. Filthy hags!</A><br> -<A NAME=4.1.128>Why do you show me this? A fourth! Start, eyes!</A><br> -<A NAME=4.1.129>What, will the line stretch out to the crack of doom?</A><br> -<A NAME=4.1.130>Another yet! A seventh! I'll see no more:</A><br> -<A NAME=4.1.131>And yet the eighth appears, who bears a glass</A><br> -<A NAME=4.1.132>Which shows me many more; and some I see</A><br> -<A NAME=4.1.133>That two-fold balls and treble scepters carry:</A><br> -<A NAME=4.1.134>Horrible sight! Now, I see, 'tis true;</A><br> -<A NAME=4.1.135>For the blood-bolter'd Banquo smiles upon me,</A><br> -<A NAME=4.1.136>And points at them for his.</A><br> -<p><i>Apparitions vanish</i></p> -<A NAME=4.1.137>What, is this so?</A><br> -</blockquote> - -<A NAME=speech42><b>First Witch</b></a> -<blockquote> -<A NAME=4.1.138>Ay, sir, all this is so: but why</A><br> -<A NAME=4.1.139>Stands Macbeth thus amazedly?</A><br> -<A NAME=4.1.140>Come, sisters, cheer we up his sprites,</A><br> -<A NAME=4.1.141>And show the best of our delights:</A><br> -<A NAME=4.1.142>I'll charm the air to give a sound,</A><br> -<A NAME=4.1.143>While you perform your antic round:</A><br> -<A NAME=4.1.144>That this great king may kindly say,</A><br> -<A NAME=4.1.145>Our duties did his welcome pay.</A><br> -<p><i>Music. The witches dance and then vanish, with HECATE</i></p> -</blockquote> - -<A NAME=speech43><b>MACBETH</b></a> -<blockquote> -<A NAME=4.1.146>Where are they? Gone? Let this pernicious hour</A><br> -<A NAME=4.1.147>Stand aye accursed in the calendar!</A><br> -<A NAME=4.1.148>Come in, without there!</A><br> -<p><i>Enter LENNOX</i></p> -</blockquote> - -<A NAME=speech44><b>LENNOX</b></a> -<blockquote> -<A NAME=4.1.149>What's your grace's will?</A><br> -</blockquote> - -<A NAME=speech45><b>MACBETH</b></a> -<blockquote> -<A NAME=4.1.150>Saw you the weird sisters?</A><br> -</blockquote> - -<A NAME=speech46><b>LENNOX</b></a> -<blockquote> -<A NAME=4.1.151>No, my lord.</A><br> -</blockquote> - -<A NAME=speech47><b>MACBETH</b></a> -<blockquote> -<A NAME=4.1.152>Came they not by you?</A><br> -</blockquote> - -<A NAME=speech48><b>LENNOX</b></a> -<blockquote> -<A NAME=4.1.153>No, indeed, my lord.</A><br> -</blockquote> - -<A NAME=speech49><b>MACBETH</b></a> -<blockquote> -<A NAME=4.1.154>Infected be the air whereon they ride;</A><br> -<A NAME=4.1.155>And damn'd all those that trust them! I did hear</A><br> -<A NAME=4.1.156>The galloping of horse: who was't came by?</A><br> -</blockquote> - -<A NAME=speech50><b>LENNOX</b></a> -<blockquote> -<A NAME=4.1.157>'Tis two or three, my lord, that bring you word</A><br> -<A NAME=4.1.158>Macduff is fled to England.</A><br> -</blockquote> - -<A NAME=speech51><b>MACBETH</b></a> -<blockquote> -<A NAME=4.1.159>Fled to England!</A><br> -</blockquote> - -<A NAME=speech52><b>LENNOX</b></a> -<blockquote> -<A NAME=4.1.160>Ay, my good lord.</A><br> -</blockquote> - -<A NAME=speech53><b>MACBETH</b></a> -<blockquote> -<A NAME=4.1.161>Time, thou anticipatest my dread exploits:</A><br> -<A NAME=4.1.162>The flighty purpose never is o'ertook</A><br> -<A NAME=4.1.163>Unless the deed go with it; from this moment</A><br> -<A NAME=4.1.164>The very firstlings of my heart shall be</A><br> -<A NAME=4.1.165>The firstlings of my hand. And even now,</A><br> -<A NAME=4.1.166>To crown my thoughts with acts, be it thought and done:</A><br> -<A NAME=4.1.167>The castle of Macduff I will surprise;</A><br> -<A NAME=4.1.168>Seize upon Fife; give to the edge o' the sword</A><br> -<A NAME=4.1.169>His wife, his babes, and all unfortunate souls</A><br> -<A NAME=4.1.170>That trace him in his line. No boasting like a fool;</A><br> -<A NAME=4.1.171>This deed I'll do before this purpose cool.</A><br> -<A NAME=4.1.172>But no more sights!--Where are these gentlemen?</A><br> -<A NAME=4.1.173>Come, bring me where they are.</A><br> -<p><i>Exeunt</i></p> -</blockquote> -<h3>SCENE II. Fife. Macduff's castle.</h3> -<p><blockquote> -<i>Enter LADY MACDUFF, her Son, and ROSS</i> -</blockquote> - -<A NAME=speech1><b>LADY MACDUFF</b></a> -<blockquote> -<A NAME=4.2.1>What had he done, to make him fly the land?</A><br> -</blockquote> - -<A NAME=speech2><b>ROSS</b></a> -<blockquote> -<A NAME=4.2.2>You must have patience, madam.</A><br> -</blockquote> - -<A NAME=speech3><b>LADY MACDUFF</b></a> -<blockquote> -<A NAME=4.2.3>He had none:</A><br> -<A NAME=4.2.4>His flight was madness: when our actions do not,</A><br> -<A NAME=4.2.5>Our fears do make us traitors.</A><br> -</blockquote> - -<A NAME=speech4><b>ROSS</b></a> -<blockquote> -<A NAME=4.2.6>You know not</A><br> -<A NAME=4.2.7>Whether it was his wisdom or his fear.</A><br> -</blockquote> - -<A NAME=speech5><b>LADY MACDUFF</b></a> -<blockquote> -<A NAME=4.2.8>Wisdom! to leave his wife, to leave his babes,</A><br> -<A NAME=4.2.9>His mansion and his titles in a place</A><br> -<A NAME=4.2.10>From whence himself does fly? He loves us not;</A><br> -<A NAME=4.2.11>He wants the natural touch: for the poor wren,</A><br> -<A NAME=4.2.12>The most diminutive of birds, will fight,</A><br> -<A NAME=4.2.13>Her young ones in her nest, against the owl.</A><br> -<A NAME=4.2.14>All is the fear and nothing is the love;</A><br> -<A NAME=4.2.15>As little is the wisdom, where the flight</A><br> -<A NAME=4.2.16>So runs against all reason.</A><br> -</blockquote> - -<A NAME=speech6><b>ROSS</b></a> -<blockquote> -<A NAME=4.2.17>My dearest coz,</A><br> -<A NAME=4.2.18>I pray you, school yourself: but for your husband,</A><br> -<A NAME=4.2.19>He is noble, wise, judicious, and best knows</A><br> -<A NAME=4.2.20>The fits o' the season. I dare not speak</A><br> -<A NAME=4.2.21>much further;</A><br> -<A NAME=4.2.22>But cruel are the times, when we are traitors</A><br> -<A NAME=4.2.23>And do not know ourselves, when we hold rumour</A><br> -<A NAME=4.2.24>From what we fear, yet know not what we fear,</A><br> -<A NAME=4.2.25>But float upon a wild and violent sea</A><br> -<A NAME=4.2.26>Each way and move. I take my leave of you:</A><br> -<A NAME=4.2.27>Shall not be long but I'll be here again:</A><br> -<A NAME=4.2.28>Things at the worst will cease, or else climb upward</A><br> -<A NAME=4.2.29>To what they were before. My pretty cousin,</A><br> -<A NAME=4.2.30>Blessing upon you!</A><br> -</blockquote> - -<A NAME=speech7><b>LADY MACDUFF</b></a> -<blockquote> -<A NAME=4.2.31>Father'd he is, and yet he's fatherless.</A><br> -</blockquote> - -<A NAME=speech8><b>ROSS</b></a> -<blockquote> -<A NAME=4.2.32>I am so much a fool, should I stay longer,</A><br> -<A NAME=4.2.33>It would be my disgrace and your discomfort:</A><br> -<A NAME=4.2.34>I take my leave at once.</A><br> -<p><i>Exit</i></p> -</blockquote> - -<A NAME=speech9><b>LADY MACDUFF</b></a> -<blockquote> -<A NAME=4.2.35>Sirrah, your father's dead;</A><br> -<A NAME=4.2.36>And what will you do now? How will you live?</A><br> -</blockquote> - -<A NAME=speech10><b>Son</b></a> -<blockquote> -<A NAME=4.2.37>As birds do, mother.</A><br> -</blockquote> - -<A NAME=speech11><b>LADY MACDUFF</b></a> -<blockquote> -<A NAME=4.2.38>What, with worms and flies?</A><br> -</blockquote> - -<A NAME=speech12><b>Son</b></a> -<blockquote> -<A NAME=4.2.39>With what I get, I mean; and so do they.</A><br> -</blockquote> - -<A NAME=speech13><b>LADY MACDUFF</b></a> -<blockquote> -<A NAME=4.2.40>Poor bird! thou'ldst never fear the net nor lime,</A><br> -<A NAME=4.2.41>The pitfall nor the gin.</A><br> -</blockquote> - -<A NAME=speech14><b>Son</b></a> -<blockquote> -<A NAME=4.2.42>Why should I, mother? Poor birds they are not set for.</A><br> -<A NAME=4.2.43>My father is not dead, for all your saying.</A><br> -</blockquote> - -<A NAME=speech15><b>LADY MACDUFF</b></a> -<blockquote> -<A NAME=4.2.44>Yes, he is dead; how wilt thou do for a father?</A><br> -</blockquote> - -<A NAME=speech16><b>Son</b></a> -<blockquote> -<A NAME=4.2.45>Nay, how will you do for a husband?</A><br> -</blockquote> - -<A NAME=speech17><b>LADY MACDUFF</b></a> -<blockquote> -<A NAME=4.2.46>Why, I can buy me twenty at any market.</A><br> -</blockquote> - -<A NAME=speech18><b>Son</b></a> -<blockquote> -<A NAME=4.2.47>Then you'll buy 'em to sell again.</A><br> -</blockquote> - -<A NAME=speech19><b>LADY MACDUFF</b></a> -<blockquote> -<A NAME=4.2.48>Thou speak'st with all thy wit: and yet, i' faith,</A><br> -<A NAME=4.2.49>With wit enough for thee.</A><br> -</blockquote> - -<A NAME=speech20><b>Son</b></a> -<blockquote> -<A NAME=4.2.50>Was my father a traitor, mother?</A><br> -</blockquote> - -<A NAME=speech21><b>LADY MACDUFF</b></a> -<blockquote> -<A NAME=4.2.51>Ay, that he was.</A><br> -</blockquote> - -<A NAME=speech22><b>Son</b></a> -<blockquote> -<A NAME=4.2.52>What is a traitor?</A><br> -</blockquote> - -<A NAME=speech23><b>LADY MACDUFF</b></a> -<blockquote> -<A NAME=4.2.53>Why, one that swears and lies.</A><br> -</blockquote> - -<A NAME=speech24><b>Son</b></a> -<blockquote> -<A NAME=4.2.54>And be all traitors that do so?</A><br> -</blockquote> - -<A NAME=speech25><b>LADY MACDUFF</b></a> -<blockquote> -<A NAME=4.2.55>Every one that does so is a traitor, and must be hanged.</A><br> -</blockquote> - -<A NAME=speech26><b>Son</b></a> -<blockquote> -<A NAME=4.2.56>And must they all be hanged that swear and lie?</A><br> -</blockquote> - -<A NAME=speech27><b>LADY MACDUFF</b></a> -<blockquote> -<A NAME=4.2.57>Every one.</A><br> -</blockquote> - -<A NAME=speech28><b>Son</b></a> -<blockquote> -<A NAME=4.2.58>Who must hang them?</A><br> -</blockquote> - -<A NAME=speech29><b>LADY MACDUFF</b></a> -<blockquote> -<A NAME=4.2.59>Why, the honest men.</A><br> -</blockquote> - -<A NAME=speech30><b>Son</b></a> -<blockquote> -<A NAME=4.2.60>Then the liars and swearers are fools,</A><br> -<A NAME=4.2.61>for there are liars and swearers enow to beat</A><br> -<A NAME=4.2.62>the honest men and hang up them.</A><br> -</blockquote> - -<A NAME=speech31><b>LADY MACDUFF</b></a> -<blockquote> -<A NAME=4.2.63>Now, God help thee, poor monkey!</A><br> -<A NAME=4.2.64>But how wilt thou do for a father?</A><br> -</blockquote> - -<A NAME=speech32><b>Son</b></a> -<blockquote> -<A NAME=4.2.65>If he were dead, you'ld weep for</A><br> -<A NAME=4.2.66>him: if you would not, it were a good sign</A><br> -<A NAME=4.2.67>that I should quickly have a new father.</A><br> -</blockquote> - -<A NAME=speech33><b>LADY MACDUFF</b></a> -<blockquote> -<A NAME=4.2.68>Poor prattler, how thou talk'st!</A><br> -<p><i>Enter a Messenger</i></p> -</blockquote> - -<A NAME=speech34><b>Messenger</b></a> -<blockquote> -<A NAME=4.2.69>Bless you, fair dame! I am not to you known,</A><br> -<A NAME=4.2.70>Though in your state of honour I am perfect.</A><br> -<A NAME=4.2.71>I doubt some danger does approach you nearly:</A><br> -<A NAME=4.2.72>If you will take a homely man's advice,</A><br> -<A NAME=4.2.73>Be not found here; hence, with your little ones.</A><br> -<A NAME=4.2.74>To fright you thus, methinks, I am too savage;</A><br> -<A NAME=4.2.75>To do worse to you were fell cruelty,</A><br> -<A NAME=4.2.76>Which is too nigh your person. Heaven preserve you!</A><br> -<A NAME=4.2.77>I dare abide no longer.</A><br> -<p><i>Exit</i></p> -</blockquote> - -<A NAME=speech35><b>LADY MACDUFF</b></a> -<blockquote> -<A NAME=4.2.78>Whither should I fly?</A><br> -<A NAME=4.2.79>I have done no harm. But I remember now</A><br> -<A NAME=4.2.80>I am in this earthly world; where to do harm</A><br> -<A NAME=4.2.81>Is often laudable, to do good sometime</A><br> -<A NAME=4.2.82>Accounted dangerous folly: why then, alas,</A><br> -<A NAME=4.2.83>Do I put up that womanly defence,</A><br> -<A NAME=4.2.84>To say I have done no harm?</A><br> -<p><i>Enter Murderers</i></p> -<A NAME=4.2.85>What are these faces?</A><br> -</blockquote> - -<A NAME=speech36><b>First Murderer</b></a> -<blockquote> -<A NAME=4.2.86>Where is your husband?</A><br> -</blockquote> - -<A NAME=speech37><b>LADY MACDUFF</b></a> -<blockquote> -<A NAME=4.2.87>I hope, in no place so unsanctified</A><br> -<A NAME=4.2.88>Where such as thou mayst find him.</A><br> -</blockquote> - -<A NAME=speech38><b>First Murderer</b></a> -<blockquote> -<A NAME=4.2.89>He's a traitor.</A><br> -</blockquote> - -<A NAME=speech39><b>Son</b></a> -<blockquote> -<A NAME=4.2.90>Thou liest, thou shag-hair'd villain!</A><br> -</blockquote> - -<A NAME=speech40><b>First Murderer</b></a> -<blockquote> -<A NAME=4.2.91>What, you egg!</A><br> -<p><i>Stabbing him</i></p> -<A NAME=4.2.92>Young fry of treachery!</A><br> -</blockquote> - -<A NAME=speech41><b>Son</b></a> -<blockquote> -<A NAME=4.2.93>He has kill'd me, mother:</A><br> -<A NAME=4.2.94>Run away, I pray you!</A><br> -<p><i>Dies</i></p> -<p><i>Exit LADY MACDUFF, crying 'Murder!' Exeunt Murderers, following her</i></p> -</blockquote> -<h3>SCENE III. England. Before the King's palace.</h3> -<p><blockquote> -<i>Enter MALCOLM and MACDUFF</i> -</blockquote> - -<A NAME=speech1><b>MALCOLM</b></a> -<blockquote> -<A NAME=4.3.1>Let us seek out some desolate shade, and there</A><br> -<A NAME=4.3.2>Weep our sad bosoms empty.</A><br> -</blockquote> - -<A NAME=speech2><b>MACDUFF</b></a> -<blockquote> -<A NAME=4.3.3>Let us rather</A><br> -<A NAME=4.3.4>Hold fast the mortal sword, and like good men</A><br> -<A NAME=4.3.5>Bestride our down-fall'n birthdom: each new morn</A><br> -<A NAME=4.3.6>New widows howl, new orphans cry, new sorrows</A><br> -<A NAME=4.3.7>Strike heaven on the face, that it resounds</A><br> -<A NAME=4.3.8>As if it felt with Scotland and yell'd out</A><br> -<A NAME=4.3.9>Like syllable of dolour.</A><br> -</blockquote> - -<A NAME=speech3><b>MALCOLM</b></a> -<blockquote> -<A NAME=4.3.10>What I believe I'll wail,</A><br> -<A NAME=4.3.11>What know believe, and what I can redress,</A><br> -<A NAME=4.3.12>As I shall find the time to friend, I will.</A><br> -<A NAME=4.3.13>What you have spoke, it may be so perchance.</A><br> -<A NAME=4.3.14>This tyrant, whose sole name blisters our tongues,</A><br> -<A NAME=4.3.15>Was once thought honest: you have loved him well.</A><br> -<A NAME=4.3.16>He hath not touch'd you yet. I am young;</A><br> -<A NAME=4.3.17>but something</A><br> -<A NAME=4.3.18>You may deserve of him through me, and wisdom</A><br> -<A NAME=4.3.19>To offer up a weak poor innocent lamb</A><br> -<A NAME=4.3.20>To appease an angry god.</A><br> -</blockquote> - -<A NAME=speech4><b>MACDUFF</b></a> -<blockquote> -<A NAME=4.3.21>I am not treacherous.</A><br> -</blockquote> - -<A NAME=speech5><b>MALCOLM</b></a> -<blockquote> -<A NAME=4.3.22>But Macbeth is.</A><br> -<A NAME=4.3.23>A good and virtuous nature may recoil</A><br> -<A NAME=4.3.24>In an imperial charge. But I shall crave</A><br> -<A NAME=4.3.25>your pardon;</A><br> -<A NAME=4.3.26>That which you are my thoughts cannot transpose:</A><br> -<A NAME=4.3.27>Angels are bright still, though the brightest fell;</A><br> -<A NAME=4.3.28>Though all things foul would wear the brows of grace,</A><br> -<A NAME=4.3.29>Yet grace must still look so.</A><br> -</blockquote> - -<A NAME=speech6><b>MACDUFF</b></a> -<blockquote> -<A NAME=4.3.30>I have lost my hopes.</A><br> -</blockquote> - -<A NAME=speech7><b>MALCOLM</b></a> -<blockquote> -<A NAME=4.3.31>Perchance even there where I did find my doubts.</A><br> -<A NAME=4.3.32>Why in that rawness left you wife and child,</A><br> -<A NAME=4.3.33>Those precious motives, those strong knots of love,</A><br> -<A NAME=4.3.34>Without leave-taking? I pray you,</A><br> -<A NAME=4.3.35>Let not my jealousies be your dishonours,</A><br> -<A NAME=4.3.36>But mine own safeties. You may be rightly just,</A><br> -<A NAME=4.3.37>Whatever I shall think.</A><br> -</blockquote> - -<A NAME=speech8><b>MACDUFF</b></a> -<blockquote> -<A NAME=4.3.38>Bleed, bleed, poor country!</A><br> -<A NAME=4.3.39>Great tyranny! lay thou thy basis sure,</A><br> -<A NAME=4.3.40>For goodness dare not cheque thee: wear thou</A><br> -<A NAME=4.3.41>thy wrongs;</A><br> -<A NAME=4.3.42>The title is affeer'd! Fare thee well, lord:</A><br> -<A NAME=4.3.43>I would not be the villain that thou think'st</A><br> -<A NAME=4.3.44>For the whole space that's in the tyrant's grasp,</A><br> -<A NAME=4.3.45>And the rich East to boot.</A><br> -</blockquote> - -<A NAME=speech9><b>MALCOLM</b></a> -<blockquote> -<A NAME=4.3.46>Be not offended:</A><br> -<A NAME=4.3.47>I speak not as in absolute fear of you.</A><br> -<A NAME=4.3.48>I think our country sinks beneath the yoke;</A><br> -<A NAME=4.3.49>It weeps, it bleeds; and each new day a gash</A><br> -<A NAME=4.3.50>Is added to her wounds: I think withal</A><br> -<A NAME=4.3.51>There would be hands uplifted in my right;</A><br> -<A NAME=4.3.52>And here from gracious England have I offer</A><br> -<A NAME=4.3.53>Of goodly thousands: but, for all this,</A><br> -<A NAME=4.3.54>When I shall tread upon the tyrant's head,</A><br> -<A NAME=4.3.55>Or wear it on my sword, yet my poor country</A><br> -<A NAME=4.3.56>Shall have more vices than it had before,</A><br> -<A NAME=4.3.57>More suffer and more sundry ways than ever,</A><br> -<A NAME=4.3.58>By him that shall succeed.</A><br> -</blockquote> - -<A NAME=speech10><b>MACDUFF</b></a> -<blockquote> -<A NAME=4.3.59>What should he be?</A><br> -</blockquote> - -<A NAME=speech11><b>MALCOLM</b></a> -<blockquote> -<A NAME=4.3.60>It is myself I mean: in whom I know</A><br> -<A NAME=4.3.61>All the particulars of vice so grafted</A><br> -<A NAME=4.3.62>That, when they shall be open'd, black Macbeth</A><br> -<A NAME=4.3.63>Will seem as pure as snow, and the poor state</A><br> -<A NAME=4.3.64>Esteem him as a lamb, being compared</A><br> -<A NAME=4.3.65>With my confineless harms.</A><br> -</blockquote> - -<A NAME=speech12><b>MACDUFF</b></a> -<blockquote> -<A NAME=4.3.66>Not in the legions</A><br> -<A NAME=4.3.67>Of horrid hell can come a devil more damn'd</A><br> -<A NAME=4.3.68>In evils to top Macbeth.</A><br> -</blockquote> - -<A NAME=speech13><b>MALCOLM</b></a> -<blockquote> -<A NAME=4.3.69>I grant him bloody,</A><br> -<A NAME=4.3.70>Luxurious, avaricious, false, deceitful,</A><br> -<A NAME=4.3.71>Sudden, malicious, smacking of every sin</A><br> -<A NAME=4.3.72>That has a name: but there's no bottom, none,</A><br> -<A NAME=4.3.73>In my voluptuousness: your wives, your daughters,</A><br> -<A NAME=4.3.74>Your matrons and your maids, could not fill up</A><br> -<A NAME=4.3.75>The cistern of my lust, and my desire</A><br> -<A NAME=4.3.76>All continent impediments would o'erbear</A><br> -<A NAME=4.3.77>That did oppose my will: better Macbeth</A><br> -<A NAME=4.3.78>Than such an one to reign.</A><br> -</blockquote> - -<A NAME=speech14><b>MACDUFF</b></a> -<blockquote> -<A NAME=4.3.79>Boundless intemperance</A><br> -<A NAME=4.3.80>In nature is a tyranny; it hath been</A><br> -<A NAME=4.3.81>The untimely emptying of the happy throne</A><br> -<A NAME=4.3.82>And fall of many kings. But fear not yet</A><br> -<A NAME=4.3.83>To take upon you what is yours: you may</A><br> -<A NAME=4.3.84>Convey your pleasures in a spacious plenty,</A><br> -<A NAME=4.3.85>And yet seem cold, the time you may so hoodwink.</A><br> -<A NAME=4.3.86>We have willing dames enough: there cannot be</A><br> -<A NAME=4.3.87>That vulture in you, to devour so many</A><br> -<A NAME=4.3.88>As will to greatness dedicate themselves,</A><br> -<A NAME=4.3.89>Finding it so inclined.</A><br> -</blockquote> - -<A NAME=speech15><b>MALCOLM</b></a> -<blockquote> -<A NAME=4.3.90>With this there grows</A><br> -<A NAME=4.3.91>In my most ill-composed affection such</A><br> -<A NAME=4.3.92>A stanchless avarice that, were I king,</A><br> -<A NAME=4.3.93>I should cut off the nobles for their lands,</A><br> -<A NAME=4.3.94>Desire his jewels and this other's house:</A><br> -<A NAME=4.3.95>And my more-having would be as a sauce</A><br> -<A NAME=4.3.96>To make me hunger more; that I should forge</A><br> -<A NAME=4.3.97>Quarrels unjust against the good and loyal,</A><br> -<A NAME=4.3.98>Destroying them for wealth.</A><br> -</blockquote> - -<A NAME=speech16><b>MACDUFF</b></a> -<blockquote> -<A NAME=4.3.99>This avarice</A><br> -<A NAME=4.3.100>Sticks deeper, grows with more pernicious root</A><br> -<A NAME=4.3.101>Than summer-seeming lust, and it hath been</A><br> -<A NAME=4.3.102>The sword of our slain kings: yet do not fear;</A><br> -<A NAME=4.3.103>Scotland hath foisons to fill up your will.</A><br> -<A NAME=4.3.104>Of your mere own: all these are portable,</A><br> -<A NAME=4.3.105>With other graces weigh'd.</A><br> -</blockquote> - -<A NAME=speech17><b>MALCOLM</b></a> -<blockquote> -<A NAME=4.3.106>But I have none: the king-becoming graces,</A><br> -<A NAME=4.3.107>As justice, verity, temperance, stableness,</A><br> -<A NAME=4.3.108>Bounty, perseverance, mercy, lowliness,</A><br> -<A NAME=4.3.109>Devotion, patience, courage, fortitude,</A><br> -<A NAME=4.3.110>I have no relish of them, but abound</A><br> -<A NAME=4.3.111>In the division of each several crime,</A><br> -<A NAME=4.3.112>Acting it many ways. Nay, had I power, I should</A><br> -<A NAME=4.3.113>Pour the sweet milk of concord into hell,</A><br> -<A NAME=4.3.114>Uproar the universal peace, confound</A><br> -<A NAME=4.3.115>All unity on earth.</A><br> -</blockquote> - -<A NAME=speech18><b>MACDUFF</b></a> -<blockquote> -<A NAME=4.3.116>O Scotland, Scotland!</A><br> -</blockquote> - -<A NAME=speech19><b>MALCOLM</b></a> -<blockquote> -<A NAME=4.3.117>If such a one be fit to govern, speak:</A><br> -<A NAME=4.3.118>I am as I have spoken.</A><br> -</blockquote> - -<A NAME=speech20><b>MACDUFF</b></a> -<blockquote> -<A NAME=4.3.119>Fit to govern!</A><br> -<A NAME=4.3.120>No, not to live. O nation miserable,</A><br> -<A NAME=4.3.121>With an untitled tyrant bloody-scepter'd,</A><br> -<A NAME=4.3.122>When shalt thou see thy wholesome days again,</A><br> -<A NAME=4.3.123>Since that the truest issue of thy throne</A><br> -<A NAME=4.3.124>By his own interdiction stands accursed,</A><br> -<A NAME=4.3.125>And does blaspheme his breed? Thy royal father</A><br> -<A NAME=4.3.126>Was a most sainted king: the queen that bore thee,</A><br> -<A NAME=4.3.127>Oftener upon her knees than on her feet,</A><br> -<A NAME=4.3.128>Died every day she lived. Fare thee well!</A><br> -<A NAME=4.3.129>These evils thou repeat'st upon thyself</A><br> -<A NAME=4.3.130>Have banish'd me from Scotland. O my breast,</A><br> -<A NAME=4.3.131>Thy hope ends here!</A><br> -</blockquote> - -<A NAME=speech21><b>MALCOLM</b></a> -<blockquote> -<A NAME=4.3.132>Macduff, this noble passion,</A><br> -<A NAME=4.3.133>Child of integrity, hath from my soul</A><br> -<A NAME=4.3.134>Wiped the black scruples, reconciled my thoughts</A><br> -<A NAME=4.3.135>To thy good truth and honour. Devilish Macbeth</A><br> -<A NAME=4.3.136>By many of these trains hath sought to win me</A><br> -<A NAME=4.3.137>Into his power, and modest wisdom plucks me</A><br> -<A NAME=4.3.138>From over-credulous haste: but God above</A><br> -<A NAME=4.3.139>Deal between thee and me! for even now</A><br> -<A NAME=4.3.140>I put myself to thy direction, and</A><br> -<A NAME=4.3.141>Unspeak mine own detraction, here abjure</A><br> -<A NAME=4.3.142>The taints and blames I laid upon myself,</A><br> -<A NAME=4.3.143>For strangers to my nature. I am yet</A><br> -<A NAME=4.3.144>Unknown to woman, never was forsworn,</A><br> -<A NAME=4.3.145>Scarcely have coveted what was mine own,</A><br> -<A NAME=4.3.146>At no time broke my faith, would not betray</A><br> -<A NAME=4.3.147>The devil to his fellow and delight</A><br> -<A NAME=4.3.148>No less in truth than life: my first false speaking</A><br> -<A NAME=4.3.149>Was this upon myself: what I am truly,</A><br> -<A NAME=4.3.150>Is thine and my poor country's to command:</A><br> -<A NAME=4.3.151>Whither indeed, before thy here-approach,</A><br> -<A NAME=4.3.152>Old Siward, with ten thousand warlike men,</A><br> -<A NAME=4.3.153>Already at a point, was setting forth.</A><br> -<A NAME=4.3.154>Now we'll together; and the chance of goodness</A><br> -<A NAME=4.3.155>Be like our warranted quarrel! Why are you silent?</A><br> -</blockquote> - -<A NAME=speech22><b>MACDUFF</b></a> -<blockquote> -<A NAME=4.3.156>Such welcome and unwelcome things at once</A><br> -<A NAME=4.3.157>'Tis hard to reconcile.</A><br> -<p><i>Enter a Doctor</i></p> -</blockquote> - -<A NAME=speech23><b>MALCOLM</b></a> -<blockquote> -<A NAME=4.3.158>Well; more anon.--Comes the king forth, I pray you?</A><br> -</blockquote> - -<A NAME=speech24><b>Doctor</b></a> -<blockquote> -<A NAME=4.3.159>Ay, sir; there are a crew of wretched souls</A><br> -<A NAME=4.3.160>That stay his cure: their malady convinces</A><br> -<A NAME=4.3.161>The great assay of art; but at his touch--</A><br> -<A NAME=4.3.162>Such sanctity hath heaven given his hand--</A><br> -<A NAME=4.3.163>They presently amend.</A><br> -</blockquote> - -<A NAME=speech25><b>MALCOLM</b></a> -<blockquote> -<A NAME=4.3.164>I thank you, doctor.</A><br> -<p><i>Exit Doctor</i></p> -</blockquote> - -<A NAME=speech26><b>MACDUFF</b></a> -<blockquote> -<A NAME=4.3.165>What's the disease he means?</A><br> -</blockquote> - -<A NAME=speech27><b>MALCOLM</b></a> -<blockquote> -<A NAME=4.3.166>'Tis call'd the evil:</A><br> -<A NAME=4.3.167>A most miraculous work in this good king;</A><br> -<A NAME=4.3.168>Which often, since my here-remain in England,</A><br> -<A NAME=4.3.169>I have seen him do. How he solicits heaven,</A><br> -<A NAME=4.3.170>Himself best knows: but strangely-visited people,</A><br> -<A NAME=4.3.171>All swoln and ulcerous, pitiful to the eye,</A><br> -<A NAME=4.3.172>The mere despair of surgery, he cures,</A><br> -<A NAME=4.3.173>Hanging a golden stamp about their necks,</A><br> -<A NAME=4.3.174>Put on with holy prayers: and 'tis spoken,</A><br> -<A NAME=4.3.175>To the succeeding royalty he leaves</A><br> -<A NAME=4.3.176>The healing benediction. With this strange virtue,</A><br> -<A NAME=4.3.177>He hath a heavenly gift of prophecy,</A><br> -<A NAME=4.3.178>And sundry blessings hang about his throne,</A><br> -<A NAME=4.3.179>That speak him full of grace.</A><br> -<p><i>Enter ROSS</i></p> -</blockquote> - -<A NAME=speech28><b>MACDUFF</b></a> -<blockquote> -<A NAME=4.3.180>See, who comes here?</A><br> -</blockquote> - -<A NAME=speech29><b>MALCOLM</b></a> -<blockquote> -<A NAME=4.3.181>My countryman; but yet I know him not.</A><br> -</blockquote> - -<A NAME=speech30><b>MACDUFF</b></a> -<blockquote> -<A NAME=4.3.182>My ever-gentle cousin, welcome hither.</A><br> -</blockquote> - -<A NAME=speech31><b>MALCOLM</b></a> -<blockquote> -<A NAME=4.3.183>I know him now. Good God, betimes remove</A><br> -<A NAME=4.3.184>The means that makes us strangers!</A><br> -</blockquote> - -<A NAME=speech32><b>ROSS</b></a> -<blockquote> -<A NAME=4.3.185>Sir, amen.</A><br> -</blockquote> - -<A NAME=speech33><b>MACDUFF</b></a> -<blockquote> -<A NAME=4.3.186>Stands Scotland where it did?</A><br> -</blockquote> - -<A NAME=speech34><b>ROSS</b></a> -<blockquote> -<A NAME=4.3.187>Alas, poor country!</A><br> -<A NAME=4.3.188>Almost afraid to know itself. It cannot</A><br> -<A NAME=4.3.189>Be call'd our mother, but our grave; where nothing,</A><br> -<A NAME=4.3.190>But who knows nothing, is once seen to smile;</A><br> -<A NAME=4.3.191>Where sighs and groans and shrieks that rend the air</A><br> -<A NAME=4.3.192>Are made, not mark'd; where violent sorrow seems</A><br> -<A NAME=4.3.193>A modern ecstasy; the dead man's knell</A><br> -<A NAME=4.3.194>Is there scarce ask'd for who; and good men's lives</A><br> -<A NAME=4.3.195>Expire before the flowers in their caps,</A><br> -<A NAME=4.3.196>Dying or ere they sicken.</A><br> -</blockquote> - -<A NAME=speech35><b>MACDUFF</b></a> -<blockquote> -<A NAME=4.3.197>O, relation</A><br> -<A NAME=4.3.198>Too nice, and yet too true!</A><br> -</blockquote> - -<A NAME=speech36><b>MALCOLM</b></a> -<blockquote> -<A NAME=4.3.199>What's the newest grief?</A><br> -</blockquote> - -<A NAME=speech37><b>ROSS</b></a> -<blockquote> -<A NAME=4.3.200>That of an hour's age doth hiss the speaker:</A><br> -<A NAME=4.3.201>Each minute teems a new one.</A><br> -</blockquote> - -<A NAME=speech38><b>MACDUFF</b></a> -<blockquote> -<A NAME=4.3.202>How does my wife?</A><br> -</blockquote> - -<A NAME=speech39><b>ROSS</b></a> -<blockquote> -<A NAME=4.3.203>Why, well.</A><br> -</blockquote> - -<A NAME=speech40><b>MACDUFF</b></a> -<blockquote> -<A NAME=4.3.204> And all my children?</A><br> -</blockquote> - -<A NAME=speech41><b>ROSS</b></a> -<blockquote> -<A NAME=4.3.205>Well too.</A><br> -</blockquote> - -<A NAME=speech42><b>MACDUFF</b></a> -<blockquote> -<A NAME=4.3.206>The tyrant has not batter'd at their peace?</A><br> -</blockquote> - -<A NAME=speech43><b>ROSS</b></a> -<blockquote> -<A NAME=4.3.207>No; they were well at peace when I did leave 'em.</A><br> -</blockquote> - -<A NAME=speech44><b>MACDUFF</b></a> -<blockquote> -<A NAME=4.3.208>But not a niggard of your speech: how goes't?</A><br> -</blockquote> - -<A NAME=speech45><b>ROSS</b></a> -<blockquote> -<A NAME=4.3.209>When I came hither to transport the tidings,</A><br> -<A NAME=4.3.210>Which I have heavily borne, there ran a rumour</A><br> -<A NAME=4.3.211>Of many worthy fellows that were out;</A><br> -<A NAME=4.3.212>Which was to my belief witness'd the rather,</A><br> -<A NAME=4.3.213>For that I saw the tyrant's power a-foot:</A><br> -<A NAME=4.3.214>Now is the time of help; your eye in Scotland</A><br> -<A NAME=4.3.215>Would create soldiers, make our women fight,</A><br> -<A NAME=4.3.216>To doff their dire distresses.</A><br> -</blockquote> - -<A NAME=speech46><b>MALCOLM</b></a> -<blockquote> -<A NAME=4.3.217>Be't their comfort</A><br> -<A NAME=4.3.218>We are coming thither: gracious England hath</A><br> -<A NAME=4.3.219>Lent us good Siward and ten thousand men;</A><br> -<A NAME=4.3.220>An older and a better soldier none</A><br> -<A NAME=4.3.221>That Christendom gives out.</A><br> -</blockquote> - -<A NAME=speech47><b>ROSS</b></a> -<blockquote> -<A NAME=4.3.222>Would I could answer</A><br> -<A NAME=4.3.223>This comfort with the like! But I have words</A><br> -<A NAME=4.3.224>That would be howl'd out in the desert air,</A><br> -<A NAME=4.3.225>Where hearing should not latch them.</A><br> -</blockquote> - -<A NAME=speech48><b>MACDUFF</b></a> -<blockquote> -<A NAME=4.3.226>What concern they?</A><br> -<A NAME=4.3.227>The general cause? or is it a fee-grief</A><br> -<A NAME=4.3.228>Due to some single breast?</A><br> -</blockquote> - -<A NAME=speech49><b>ROSS</b></a> -<blockquote> -<A NAME=4.3.229>No mind that's honest</A><br> -<A NAME=4.3.230>But in it shares some woe; though the main part</A><br> -<A NAME=4.3.231>Pertains to you alone.</A><br> -</blockquote> - -<A NAME=speech50><b>MACDUFF</b></a> -<blockquote> -<A NAME=4.3.232>If it be mine,</A><br> -<A NAME=4.3.233>Keep it not from me, quickly let me have it.</A><br> -</blockquote> - -<A NAME=speech51><b>ROSS</b></a> -<blockquote> -<A NAME=4.3.234>Let not your ears despise my tongue for ever,</A><br> -<A NAME=4.3.235>Which shall possess them with the heaviest sound</A><br> -<A NAME=4.3.236>That ever yet they heard.</A><br> -</blockquote> - -<A NAME=speech52><b>MACDUFF</b></a> -<blockquote> -<A NAME=4.3.237>Hum! I guess at it.</A><br> -</blockquote> - -<A NAME=speech53><b>ROSS</b></a> -<blockquote> -<A NAME=4.3.238>Your castle is surprised; your wife and babes</A><br> -<A NAME=4.3.239>Savagely slaughter'd: to relate the manner,</A><br> -<A NAME=4.3.240>Were, on the quarry of these murder'd deer,</A><br> -<A NAME=4.3.241>To add the death of you.</A><br> -</blockquote> - -<A NAME=speech54><b>MALCOLM</b></a> -<blockquote> -<A NAME=4.3.242>Merciful heaven!</A><br> -<A NAME=4.3.243>What, man! ne'er pull your hat upon your brows;</A><br> -<A NAME=4.3.244>Give sorrow words: the grief that does not speak</A><br> -<A NAME=4.3.245>Whispers the o'er-fraught heart and bids it break.</A><br> -</blockquote> - -<A NAME=speech55><b>MACDUFF</b></a> -<blockquote> -<A NAME=4.3.246>My children too?</A><br> -</blockquote> - -<A NAME=speech56><b>ROSS</b></a> -<blockquote> -<A NAME=4.3.247> Wife, children, servants, all</A><br> -<A NAME=4.3.248>That could be found.</A><br> -</blockquote> - -<A NAME=speech57><b>MACDUFF</b></a> -<blockquote> -<A NAME=4.3.249>And I must be from thence!</A><br> -<A NAME=4.3.250>My wife kill'd too?</A><br> -</blockquote> - -<A NAME=speech58><b>ROSS</b></a> -<blockquote> -<A NAME=4.3.251>I have said.</A><br> -</blockquote> - -<A NAME=speech59><b>MALCOLM</b></a> -<blockquote> -<A NAME=4.3.252>Be comforted:</A><br> -<A NAME=4.3.253>Let's make us medicines of our great revenge,</A><br> -<A NAME=4.3.254>To cure this deadly grief.</A><br> -</blockquote> - -<A NAME=speech60><b>MACDUFF</b></a> -<blockquote> -<A NAME=4.3.255>He has no children. All my pretty ones?</A><br> -<A NAME=4.3.256>Did you say all? O hell-kite! All?</A><br> -<A NAME=4.3.257>What, all my pretty chickens and their dam</A><br> -<A NAME=4.3.258>At one fell swoop?</A><br> -</blockquote> - -<A NAME=speech61><b>MALCOLM</b></a> -<blockquote> -<A NAME=4.3.259>Dispute it like a man.</A><br> -</blockquote> - -<A NAME=speech62><b>MACDUFF</b></a> -<blockquote> -<A NAME=4.3.260>I shall do so;</A><br> -<A NAME=4.3.261>But I must also feel it as a man:</A><br> -<A NAME=4.3.262>I cannot but remember such things were,</A><br> -<A NAME=4.3.263>That were most precious to me. Did heaven look on,</A><br> -<A NAME=4.3.264>And would not take their part? Sinful Macduff,</A><br> -<A NAME=4.3.265>They were all struck for thee! naught that I am,</A><br> -<A NAME=4.3.266>Not for their own demerits, but for mine,</A><br> -<A NAME=4.3.267>Fell slaughter on their souls. Heaven rest them now!</A><br> -</blockquote> - -<A NAME=speech63><b>MALCOLM</b></a> -<blockquote> -<A NAME=4.3.268>Be this the whetstone of your sword: let grief</A><br> -<A NAME=4.3.269>Convert to anger; blunt not the heart, enrage it.</A><br> -</blockquote> - -<A NAME=speech64><b>MACDUFF</b></a> -<blockquote> -<A NAME=4.3.270>O, I could play the woman with mine eyes</A><br> -<A NAME=4.3.271>And braggart with my tongue! But, gentle heavens,</A><br> -<A NAME=4.3.272>Cut short all intermission; front to front</A><br> -<A NAME=4.3.273>Bring thou this fiend of Scotland and myself;</A><br> -<A NAME=4.3.274>Within my sword's length set him; if he 'scape,</A><br> -<A NAME=4.3.275>Heaven forgive him too!</A><br> -</blockquote> - -<A NAME=speech65><b>MALCOLM</b></a> -<blockquote> -<A NAME=4.3.276>This tune goes manly.</A><br> -<A NAME=4.3.277>Come, go we to the king; our power is ready;</A><br> -<A NAME=4.3.278>Our lack is nothing but our leave; Macbeth</A><br> -<A NAME=4.3.279>Is ripe for shaking, and the powers above</A><br> -<A NAME=4.3.280>Put on their instruments. Receive what cheer you may:</A><br> -<A NAME=4.3.281>The night is long that never finds the day.</A><br> -<p><i>Exeunt</i></p> -</blockquote><p> -<H3>ACT V</h3> -<h3>SCENE I. Dunsinane. Ante-room in the castle.</h3> -<p><blockquote> -<i>Enter a Doctor of Physic and a Waiting-Gentlewoman</i> -</blockquote> - -<A NAME=speech1><b>Doctor</b></a> -<blockquote> -<A NAME=5.1.1>I have two nights watched with you, but can perceive</A><br> -<A NAME=5.1.2>no truth in your report. When was it she last walked?</A><br> -</blockquote> - -<A NAME=speech2><b>Gentlewoman</b></a> -<blockquote> -<A NAME=5.1.3>Since his majesty went into the field, I have seen</A><br> -<A NAME=5.1.4>her rise from her bed, throw her night-gown upon</A><br> -<A NAME=5.1.5>her, unlock her closet, take forth paper, fold it,</A><br> -<A NAME=5.1.6>write upon't, read it, afterwards seal it, and again</A><br> -<A NAME=5.1.7>return to bed; yet all this while in a most fast sleep.</A><br> -</blockquote> - -<A NAME=speech3><b>Doctor</b></a> -<blockquote> -<A NAME=5.1.8>A great perturbation in nature, to receive at once</A><br> -<A NAME=5.1.9>the benefit of sleep, and do the effects of</A><br> -<A NAME=5.1.10>watching! In this slumbery agitation, besides her</A><br> -<A NAME=5.1.11>walking and other actual performances, what, at any</A><br> -<A NAME=5.1.12>time, have you heard her say?</A><br> -</blockquote> - -<A NAME=speech4><b>Gentlewoman</b></a> -<blockquote> -<A NAME=5.1.13>That, sir, which I will not report after her.</A><br> -</blockquote> - -<A NAME=speech5><b>Doctor</b></a> -<blockquote> -<A NAME=5.1.14>You may to me: and 'tis most meet you should.</A><br> -</blockquote> - -<A NAME=speech6><b>Gentlewoman</b></a> -<blockquote> -<A NAME=5.1.15>Neither to you nor any one; having no witness to</A><br> -<A NAME=5.1.16>confirm my speech.</A><br> -<p><i>Enter LADY MACBETH, with a taper</i></p> -<A NAME=5.1.17>Lo you, here she comes! This is her very guise;</A><br> -<A NAME=5.1.18>and, upon my life, fast asleep. Observe her; stand close.</A><br> -</blockquote> - -<A NAME=speech7><b>Doctor</b></a> -<blockquote> -<A NAME=5.1.19>How came she by that light?</A><br> -</blockquote> - -<A NAME=speech8><b>Gentlewoman</b></a> -<blockquote> -<A NAME=5.1.20>Why, it stood by her: she has light by her</A><br> -<A NAME=5.1.21>continually; 'tis her command.</A><br> -</blockquote> - -<A NAME=speech9><b>Doctor</b></a> -<blockquote> -<A NAME=5.1.22>You see, her eyes are open.</A><br> -</blockquote> - -<A NAME=speech10><b>Gentlewoman</b></a> -<blockquote> -<A NAME=5.1.23>Ay, but their sense is shut.</A><br> -</blockquote> - -<A NAME=speech11><b>Doctor</b></a> -<blockquote> -<A NAME=5.1.24>What is it she does now? Look, how she rubs her hands.</A><br> -</blockquote> - -<A NAME=speech12><b>Gentlewoman</b></a> -<blockquote> -<A NAME=5.1.25>It is an accustomed action with her, to seem thus</A><br> -<A NAME=5.1.26>washing her hands: I have known her continue in</A><br> -<A NAME=5.1.27>this a quarter of an hour.</A><br> -</blockquote> - -<A NAME=speech13><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=5.1.28>Yet here's a spot.</A><br> -</blockquote> - -<A NAME=speech14><b>Doctor</b></a> -<blockquote> -<A NAME=5.1.29>Hark! she speaks: I will set down what comes from</A><br> -<A NAME=5.1.30>her, to satisfy my remembrance the more strongly.</A><br> -</blockquote> - -<A NAME=speech15><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=5.1.31>Out, damned spot! out, I say!--One: two: why,</A><br> -<A NAME=5.1.32>then, 'tis time to do't.--Hell is murky!--Fie, my</A><br> -<A NAME=5.1.33>lord, fie! a soldier, and afeard? What need we</A><br> -<A NAME=5.1.34>fear who knows it, when none can call our power to</A><br> -<A NAME=5.1.35>account?--Yet who would have thought the old man</A><br> -<A NAME=5.1.36>to have had so much blood in him.</A><br> -</blockquote> - -<A NAME=speech16><b>Doctor</b></a> -<blockquote> -<A NAME=5.1.37>Do you mark that?</A><br> -</blockquote> - -<A NAME=speech17><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=5.1.38>The thane of Fife had a wife: where is she now?--</A><br> -<A NAME=5.1.39>What, will these hands ne'er be clean?--No more o'</A><br> -<A NAME=5.1.40>that, my lord, no more o' that: you mar all with</A><br> -<A NAME=5.1.41>this starting.</A><br> -</blockquote> - -<A NAME=speech18><b>Doctor</b></a> -<blockquote> -<A NAME=5.1.42>Go to, go to; you have known what you should not.</A><br> -</blockquote> - -<A NAME=speech19><b>Gentlewoman</b></a> -<blockquote> -<A NAME=5.1.43>She has spoke what she should not, I am sure of</A><br> -<A NAME=5.1.44>that: heaven knows what she has known.</A><br> -</blockquote> - -<A NAME=speech20><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=5.1.45>Here's the smell of the blood still: all the</A><br> -<A NAME=5.1.46>perfumes of Arabia will not sweeten this little</A><br> -<A NAME=5.1.47>hand. Oh, oh, oh!</A><br> -</blockquote> - -<A NAME=speech21><b>Doctor</b></a> -<blockquote> -<A NAME=5.1.48>What a sigh is there! The heart is sorely charged.</A><br> -</blockquote> - -<A NAME=speech22><b>Gentlewoman</b></a> -<blockquote> -<A NAME=5.1.49>I would not have such a heart in my bosom for the</A><br> -<A NAME=5.1.50>dignity of the whole body.</A><br> -</blockquote> - -<A NAME=speech23><b>Doctor</b></a> -<blockquote> -<A NAME=5.1.51>Well, well, well,--</A><br> -</blockquote> - -<A NAME=speech24><b>Gentlewoman</b></a> -<blockquote> -<A NAME=5.1.52>Pray God it be, sir.</A><br> -</blockquote> - -<A NAME=speech25><b>Doctor</b></a> -<blockquote> -<A NAME=5.1.53>This disease is beyond my practise: yet I have known</A><br> -<A NAME=5.1.54>those which have walked in their sleep who have died</A><br> -<A NAME=5.1.55>holily in their beds.</A><br> -</blockquote> - -<A NAME=speech26><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=5.1.56>Wash your hands, put on your nightgown; look not so</A><br> -<A NAME=5.1.57>pale.--I tell you yet again, Banquo's buried; he</A><br> -<A NAME=5.1.58>cannot come out on's grave.</A><br> -</blockquote> - -<A NAME=speech27><b>Doctor</b></a> -<blockquote> -<A NAME=5.1.59>Even so?</A><br> -</blockquote> - -<A NAME=speech28><b>LADY MACBETH</b></a> -<blockquote> -<A NAME=5.1.60>To bed, to bed! there's knocking at the gate:</A><br> -<A NAME=5.1.61>come, come, come, come, give me your hand. What's</A><br> -<A NAME=5.1.62>done cannot be undone.--To bed, to bed, to bed!</A><br> -<p><i>Exit</i></p> -</blockquote> - -<A NAME=speech29><b>Doctor</b></a> -<blockquote> -<A NAME=5.1.63>Will she go now to bed?</A><br> -</blockquote> - -<A NAME=speech30><b>Gentlewoman</b></a> -<blockquote> -<A NAME=5.1.64>Directly.</A><br> -</blockquote> - -<A NAME=speech31><b>Doctor</b></a> -<blockquote> -<A NAME=5.1.65>Foul whisperings are abroad: unnatural deeds</A><br> -<A NAME=5.1.66>Do breed unnatural troubles: infected minds</A><br> -<A NAME=5.1.67>To their deaf pillows will discharge their secrets:</A><br> -<A NAME=5.1.68>More needs she the divine than the physician.</A><br> -<A NAME=5.1.69>God, God forgive us all! Look after her;</A><br> -<A NAME=5.1.70>Remove from her the means of all annoyance,</A><br> -<A NAME=5.1.71>And still keep eyes upon her. So, good night:</A><br> -<A NAME=5.1.72>My mind she has mated, and amazed my sight.</A><br> -<A NAME=5.1.73>I think, but dare not speak.</A><br> -</blockquote> - -<A NAME=speech32><b>Gentlewoman</b></a> -<blockquote> -<A NAME=5.1.74>Good night, good doctor.</A><br> -<p><i>Exeunt</i></p> -</blockquote> -<h3>SCENE II. The country near Dunsinane.</h3> -<p><blockquote> -<i>Drum and colours. Enter MENTEITH, CAITHNESS, ANGUS, LENNOX, and Soldiers</i> -</blockquote> - -<A NAME=speech1><b>MENTEITH</b></a> -<blockquote> -<A NAME=5.2.1>The English power is near, led on by Malcolm,</A><br> -<A NAME=5.2.2>His uncle Siward and the good Macduff:</A><br> -<A NAME=5.2.3>Revenges burn in them; for their dear causes</A><br> -<A NAME=5.2.4>Would to the bleeding and the grim alarm</A><br> -<A NAME=5.2.5>Excite the mortified man.</A><br> -</blockquote> - -<A NAME=speech2><b>ANGUS</b></a> -<blockquote> -<A NAME=5.2.6>Near Birnam wood</A><br> -<A NAME=5.2.7>Shall we well meet them; that way are they coming.</A><br> -</blockquote> - -<A NAME=speech3><b>CAITHNESS</b></a> -<blockquote> -<A NAME=5.2.8>Who knows if Donalbain be with his brother?</A><br> -</blockquote> - -<A NAME=speech4><b>LENNOX</b></a> -<blockquote> -<A NAME=5.2.9>For certain, sir, he is not: I have a file</A><br> -<A NAME=5.2.10>Of all the gentry: there is Siward's son,</A><br> -<A NAME=5.2.11>And many unrough youths that even now</A><br> -<A NAME=5.2.12>Protest their first of manhood.</A><br> -</blockquote> - -<A NAME=speech5><b>MENTEITH</b></a> -<blockquote> -<A NAME=5.2.13>What does the tyrant?</A><br> -</blockquote> - -<A NAME=speech6><b>CAITHNESS</b></a> -<blockquote> -<A NAME=5.2.14>Great Dunsinane he strongly fortifies:</A><br> -<A NAME=5.2.15>Some say he's mad; others that lesser hate him</A><br> -<A NAME=5.2.16>Do call it valiant fury: but, for certain,</A><br> -<A NAME=5.2.17>He cannot buckle his distemper'd cause</A><br> -<A NAME=5.2.18>Within the belt of rule.</A><br> -</blockquote> - -<A NAME=speech7><b>ANGUS</b></a> -<blockquote> -<A NAME=5.2.19>Now does he feel</A><br> -<A NAME=5.2.20>His secret murders sticking on his hands;</A><br> -<A NAME=5.2.21>Now minutely revolts upbraid his faith-breach;</A><br> -<A NAME=5.2.22>Those he commands move only in command,</A><br> -<A NAME=5.2.23>Nothing in love: now does he feel his title</A><br> -<A NAME=5.2.24>Hang loose about him, like a giant's robe</A><br> -<A NAME=5.2.25>Upon a dwarfish thief.</A><br> -</blockquote> - -<A NAME=speech8><b>MENTEITH</b></a> -<blockquote> -<A NAME=5.2.26>Who then shall blame</A><br> -<A NAME=5.2.27>His pester'd senses to recoil and start,</A><br> -<A NAME=5.2.28>When all that is within him does condemn</A><br> -<A NAME=5.2.29>Itself for being there?</A><br> -</blockquote> - -<A NAME=speech9><b>CAITHNESS</b></a> -<blockquote> -<A NAME=5.2.30>Well, march we on,</A><br> -<A NAME=5.2.31>To give obedience where 'tis truly owed:</A><br> -<A NAME=5.2.32>Meet we the medicine of the sickly weal,</A><br> -<A NAME=5.2.33>And with him pour we in our country's purge</A><br> -<A NAME=5.2.34>Each drop of us.</A><br> -</blockquote> - -<A NAME=speech10><b>LENNOX</b></a> -<blockquote> -<A NAME=5.2.35> Or so much as it needs,</A><br> -<A NAME=5.2.36>To dew the sovereign flower and drown the weeds.</A><br> -<A NAME=5.2.37>Make we our march towards Birnam.</A><br> -<p><i>Exeunt, marching</i></p> -</blockquote> -<h3>SCENE III. Dunsinane. A room in the castle.</h3> -<p><blockquote> -<i>Enter MACBETH, Doctor, and Attendants</i> -</blockquote> - -<A NAME=speech1><b>MACBETH</b></a> -<blockquote> -<A NAME=5.3.1>Bring me no more reports; let them fly all:</A><br> -<A NAME=5.3.2>Till Birnam wood remove to Dunsinane,</A><br> -<A NAME=5.3.3>I cannot taint with fear. What's the boy Malcolm?</A><br> -<A NAME=5.3.4>Was he not born of woman? The spirits that know</A><br> -<A NAME=5.3.5>All mortal consequences have pronounced me thus:</A><br> -<A NAME=5.3.6>'Fear not, Macbeth; no man that's born of woman</A><br> -<A NAME=5.3.7>Shall e'er have power upon thee.' Then fly,</A><br> -<A NAME=5.3.8>false thanes,</A><br> -<A NAME=5.3.9>And mingle with the English epicures:</A><br> -<A NAME=5.3.10>The mind I sway by and the heart I bear</A><br> -<A NAME=5.3.11>Shall never sag with doubt nor shake with fear.</A><br> -<p><i>Enter a Servant</i></p> -<A NAME=5.3.12>The devil damn thee black, thou cream-faced loon!</A><br> -<A NAME=5.3.13>Where got'st thou that goose look?</A><br> -</blockquote> - -<A NAME=speech2><b>Servant</b></a> -<blockquote> -<A NAME=5.3.14>There is ten thousand--</A><br> -</blockquote> - -<A NAME=speech3><b>MACBETH</b></a> -<blockquote> -<A NAME=5.3.15>Geese, villain!</A><br> -</blockquote> - -<A NAME=speech4><b>Servant</b></a> -<blockquote> -<A NAME=5.3.16>Soldiers, sir.</A><br> -</blockquote> - -<A NAME=speech5><b>MACBETH</b></a> -<blockquote> -<A NAME=5.3.17>Go prick thy face, and over-red thy fear,</A><br> -<A NAME=5.3.18>Thou lily-liver'd boy. What soldiers, patch?</A><br> -<A NAME=5.3.19>Death of thy soul! those linen cheeks of thine</A><br> -<A NAME=5.3.20>Are counsellors to fear. What soldiers, whey-face?</A><br> -</blockquote> - -<A NAME=speech6><b>Servant</b></a> -<blockquote> -<A NAME=5.3.21>The English force, so please you.</A><br> -</blockquote> - -<A NAME=speech7><b>MACBETH</b></a> -<blockquote> -<A NAME=5.3.22>Take thy face hence.</A><br> -<p><i>Exit Servant</i></p> -<A NAME=5.3.23>Seyton!--I am sick at heart,</A><br> -<A NAME=5.3.24>When I behold--Seyton, I say!--This push</A><br> -<A NAME=5.3.25>Will cheer me ever, or disseat me now.</A><br> -<A NAME=5.3.26>I have lived long enough: my way of life</A><br> -<A NAME=5.3.27>Is fall'n into the sear, the yellow leaf;</A><br> -<A NAME=5.3.28>And that which should accompany old age,</A><br> -<A NAME=5.3.29>As honour, love, obedience, troops of friends,</A><br> -<A NAME=5.3.30>I must not look to have; but, in their stead,</A><br> -<A NAME=5.3.31>Curses, not loud but deep, mouth-honour, breath,</A><br> -<A NAME=5.3.32>Which the poor heart would fain deny, and dare not. Seyton!</A><br> -<p><i>Enter SEYTON</i></p> -</blockquote> - -<A NAME=speech8><b>SEYTON</b></a> -<blockquote> -<A NAME=5.3.33>What is your gracious pleasure?</A><br> -</blockquote> - -<A NAME=speech9><b>MACBETH</b></a> -<blockquote> -<A NAME=5.3.34>What news more?</A><br> -</blockquote> - -<A NAME=speech10><b>SEYTON</b></a> -<blockquote> -<A NAME=5.3.35>All is confirm'd, my lord, which was reported.</A><br> -</blockquote> - -<A NAME=speech11><b>MACBETH</b></a> -<blockquote> -<A NAME=5.3.36>I'll fight till from my bones my flesh be hack'd.</A><br> -<A NAME=5.3.37>Give me my armour.</A><br> -</blockquote> - -<A NAME=speech12><b>SEYTON</b></a> -<blockquote> -<A NAME=5.3.38>'Tis not needed yet.</A><br> -</blockquote> - -<A NAME=speech13><b>MACBETH</b></a> -<blockquote> -<A NAME=5.3.39>I'll put it on.</A><br> -<A NAME=5.3.40>Send out more horses; skirr the country round;</A><br> -<A NAME=5.3.41>Hang those that talk of fear. Give me mine armour.</A><br> -<A NAME=5.3.42>How does your patient, doctor?</A><br> -</blockquote> - -<A NAME=speech14><b>Doctor</b></a> -<blockquote> -<A NAME=5.3.43>Not so sick, my lord,</A><br> -<A NAME=5.3.44>As she is troubled with thick coming fancies,</A><br> -<A NAME=5.3.45>That keep her from her rest.</A><br> -</blockquote> - -<A NAME=speech15><b>MACBETH</b></a> -<blockquote> -<A NAME=5.3.46>Cure her of that.</A><br> -<A NAME=5.3.47>Canst thou not minister to a mind diseased,</A><br> -<A NAME=5.3.48>Pluck from the memory a rooted sorrow,</A><br> -<A NAME=5.3.49>Raze out the written troubles of the brain</A><br> -<A NAME=5.3.50>And with some sweet oblivious antidote</A><br> -<A NAME=5.3.51>Cleanse the stuff'd bosom of that perilous stuff</A><br> -<A NAME=5.3.52>Which weighs upon the heart?</A><br> -</blockquote> - -<A NAME=speech16><b>Doctor</b></a> -<blockquote> -<A NAME=5.3.53>Therein the patient</A><br> -<A NAME=5.3.54>Must minister to himself.</A><br> -</blockquote> - -<A NAME=speech17><b>MACBETH</b></a> -<blockquote> -<A NAME=5.3.55>Throw physic to the dogs; I'll none of it.</A><br> -<A NAME=5.3.56>Come, put mine armour on; give me my staff.</A><br> -<A NAME=5.3.57>Seyton, send out. Doctor, the thanes fly from me.</A><br> -<A NAME=5.3.58>Come, sir, dispatch. If thou couldst, doctor, cast</A><br> -<A NAME=5.3.59>The water of my land, find her disease,</A><br> -<A NAME=5.3.60>And purge it to a sound and pristine health,</A><br> -<A NAME=5.3.61>I would applaud thee to the very echo,</A><br> -<A NAME=5.3.62>That should applaud again.--Pull't off, I say.--</A><br> -<A NAME=5.3.63>What rhubarb, cyme, or what purgative drug,</A><br> -<A NAME=5.3.64>Would scour these English hence? Hear'st thou of them?</A><br> -</blockquote> - -<A NAME=speech18><b>Doctor</b></a> -<blockquote> -<A NAME=5.3.65>Ay, my good lord; your royal preparation</A><br> -<A NAME=5.3.66>Makes us hear something.</A><br> -</blockquote> - -<A NAME=speech19><b>MACBETH</b></a> -<blockquote> -<A NAME=5.3.67>Bring it after me.</A><br> -<A NAME=5.3.68>I will not be afraid of death and bane,</A><br> -<A NAME=5.3.69>Till Birnam forest come to Dunsinane.</A><br> -</blockquote> - -<A NAME=speech20><b>Doctor</b></a> -<blockquote> -<A NAME=5.3.70>[Aside] Were I from Dunsinane away and clear,</A><br> -<A NAME=5.3.71>Profit again should hardly draw me here.</A><br> -<p><i>Exeunt</i></p> -</blockquote> -<h3>SCENE IV. Country near Birnam wood.</h3> -<p><blockquote> -<i>Drum and colours. Enter MALCOLM, SIWARD and YOUNG SIWARD, MACDUFF, MENTEITH, CAITHNESS, ANGUS, LENNOX, ROSS, and Soldiers, marching</i> -</blockquote> - -<A NAME=speech1><b>MALCOLM</b></a> -<blockquote> -<A NAME=5.4.1>Cousins, I hope the days are near at hand</A><br> -<A NAME=5.4.2>That chambers will be safe.</A><br> -</blockquote> - -<A NAME=speech2><b>MENTEITH</b></a> -<blockquote> -<A NAME=5.4.3>We doubt it nothing.</A><br> -</blockquote> - -<A NAME=speech3><b>SIWARD</b></a> -<blockquote> -<A NAME=5.4.4>What wood is this before us?</A><br> -</blockquote> - -<A NAME=speech4><b>MENTEITH</b></a> -<blockquote> -<A NAME=5.4.5>The wood of Birnam.</A><br> -</blockquote> - -<A NAME=speech5><b>MALCOLM</b></a> -<blockquote> -<A NAME=5.4.6>Let every soldier hew him down a bough</A><br> -<A NAME=5.4.7>And bear't before him: thereby shall we shadow</A><br> -<A NAME=5.4.8>The numbers of our host and make discovery</A><br> -<A NAME=5.4.9>Err in report of us.</A><br> -</blockquote> - -<A NAME=speech6><b>Soldiers</b></a> -<blockquote> -<A NAME=5.4.10>It shall be done.</A><br> -</blockquote> - -<A NAME=speech7><b>SIWARD</b></a> -<blockquote> -<A NAME=5.4.11>We learn no other but the confident tyrant</A><br> -<A NAME=5.4.12>Keeps still in Dunsinane, and will endure</A><br> -<A NAME=5.4.13>Our setting down before 't.</A><br> -</blockquote> - -<A NAME=speech8><b>MALCOLM</b></a> -<blockquote> -<A NAME=5.4.14>'Tis his main hope:</A><br> -<A NAME=5.4.15>For where there is advantage to be given,</A><br> -<A NAME=5.4.16>Both more and less have given him the revolt,</A><br> -<A NAME=5.4.17>And none serve with him but constrained things</A><br> -<A NAME=5.4.18>Whose hearts are absent too.</A><br> -</blockquote> - -<A NAME=speech9><b>MACDUFF</b></a> -<blockquote> -<A NAME=5.4.19>Let our just censures</A><br> -<A NAME=5.4.20>Attend the true event, and put we on</A><br> -<A NAME=5.4.21>Industrious soldiership.</A><br> -</blockquote> - -<A NAME=speech10><b>SIWARD</b></a> -<blockquote> -<A NAME=5.4.22>The time approaches</A><br> -<A NAME=5.4.23>That will with due decision make us know</A><br> -<A NAME=5.4.24>What we shall say we have and what we owe.</A><br> -<A NAME=5.4.25>Thoughts speculative their unsure hopes relate,</A><br> -<A NAME=5.4.26>But certain issue strokes must arbitrate:</A><br> -<A NAME=5.4.27>Towards which advance the war.</A><br> -<p><i>Exeunt, marching</i></p> -</blockquote> -<h3>SCENE V. Dunsinane. Within the castle.</h3> -<p><blockquote> -<i>Enter MACBETH, SEYTON, and Soldiers, with drum and colours</i> -</blockquote> - -<A NAME=speech1><b>MACBETH</b></a> -<blockquote> -<A NAME=5.5.1>Hang out our banners on the outward walls;</A><br> -<A NAME=5.5.2>The cry is still 'They come:' our castle's strength</A><br> -<A NAME=5.5.3>Will laugh a siege to scorn: here let them lie</A><br> -<A NAME=5.5.4>Till famine and the ague eat them up:</A><br> -<A NAME=5.5.5>Were they not forced with those that should be ours,</A><br> -<A NAME=5.5.6>We might have met them dareful, beard to beard,</A><br> -<A NAME=5.5.7>And beat them backward home.</A><br> -<p><i>A cry of women within</i></p> -<A NAME=5.5.8>What is that noise?</A><br> -</blockquote> - -<A NAME=speech2><b>SEYTON</b></a> -<blockquote> -<A NAME=5.5.9>It is the cry of women, my good lord.</A><br> -<p><i>Exit</i></p> -</blockquote> - -<A NAME=speech3><b>MACBETH</b></a> -<blockquote> -<A NAME=5.5.10>I have almost forgot the taste of fears;</A><br> -<A NAME=5.5.11>The time has been, my senses would have cool'd</A><br> -<A NAME=5.5.12>To hear a night-shriek; and my fell of hair</A><br> -<A NAME=5.5.13>Would at a dismal treatise rouse and stir</A><br> -<A NAME=5.5.14>As life were in't: I have supp'd full with horrors;</A><br> -<A NAME=5.5.15>Direness, familiar to my slaughterous thoughts</A><br> -<A NAME=5.5.16>Cannot once start me.</A><br> -<p><i>Re-enter SEYTON</i></p> -<A NAME=5.5.17>Wherefore was that cry?</A><br> -</blockquote> - -<A NAME=speech4><b>SEYTON</b></a> -<blockquote> -<A NAME=5.5.18>The queen, my lord, is dead.</A><br> -</blockquote> - -<A NAME=speech5><b>MACBETH</b></a> -<blockquote> -<A NAME=5.5.19>She should have died hereafter;</A><br> -<A NAME=5.5.20>There would have been a time for such a word.</A><br> -<A NAME=5.5.21>To-morrow, and to-morrow, and to-morrow,</A><br> -<A NAME=5.5.22>Creeps in this petty pace from day to day</A><br> -<A NAME=5.5.23>To the last syllable of recorded time,</A><br> -<A NAME=5.5.24>And all our yesterdays have lighted fools</A><br> -<A NAME=5.5.25>The way to dusty death. Out, out, brief candle!</A><br> -<A NAME=5.5.26>Life's but a walking shadow, a poor player</A><br> -<A NAME=5.5.27>That struts and frets his hour upon the stage</A><br> -<A NAME=5.5.28>And then is heard no more: it is a tale</A><br> -<A NAME=5.5.29>Told by an idiot, full of sound and fury,</A><br> -<A NAME=5.5.30>Signifying nothing.</A><br> -<p><i>Enter a Messenger</i></p> -<A NAME=5.5.31>Thou comest to use thy tongue; thy story quickly.</A><br> -</blockquote> - -<A NAME=speech6><b>Messenger</b></a> -<blockquote> -<A NAME=5.5.32>Gracious my lord,</A><br> -<A NAME=5.5.33>I should report that which I say I saw,</A><br> -<A NAME=5.5.34>But know not how to do it.</A><br> -</blockquote> - -<A NAME=speech7><b>MACBETH</b></a> -<blockquote> -<A NAME=5.5.35>Well, say, sir.</A><br> -</blockquote> - -<A NAME=speech8><b>Messenger</b></a> -<blockquote> -<A NAME=5.5.36>As I did stand my watch upon the hill,</A><br> -<A NAME=5.5.37>I look'd toward Birnam, and anon, methought,</A><br> -<A NAME=5.5.38>The wood began to move.</A><br> -</blockquote> - -<A NAME=speech9><b>MACBETH</b></a> -<blockquote> -<A NAME=5.5.39>Liar and slave!</A><br> -</blockquote> - -<A NAME=speech10><b>Messenger</b></a> -<blockquote> -<A NAME=5.5.40>Let me endure your wrath, if't be not so:</A><br> -<A NAME=5.5.41>Within this three mile may you see it coming;</A><br> -<A NAME=5.5.42>I say, a moving grove.</A><br> -</blockquote> - -<A NAME=speech11><b>MACBETH</b></a> -<blockquote> -<A NAME=5.5.43>If thou speak'st false,</A><br> -<A NAME=5.5.44>Upon the next tree shalt thou hang alive,</A><br> -<A NAME=5.5.45>Till famine cling thee: if thy speech be sooth,</A><br> -<A NAME=5.5.46>I care not if thou dost for me as much.</A><br> -<A NAME=5.5.47>I pull in resolution, and begin</A><br> -<A NAME=5.5.48>To doubt the equivocation of the fiend</A><br> -<A NAME=5.5.49>That lies like truth: 'Fear not, till Birnam wood</A><br> -<A NAME=5.5.50>Do come to Dunsinane:' and now a wood</A><br> -<A NAME=5.5.51>Comes toward Dunsinane. Arm, arm, and out!</A><br> -<A NAME=5.5.52>If this which he avouches does appear,</A><br> -<A NAME=5.5.53>There is nor flying hence nor tarrying here.</A><br> -<A NAME=5.5.54>I gin to be aweary of the sun,</A><br> -<A NAME=5.5.55>And wish the estate o' the world were now undone.</A><br> -<A NAME=5.5.56>Ring the alarum-bell! Blow, wind! come, wrack!</A><br> -<A NAME=5.5.57>At least we'll die with harness on our back.</A><br> -<p><i>Exeunt</i></p> -</blockquote> -<h3>SCENE VI. Dunsinane. Before the castle.</h3> -<p><blockquote> -<i>Drum and colours. Enter MALCOLM, SIWARD, MACDUFF, and their Army, with boughs</i> -</blockquote> - -<A NAME=speech1><b>MALCOLM</b></a> -<blockquote> -<A NAME=5.6.1>Now near enough: your leafy screens throw down.</A><br> -<A NAME=5.6.2>And show like those you are. You, worthy uncle,</A><br> -<A NAME=5.6.3>Shall, with my cousin, your right-noble son,</A><br> -<A NAME=5.6.4>Lead our first battle: worthy Macduff and we</A><br> -<A NAME=5.6.5>Shall take upon 's what else remains to do,</A><br> -<A NAME=5.6.6>According to our order.</A><br> -</blockquote> - -<A NAME=speech2><b>SIWARD</b></a> -<blockquote> -<A NAME=5.6.7>Fare you well.</A><br> -<A NAME=5.6.8>Do we but find the tyrant's power to-night,</A><br> -<A NAME=5.6.9>Let us be beaten, if we cannot fight.</A><br> -</blockquote> - -<A NAME=speech3><b>MACDUFF</b></a> -<blockquote> -<A NAME=5.6.10>Make all our trumpets speak; give them all breath,</A><br> -<A NAME=5.6.11>Those clamorous harbingers of blood and death.</A><br> -<p><i>Exeunt</i></p> -</blockquote> -<h3>SCENE VII. Another part of the field.</h3> -<p><blockquote> -<i>Alarums. Enter MACBETH</i> -</blockquote> - -<A NAME=speech1><b>MACBETH</b></a> -<blockquote> -<A NAME=5.7.1>They have tied me to a stake; I cannot fly,</A><br> -<A NAME=5.7.2>But, bear-like, I must fight the course. What's he</A><br> -<A NAME=5.7.3>That was not born of woman? Such a one</A><br> -<A NAME=5.7.4>Am I to fear, or none.</A><br> -<p><i>Enter YOUNG SIWARD</i></p> -</blockquote> - -<A NAME=speech2><b>YOUNG SIWARD</b></a> -<blockquote> -<A NAME=5.7.5>What is thy name?</A><br> -</blockquote> - -<A NAME=speech3><b>MACBETH</b></a> -<blockquote> -<A NAME=5.7.6> Thou'lt be afraid to hear it.</A><br> -</blockquote> - -<A NAME=speech4><b>YOUNG SIWARD</b></a> -<blockquote> -<A NAME=5.7.7>No; though thou call'st thyself a hotter name</A><br> -<A NAME=5.7.8>Than any is in hell.</A><br> -</blockquote> - -<A NAME=speech5><b>MACBETH</b></a> -<blockquote> -<A NAME=5.7.9>My name's Macbeth.</A><br> -</blockquote> - -<A NAME=speech6><b>YOUNG SIWARD</b></a> -<blockquote> -<A NAME=5.7.10>The devil himself could not pronounce a title</A><br> -<A NAME=5.7.11>More hateful to mine ear.</A><br> -</blockquote> - -<A NAME=speech7><b>MACBETH</b></a> -<blockquote> -<A NAME=5.7.12>No, nor more fearful.</A><br> -</blockquote> - -<A NAME=speech8><b>YOUNG SIWARD</b></a> -<blockquote> -<A NAME=5.7.13>Thou liest, abhorred tyrant; with my sword</A><br> -<A NAME=5.7.14>I'll prove the lie thou speak'st.</A><br> -<p><i>They fight and YOUNG SIWARD is slain</i></p> -</blockquote> - -<A NAME=speech9><b>MACBETH</b></a> -<blockquote> -<A NAME=5.7.15>Thou wast born of woman</A><br> -<A NAME=5.7.16>But swords I smile at, weapons laugh to scorn,</A><br> -<A NAME=5.7.17>Brandish'd by man that's of a woman born.</A><br> -<p><i>Exit</i></p> -<p><i>Alarums. Enter MACDUFF</i></p> -</blockquote> - -<A NAME=speech10><b>MACDUFF</b></a> -<blockquote> -<A NAME=5.7.18>That way the noise is. Tyrant, show thy face!</A><br> -<A NAME=5.7.19>If thou be'st slain and with no stroke of mine,</A><br> -<A NAME=5.7.20>My wife and children's ghosts will haunt me still.</A><br> -<A NAME=5.7.21>I cannot strike at wretched kerns, whose arms</A><br> -<A NAME=5.7.22>Are hired to bear their staves: either thou, Macbeth,</A><br> -<A NAME=5.7.23>Or else my sword with an unbatter'd edge</A><br> -<A NAME=5.7.24>I sheathe again undeeded. There thou shouldst be;</A><br> -<A NAME=5.7.25>By this great clatter, one of greatest note</A><br> -<A NAME=5.7.26>Seems bruited. Let me find him, fortune!</A><br> -<A NAME=5.7.27>And more I beg not.</A><br> -<p><i>Exit. Alarums</i></p> -<p><i>Enter MALCOLM and SIWARD</i></p> -</blockquote> - -<A NAME=speech11><b>SIWARD</b></a> -<blockquote> -<A NAME=5.7.28>This way, my lord; the castle's gently render'd:</A><br> -<A NAME=5.7.29>The tyrant's people on both sides do fight;</A><br> -<A NAME=5.7.30>The noble thanes do bravely in the war;</A><br> -<A NAME=5.7.31>The day almost itself professes yours,</A><br> -<A NAME=5.7.32>And little is to do.</A><br> -</blockquote> - -<A NAME=speech12><b>MALCOLM</b></a> -<blockquote> -<A NAME=5.7.33>We have met with foes</A><br> -<A NAME=5.7.34>That strike beside us.</A><br> -</blockquote> - -<A NAME=speech13><b>SIWARD</b></a> -<blockquote> -<A NAME=5.7.35>Enter, sir, the castle.</A><br> -<p><i>Exeunt. Alarums</i></p> -</blockquote> -<h3>SCENE VIII. Another part of the field.</h3> -<p><blockquote> -<i>Enter MACBETH</i> -</blockquote> - -<A NAME=speech1><b>MACBETH</b></a> -<blockquote> -<A NAME=5.8.1>Why should I play the Roman fool, and die</A><br> -<A NAME=5.8.2>On mine own sword? whiles I see lives, the gashes</A><br> -<A NAME=5.8.3>Do better upon them.</A><br> -<p><i>Enter MACDUFF</i></p> -</blockquote> - -<A NAME=speech2><b>MACDUFF</b></a> -<blockquote> -<A NAME=5.8.4>Turn, hell-hound, turn!</A><br> -</blockquote> - -<A NAME=speech3><b>MACBETH</b></a> -<blockquote> -<A NAME=5.8.5>Of all men else I have avoided thee:</A><br> -<A NAME=5.8.6>But get thee back; my soul is too much charged</A><br> -<A NAME=5.8.7>With blood of thine already.</A><br> -</blockquote> - -<A NAME=speech4><b>MACDUFF</b></a> -<blockquote> -<A NAME=5.8.8>I have no words:</A><br> -<A NAME=5.8.9>My voice is in my sword: thou bloodier villain</A><br> -<A NAME=5.8.10>Than terms can give thee out!</A><br> -<p><i>They fight</i></p> -</blockquote> - -<A NAME=speech5><b>MACBETH</b></a> -<blockquote> -<A NAME=5.8.11>Thou losest labour:</A><br> -<A NAME=5.8.12>As easy mayst thou the intrenchant air</A><br> -<A NAME=5.8.13>With thy keen sword impress as make me bleed:</A><br> -<A NAME=5.8.14>Let fall thy blade on vulnerable crests;</A><br> -<A NAME=5.8.15>I bear a charmed life, which must not yield,</A><br> -<A NAME=5.8.16>To one of woman born.</A><br> -</blockquote> - -<A NAME=speech6><b>MACDUFF</b></a> -<blockquote> -<A NAME=5.8.17>Despair thy charm;</A><br> -<A NAME=5.8.18>And let the angel whom thou still hast served</A><br> -<A NAME=5.8.19>Tell thee, Macduff was from his mother's womb</A><br> -<A NAME=5.8.20>Untimely ripp'd.</A><br> -</blockquote> - -<A NAME=speech7><b>MACBETH</b></a> -<blockquote> -<A NAME=5.8.21>Accursed be that tongue that tells me so,</A><br> -<A NAME=5.8.22>For it hath cow'd my better part of man!</A><br> -<A NAME=5.8.23>And be these juggling fiends no more believed,</A><br> -<A NAME=5.8.24>That palter with us in a double sense;</A><br> -<A NAME=5.8.25>That keep the word of promise to our ear,</A><br> -<A NAME=5.8.26>And break it to our hope. I'll not fight with thee.</A><br> -</blockquote> - -<A NAME=speech8><b>MACDUFF</b></a> -<blockquote> -<A NAME=5.8.27>Then yield thee, coward,</A><br> -<A NAME=5.8.28>And live to be the show and gaze o' the time:</A><br> -<A NAME=5.8.29>We'll have thee, as our rarer monsters are,</A><br> -<A NAME=5.8.30>Painted on a pole, and underwrit,</A><br> -<A NAME=5.8.31>'Here may you see the tyrant.'</A><br> -</blockquote> - -<A NAME=speech9><b>MACBETH</b></a> -<blockquote> -<A NAME=5.8.32>I will not yield,</A><br> -<A NAME=5.8.33>To kiss the ground before young Malcolm's feet,</A><br> -<A NAME=5.8.34>And to be baited with the rabble's curse.</A><br> -<A NAME=5.8.35>Though Birnam wood be come to Dunsinane,</A><br> -<A NAME=5.8.36>And thou opposed, being of no woman born,</A><br> -<A NAME=5.8.37>Yet I will try the last. Before my body</A><br> -<A NAME=5.8.38>I throw my warlike shield. Lay on, Macduff,</A><br> -<A NAME=5.8.39>And damn'd be him that first cries, 'Hold, enough!'</A><br> -<p><i>Exeunt, fighting. Alarums</i></p> -<p><i>Retreat. Flourish. Enter, with drum and colours, MALCOLM, SIWARD, ROSS, the other Thanes, and Soldiers</i></p> -</blockquote> - -<A NAME=speech10><b>MALCOLM</b></a> -<blockquote> -<A NAME=5.8.40>I would the friends we miss were safe arrived.</A><br> -</blockquote> - -<A NAME=speech11><b>SIWARD</b></a> -<blockquote> -<A NAME=5.8.41>Some must go off: and yet, by these I see,</A><br> -<A NAME=5.8.42>So great a day as this is cheaply bought.</A><br> -</blockquote> - -<A NAME=speech12><b>MALCOLM</b></a> -<blockquote> -<A NAME=5.8.43>Macduff is missing, and your noble son.</A><br> -</blockquote> - -<A NAME=speech13><b>ROSS</b></a> -<blockquote> -<A NAME=5.8.44>Your son, my lord, has paid a soldier's debt:</A><br> -<A NAME=5.8.45>He only lived but till he was a man;</A><br> -<A NAME=5.8.46>The which no sooner had his prowess confirm'd</A><br> -<A NAME=5.8.47>In the unshrinking station where he fought,</A><br> -<A NAME=5.8.48>But like a man he died.</A><br> -</blockquote> - -<A NAME=speech14><b>SIWARD</b></a> -<blockquote> -<A NAME=5.8.49>Then he is dead?</A><br> -</blockquote> - -<A NAME=speech15><b>ROSS</b></a> -<blockquote> -<A NAME=5.8.50>Ay, and brought off the field: your cause of sorrow</A><br> -<A NAME=5.8.51>Must not be measured by his worth, for then</A><br> -<A NAME=5.8.52>It hath no end.</A><br> -</blockquote> - -<A NAME=speech16><b>SIWARD</b></a> -<blockquote> -<A NAME=5.8.53> Had he his hurts before?</A><br> -</blockquote> - -<A NAME=speech17><b>ROSS</b></a> -<blockquote> -<A NAME=5.8.54>Ay, on the front.</A><br> -</blockquote> - -<A NAME=speech18><b>SIWARD</b></a> -<blockquote> -<A NAME=5.8.55> Why then, God's soldier be he!</A><br> -<A NAME=5.8.56>Had I as many sons as I have hairs,</A><br> -<A NAME=5.8.57>I would not wish them to a fairer death:</A><br> -<A NAME=5.8.58>And so, his knell is knoll'd.</A><br> -</blockquote> - -<A NAME=speech19><b>MALCOLM</b></a> -<blockquote> -<A NAME=5.8.59>He's worth more sorrow,</A><br> -<A NAME=5.8.60>And that I'll spend for him.</A><br> -</blockquote> - -<A NAME=speech20><b>SIWARD</b></a> -<blockquote> -<A NAME=5.8.61>He's worth no more</A><br> -<A NAME=5.8.62>They say he parted well, and paid his score:</A><br> -<A NAME=5.8.63>And so, God be with him! Here comes newer comfort.</A><br> -<p><i>Re-enter MACDUFF, with MACBETH's head</i></p> -</blockquote> - -<A NAME=speech21><b>MACDUFF</b></a> -<blockquote> -<A NAME=5.8.64>Hail, king! for so thou art: behold, where stands</A><br> -<A NAME=5.8.65>The usurper's cursed head: the time is free:</A><br> -<A NAME=5.8.66>I see thee compass'd with thy kingdom's pearl,</A><br> -<A NAME=5.8.67>That speak my salutation in their minds;</A><br> -<A NAME=5.8.68>Whose voices I desire aloud with mine:</A><br> -<A NAME=5.8.69>Hail, King of Scotland!</A><br> -</blockquote> - -<A NAME=speech22><b>ALL</b></a> -<blockquote> -<A NAME=5.8.70>Hail, King of Scotland!</A><br> -<p><i>Flourish</i></p> -</blockquote> - -<A NAME=speech23><b>MALCOLM</b></a> -<blockquote> -<A NAME=5.8.71>We shall not spend a large expense of time</A><br> -<A NAME=5.8.72>Before we reckon with your several loves,</A><br> -<A NAME=5.8.73>And make us even with you. My thanes and kinsmen,</A><br> -<A NAME=5.8.74>Henceforth be earls, the first that ever Scotland</A><br> -<A NAME=5.8.75>In such an honour named. What's more to do,</A><br> -<A NAME=5.8.76>Which would be planted newly with the time,</A><br> -<A NAME=5.8.77>As calling home our exiled friends abroad</A><br> -<A NAME=5.8.78>That fled the snares of watchful tyranny;</A><br> -<A NAME=5.8.79>Producing forth the cruel ministers</A><br> -<A NAME=5.8.80>Of this dead butcher and his fiend-like queen,</A><br> -<A NAME=5.8.81>Who, as 'tis thought, by self and violent hands</A><br> -<A NAME=5.8.82>Took off her life; this, and what needful else</A><br> -<A NAME=5.8.83>That calls upon us, by the grace of Grace,</A><br> -<A NAME=5.8.84>We will perform in measure, time and place:</A><br> -<A NAME=5.8.85>So, thanks to all at once and to each one,</A><br> -<A NAME=5.8.86>Whom we invite to see us crown'd at Scone.</A><br> -<p><i>Flourish. Exeunt</i></p> -</body> -</html> diff --git a/testing/marionette/harness/marionette_harness/www/modal_dialogs.html b/testing/marionette/harness/marionette_harness/www/modal_dialogs.html deleted file mode 100644 index 8da5b92a7..000000000 --- a/testing/marionette/harness/marionette_harness/www/modal_dialogs.html +++ /dev/null @@ -1,39 +0,0 @@ -<!-- 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/. --> - -<!DOCTYPE html> -<html> -<head> - <title>Marionette Test</title> - <script type="text/javascript"> - function handleAlert () { - window.alert('Marionette alert'); - } - - function handleConfirm () { - var alertAccepted = window.confirm('Marionette confirm'); - document.getElementById('confirm-result').innerHTML = alertAccepted; - } - - function handlePrompt () { - var promptText = window.prompt('Marionette prompt'); - document.getElementById('prompt-result').innerHTML = promptText === null ? 'null' : promptText; - } - - function onBeforeUnload () { - window.onbeforeunload = function () { return "Are you sure?"; } - } - </script> -</head> -<body> - <a href="#" id="modal-alert" onclick="handleAlert()">Open an alert dialog.</a> - <a href="#" id="modal-confirm" onclick="handleConfirm()">Open a confirm dialog.</a> - <a href="#" id="modal-prompt" onclick="handlePrompt()">Open a prompt dialog.</a> - <a href="#" id="onbeforeunload-handler" onclick="onBeforeUnload()">Add an onbeforeunload handler.</a> - <a href="#" id="click-handler" onclick="document.getElementById('click-result').innerHTML='result';">Make text appear.</a> - <div id="confirm-result"></div> - <div id="prompt-result"></div> - <div id="click-result"></div> -</body> -</html> diff --git a/testing/marionette/harness/marionette_harness/www/nestedElements.html b/testing/marionette/harness/marionette_harness/www/nestedElements.html deleted file mode 100644 index 618bf3231..000000000 --- a/testing/marionette/harness/marionette_harness/www/nestedElements.html +++ /dev/null @@ -1,9 +0,0 @@ -<!DOCTYPE html> -<a href="1.html">hello world</a> -<a href="1.html">hello world</a><a href="1.html">hello world</a> -<div name="div1"> - <a href="2.html" name="link1">hello world</a> - <a href="2.html" name="link2">hello world</a> -</div> - -<a href="1.html">hello world</a><a href="1.html">hello world</a><a href="1.html">hello world</a> diff --git a/testing/marionette/harness/marionette_harness/www/rectangles.html b/testing/marionette/harness/marionette_harness/www/rectangles.html deleted file mode 100644 index 59871c6de..000000000 --- a/testing/marionette/harness/marionette_harness/www/rectangles.html +++ /dev/null @@ -1,40 +0,0 @@ -<?xml version="1.0"?>
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-<head>
- <title>Rectangles</title>
- <style type="text/css">
- div {
- position: absolute;
- margin: 0;
- border: 0;
- padding: 0;
- }
- #r1 {
- background-color: blue;
- left: 10px;
- top: 10px;
- width: 100px;
- height: 50px;
- }
- #r2 {
- background-color: red;
- left: 11px;
- top: 10px;
- width: 48.666666667px;
- height: 49.333333333px;
- }
- #r3 {
- background-color: yellow;
- left: 60px;
- top: 10px;
- width: 50px;
- height: 25px;
- }
- </style>
-</head>
- <body>
- <div id="r1">r1</div>
- <div id="r2">r2</div>
- <div id="r3">r3</div>
- </body>
-</html>
diff --git a/testing/marionette/harness/marionette_harness/www/resultPage.html b/testing/marionette/harness/marionette_harness/www/resultPage.html deleted file mode 100644 index 342e9930b..000000000 --- a/testing/marionette/harness/marionette_harness/www/resultPage.html +++ /dev/null @@ -1,17 +0,0 @@ -<!-- 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/. --> - -<html> -<head> - <title>We Arrive Here</title> -</head> -<body> - - -<div> - <input type='text' id='email'/> -</div> -</body> -</html> - diff --git a/testing/marionette/harness/marionette_harness/www/scroll.html b/testing/marionette/harness/marionette_harness/www/scroll.html deleted file mode 100644 index 8a654bb50..000000000 --- a/testing/marionette/harness/marionette_harness/www/scroll.html +++ /dev/null @@ -1,30 +0,0 @@ -<!-- 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/. --> - -<html> -<head></head> -<body> - <script> - function dump(event) { - var elt = event.target || event.srcElement; - document.getElementById('clicked').innerHTML = elt.innerHTML; - } - </script> - <div style='height: 150px'></div> - <ul style='overflow: scroll; width: 150px; height: 80px; background-color: yellow' onclick="dump(event)"> - <li id='line1'>line1</li> - <li id='line2'>line2</li> - <li id='line3'>line3</li> - <li id='line4'>line4</li> - <li id='line5'>line5</li> - <li id='line6'>line6</li> - <li id='line7'>line7</li> - <li id='line8'>line8</li> - <li id='line9'>line9</li> - </ul> - <div> - Clicked: <span id='clicked'></span> - </div> -</body> -</html> diff --git a/testing/marionette/harness/marionette_harness/www/scroll2.html b/testing/marionette/harness/marionette_harness/www/scroll2.html deleted file mode 100644 index bd00f0e22..000000000 --- a/testing/marionette/harness/marionette_harness/www/scroll2.html +++ /dev/null @@ -1,24 +0,0 @@ -<!-- 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/. --> - -<html> -<head></head> -<body> - <ul style='overflow: scroll; height: 100px;'> - <li></li> - <li></li> - <li id="desired">Text</li> - <li></li> - <li></li> - <li></li> - <li></li> - <li></li> - <li></li> - <li></li> - <li></li> - <li></li> - <li></li> - </ul> -</body> -</html> diff --git a/testing/marionette/harness/marionette_harness/www/scroll3.html b/testing/marionette/harness/marionette_harness/www/scroll3.html deleted file mode 100644 index b615e38c3..000000000 --- a/testing/marionette/harness/marionette_harness/www/scroll3.html +++ /dev/null @@ -1,18 +0,0 @@ -<!-- 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/. --> - -<html> -<head> - <meta http-equiv="Content-Type" content="text/html; charset=windows-1251"> - <style type="text/css"></style> -</head> -<body> -<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br> -<button id="button1">Button1</button> -<br><br><br><br> - - <button id="button2">Button2</button> -<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br> -</body> -</html> diff --git a/testing/marionette/harness/marionette_harness/www/scroll4.html b/testing/marionette/harness/marionette_harness/www/scroll4.html deleted file mode 100644 index ce0df0313..000000000 --- a/testing/marionette/harness/marionette_harness/www/scroll4.html +++ /dev/null @@ -1,15 +0,0 @@ -<!-- 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/. --> - -<html> -<head> - <meta http-equiv="Content-Type" content="text/html; charset=windows-1251"> - <style type="text/css"></style> -</head> -<body> -<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br> -<input type="radio" id="radio"> -<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br> -</body> -</html> diff --git a/testing/marionette/harness/marionette_harness/www/scroll5.html b/testing/marionette/harness/marionette_harness/www/scroll5.html deleted file mode 100644 index 3dd00721e..000000000 --- a/testing/marionette/harness/marionette_harness/www/scroll5.html +++ /dev/null @@ -1,20 +0,0 @@ -<!-- 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/. --> - -<html> -<head></head> -<body> - <script> - function dump(text) { - document.getElementById('clicked').innerHTML = text; - } - </script> - <div style='overflow: scroll; width: 150px; height: 200px; background-color: yellow' id="outer"> - <div style="width: 150px; height: 5000px; background-color: red;" onclick="dump('clicked')" id="inner"></div> - </div> - <div> - Clicked: <span id='clicked'></span> - </div> -</body> -</html> diff --git a/testing/marionette/harness/marionette_harness/www/shim.js b/testing/marionette/harness/marionette_harness/www/shim.js deleted file mode 100644 index 2a74e6949..000000000 --- a/testing/marionette/harness/marionette_harness/www/shim.js +++ /dev/null @@ -1,282 +0,0 @@ -/** -* mouse_event_shim.js: generate mouse events from touch events. -* -* This library listens for touch events and generates mousedown, mousemove -* mouseup, and click events to match them. It captures and dicards any -* real mouse events (non-synthetic events with isTrusted true) that are -* send by gecko so that there are not duplicates. -* -* This library does emit mouseover/mouseout and mouseenter/mouseleave -* events. You can turn them off by setting MouseEventShim.trackMouseMoves to -* false. This means that mousemove events will always have the same target -* as the mousedown even that began the series. You can also call -* MouseEventShim.setCapture() from a mousedown event handler to prevent -* mouse tracking until the next mouseup event. -* -* This library does not support multi-touch but should be sufficient -* to do drags based on mousedown/mousemove/mouseup events. -* -* This library does not emit dblclick events or contextmenu events -*/ - -'use strict'; - -(function() { - // Make sure we don't run more than once - if (MouseEventShim) - return; - - // Bail if we're not on running on a platform that sends touch - // events. We don't need the shim code for mouse events. - try { - document.createEvent('TouchEvent'); - } catch (e) { - return; - } - - var starttouch; // The Touch object that we started with - var target; // The element the touch is currently over - var emitclick; // Will we be sending a click event after mouseup? - - // Use capturing listeners to discard all mouse events from gecko - window.addEventListener('mousedown', discardEvent, true); - window.addEventListener('mouseup', discardEvent, true); - window.addEventListener('mousemove', discardEvent, true); - window.addEventListener('click', discardEvent, true); - - function discardEvent(e) { - if (e.isTrusted) { - e.stopImmediatePropagation(); // so it goes no further - if (e.type === 'click') - e.preventDefault(); // so it doesn't trigger a change event - } - } - - // Listen for touch events that bubble up to the window. - // If other code has called stopPropagation on the touch events - // then we'll never see them. Also, we'll honor the defaultPrevented - // state of the event and will not generate synthetic mouse events - window.addEventListener('touchstart', handleTouchStart); - window.addEventListener('touchmove', handleTouchMove); - window.addEventListener('touchend', handleTouchEnd); - window.addEventListener('touchcancel', handleTouchEnd); // Same as touchend - - function handleTouchStart(e) { - // If we're already handling a touch, ignore this one - if (starttouch) - return; - - // Ignore any event that has already been prevented - if (e.defaultPrevented) - return; - - // Sometimes an unknown gecko bug causes us to get a touchstart event - // for an iframe target that we can't use because it is cross origin. - // Don't start handling a touch in that case - try { - e.changedTouches[0].target.ownerDocument; - } - catch (e) { - // Ignore the event if we can't see the properties of the target - return; - } - - // If there is more than one simultaneous touch, ignore all but the first - starttouch = e.changedTouches[0]; - target = starttouch.target; - emitclick = true; - - // Move to the position of the touch - emitEvent('mousemove', target, starttouch); - - // Now send a synthetic mousedown - var result = emitEvent('mousedown', target, starttouch); - - // If the mousedown was prevented, pass that on to the touch event. - // And remember not to send a click event - if (!result) { - e.preventDefault(); - emitclick = false; - } - } - - function handleTouchEnd(e) { - if (!starttouch) - return; - - // End a MouseEventShim.setCapture() call - if (MouseEventShim.capturing) { - MouseEventShim.capturing = false; - MouseEventShim.captureTarget = null; - } - - for (var i = 0; i < e.changedTouches.length; i++) { - var touch = e.changedTouches[i]; - // If the ended touch does not have the same id, skip it - if (touch.identifier !== starttouch.identifier) - continue; - - emitEvent('mouseup', target, touch); - - // If target is still the same element we started and the touch did not - // move more than the threshold and if the user did not prevent - // the mousedown, then send a click event, too. - if (emitclick) - emitEvent('click', starttouch.target, touch); - - starttouch = null; - return; - } - } - - function handleTouchMove(e) { - if (!starttouch) - return; - - for (var i = 0; i < e.changedTouches.length; i++) { - var touch = e.changedTouches[i]; - // If the ended touch does not have the same id, skip it - if (touch.identifier !== starttouch.identifier) - continue; - - // Don't send a mousemove if the touchmove was prevented - if (e.defaultPrevented) - return; - - // See if we've moved too much to emit a click event - var dx = Math.abs(touch.screenX - starttouch.screenX); - var dy = Math.abs(touch.screenY - starttouch.screenY); - if (dx > MouseEventShim.dragThresholdX || - dy > MouseEventShim.dragThresholdY) { - emitclick = false; - } - - var tracking = MouseEventShim.trackMouseMoves && - !MouseEventShim.capturing; - - if (tracking) { - // If the touch point moves, then the element it is over - // may have changed as well. Note that calling elementFromPoint() - // forces a layout if one is needed. - // XXX: how expensive is it to do this on each touchmove? - // Can we listen for (non-standard) touchleave events instead? - var oldtarget = target; - var newtarget = document.elementFromPoint(touch.clientX, touch.clientY); - if (newtarget === null) { - // this can happen as the touch is moving off of the screen, e.g. - newtarget = oldtarget; - } - if (newtarget !== oldtarget) { - leave(oldtarget, newtarget, touch); // mouseout, mouseleave - target = newtarget; - } - } - else if (MouseEventShim.captureTarget) { - target = MouseEventShim.captureTarget; - } - - emitEvent('mousemove', target, touch); - - if (tracking && newtarget !== oldtarget) { - enter(newtarget, oldtarget, touch); // mouseover, mouseenter - } - } - } - - // Return true if element a contains element b - function contains(a, b) { - return (a.compareDocumentPosition(b) & 16) !== 0; - } - - // A touch has left oldtarget and entered newtarget - // Send out all the events that are required - function leave(oldtarget, newtarget, touch) { - emitEvent('mouseout', oldtarget, touch, newtarget); - - // If the touch has actually left oldtarget (and has not just moved - // into a child of oldtarget) send a mouseleave event. mouseleave - // events don't bubble, so we have to repeat this up the hierarchy. - for (var e = oldtarget; !contains(e, newtarget); e = e.parentNode) { - emitEvent('mouseleave', e, touch, newtarget); - } - } - - // A touch has entered newtarget from oldtarget - // Send out all the events that are required. - function enter(newtarget, oldtarget, touch) { - emitEvent('mouseover', newtarget, touch, oldtarget); - - // Emit non-bubbling mouseenter events if the touch actually entered - // newtarget and wasn't already in some child of it - for (var e = newtarget; !contains(e, oldtarget); e = e.parentNode) { - emitEvent('mouseenter', e, touch, oldtarget); - } - } - - function emitEvent(type, target, touch, relatedTarget) { - var synthetic = document.createEvent('MouseEvents'); - var bubbles = (type !== 'mouseenter' && type !== 'mouseleave'); - var count = - (type === 'mousedown' || type === 'mouseup' || type === 'click') ? 1 : 0; - - synthetic.initMouseEvent(type, - bubbles, // canBubble - true, // cancelable - window, - count, // detail: click count - touch.screenX, - touch.screenY, - touch.clientX, - touch.clientY, - false, // ctrlKey: we don't have one - false, // altKey: we don't have one - false, // shiftKey: we don't have one - false, // metaKey: we don't have one - 0, // we're simulating the left button - relatedTarget || null); - - try { - return target.dispatchEvent(synthetic); - } - catch (e) { - console.warn('Exception calling dispatchEvent', type, e); - return true; - } - } -}()); - -var MouseEventShim = { - // It is a known gecko bug that synthetic events have timestamps measured - // in microseconds while regular events have timestamps measured in - // milliseconds. This utility function returns a the timestamp converted - // to milliseconds, if necessary. - getEventTimestamp: function(e) { - if (e.isTrusted) // XXX: Are real events always trusted? - return e.timeStamp; - else - return e.timeStamp / 1000; - }, - - // Set this to false if you don't care about mouseover/out events - // and don't want the target of mousemove events to follow the touch - trackMouseMoves: true, - - // Call this function from a mousedown event handler if you want to guarantee - // that the mousemove and mouseup events will go to the same element - // as the mousedown even if they leave the bounds of the element. This is - // like setting trackMouseMoves to false for just one drag. It is a - // substitute for event.target.setCapture(true) - setCapture: function(target) { - this.capturing = true; // Will be set back to false on mouseup - if (target) - this.captureTarget = target; - }, - - capturing: false, - - // Keep these in sync with ui.dragThresholdX and ui.dragThresholdY prefs. - // If a touch ever moves more than this many pixels from its starting point - // then we will not synthesize a click event when the touch ends. - dragThresholdX: 25, - dragThresholdY: 25 -}; diff --git a/testing/marionette/harness/marionette_harness/www/test.html b/testing/marionette/harness/marionette_harness/www/test.html deleted file mode 100644 index 053ff171d..000000000 --- a/testing/marionette/harness/marionette_harness/www/test.html +++ /dev/null @@ -1,38 +0,0 @@ -<!-- 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/. --> - -<!DOCTYPE html> -<html> -<head> -<title>Marionette Test</title> -</head> -<body> - <h1 id="testh1">Test Page</h1> - <script type="text/javascript"> - window.ready = true; - function addDelayedElement() { - setTimeout(createDiv, 2000); - function createDiv() { - var newDiv = document.createElement("div"); - newDiv.id = "newDiv"; - var newContent = document.createTextNode("I am a newly created div!"); - newDiv.appendChild(newContent); - document.body.appendChild(newDiv); - } - } - function clicked() { - var link = document.getElementById("mozLink"); - link.innerHTML = "Clicked"; - } - </script> - <a href="#" id="mozLink" class="linkClass" onclick="clicked()">Click me!</a> - <div id="testDiv"> - <a href="#" id="divLink" class="linkClass" onclick="clicked()">Div click me!</a> - <a href="#" id="divLink2" class="linkClass" onclick="clicked()">Div click me!</a> - </div> - <input name="myInput" type="text" value="asdf"/> - <input name="myCheckBox" type="checkbox" /> - <input id="createDivButton" type="button" value="create a div" onclick="addDelayedElement()" /> -</body> -</html> diff --git a/testing/marionette/harness/marionette_harness/www/testAction.html b/testing/marionette/harness/marionette_harness/www/testAction.html deleted file mode 100644 index eb7e44f3e..000000000 --- a/testing/marionette/harness/marionette_harness/www/testAction.html +++ /dev/null @@ -1,94 +0,0 @@ -<!-- 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/. --> - -<!DOCTYPE html> - -<html> -<meta charset="UTF-8"> -<head> -<title>Marionette Test</title> -</head> -<body> - <h1 id="testh1">Test Page</h1> - <button id="button1" style="position:absolute;left:0px;top:55px;" type="button" allowevents=true>button1</button> - <button id="button2" style="position:absolute;left:0px;top:355px;" type="button" allowevents=true>button2</button> - <button id="button3" style="position:absolute;left:0px;top:455px;" type="button" allowevents=true>button3</button> - <button id="button4" style="position:absolute;left:100px;top:455px;" type="button" allowevents=true>button4</button> - <button id="buttonScroll" style="position:absolute;left:100px;top:855px;" type="button" allowevents=true>buttonScroll</button> - <h2 id="hidden" style="visibility: hidden" class="linkClass">Hidden</h2> - <button id="buttonFlick" style="position:absolute;left:0px;top:255px;" type="button" allowevents=true>buttonFlick</button> - <script type="text/javascript"> - var button3Timer = null; - var button4Timer = null; - //appends passed in text to the innerHTML of the event's target - function appendText(text) { - return function(evt) { - var element; - if (evt.type.indexOf("touch") !== -1) { - if (evt.type == "touchstart") { - element = evt.target; - } - else { - //since the target of touchstart is the target of all subsequent events, then - //changedTouches holds the current coordinates of this touch event, so we - //use these coordinates to find the element under the touch event - var touches = evt.changedTouches; - var x = touches[0].clientX; - var y = touches[0].clientY; - element = document.elementFromPoint(x,y); - } - } - //handle mouse events or contextmenu - else { - element = evt.target; - } - element.innerHTML += text; - }; - }; - //use this function outside of attachListeners when you want to test sendMouseOnlyEvents on a target - function attachMouseListeners(element) { - element.addEventListener("contextmenu", appendText("-contextmenu"), false); - element.addEventListener("mousedown", appendText("-mousedown"), false); - element.addEventListener("mousemove", appendText("-mousemove"), false); - element.addEventListener("mouseup", appendText("-mouseup"), false); - element.addEventListener("click", appendText("-click"), false); - }; - function attachListeners(id) { - var element = document.getElementById(id); - element.addEventListener("touchstart", appendText("-touchstart"), false); - element.addEventListener("touchmove", appendText("-touchmove"), false); - element.addEventListener("touchend", appendText("-touchend"), false); - element.addEventListener("touchcancel", appendText("-touchcancel"), false); - attachMouseListeners(element); - }; - //for tracking time on an element - function addTimers(id, timer) { - var element = document.getElementById(id); - element.addEventListener("touchstart", function(evt) { timer = (new Date()).getTime();}, false); - element.addEventListener("touchend", function(evt) { timer = (new Date()).getTime() - timer; evt.target.innerHTML += "-" + timer;}, false); - } - attachListeners("button1"); - attachListeners("button2"); - attachListeners("button3"); - attachListeners("button4"); - attachListeners("buttonScroll"); - addTimers("button3"); - addTimers("button4"); - var buttonFlick = document.getElementById("buttonFlick"); - attachMouseListeners(buttonFlick); - function createDelayed() { - var newButton = document.createElement("button"); - newButton.id = "delayed"; - newButton.setAttribute("style", "position:absolute;left:220px;top:455px;"); - var content = document.createTextNode("delayed"); - newButton.appendChild(content); - document.body.appendChild(newButton); - newButton.addEventListener("mousemove", appendText("-mousemove"), false); - newButton.addEventListener("mouseup", appendText("-mouseup"), false); - newButton.addEventListener("click", appendText("-click"), false); - }; - window.setTimeout(createDelayed, 5000); - </script> -</body> -</html> diff --git a/testing/marionette/harness/marionette_harness/www/testPageSource.html b/testing/marionette/harness/marionette_harness/www/testPageSource.html deleted file mode 100644 index f19b9d30c..000000000 --- a/testing/marionette/harness/marionette_harness/www/testPageSource.html +++ /dev/null @@ -1,14 +0,0 @@ - -<!-- 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/. --> - -<!DOCTYPE html> -<html> -<head> -<title>PageSource Test</title> -</head> -<body> - <p> Check the PageSource -</body> -</html> diff --git a/testing/marionette/harness/marionette_harness/www/testPageSource.xml b/testing/marionette/harness/marionette_harness/www/testPageSource.xml deleted file mode 100644 index 1480a1f38..000000000 --- a/testing/marionette/harness/marionette_harness/www/testPageSource.xml +++ /dev/null @@ -1,5 +0,0 @@ -<xml> - <foo> - <bar>baz</bar> - </foo> -</xml> diff --git a/testing/marionette/harness/marionette_harness/www/testPageSourceWithUnicodeChars.html b/testing/marionette/harness/marionette_harness/www/testPageSourceWithUnicodeChars.html deleted file mode 100644 index d16cf52c8..000000000 --- a/testing/marionette/harness/marionette_harness/www/testPageSourceWithUnicodeChars.html +++ /dev/null @@ -1,11 +0,0 @@ -<!DOCTYPE html> -<html> - <head> - <meta charset="utf-8"/> - <meta http-equiv="pragma" content="no-cache"/> - <!-- - - the « section[id^="wifi-"] » selector. - --> - </body> -</html> - diff --git a/testing/marionette/harness/marionette_harness/www/testSize.html b/testing/marionette/harness/marionette_harness/www/testSize.html deleted file mode 100644 index 1df27ad23..000000000 --- a/testing/marionette/harness/marionette_harness/www/testSize.html +++ /dev/null @@ -1,9 +0,0 @@ -<?xml version="1.0"?> -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> - <head> - <title>Test page for element size</title> - </head> - <body> - <p>Let's get the size of <a href='#' id='linkId'>some really cool link</a></p> - </body> -</html> diff --git a/testing/marionette/harness/marionette_harness/www/test_accessibility.html b/testing/marionette/harness/marionette_harness/www/test_accessibility.html deleted file mode 100644 index 8cc9fd649..000000000 --- a/testing/marionette/harness/marionette_harness/www/test_accessibility.html +++ /dev/null @@ -1,57 +0,0 @@ -<!-- 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/. --> - -<!DOCTYPE html> - -<html> -<meta charset="UTF-8"> -<head> -<title>Marionette Test</title> -</head> -<body> - <button id="button1">button1</button> - <button id="button2" aria-label="button2"></button> - <span id="button3">I am a bad button with no accessible</span> - <h1 id="button4">I am a bad button that is actually a header</h1> - <h1 id="button5"> - I am a bad button that is actually an actionable header with a listener - </h1> - <button id="button6"></button> - <button id="button7" aria-hidden="true">button7</button> - <div aria-hidden="true"> - <button id="button8">button8</button> - </div> - <button id="button9" style="position:absolute;left:-100px;top:-455px;"> - button9 - </button> - <button id="button10" style="visibility:hidden;"> - button10 - </button> - <span id="no_accessible_but_displayed">I have no accessible object</span> - <button id="button11" disabled>button11</button> - <button id="button12" aria-disabled="true">button12</button> - <span id="no_accessible_but_disabled" disabled>I have no accessible object</span> - <span id="button13" tabindex="0" role="button" aria-label="Span button">Span button</span> - <span id="button14" role="button" aria-label="Span button">Unexplorable Span button</span> - <button id="button15" style="pointer-events:none;">button15</button> - <div style="pointer-events:none;"> - <button id="button16">button16</button> - </div> - <div style="pointer-events:none;"> - <button style="pointer-events:all;" id="button17">button17</button> - </div> - <input id="input1" title="My Input 1" name="myInput1" type="text" value="asdf"/> - <select> - <option id="option1" value="val1">Val1</option> - <option id="option2" value="val2" selected>Val2</option> - </select> - <script> - 'use strict'; - document.getElementById('button5').addEventListener('click', function() { - // A pseudo button that has a listener but is missing button semantics. - return true; - }); - </script> -</body> -</html> diff --git a/testing/marionette/harness/marionette_harness/www/test_carets_columns.html b/testing/marionette/harness/marionette_harness/www/test_carets_columns.html deleted file mode 100644 index 495236108..000000000 --- a/testing/marionette/harness/marionette_harness/www/test_carets_columns.html +++ /dev/null @@ -1,31 +0,0 @@ -<!DOCTYPE html> -<!-- Any copyright is dedicated to the Public Domain. - - http://creativecommons.org/publicdomain/zero/1.0/ --> - -<html> - <head> - <meta charset="UTF-8"> - <style> - #columns { - -moz-column-count: 2; - -webkit-column-count: 2; - -moz-column-rule: 1px solid lightgray; - -webkit-column-rule: 1px solid lightgray; - border: 1px solid lightblue; - width: 450px; - } - </style> - </head> - <body> - <div id="columns"> - <div id="columns-inner" style="border: 1px solid red;" contenteditable="true"> - <p id="before-image-1">Before image 1</p> - <p><img width="100px" height="30px" src="data:image/gif;base64,R0lGODlhAQABAIABAAD/AP///ywAAAAAAQABAAACAkQBADs="></p> - <p>After image 1</p> - <p>Before image 2</p> - <p><img width="100px" height="30px" src="data:image/gif;base64,R0lGODlhAQABAIABAAD/AP///ywAAAAAAQABAAACAkQBADs="></p> - <p>After image 2</p> - </div> - </div> - </body> -</html> diff --git a/testing/marionette/harness/marionette_harness/www/test_carets_cursor.html b/testing/marionette/harness/marionette_harness/www/test_carets_cursor.html deleted file mode 100644 index fdbd6fe7a..000000000 --- a/testing/marionette/harness/marionette_harness/www/test_carets_cursor.html +++ /dev/null @@ -1,31 +0,0 @@ -<!-- 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/. --> - -<!DOCTYPE html> -<html id="html"> - <head> - <title>Marionette tests for AccessibleCaret in cursor mode</title> - <style> - .block { - width: 10em; - height: 6em; - word-wrap: break-word; - overflow: auto; - } - </style> - </head> - <body> - <div> - <input id="input" value="ABCDEFGHI"> - <input id="input-padding" style="padding: 1em;" value="ABCDEFGHI"> - </div> - <br> - <div> - <textarea name="textarea" id="textarea" rows="4" cols="6">ABCDEFGHI</textarea> - <textarea id="textarea-one-line" rows="3">ABCDEFGHI</textarea> - </div> - <br> - <div class="block" contenteditable="true" id="contenteditable">ABCDEFGHI</div> - </body> -</html> diff --git a/testing/marionette/harness/marionette_harness/www/test_carets_display_none.html b/testing/marionette/harness/marionette_harness/www/test_carets_display_none.html deleted file mode 100644 index 766f32001..000000000 --- a/testing/marionette/harness/marionette_harness/www/test_carets_display_none.html +++ /dev/null @@ -1,10 +0,0 @@ -<!-- 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/. --> - -<!DOCTYPE html> -<html id="html" style="display: none"> - <body> - <div id="content">ABC DEF GHI</div> - </body> -</html> diff --git a/testing/marionette/harness/marionette_harness/www/test_carets_iframe.html b/testing/marionette/harness/marionette_harness/www/test_carets_iframe.html deleted file mode 100644 index f6e6df9ba..000000000 --- a/testing/marionette/harness/marionette_harness/www/test_carets_iframe.html +++ /dev/null @@ -1,20 +0,0 @@ -<!-- 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/. --> - -<!DOCTYPE html> -<html id="html"> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> - <title>Marionette tests for AccessibleCaret in selection mode (iframe)</title> - </head> - <body> - <style> - * - { - -moz-user-select:none; - } - </style> - <iframe id="frame" src="test_carets_longtext.html" style="width: 10em; height: 8em;"></iframe> - </body> -</html> diff --git a/testing/marionette/harness/marionette_harness/www/test_carets_longtext.html b/testing/marionette/harness/marionette_harness/www/test_carets_longtext.html deleted file mode 100644 index 7e2495509..000000000 --- a/testing/marionette/harness/marionette_harness/www/test_carets_longtext.html +++ /dev/null @@ -1,9 +0,0 @@ -<html> - <head> - <title>Bug 1094072: Orientation change test for AccessibleCaret positions</title> - </head> - <body id="bd"> - <h3 id="longtext">long long text for orientation change test long long text for orientation change test long long text for orientation change test long long text for orientation change test</h3> - <div contenteditable="true" id="bottomtext">bottom text</div> -</body> -</html> diff --git a/testing/marionette/harness/marionette_harness/www/test_carets_multipleline.html b/testing/marionette/harness/marionette_harness/www/test_carets_multipleline.html deleted file mode 100644 index ff46a954b..000000000 --- a/testing/marionette/harness/marionette_harness/www/test_carets_multipleline.html +++ /dev/null @@ -1,24 +0,0 @@ -<!-- 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/. --> - -<!DOCTYPE html> -<html id="html"> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> - <title>Bug 1019441: Marionette tests for AccessibleCaret (multiple lines)</title> - </head> - <body> - <style> - * - { - -moz-user-select:none; - } - </style> - <div><textarea id="textarea2" style="width: 10em; height: 6em; overflow: auto;">First Line Second Line Third Line</textarea></div> - <br> - <div style="width: 10em; height: 6em; overflow: auto; -moz-user-select:text" id="contenteditable2" contenteditable="true">First Line<br><br>Second Line<br><br>Third Line</div> - <br> - <div style="width: 10em; height: 6em; overflow: auto; -moz-user-select:text" id="content2">First Line<br><br>Second Line<br><br>Third Line</div> - </body> -</html> diff --git a/testing/marionette/harness/marionette_harness/www/test_carets_multiplerange.html b/testing/marionette/harness/marionette_harness/www/test_carets_multiplerange.html deleted file mode 100644 index 394630c1f..000000000 --- a/testing/marionette/harness/marionette_harness/www/test_carets_multiplerange.html +++ /dev/null @@ -1,19 +0,0 @@ -<html> -<style> -h4 { - -moz-user-select: none; -} -</style> -<body id=bd> -<h3 id=sel1>user can select this 1</h3> -<h3 id=sel2>user can select this 2</h3> -<h3 id=sel3>user can select this 3</h3> -<h4 id=nonsel1>user cannot select this 1</h4> -<h4 id=nonsel2>user cannot select this 2</h4> -<h3 id=sel4>user can select this 4</h3> -<h3 id=sel5>user can select this 5</h3> -<h4 id=nonsel3>user cannot select this 3</h4> -<h3 id=sel6>user can select this 6</h3> -<h3 id=sel7>user can select this 7</h3> -</body> -</html> diff --git a/testing/marionette/harness/marionette_harness/www/test_carets_selection.html b/testing/marionette/harness/marionette_harness/www/test_carets_selection.html deleted file mode 100644 index f58b92fbf..000000000 --- a/testing/marionette/harness/marionette_harness/www/test_carets_selection.html +++ /dev/null @@ -1,38 +0,0 @@ -<!-- 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/. --> - -<!DOCTYPE html> -<html id="html"> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> - <title>Marionette tests for AccessibleCaret in selection mode</title> - <style> - .block { - width: 10em; - height: 4em; - word-wrap: break-word; - overflow: auto; - } - </style> - </head> - <body> - <div> - <input id="input" value="ABC DEF GHI"> - <input id="input-padding" style="padding: 1em;" value="ABC DEF GHI"> - </div> - <br> - <div> - <textarea id="textarea" rows="4" cols="8">ABC DEF GHI JKL MNO PQR</textarea> - <textarea id="textarea-one-line" rows="4" cols="12">ABC DEF GHI</textarea> - </div> - <br> - <div><textarea dir="rtl" id="textarea-rtl" rows="8" cols="8">موزيلا ÙيرÙكس موزيلا ÙيرÙكس</textarea></div> - <br> - <div class="block" contenteditable="true" id="contenteditable">ABC DEF GHI</div> - <br> - <div class="block" id="content">ABC DEF GHI</div> - <br> - <div style="-moz-user-select: none;" id="non-selectable">Non-selectable</div> - </body> -</html> diff --git a/testing/marionette/harness/marionette_harness/www/test_clearing.html b/testing/marionette/harness/marionette_harness/www/test_clearing.html deleted file mode 100644 index 2aa3c6a21..000000000 --- a/testing/marionette/harness/marionette_harness/www/test_clearing.html +++ /dev/null @@ -1,24 +0,0 @@ -<html> - <!-- 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/. --> - <body> - <input id="writableTextInput" type="text" value="Test"/> - - <input id="readOnlyTextInput" type="text" readonly value="Test"/> - - <input id="textInputnotenabled" type="text" disabled="true" value="Test"/> - - <textarea id="writableTextArea" rows="2" cols="20"> - This is a sample text area which is supposed to be cleared - </textarea> - - <textarea id="textAreaReadOnly" readonly rows="5" cols="20"> - text area which is not supposed to be cleared</textarea> - - <textarea rows="5" id="textAreaNotenabled" disabled="true" cols="20"> - text area which is not supposed to be cleared</textarea> - - <div id="content-editable" contentEditable="true">This is a contentEditable area</div> - </body> -</html> diff --git a/testing/marionette/harness/marionette_harness/www/test_dynamic.html b/testing/marionette/harness/marionette_harness/www/test_dynamic.html deleted file mode 100644 index dccc2c6ac..000000000 --- a/testing/marionette/harness/marionette_harness/www/test_dynamic.html +++ /dev/null @@ -1,38 +0,0 @@ -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" - "http://www.w3.org/TR/html4/loose.dtd"> -<html> - <head> - <title></title> - <script type="text/javascript"> - var next = 0; - - function addMore() { - var box = document.createElement('DIV'); - box.id = 'box' + next++; - box.className = 'redbox'; - box.style.width = '150px'; - box.style.height = '150px'; - box.style.backgroundColor = 'red'; - box.style.border = '1px solid black'; - box.style.margin = '5px'; - window.setTimeout(function() { - document.body.appendChild(box); - }, 1000); - } - - function reveal() { - var elem = document.getElementById('revealed'); - window.setTimeout(function() { - elem.style.display = ''; - }, 1000); - } - </script> - </head> - <body> - <input id="adder" type="button" value="Add a box!" onclick="addMore()"/> - - <input id="reveal" type="button" value="Reveal a new input" onclick="reveal();" /> - - <input id="revealed" style="display:none;" /> - </body> - </html> diff --git a/testing/marionette/harness/marionette_harness/www/test_iframe.html b/testing/marionette/harness/marionette_harness/www/test_iframe.html deleted file mode 100644 index 7ed88665c..000000000 --- a/testing/marionette/harness/marionette_harness/www/test_iframe.html +++ /dev/null @@ -1,16 +0,0 @@ -<!-- 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/. --> - -<!doctype html> -<html> -<head> -<title>Marionette IFrame Test</title> -</head> -<body> - <h1 id="iframe_page_heading">This is the heading</h1> - - <iframe src="test.html" id="test_iframe"></iframe> - <iframe src="test.html" id="test_iframe" name="test_iframe_name"></iframe> -</body> -</html> diff --git a/testing/marionette/harness/marionette_harness/www/test_inner_iframe.html b/testing/marionette/harness/marionette_harness/www/test_inner_iframe.html deleted file mode 100644 index c836fc6e4..000000000 --- a/testing/marionette/harness/marionette_harness/www/test_inner_iframe.html +++ /dev/null @@ -1,13 +0,0 @@ -<!-- 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/. --> - -<!doctype html> -<html> -<head> -<title>Inner Iframe</title> -</head> -<body> - <iframe src="test.html" id="inner_frame"></iframe> -</body> -</html> diff --git a/testing/marionette/harness/marionette_harness/www/test_nested_iframe.html b/testing/marionette/harness/marionette_harness/www/test_nested_iframe.html deleted file mode 100644 index b4482183e..000000000 --- a/testing/marionette/harness/marionette_harness/www/test_nested_iframe.html +++ /dev/null @@ -1,13 +0,0 @@ -<!-- 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/. --> - -<!doctype html> -<html> -<head> -<title>Marionette IFrame Test</title> -</head> -<body> - <iframe src="test_inner_iframe.html" id="test_iframe"></iframe> -</body> -</html> diff --git a/testing/marionette/harness/marionette_harness/www/test_oop_1.html b/testing/marionette/harness/marionette_harness/www/test_oop_1.html deleted file mode 100644 index 62e4ad047..000000000 --- a/testing/marionette/harness/marionette_harness/www/test_oop_1.html +++ /dev/null @@ -1,15 +0,0 @@ -<!-- 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/. --> - -<!DOCTYPE html> -<html> -<head> -<title>OOP Test Frame 1</title> -</head> -<body> - <h1 id="testh1">OOP Test Frame 1</h1> - Hello! -</body> -</html> - diff --git a/testing/marionette/harness/marionette_harness/www/test_oop_2.html b/testing/marionette/harness/marionette_harness/www/test_oop_2.html deleted file mode 100644 index 05aca5adb..000000000 --- a/testing/marionette/harness/marionette_harness/www/test_oop_2.html +++ /dev/null @@ -1,15 +0,0 @@ -<!-- 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/. --> - -<!DOCTYPE html> -<html> -<head> -<title>OOP Test Frame 2</title> -</head> -<body> - <h1 id="testh1">OOP Test Frame 2</h1> - Hello! -</body> -</html> - diff --git a/testing/marionette/harness/marionette_harness/www/test_shadow_dom.html b/testing/marionette/harness/marionette_harness/www/test_shadow_dom.html deleted file mode 100644 index 3ee893e6d..000000000 --- a/testing/marionette/harness/marionette_harness/www/test_shadow_dom.html +++ /dev/null @@ -1,26 +0,0 @@ -<!-- 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/. --> - -<!DOCTYPE html> - -<html> -<meta charset="UTF-8"> -<head> -<title>Marionette Test</title> -</head> -<body> - <div id="host"></div> - <div id="empty-host"></div> - <script> - 'use strict'; - var host = document.getElementById('host'); - var root = host.createShadowRoot(); - root.innerHTML = '<button id="button">Foo</button>' + - '<div id="inner-host"></div>'; - var innerHost = host.shadowRoot.getElementById('inner-host'); - var innerRoot = innerHost.createShadowRoot(); - innerRoot.innerHTML = '<button id="inner-button">Bar</button>'; - </script> -</body> -</html> diff --git a/testing/marionette/harness/marionette_harness/www/test_windows.html b/testing/marionette/harness/marionette_harness/www/test_windows.html deleted file mode 100644 index da1fd799e..000000000 --- a/testing/marionette/harness/marionette_harness/www/test_windows.html +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0"?> -<!-- 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/. --> - -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> -<head> - <title>XHTML Test Page</title> -</head> -<body> - <p><a href="resultPage.html" onClick='javascript:window.open("resultPage.html",null, "menubar=0,location=1,resizable=1,scrollbars=1,status=0,width=700,height=375");' name="windowOne">Open new window</a></p> -</body> -</html> - diff --git a/testing/marionette/harness/marionette_harness/www/white.png b/testing/marionette/harness/marionette_harness/www/white.png Binary files differdeleted file mode 100644 index 8a68c1154..000000000 --- a/testing/marionette/harness/marionette_harness/www/white.png +++ /dev/null diff --git a/testing/marionette/harness/marionette_harness/www/windowHandles.html b/testing/marionette/harness/marionette_harness/www/windowHandles.html deleted file mode 100644 index 165526c8a..000000000 --- a/testing/marionette/harness/marionette_harness/www/windowHandles.html +++ /dev/null @@ -1,16 +0,0 @@ -<!-- 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/. --> - -<!DOCTYPE html> -<html> -<head> -<title>Marionette New Tab Link</title> -</head> -<body> - <a href="empty.html" id="new-tab" target="_blank">New Tab</a> - <a href="about:blank" id="new-blank-tab" target="_blank">New blank Tab</a> - - <a href="" id="new-window" onClick='javascript:window.open("empty.html", null, "location=1,toolbar=1");'>New Window</a> -</body> -</html>
\ No newline at end of file diff --git a/testing/marionette/harness/marionette_harness/www/xhtmlTest.html b/testing/marionette/harness/marionette_harness/www/xhtmlTest.html deleted file mode 100644 index 146def33a..000000000 --- a/testing/marionette/harness/marionette_harness/www/xhtmlTest.html +++ /dev/null @@ -1,79 +0,0 @@ -<?xml version="1.0"?> -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> - <!-- 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/. --> -<head> - <title>XHTML Test Page</title> -</head> -<body> -<div class="navigation"> - <p><a href="resultPage.html" target="result" name="windowOne">Open new window</a></p> - <p><a href="iframes.html" target="_blank" name="windowTwo">Create a new anonymous window</a></p> - <p><a href="test_iframe.html" name="sameWindow">Open page with iframes in same window</a></p> - <p><a href="javascriptPage.html" target="result" name="windowThree">Open a window with a close button</a></p> -</div> - -<a name="notext"><b></b></a> - -<div class="content"> - <h1 class="header">XHTML Might Be The Future</h1> - - <p>If you'd like to go elsewhere then <a href="resultPage.html">click me</a>.</p> - - <p>Alternatively, <a href="resultPage.html" id="linkId">this goes to the same place</a>.</p> - - <form name="someForm"> - <input id="username" type="text" value="change"/> - </form> - - This link has the same text as another link: <a href="resultPage.html">click me</a>. -</div> - -<div class="extraDiv">Another div starts here.<p/> - <h2 class="nameA nameBnoise nameC">An H2 title</h2> - <p class="nameC">Some more text</p> -</div> - -<div> - <a id="id1" href="#">Foo</a> - <ul id="id2" /> - <span id="id3"/> -</div> - -<div> - <table id="table" ></table> -</div> - -<span id="amazing"> -<div> - <div> - <div> - <span/> - <a>I have width</a> - </div> - </div> -</div> -</span> - -<a name="text" /> -<p id="spaces"> </p> -<p id="empty"></p> -<a href="foo" id="linkWithEqualsSign">Link=equalssign</a> - -<p class=" spaceAround ">Spaced out</p> - -<span id="my_span"> - <div>first_div</div> - <div>second_div</div> - <span>first_span</span> - <span>second_span</span> -</span> - -<div id="parent">I'm a parent - <div id="child">I'm a child</div> -</div> - -<div id="only-exists-on-xhtmltest">Woo woo</div> -</body> -</html> diff --git a/testing/marionette/harness/requirements.txt b/testing/marionette/harness/requirements.txt deleted file mode 100644 index 75ab9ce92..000000000 --- a/testing/marionette/harness/requirements.txt +++ /dev/null @@ -1,14 +0,0 @@ -browsermob-proxy >= 0.6.0 -manifestparser >= 1.1 -marionette-driver >= 2.2.0 -mozcrash >= 0.5 -mozdevice >= 0.44 -mozinfo >= 0.8 -mozlog >= 3.0 -moznetwork >= 0.21 -mozprocess >= 0.9 -mozprofile >= 0.7 -mozrunner >= 6.13 -moztest >= 0.8 -mozversion >= 1.1 -wptserve >= 1.3.0 diff --git a/testing/marionette/harness/setup.py b/testing/marionette/harness/setup.py deleted file mode 100644 index d28a970c1..000000000 --- a/testing/marionette/harness/setup.py +++ /dev/null @@ -1,59 +0,0 @@ -import os -import re - -from setuptools import find_packages, setup - - -THIS_DIR = os.path.dirname(os.path.realpath(__name__)) - - -def read(*parts): - with open(os.path.join(THIS_DIR, *parts)) as f: - return f.read() - - -def get_version(): - return re.findall("__version__ = '([\d\.]+)'", - read('marionette_harness', '__init__.py'), re.M)[0] - - -setup(name='marionette-harness', - version=get_version(), - description="Marionette test automation harness", - long_description=open('README.rst').read(), - # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers - classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)', - 'Operating System :: MacOS :: MacOS X', - 'Operating System :: Microsoft :: Windows', - 'Operating System :: POSIX', - 'Topic :: Software Development :: Quality Assurance', - 'Topic :: Software Development :: Testing', - 'Topic :: Utilities', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2.7', - ], - keywords='mozilla', - author='Auto-tools', - author_email='tools-marionette@lists.mozilla.org', - url='https://wiki.mozilla.org/Auto-tools/Projects/Marionette', - license='Mozilla Public License 2.0 (MPL 2.0)', - packages=find_packages(), - package_data={ - 'marionette_harness': [ - 'runner/test.cert', - 'runner/test.key' - ], - }, - # Needed to include package data as specified in MANIFEST.in - include_package_data=True, - install_requires=read('requirements.txt').splitlines(), - zip_safe=False, - entry_points=""" - # -*- Entry points: -*- - [console_scripts] - marionette = marionette_harness.runtests:cli - """, - ) diff --git a/testing/marionette/interaction.js b/testing/marionette/interaction.js deleted file mode 100644 index 2392485d7..000000000 --- a/testing/marionette/interaction.js +++ /dev/null @@ -1,505 +0,0 @@ -/* 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/. */ - -"use strict"; - -const {utils: Cu} = Components; - -Cu.import("chrome://marionette/content/accessibility.js"); -Cu.import("chrome://marionette/content/atom.js"); -Cu.import("chrome://marionette/content/error.js"); -Cu.import("chrome://marionette/content/element.js"); -Cu.import("chrome://marionette/content/event.js"); - -Cu.importGlobalProperties(["File"]); - -this.EXPORTED_SYMBOLS = ["interaction"]; - -/** - * XUL elements that support disabled attribute. - */ -const DISABLED_ATTRIBUTE_SUPPORTED_XUL = new Set([ - "ARROWSCROLLBOX", - "BUTTON", - "CHECKBOX", - "COLORPICKER", - "COMMAND", - "DATEPICKER", - "DESCRIPTION", - "KEY", - "KEYSET", - "LABEL", - "LISTBOX", - "LISTCELL", - "LISTHEAD", - "LISTHEADER", - "LISTITEM", - "MENU", - "MENUITEM", - "MENULIST", - "MENUSEPARATOR", - "PREFERENCE", - "RADIO", - "RADIOGROUP", - "RICHLISTBOX", - "RICHLISTITEM", - "SCALE", - "TAB", - "TABS", - "TEXTBOX", - "TIMEPICKER", - "TOOLBARBUTTON", - "TREE", -]); - -/** - * XUL elements that support checked property. - */ -const CHECKED_PROPERTY_SUPPORTED_XUL = new Set([ - "BUTTON", - "CHECKBOX", - "LISTITEM", - "TOOLBARBUTTON", -]); - -/** - * XUL elements that support selected property. - */ -const SELECTED_PROPERTY_SUPPORTED_XUL = new Set([ - "LISTITEM", - "MENU", - "MENUITEM", - "MENUSEPARATOR", - "RADIO", - "RICHLISTITEM", - "TAB", -]); - -/** - * Common form controls that user can change the value property interactively. - */ -const COMMON_FORM_CONTROLS = new Set([ - "input", - "textarea", - "select", -]); - -/** - * Input elements that do not fire "input" and "change" events when value - * property changes. - */ -const INPUT_TYPES_NO_EVENT = new Set([ - "checkbox", - "radio", - "file", - "hidden", - "image", - "reset", - "button", - "submit", -]); - -this.interaction = {}; - -/** - * Interact with an element by clicking it. - * - * The element is scrolled into view before visibility- or interactability - * checks are performed. - * - * Selenium-style visibility checks will be performed if |specCompat| - * is false (default). Otherwise pointer-interactability checks will be - * performed. If either of these fail an - * {@code ElementNotInteractableError} is thrown. - * - * If |strict| is enabled (defaults to disabled), further accessibility - * checks will be performed, and these may result in an - * {@code ElementNotAccessibleError} being returned. - * - * When |el| is not enabled, an {@code InvalidElementStateError} - * is returned. - * - * @param {DOMElement|XULElement} el - * Element to click. - * @param {boolean=} strict - * Enforce strict accessibility tests. - * @param {boolean=} specCompat - * Use WebDriver specification compatible interactability definition. - * - * @throws {ElementNotInteractableError} - * If either Selenium-style visibility check or - * pointer-interactability check fails. - * @throws {ElementClickInterceptedError} - * If |el| is obscured by another element and a click would not hit, - * in |specCompat| mode. - * @throws {ElementNotAccessibleError} - * If |strict| is true and element is not accessible. - * @throws {InvalidElementStateError} - * If |el| is not enabled. - */ -interaction.clickElement = function* (el, strict = false, specCompat = false) { - const a11y = accessibility.get(strict); - if (specCompat) { - yield webdriverClickElement(el, a11y); - } else { - yield seleniumClickElement(el, a11y); - } -}; - -function* webdriverClickElement (el, a11y) { - const win = getWindow(el); - const doc = win.document; - - // step 3 - if (el.localName == "input" && el.type == "file") { - throw new InvalidArgumentError( - "Cannot click <input type=file> elements"); - } - - let containerEl = element.getContainer(el); - - // step 4 - if (!element.isInView(containerEl)) { - element.scrollIntoView(containerEl); - } - - // step 5 - // TODO(ato): wait for containerEl to be in view - - // step 6 - // if we cannot bring the container element into the viewport - // there is no point in checking if it is pointer-interactable - if (!element.isInView(containerEl)) { - throw new ElementNotInteractableError( - error.pprint`Element ${el} could not be scrolled into view`); - } - - // step 7 - let rects = containerEl.getClientRects(); - let clickPoint = element.getInViewCentrePoint(rects[0], win); - - if (!element.isPointerInteractable(containerEl)) { - throw new ElementClickInterceptedError(containerEl, clickPoint); - } - - yield a11y.getAccessible(el, true).then(acc => { - a11y.assertVisible(acc, el, true); - a11y.assertEnabled(acc, el, true); - a11y.assertActionable(acc, el); - }); - - // step 8 - - // chrome elements - if (element.isXULElement(el)) { - if (el.localName == "option") { - interaction.selectOption(el); - } else { - el.click(); - } - - // content elements - } else { - if (el.localName == "option") { - interaction.selectOption(el); - } else { - event.synthesizeMouseAtPoint(clickPoint.x, clickPoint.y, {}, win); - } - } - - // step 9 - yield interaction.flushEventLoop(win); - - // step 10 - // TODO(ato): if the click causes navigation, - // run post-navigation checks -} - -function* seleniumClickElement (el, a11y) { - let win = getWindow(el); - - let visibilityCheckEl = el; - if (el.localName == "option") { - visibilityCheckEl = element.getContainer(el); - } - - if (!element.isVisible(visibilityCheckEl)) { - throw new ElementNotInteractableError(); - } - - if (!atom.isElementEnabled(el)) { - throw new InvalidElementStateError("Element is not enabled"); - } - - yield a11y.getAccessible(el, true).then(acc => { - a11y.assertVisible(acc, el, true); - a11y.assertEnabled(acc, el, true); - a11y.assertActionable(acc, el); - }); - - // chrome elements - if (element.isXULElement(el)) { - if (el.localName == "option") { - interaction.selectOption(el); - } else { - el.click(); - } - - // content elements - } else { - if (el.localName == "option") { - interaction.selectOption(el); - } else { - let rects = el.getClientRects(); - let centre = element.getInViewCentrePoint(rects[0], win); - let opts = {}; - event.synthesizeMouseAtPoint(centre.x, centre.y, opts, win); - } - } -}; - -/** - * Select <option> element in a <select> list. - * - * Because the dropdown list of select elements are implemented using - * native widget technology, our trusted synthesised events are not able - * to reach them. Dropdowns are instead handled mimicking DOM events, - * which for obvious reasons is not ideal, but at the current point in - * time considered to be good enough. - * - * @param {HTMLOptionElement} option - * Option element to select. - * - * @throws TypeError - * If |el| is a XUL element or not an <option> element. - * @throws Error - * If unable to find |el|'s parent <select> element. - */ -interaction.selectOption = function (el) { - if (element.isXULElement(el)) { - throw new Error("XUL dropdowns not supported"); - } - if (el.localName != "option") { - throw new TypeError("Invalid elements"); - } - - let win = getWindow(el); - let containerEl = element.getContainer(el); - - event.mouseover(containerEl); - event.mousemove(containerEl); - event.mousedown(containerEl); - event.focus(containerEl); - event.input(containerEl); - - // toggle selectedness the way holding down control works - el.selected = !el.selected; - - event.change(containerEl); - event.mouseup(containerEl); - event.click(containerEl); -}; - -/** - * Flushes the event loop by requesting an animation frame. - * - * This will wait for the browser to repaint before returning, typically - * flushing any queued events. - * - * If the document is unloaded during this request, the promise is - * rejected. - * - * @param {Window} win - * Associated window. - * - * @return {Promise} - * Promise is accepted once event queue is flushed, or rejected if - * |win| is unloaded before the queue can be flushed. - */ -interaction.flushEventLoop = function* (win) { - let unloadEv; - return new Promise((resolve, reject) => { - unloadEv = reject; - win.addEventListener("unload", unloadEv, {once: true}); - win.requestAnimationFrame(resolve); - }).then(() => { - win.removeEventListener("unload", unloadEv); - }); -}; - -/** - * Appends |path| to an <input type=file>'s file list. - * - * @param {HTMLInputElement} el - * An <input type=file> element. - * @param {string} path - * Full path to file. - */ -interaction.uploadFile = function (el, path) { - let file; - try { - file = File.createFromFileName(path); - } catch (e) { - throw new InvalidArgumentError("File not found: " + path); - } - - let fs = Array.prototype.slice.call(el.files); - fs.push(file); - - // <input type=file> opens OS widget dialogue - // which means the mousedown/focus/mouseup/click events - // occur before the change event - event.mouseover(el); - event.mousemove(el); - event.mousedown(el); - event.focus(el); - event.mouseup(el); - event.click(el); - - el.mozSetFileArray(fs); - - event.change(el); -}; - -/** - * Sets a form element's value. - * - * @param {DOMElement} el - * An form element, e.g. input, textarea, etc. - * @param {string} value - * The value to be set. - * - * @throws TypeError - * If |el| is not an supported form element. - */ -interaction.setFormControlValue = function* (el, value) { - if (!COMMON_FORM_CONTROLS.has(el.localName)) { - throw new TypeError("This function is for form elements only"); - } - - el.value = value; - - if (INPUT_TYPES_NO_EVENT.has(el.type)) { - return; - } - - event.input(el); - event.change(el); -}; - -/** - * Send keys to element. - * - * @param {DOMElement|XULElement} el - * Element to send key events to. - * @param {Array.<string>} value - * Sequence of keystrokes to send to the element. - * @param {boolean} ignoreVisibility - * Flag to enable or disable element visibility tests. - * @param {boolean=} strict - * Enforce strict accessibility tests. - */ -interaction.sendKeysToElement = function (el, value, ignoreVisibility, strict = false) { - let win = getWindow(el); - let a11y = accessibility.get(strict); - return a11y.getAccessible(el, true).then(acc => { - a11y.assertActionable(acc, el); - event.sendKeysToElement(value, el, {ignoreVisibility: false}, win); - }); -}; - -/** - * Determine the element displayedness of an element. - * - * @param {DOMElement|XULElement} el - * Element to determine displayedness of. - * @param {boolean=} strict - * Enforce strict accessibility tests. - * - * @return {boolean} - * True if element is displayed, false otherwise. - */ -interaction.isElementDisplayed = function (el, strict = false) { - let win = getWindow(el); - let displayed = atom.isElementDisplayed(el, win); - - let a11y = accessibility.get(strict); - return a11y.getAccessible(el).then(acc => { - a11y.assertVisible(acc, el, displayed); - return displayed; - }); -}; - -/** - * Check if element is enabled. - * - * @param {DOMElement|XULElement} el - * Element to test if is enabled. - * - * @return {boolean} - * True if enabled, false otherwise. - */ -interaction.isElementEnabled = function (el, strict = false) { - let enabled = true; - let win = getWindow(el); - - if (element.isXULElement(el)) { - // check if XUL element supports disabled attribute - if (DISABLED_ATTRIBUTE_SUPPORTED_XUL.has(el.tagName.toUpperCase())) { - let disabled = atom.getElementAttribute(el, "disabled", win); - if (disabled && disabled === "true") { - enabled = false; - } - } - } else { - enabled = atom.isElementEnabled(el, {frame: win}); - } - - let a11y = accessibility.get(strict); - return a11y.getAccessible(el).then(acc => { - a11y.assertEnabled(acc, el, enabled); - return enabled; - }); -}; - -/** - * Determines if the referenced element is selected or not. - * - * This operation only makes sense on input elements of the Checkbox- - * and Radio Button states, or option elements. - * - * @param {DOMElement|XULElement} el - * Element to test if is selected. - * @param {boolean=} strict - * Enforce strict accessibility tests. - * - * @return {boolean} - * True if element is selected, false otherwise. - */ -interaction.isElementSelected = function (el, strict = false) { - let selected = true; - let win = getWindow(el); - - if (element.isXULElement(el)) { - let tagName = el.tagName.toUpperCase(); - if (CHECKED_PROPERTY_SUPPORTED_XUL.has(tagName)) { - selected = el.checked; - } - if (SELECTED_PROPERTY_SUPPORTED_XUL.has(tagName)) { - selected = el.selected; - } - } else { - selected = atom.isElementSelected(el, win); - } - - let a11y = accessibility.get(strict); - return a11y.getAccessible(el).then(acc => { - a11y.assertSelected(acc, el, selected); - return selected; - }); -}; - -function getWindow(el) { - return el.ownerDocument.defaultView; -} diff --git a/testing/marionette/jar.mn b/testing/marionette/jar.mn deleted file mode 100644 index c9ea8b213..000000000 --- a/testing/marionette/jar.mn +++ /dev/null @@ -1,43 +0,0 @@ -# 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/. - -marionette.jar: -% content marionette %content/ - content/server.js (server.js) - content/driver.js (driver.js) - content/action.js (action.js) - content/legacyaction.js (legacyaction.js) - content/browser.js (browser.js) - content/interaction.js (interaction.js) - content/accessibility.js (accessibility.js) - content/listener.js (listener.js) - content/element.js (element.js) - content/simpletest.js (simpletest.js) - content/frame.js (frame.js) - content/cert.js (cert.js) - content/event.js (event.js) - content/error.js (error.js) - content/message.js (message.js) - content/dispatcher.js (dispatcher.js) - content/modal.js (modal.js) - content/proxy.js (proxy.js) - content/capture.js (capture.js) - content/cookies.js (cookies.js) - content/atom.js (atom.js) - content/evaluate.js (evaluate.js) - content/logging.js (logging.js) - content/navigate.js (navigate.js) - content/l10n.js (l10n.js) - content/assert.js (assert.js) - content/addon.js (addon.js) - content/session.js (session.js) -#ifdef ENABLE_TESTS - content/test.xul (chrome/test.xul) - content/test2.xul (chrome/test2.xul) - content/test_dialog.dtd (chrome/test_dialog.dtd) - content/test_dialog.properties (chrome/test_dialog.properties) - content/test_dialog.xul (chrome/test_dialog.xul) - content/test_nested_iframe.xul (chrome/test_nested_iframe.xul) - content/test_anonymous_content.xul (chrome/test_anonymous_content.xul) -#endif diff --git a/testing/marionette/l10n.js b/testing/marionette/l10n.js deleted file mode 100644 index de5dbf2a7..000000000 --- a/testing/marionette/l10n.js +++ /dev/null @@ -1,95 +0,0 @@ -/* 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/. */ - -"use strict"; - -/** - * An API which allows Marionette to handle localized content. - * - * The localization (https://mzl.la/2eUMjyF) of UI elements in Gecko based - * applications is done via entities and properties. For static values entities - * are used, which are located in .dtd files. Whereby for dynamically updated - * content the values come from .property files. Both types of elements can be - * identifed via a unique id, and the translated content retrieved. - */ - -const {classes: Cc, interfaces: Ci, utils: Cu} = Components; - -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -XPCOMUtils.defineLazyServiceGetter( - this, "domParser", "@mozilla.org/xmlextras/domparser;1", "nsIDOMParser"); - -Cu.import("chrome://marionette/content/error.js"); - -this.EXPORTED_SYMBOLS = ["l10n"]; - -this.l10n = {}; - -/** - * Retrieve the localized string for the specified entity id. - * - * Example: - * localizeEntity(["chrome://global/locale/about.dtd"], "about.version") - * - * @param {Array.<string>} urls - * Array of .dtd URLs. - * @param {string} id - * The ID of the entity to retrieve the localized string for. - * - * @return {string} - * The localized string for the requested entity. - */ -l10n.localizeEntity = function (urls, id) { - // Build a string which contains all possible entity locations - let locations = []; - urls.forEach((url, index) => { - locations.push(`<!ENTITY % dtd_${index} SYSTEM "${url}">%dtd_${index};`); - }) - - // Use the DOM parser to resolve the entity and extract its real value - let header = `<?xml version="1.0"?><!DOCTYPE elem [${locations.join("")}]>`; - let elem = `<elem id="elementID">&${id};</elem>`; - let doc = domParser.parseFromString(header + elem, "text/xml"); - let element = doc.querySelector("elem[id='elementID']"); - - if (element === null) { - throw new NoSuchElementError(`Entity with id='${id}' hasn't been found`); - } - - return element.textContent; -}; - -/** - * Retrieve the localized string for the specified property id. - * - * Example: - * localizeProperty(["chrome://global/locale/findbar.properties"], "FastFind") - * - * @param {Array.<string>} urls - * Array of .properties URLs. - * @param {string} id - * The ID of the property to retrieve the localized string for. - * - * @return {string} - * The localized string for the requested property. - */ -l10n.localizeProperty = function (urls, id) { - let property = null; - - for (let url of urls) { - let bundle = Services.strings.createBundle(url); - try { - property = bundle.GetStringFromName(id); - break; - } catch (e) {} - }; - - if (property === null) { - throw new NoSuchElementError(`Property with id='${id}' hasn't been found`); - } - - return property; -}; diff --git a/testing/marionette/legacyaction.js b/testing/marionette/legacyaction.js deleted file mode 100644 index 5d108210c..000000000 --- a/testing/marionette/legacyaction.js +++ /dev/null @@ -1,477 +0,0 @@ -/* 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/. */ - -const {classes: Cc, interfaces: Ci, utils: Cu} = Components; - -Cu.import("resource://gre/modules/Log.jsm"); -Cu.import("resource://gre/modules/Preferences.jsm"); - -Cu.import("chrome://marionette/content/element.js"); -Cu.import("chrome://marionette/content/event.js"); - -const CONTEXT_MENU_DELAY_PREF = "ui.click_hold_context_menus.delay"; -const DEFAULT_CONTEXT_MENU_DELAY = 750; // ms - -this.EXPORTED_SYMBOLS = ["legacyaction"]; - -const logger = Log.repository.getLogger("Marionette"); - -this.legacyaction = this.action = {}; - -/** - * Functionality for (single finger) action chains. - */ -action.Chain = function (checkForInterrupted) { - // for assigning unique ids to all touches - this.nextTouchId = 1000; - // keep track of active Touches - this.touchIds = {}; - // last touch for each fingerId - this.lastCoordinates = null; - this.isTap = false; - this.scrolling = false; - // whether to send mouse event - this.mouseEventsOnly = false; - this.checkTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - - if (typeof checkForInterrupted == "function") { - this.checkForInterrupted = checkForInterrupted; - } else { - this.checkForInterrupted = () => {}; - } - - // determines if we create touch events - this.inputSource = null; -}; - -action.Chain.prototype.dispatchActions = function ( - args, - touchId, - container, - seenEls, - touchProvider) { - // Some touch events code in the listener needs to do ipc, so we can't - // share this code across chrome/content. - if (touchProvider) { - this.touchProvider = touchProvider; - } - - this.seenEls = seenEls; - this.container = container; - let commandArray = element.fromJson( - args, seenEls, container.frame, container.shadowRoot); - - if (touchId == null) { - touchId = this.nextTouchId++; - } - - if (!container.frame.document.createTouch) { - this.mouseEventsOnly = true; - } - - let keyModifiers = { - shiftKey: false, - ctrlKey: false, - altKey: false, - metaKey: false, - }; - - return new Promise(resolve => { - this.actions(commandArray, touchId, 0, keyModifiers, resolve); - }).catch(this.resetValues); -}; - -/** - * This function emit mouse event. - * - * @param {Document} doc - * Current document. - * @param {string} type - * Type of event to dispatch. - * @param {number} clickCount - * Number of clicks, button notes the mouse button. - * @param {number} elClientX - * X coordinate of the mouse relative to the viewport. - * @param {number} elClientY - * Y coordinate of the mouse relative to the viewport. - * @param {Object} modifiers - * An object of modifier keys present. - */ -action.Chain.prototype.emitMouseEvent = function ( - doc, - type, - elClientX, - elClientY, - button, - clickCount, - modifiers) { - if (!this.checkForInterrupted()) { - logger.debug(`Emitting ${type} mouse event ` + - `at coordinates (${elClientX}, ${elClientY}) ` + - `relative to the viewport, ` + - `button: ${button}, ` + - `clickCount: ${clickCount}`); - - let win = doc.defaultView; - let domUtils = win.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); - - let mods; - if (typeof modifiers != "undefined") { - mods = event.parseModifiers_(modifiers); - } else { - mods = 0; - } - - domUtils.sendMouseEvent( - type, - elClientX, - elClientY, - button || 0, - clickCount || 1, - mods, - false, - 0, - this.inputSource); - } -}; - -/** - * Reset any persisted values after a command completes. - */ -action.Chain.prototype.resetValues = function() { - this.container = null; - this.seenEls = null; - this.touchProvider = null; - this.mouseEventsOnly = false; -}; - -/** - * Emit events for each action in the provided chain. - * - * To emit touch events for each finger, one might send a [["press", id], - * ["wait", 5], ["release"]] chain. - * - * @param {Array.<Array<?>>} chain - * A multi-dimensional array of actions. - * @param {Object.<string, number>} touchId - * Represents the finger ID. - * @param {number} i - * Keeps track of the current action of the chain. - * @param {Object.<string, boolean>} keyModifiers - * Keeps track of keyDown/keyUp pairs through an action chain. - * @param {function(?)} cb - * Called on success. - * - * @return {Object.<string, number>} - * Last finger ID, or an empty object. - */ -action.Chain.prototype.actions = function (chain, touchId, i, keyModifiers, cb) { - if (i == chain.length) { - cb(touchId || null); - this.resetValues(); - return; - } - - let pack = chain[i]; - let command = pack[0]; - let el; - let c; - i++; - - if (["press", "wait", "keyDown", "keyUp", "click"].indexOf(command) == -1) { - // if mouseEventsOnly, then touchIds isn't used - if (!(touchId in this.touchIds) && !this.mouseEventsOnly) { - this.resetValues(); - throw new WebDriverError("Element has not been pressed"); - } - } - - switch (command) { - case "keyDown": - event.sendKeyDown(pack[1], keyModifiers, this.container.frame); - this.actions(chain, touchId, i, keyModifiers, cb); - break; - - case "keyUp": - event.sendKeyUp(pack[1], keyModifiers, this.container.frame); - this.actions(chain, touchId, i, keyModifiers, cb); - break; - - case "click": - el = this.seenEls.get(pack[1], this.container); - let button = pack[2]; - let clickCount = pack[3]; - c = element.coordinates(el); - this.mouseTap(el.ownerDocument, c.x, c.y, button, clickCount, keyModifiers); - if (button == 2) { - this.emitMouseEvent(el.ownerDocument, "contextmenu", c.x, c.y, - button, clickCount, keyModifiers); - } - this.actions(chain, touchId, i, keyModifiers, cb); - break; - - case "press": - if (this.lastCoordinates) { - this.generateEvents( - "cancel", - this.lastCoordinates[0], - this.lastCoordinates[1], - touchId, - null, - keyModifiers); - this.resetValues(); - throw new WebDriverError( - "Invalid Command: press cannot follow an active touch event"); - } - - // look ahead to check if we're scrolling, - // needed for APZ touch dispatching - if ((i != chain.length) && (chain[i][0].indexOf('move') !== -1)) { - this.scrolling = true; - } - el = this.seenEls.get(pack[1], this.container); - c = element.coordinates(el, pack[2], pack[3]); - touchId = this.generateEvents("press", c.x, c.y, null, el, keyModifiers); - this.actions(chain, touchId, i, keyModifiers, cb); - break; - - case "release": - this.generateEvents( - "release", - this.lastCoordinates[0], - this.lastCoordinates[1], - touchId, - null, - keyModifiers); - this.actions(chain, null, i, keyModifiers, cb); - this.scrolling = false; - break; - - case "move": - el = this.seenEls.get(pack[1], this.container); - c = element.coordinates(el); - this.generateEvents("move", c.x, c.y, touchId, null, keyModifiers); - this.actions(chain, touchId, i, keyModifiers, cb); - break; - - case "moveByOffset": - this.generateEvents( - "move", - this.lastCoordinates[0] + pack[1], - this.lastCoordinates[1] + pack[2], - touchId, - null, - keyModifiers); - this.actions(chain, touchId, i, keyModifiers, cb); - break; - - case "wait": - if (pack[1] != null) { - let time = pack[1] * 1000; - - // standard waiting time to fire contextmenu - let standard = Preferences.get( - CONTEXT_MENU_DELAY_PREF, - DEFAULT_CONTEXT_MENU_DELAY); - - if (time >= standard && this.isTap) { - chain.splice(i, 0, ["longPress"], ["wait", (time - standard) / 1000]); - time = standard; - } - this.checkTimer.initWithCallback( - () => this.actions(chain, touchId, i, keyModifiers, cb), - time, Ci.nsITimer.TYPE_ONE_SHOT); - } else { - this.actions(chain, touchId, i, keyModifiers, cb); - } - break; - - case "cancel": - this.generateEvents( - "cancel", - this.lastCoordinates[0], - this.lastCoordinates[1], - touchId, - null, - keyModifiers); - this.actions(chain, touchId, i, keyModifiers, cb); - this.scrolling = false; - break; - - case "longPress": - this.generateEvents( - "contextmenu", - this.lastCoordinates[0], - this.lastCoordinates[1], - touchId, - null, - keyModifiers); - this.actions(chain, touchId, i, keyModifiers, cb); - break; - } -}; - -/** - * Given an element and a pair of coordinates, returns an array of the - * form [clientX, clientY, pageX, pageY, screenX, screenY]. - */ -action.Chain.prototype.getCoordinateInfo = function (el, corx, cory) { - let win = el.ownerDocument.defaultView; - return [ - corx, // clientX - cory, // clientY - corx + win.pageXOffset, // pageX - cory + win.pageYOffset, // pageY - corx + win.mozInnerScreenX, // screenX - cory + win.mozInnerScreenY // screenY - ]; -}; - -/** - * @param {number} x - * X coordinate of the location to generate the event that is relative - * to the viewport. - * @param {number} y - * Y coordinate of the location to generate the event that is relative - * to the viewport. - */ -action.Chain.prototype.generateEvents = function ( - type, x, y, touchId, target, keyModifiers) { - this.lastCoordinates = [x, y]; - let doc = this.container.frame.document; - - switch (type) { - case "tap": - if (this.mouseEventsOnly) { - this.mouseTap( - touch.target.ownerDocument, - touch.clientX, - touch.clientY, - null, - null, - keyModifiers); - } else { - touchId = this.nextTouchId++; - let touch = this.touchProvider.createATouch(target, x, y, touchId); - this.touchProvider.emitTouchEvent("touchstart", touch); - this.touchProvider.emitTouchEvent("touchend", touch); - this.mouseTap( - touch.target.ownerDocument, - touch.clientX, - touch.clientY, - null, - null, - keyModifiers); - } - this.lastCoordinates = null; - break; - - case "press": - this.isTap = true; - if (this.mouseEventsOnly) { - this.emitMouseEvent(doc, "mousemove", x, y, null, null, keyModifiers); - this.emitMouseEvent(doc, "mousedown", x, y, null, null, keyModifiers); - } else { - touchId = this.nextTouchId++; - let touch = this.touchProvider.createATouch(target, x, y, touchId); - this.touchProvider.emitTouchEvent("touchstart", touch); - this.touchIds[touchId] = touch; - return touchId; - } - break; - - case "release": - if (this.mouseEventsOnly) { - let [x, y] = this.lastCoordinates; - this.emitMouseEvent(doc, "mouseup", x, y, null, null, keyModifiers); - } else { - let touch = this.touchIds[touchId]; - let [x, y] = this.lastCoordinates; - - touch = this.touchProvider.createATouch(touch.target, x, y, touchId); - this.touchProvider.emitTouchEvent("touchend", touch); - - if (this.isTap) { - this.mouseTap( - touch.target.ownerDocument, - touch.clientX, - touch.clientY, - null, - null, - keyModifiers); - } - delete this.touchIds[touchId]; - } - - this.isTap = false; - this.lastCoordinates = null; - break; - - case "cancel": - this.isTap = false; - if (this.mouseEventsOnly) { - let [x, y] = this.lastCoordinates; - this.emitMouseEvent(doc, "mouseup", x, y, null, null, keyModifiers); - } else { - this.touchProvider.emitTouchEvent("touchcancel", this.touchIds[touchId]); - delete this.touchIds[touchId]; - } - this.lastCoordinates = null; - break; - - case "move": - this.isTap = false; - if (this.mouseEventsOnly) { - this.emitMouseEvent(doc, "mousemove", x, y, null, null, keyModifiers); - } else { - let touch = this.touchProvider.createATouch( - this.touchIds[touchId].target, x, y, touchId); - this.touchIds[touchId] = touch; - this.touchProvider.emitTouchEvent("touchmove", touch); - } - break; - - case "contextmenu": - this.isTap = false; - let event = this.container.frame.document.createEvent("MouseEvents"); - if (this.mouseEventsOnly) { - target = doc.elementFromPoint(this.lastCoordinates[0], this.lastCoordinates[1]); - } else { - target = this.touchIds[touchId].target; - } - - let [clientX, clientY, pageX, pageY, screenX, screenY] = - this.getCoordinateInfo(target, x, y); - - event.initMouseEvent( - "contextmenu", - true, - true, - target.ownerDocument.defaultView, - 1, - screenX, - screenY, - clientX, - clientY, - false, - false, - false, - false, - 0, - null); - target.dispatchEvent(event); - break; - - default: - throw new WebDriverError("Unknown event type: " + type); - } - this.checkForInterrupted(); -}; - -action.Chain.prototype.mouseTap = function (doc, x, y, button, count, mod) { - this.emitMouseEvent(doc, "mousemove", x, y, button, count, mod); - this.emitMouseEvent(doc, "mousedown", x, y, button, count, mod); - this.emitMouseEvent(doc, "mouseup", x, y, button, count, mod); -}; diff --git a/testing/marionette/listener.js b/testing/marionette/listener.js deleted file mode 100644 index 619ac249d..000000000 --- a/testing/marionette/listener.js +++ /dev/null @@ -1,1816 +0,0 @@ -/* 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/. */ - -"use strict"; - -var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; - -var uuidGen = Cc["@mozilla.org/uuid-generator;1"] - .getService(Ci.nsIUUIDGenerator); - -var loader = Cc["@mozilla.org/moz/jssubscript-loader;1"] - .getService(Ci.mozIJSSubScriptLoader); - -Cu.import("chrome://marionette/content/accessibility.js"); -Cu.import("chrome://marionette/content/action.js"); -Cu.import("chrome://marionette/content/atom.js"); -Cu.import("chrome://marionette/content/capture.js"); -Cu.import("chrome://marionette/content/cookies.js"); -Cu.import("chrome://marionette/content/element.js"); -Cu.import("chrome://marionette/content/error.js"); -Cu.import("chrome://marionette/content/evaluate.js"); -Cu.import("chrome://marionette/content/event.js"); -Cu.import("chrome://marionette/content/interaction.js"); -Cu.import("chrome://marionette/content/legacyaction.js"); -Cu.import("chrome://marionette/content/logging.js"); -Cu.import("chrome://marionette/content/navigate.js"); -Cu.import("chrome://marionette/content/proxy.js"); -Cu.import("chrome://marionette/content/session.js"); -Cu.import("chrome://marionette/content/simpletest.js"); - -Cu.import("resource://gre/modules/FileUtils.jsm"); -Cu.import("resource://gre/modules/Preferences.jsm"); -Cu.import("resource://gre/modules/Task.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -Cu.importGlobalProperties(["URL"]); - -var contentLog = new logging.ContentLogger(); - -var isB2G = false; - -var marionetteTestName; -var winUtil = content.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); -var listenerId = null; // unique ID of this listener -var curContainer = { frame: content, shadowRoot: null }; -var isRemoteBrowser = () => curContainer.frame.contentWindow !== null; -var previousContainer = null; - -var seenEls = new element.Store(); -var SUPPORTED_STRATEGIES = new Set([ - element.Strategy.ClassName, - element.Strategy.Selector, - element.Strategy.ID, - element.Strategy.Name, - element.Strategy.LinkText, - element.Strategy.PartialLinkText, - element.Strategy.TagName, - element.Strategy.XPath, -]); - -var capabilities; - -var legacyactions = new legacyaction.Chain(checkForInterrupted); - -// the unload handler -var onunload; - -// Flag to indicate whether an async script is currently running or not. -var asyncTestRunning = false; -var asyncTestCommandId; -var asyncTestTimeoutId; - -var inactivityTimeoutId = null; - -var originalOnError; -//timer for doc changes -var checkTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); -//timer for readystate -var readyStateTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); -// timer for navigation commands. -var navTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); -var onDOMContentLoaded; -// Send move events about this often -var EVENT_INTERVAL = 30; // milliseconds -// last touch for each fingerId -var multiLast = {}; -var asyncChrome = proxy.toChromeAsync({ - addMessageListener: addMessageListenerId.bind(this), - removeMessageListener: removeMessageListenerId.bind(this), - sendAsyncMessage: sendAsyncMessage.bind(this), -}); -var syncChrome = proxy.toChrome(sendSyncMessage.bind(this)); -var cookies = new Cookies(() => curContainer.frame.document, syncChrome); -var importedScripts = new evaluate.ScriptStorageServiceClient(syncChrome); - -Cu.import("resource://gre/modules/Log.jsm"); -var logger = Log.repository.getLogger("Marionette"); -logger.debug("loaded listener.js"); - -var modalHandler = function() { - // This gets called on the system app only since it receives the mozbrowserprompt event - sendSyncMessage("Marionette:switchedToFrame", {frameValue: null, storePrevious: true}); - let isLocal = sendSyncMessage("MarionetteFrame:handleModal", {})[0].value; - if (isLocal) { - previousContainer = curContainer; - } - curContainer = {frame: content, shadowRoot: null}; -}; - -// sandbox storage and name of the current sandbox -var sandboxes = new Sandboxes(() => curContainer.frame); -var sandboxName = "default"; - -/** - * Called when listener is first started up. - * The listener sends its unique window ID and its current URI to the actor. - * If the actor returns an ID, we start the listeners. Otherwise, nothing happens. - */ -function registerSelf() { - let msg = {value: winUtil.outerWindowID}; - // register will have the ID and a boolean describing if this is the main process or not - let register = sendSyncMessage("Marionette:register", msg); - - if (register[0]) { - let {id, remotenessChange} = register[0][0]; - capabilities = session.Capabilities.fromJSON(register[0][2]); - listenerId = id; - if (typeof id != "undefined") { - // check if we're the main process - if (register[0][1]) { - addMessageListener("MarionetteMainListener:emitTouchEvent", emitTouchEventForIFrame); - } - startListeners(); - let rv = {}; - if (remotenessChange) { - rv.listenerId = id; - } - sendAsyncMessage("Marionette:listenersAttached", rv); - } - } -} - -function emitTouchEventForIFrame(message) { - message = message.json; - let identifier = legacyactions.nextTouchId; - - let domWindowUtils = curContainer.frame. - QueryInterface(Components.interfaces.nsIInterfaceRequestor). - getInterface(Components.interfaces.nsIDOMWindowUtils); - var ratio = domWindowUtils.screenPixelsPerCSSPixel; - - var typeForUtils; - switch (message.type) { - case 'touchstart': - typeForUtils = domWindowUtils.TOUCH_CONTACT; - break; - case 'touchend': - typeForUtils = domWindowUtils.TOUCH_REMOVE; - break; - case 'touchcancel': - typeForUtils = domWindowUtils.TOUCH_CANCEL; - break; - case 'touchmove': - typeForUtils = domWindowUtils.TOUCH_CONTACT; - break; - } - domWindowUtils.sendNativeTouchPoint(identifier, typeForUtils, - Math.round(message.screenX * ratio), Math.round(message.screenY * ratio), - message.force, 90); -} - -// Eventually we will not have a closure for every single command, but -// use a generic dispatch for all listener commands. -// -// Perhaps one could even conceive having a separate instance of -// CommandProcessor for the listener, because the code is mostly the same. -function dispatch(fn) { - if (typeof fn != "function") { - throw new TypeError("Provided dispatch handler is not a function"); - } - - return function (msg) { - let id = msg.json.command_id; - - let req = Task.spawn(function*() { - if (typeof msg.json == "undefined" || msg.json instanceof Array) { - return yield fn.apply(null, msg.json); - } else { - return yield fn(msg.json); - } - }); - - let okOrValueResponse = rv => { - if (typeof rv == "undefined") { - sendOk(id); - } else { - sendResponse(rv, id); - } - }; - - req.then(okOrValueResponse, err => sendError(err, id)) - .catch(error.report); - }; -} - -/** - * Add a message listener that's tied to our listenerId. - */ -function addMessageListenerId(messageName, handler) { - addMessageListener(messageName + listenerId, handler); -} - -/** - * Remove a message listener that's tied to our listenerId. - */ -function removeMessageListenerId(messageName, handler) { - removeMessageListener(messageName + listenerId, handler); -} - -var getTitleFn = dispatch(getTitle); -var getPageSourceFn = dispatch(getPageSource); -var getActiveElementFn = dispatch(getActiveElement); -var clickElementFn = dispatch(clickElement); -var getElementAttributeFn = dispatch(getElementAttribute); -var getElementPropertyFn = dispatch(getElementProperty); -var getElementTextFn = dispatch(getElementText); -var getElementTagNameFn = dispatch(getElementTagName); -var getElementRectFn = dispatch(getElementRect); -var isElementEnabledFn = dispatch(isElementEnabled); -var getCurrentUrlFn = dispatch(getCurrentUrl); -var findElementContentFn = dispatch(findElementContent); -var findElementsContentFn = dispatch(findElementsContent); -var isElementSelectedFn = dispatch(isElementSelected); -var clearElementFn = dispatch(clearElement); -var isElementDisplayedFn = dispatch(isElementDisplayed); -var getElementValueOfCssPropertyFn = dispatch(getElementValueOfCssProperty); -var switchToShadowRootFn = dispatch(switchToShadowRoot); -var getCookiesFn = dispatch(getCookies); -var singleTapFn = dispatch(singleTap); -var takeScreenshotFn = dispatch(takeScreenshot); -var performActionsFn = dispatch(performActions); -var releaseActionsFn = dispatch(releaseActions); -var actionChainFn = dispatch(actionChain); -var multiActionFn = dispatch(multiAction); -var addCookieFn = dispatch(addCookie); -var deleteCookieFn = dispatch(deleteCookie); -var deleteAllCookiesFn = dispatch(deleteAllCookies); -var executeFn = dispatch(execute); -var executeInSandboxFn = dispatch(executeInSandbox); -var executeSimpleTestFn = dispatch(executeSimpleTest); -var sendKeysToElementFn = dispatch(sendKeysToElement); - -/** - * Start all message listeners - */ -function startListeners() { - addMessageListenerId("Marionette:newSession", newSession); - addMessageListenerId("Marionette:execute", executeFn); - addMessageListenerId("Marionette:executeInSandbox", executeInSandboxFn); - addMessageListenerId("Marionette:executeSimpleTest", executeSimpleTestFn); - addMessageListenerId("Marionette:singleTap", singleTapFn); - addMessageListenerId("Marionette:performActions", performActionsFn); - addMessageListenerId("Marionette:releaseActions", releaseActionsFn); - addMessageListenerId("Marionette:actionChain", actionChainFn); - addMessageListenerId("Marionette:multiAction", multiActionFn); - addMessageListenerId("Marionette:get", get); - addMessageListenerId("Marionette:pollForReadyState", pollForReadyState); - addMessageListenerId("Marionette:cancelRequest", cancelRequest); - addMessageListenerId("Marionette:getCurrentUrl", getCurrentUrlFn); - addMessageListenerId("Marionette:getTitle", getTitleFn); - addMessageListenerId("Marionette:getPageSource", getPageSourceFn); - addMessageListenerId("Marionette:goBack", goBack); - addMessageListenerId("Marionette:goForward", goForward); - addMessageListenerId("Marionette:refresh", refresh); - addMessageListenerId("Marionette:findElementContent", findElementContentFn); - addMessageListenerId("Marionette:findElementsContent", findElementsContentFn); - addMessageListenerId("Marionette:getActiveElement", getActiveElementFn); - addMessageListenerId("Marionette:clickElement", clickElementFn); - addMessageListenerId("Marionette:getElementAttribute", getElementAttributeFn); - addMessageListenerId("Marionette:getElementProperty", getElementPropertyFn); - addMessageListenerId("Marionette:getElementText", getElementTextFn); - addMessageListenerId("Marionette:getElementTagName", getElementTagNameFn); - addMessageListenerId("Marionette:isElementDisplayed", isElementDisplayedFn); - addMessageListenerId("Marionette:getElementValueOfCssProperty", getElementValueOfCssPropertyFn); - addMessageListenerId("Marionette:getElementRect", getElementRectFn); - addMessageListenerId("Marionette:isElementEnabled", isElementEnabledFn); - addMessageListenerId("Marionette:isElementSelected", isElementSelectedFn); - addMessageListenerId("Marionette:sendKeysToElement", sendKeysToElementFn); - addMessageListenerId("Marionette:clearElement", clearElementFn); - addMessageListenerId("Marionette:switchToFrame", switchToFrame); - addMessageListenerId("Marionette:switchToParentFrame", switchToParentFrame); - addMessageListenerId("Marionette:switchToShadowRoot", switchToShadowRootFn); - addMessageListenerId("Marionette:deleteSession", deleteSession); - addMessageListenerId("Marionette:sleepSession", sleepSession); - addMessageListenerId("Marionette:getAppCacheStatus", getAppCacheStatus); - addMessageListenerId("Marionette:setTestName", setTestName); - addMessageListenerId("Marionette:takeScreenshot", takeScreenshotFn); - addMessageListenerId("Marionette:addCookie", addCookieFn); - addMessageListenerId("Marionette:getCookies", getCookiesFn); - addMessageListenerId("Marionette:deleteAllCookies", deleteAllCookiesFn); - addMessageListenerId("Marionette:deleteCookie", deleteCookieFn); -} - -/** - * Used during newSession and restart, called to set up the modal dialog listener in b2g - */ -function waitForReady() { - if (content.document.readyState == 'complete') { - readyStateTimer.cancel(); - content.addEventListener("mozbrowsershowmodalprompt", modalHandler, false); - content.addEventListener("unload", waitForReady, false); - } - else { - readyStateTimer.initWithCallback(waitForReady, 100, Ci.nsITimer.TYPE_ONE_SHOT); - } -} - -/** - * Called when we start a new session. It registers the - * current environment, and resets all values - */ -function newSession(msg) { - capabilities = session.Capabilities.fromJSON(msg.json); - isB2G = capabilities.get("platformName") === "B2G"; - resetValues(); - if (isB2G) { - readyStateTimer.initWithCallback(waitForReady, 100, Ci.nsITimer.TYPE_ONE_SHOT); - // We have to set correct mouse event source to MOZ_SOURCE_TOUCH - // to offer a way for event listeners to differentiate - // events being the result of a physical mouse action. - // This is especially important for the touch event shim, - // in order to prevent creating touch event for these fake mouse events. - legacyactions.inputSource = Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH; - } -} - -/** - * Puts the current session to sleep, so all listeners are removed except - * for the 'restart' listener. This is used to keep the content listener - * alive for reuse in B2G instead of reloading it each time. - */ -function sleepSession(msg) { - deleteSession(); - addMessageListener("Marionette:restart", restart); -} - -/** - * Restarts all our listeners after this listener was put to sleep - */ -function restart(msg) { - removeMessageListener("Marionette:restart", restart); - if (isB2G) { - readyStateTimer.initWithCallback(waitForReady, 100, Ci.nsITimer.TYPE_ONE_SHOT); - } - registerSelf(); -} - -/** - * Removes all listeners - */ -function deleteSession(msg) { - removeMessageListenerId("Marionette:newSession", newSession); - removeMessageListenerId("Marionette:execute", executeFn); - removeMessageListenerId("Marionette:executeInSandbox", executeInSandboxFn); - removeMessageListenerId("Marionette:executeSimpleTest", executeSimpleTestFn); - removeMessageListenerId("Marionette:singleTap", singleTapFn); - removeMessageListenerId("Marionette:performActions", performActionsFn); - removeMessageListenerId("Marionette:releaseActions", releaseActionsFn); - removeMessageListenerId("Marionette:actionChain", actionChainFn); - removeMessageListenerId("Marionette:multiAction", multiActionFn); - removeMessageListenerId("Marionette:get", get); - removeMessageListenerId("Marionette:pollForReadyState", pollForReadyState); - removeMessageListenerId("Marionette:cancelRequest", cancelRequest); - removeMessageListenerId("Marionette:getTitle", getTitleFn); - removeMessageListenerId("Marionette:getPageSource", getPageSourceFn); - removeMessageListenerId("Marionette:getCurrentUrl", getCurrentUrlFn); - removeMessageListenerId("Marionette:goBack", goBack); - removeMessageListenerId("Marionette:goForward", goForward); - removeMessageListenerId("Marionette:refresh", refresh); - removeMessageListenerId("Marionette:findElementContent", findElementContentFn); - removeMessageListenerId("Marionette:findElementsContent", findElementsContentFn); - removeMessageListenerId("Marionette:getActiveElement", getActiveElementFn); - removeMessageListenerId("Marionette:clickElement", clickElementFn); - removeMessageListenerId("Marionette:getElementAttribute", getElementAttributeFn); - removeMessageListenerId("Marionette:getElementProperty", getElementPropertyFn); - removeMessageListenerId("Marionette:getElementText", getElementTextFn); - removeMessageListenerId("Marionette:getElementTagName", getElementTagNameFn); - removeMessageListenerId("Marionette:isElementDisplayed", isElementDisplayedFn); - removeMessageListenerId("Marionette:getElementValueOfCssProperty", getElementValueOfCssPropertyFn); - removeMessageListenerId("Marionette:getElementRect", getElementRectFn); - removeMessageListenerId("Marionette:isElementEnabled", isElementEnabledFn); - removeMessageListenerId("Marionette:isElementSelected", isElementSelectedFn); - removeMessageListenerId("Marionette:sendKeysToElement", sendKeysToElementFn); - removeMessageListenerId("Marionette:clearElement", clearElementFn); - removeMessageListenerId("Marionette:switchToFrame", switchToFrame); - removeMessageListenerId("Marionette:switchToParentFrame", switchToParentFrame); - removeMessageListenerId("Marionette:switchToShadowRoot", switchToShadowRootFn); - removeMessageListenerId("Marionette:deleteSession", deleteSession); - removeMessageListenerId("Marionette:sleepSession", sleepSession); - removeMessageListenerId("Marionette:getAppCacheStatus", getAppCacheStatus); - removeMessageListenerId("Marionette:setTestName", setTestName); - removeMessageListenerId("Marionette:takeScreenshot", takeScreenshotFn); - removeMessageListenerId("Marionette:addCookie", addCookieFn); - removeMessageListenerId("Marionette:getCookies", getCookiesFn); - removeMessageListenerId("Marionette:deleteAllCookies", deleteAllCookiesFn); - removeMessageListenerId("Marionette:deleteCookie", deleteCookieFn); - if (isB2G) { - content.removeEventListener("mozbrowsershowmodalprompt", modalHandler, false); - } - seenEls.clear(); - // reset container frame to the top-most frame - curContainer = { frame: content, shadowRoot: null }; - curContainer.frame.focus(); - legacyactions.touchIds = {}; - if (action.inputStateMap !== undefined) { - action.inputStateMap.clear(); - } - if (action.inputsToCancel !== undefined) { - action.inputsToCancel.length = 0; - } -} - -/** - * Send asynchronous reply to chrome. - * - * @param {UUID} uuid - * Unique identifier of the request. - * @param {AsyncContentSender.ResponseType} type - * Type of response. - * @param {?=} data - * JSON serialisable object to accompany the message. Defaults to - * an empty dictionary. - */ -function sendToServer(uuid, data = undefined) { - let channel = new proxy.AsyncMessageChannel( - () => this, - sendAsyncMessage.bind(this)); - channel.reply(uuid, data); -} - -/** - * Send asynchronous reply with value to chrome. - * - * @param {?} obj - * JSON serialisable object of arbitrary type and complexity. - * @param {UUID} uuid - * Unique identifier of the request. - */ -function sendResponse(obj, id) { - sendToServer(id, obj); -} - -/** - * Send asynchronous reply to chrome. - * - * @param {UUID} uuid - * Unique identifier of the request. - */ -function sendOk(uuid) { - sendToServer(uuid); -} - -/** - * Send asynchronous error reply to chrome. - * - * @param {Error} err - * Error to notify chrome of. - * @param {UUID} uuid - * Unique identifier of the request. - */ -function sendError(err, uuid) { - sendToServer(uuid, err); -} - -/** - * Send log message to server - */ -function sendLog(msg) { - sendToServer("Marionette:log", {message: msg}); -} - -/** - * Clear test values after completion of test - */ -function resetValues() { - sandboxes.clear(); - curContainer = {frame: content, shadowRoot: null}; - legacyactions.mouseEventsOnly = false; - action.inputStateMap = new Map(); - action.inputsToCancel = []; -} - -/** - * Dump a logline to stdout. Prepends logline with a timestamp. - */ -function dumpLog(logline) { - dump(Date.now() + " Marionette: " + logline); -} - -/** - * Check if our context was interrupted - */ -function wasInterrupted() { - if (previousContainer) { - let element = content.document.elementFromPoint((content.innerWidth/2), (content.innerHeight/2)); - if (element.id.indexOf("modal-dialog") == -1) { - return true; - } - else { - return false; - } - } - return sendSyncMessage("MarionetteFrame:getInterruptedState", {})[0].value; -} - -function checkForInterrupted() { - if (wasInterrupted()) { - if (previousContainer) { - // if previousContainer is set, then we're in a single process environment - curContainer = legacyactions.container = previousContainer; - previousContainer = null; - } - else { - //else we're in OOP environment, so we'll switch to the original OOP frame - sendSyncMessage("Marionette:switchToModalOrigin"); - } - sendSyncMessage("Marionette:switchedToFrame", { restorePrevious: true }); - } -} - -function* execute(script, args, timeout, opts) { - opts.timeout = timeout; - script = importedScripts.for("content").concat(script); - - let sb = sandbox.createMutable(curContainer.frame); - let wargs = element.fromJson( - args, seenEls, curContainer.frame, curContainer.shadowRoot); - let res = yield evaluate.sandbox(sb, script, wargs, opts); - - return element.toJson(res, seenEls); -} - -function* executeInSandbox(script, args, timeout, opts) { - opts.timeout = timeout; - script = importedScripts.for("content").concat(script); - - let sb = sandboxes.get(opts.sandboxName, opts.newSandbox); - if (opts.sandboxName) { - sb = sandbox.augment(sb, {global: sb}); - sb = sandbox.augment(sb, new logging.Adapter(contentLog)); - } - - let wargs = element.fromJson( - args, seenEls, curContainer.frame, curContainer.shadowRoot); - let evaluatePromise = evaluate.sandbox(sb, script, wargs, opts); - - let res = yield evaluatePromise; - sendSyncMessage( - "Marionette:shareData", - {log: element.toJson(contentLog.get(), seenEls)}); - return element.toJson(res, seenEls); -} - -function* executeSimpleTest(script, args, timeout, opts) { - opts.timeout = timeout; - let win = curContainer.frame; - script = importedScripts.for("content").concat(script); - - let harness = new simpletest.Harness( - win, - "content", - contentLog, - timeout, - marionetteTestName); - let sb = sandbox.createSimpleTest(curContainer.frame, harness); - // TODO(ato): Not sure this is needed: - sb = sandbox.augment(sb, new logging.Adapter(contentLog)); - - let wargs = element.fromJson( - args, seenEls, curContainer.frame, curContainer.shadowRoot); - let evaluatePromise = evaluate.sandbox(sb, script, wargs, opts); - - let res = yield evaluatePromise; - sendSyncMessage( - "Marionette:shareData", - {log: element.toJson(contentLog.get(), seenEls)}); - return element.toJson(res, seenEls); -} - -/** - * Sets the test name, used in logging messages. - */ -function setTestName(msg) { - marionetteTestName = msg.json.value; - sendOk(msg.json.command_id); -} - -/** - * This function creates a touch event given a touch type and a touch - */ -function emitTouchEvent(type, touch) { - if (!wasInterrupted()) { - let loggingInfo = "emitting Touch event of type " + type + " to element with id: " + touch.target.id + " and tag name: " + touch.target.tagName + " at coordinates (" + touch.clientX + ", " + touch.clientY + ") relative to the viewport"; - dumpLog(loggingInfo); - var docShell = curContainer.frame.document.defaultView. - QueryInterface(Components.interfaces.nsIInterfaceRequestor). - getInterface(Components.interfaces.nsIWebNavigation). - QueryInterface(Components.interfaces.nsIDocShell); - if (docShell.asyncPanZoomEnabled && legacyactions.scrolling) { - // if we're in APZ and we're scrolling, we must use sendNativeTouchPoint to dispatch our touchmove events - let index = sendSyncMessage("MarionetteFrame:getCurrentFrameId"); - // only call emitTouchEventForIFrame if we're inside an iframe. - if (index != null) { - sendSyncMessage("Marionette:emitTouchEvent", - { index: index, type: type, id: touch.identifier, - clientX: touch.clientX, clientY: touch.clientY, - screenX: touch.screenX, screenY: touch.screenY, - radiusX: touch.radiusX, radiusY: touch.radiusY, - rotation: touch.rotationAngle, force: touch.force }); - return; - } - } - // we get here if we're not in asyncPacZoomEnabled land, or if we're the main process - /* - Disabled per bug 888303 - contentLog.log(loggingInfo, "TRACE"); - sendSyncMessage( - "Marionette:shareData", - {log: element.toJson(contentLog.get(), seenEls)}); - contentLog.clear(); - */ - let domWindowUtils = curContainer.frame.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIDOMWindowUtils); - domWindowUtils.sendTouchEvent(type, [touch.identifier], [touch.clientX], [touch.clientY], [touch.radiusX], [touch.radiusY], [touch.rotationAngle], [touch.force], 1, 0); - } -} - -/** - * Function that perform a single tap - */ -function singleTap(id, corx, cory) { - let el = seenEls.get(id, curContainer); - // after this block, the element will be scrolled into view - let visible = element.isVisible(el, corx, cory); - if (!visible) { - throw new ElementNotInteractableError("Element is not currently visible and may not be manipulated"); - } - - let a11y = accessibility.get(capabilities.get("moz:accessibilityChecks")); - return a11y.getAccessible(el, true).then(acc => { - a11y.assertVisible(acc, el, visible); - a11y.assertActionable(acc, el); - if (!curContainer.frame.document.createTouch) { - legacyactions.mouseEventsOnly = true; - } - let c = element.coordinates(el, corx, cory); - if (!legacyactions.mouseEventsOnly) { - let touchId = legacyactions.nextTouchId++; - let touch = createATouch(el, c.x, c.y, touchId); - emitTouchEvent('touchstart', touch); - emitTouchEvent('touchend', touch); - } - legacyactions.mouseTap(el.ownerDocument, c.x, c.y); - }); -} - -/** - * Function to create a touch based on the element - * corx and cory are relative to the viewport, id is the touchId - */ -function createATouch(el, corx, cory, touchId) { - let doc = el.ownerDocument; - let win = doc.defaultView; - let [clientX, clientY, pageX, pageY, screenX, screenY] = - legacyactions.getCoordinateInfo(el, corx, cory); - let atouch = doc.createTouch(win, el, touchId, pageX, pageY, screenX, screenY, clientX, clientY); - return atouch; -} - -/** - * Perform a series of grouped actions at the specified points in time. - * - * @param {obj} msg - * Object with an |actions| attribute that is an Array of objects - * each of which represents an action sequence. - */ -function* performActions(msg) { - let chain = action.Chain.fromJson(msg.actions); - yield action.dispatch(chain, seenEls, curContainer); -} - -/** - * The Release Actions command is used to release all the keys and pointer - * buttons that are currently depressed. This causes events to be fired as if - * the state was released by an explicit series of actions. It also clears all - * the internal state of the virtual devices. - */ -function* releaseActions() { - yield action.dispatchTickActions(action.inputsToCancel.reverse(), 0, seenEls, curContainer); - action.inputsToCancel.length = 0; - action.inputStateMap.clear(); -} - -/** - * Start action chain on one finger. - */ -function actionChain(chain, touchId) { - let touchProvider = {}; - touchProvider.createATouch = createATouch; - touchProvider.emitTouchEvent = emitTouchEvent; - - return legacyactions.dispatchActions( - chain, - touchId, - curContainer, - seenEls, - touchProvider); -} - -/** - * Function to emit touch events which allow multi touch on the screen - * @param type represents the type of event, touch represents the current touch,touches are all pending touches - */ -function emitMultiEvents(type, touch, touches) { - let target = touch.target; - let doc = target.ownerDocument; - let win = doc.defaultView; - // touches that are in the same document - let documentTouches = doc.createTouchList(touches.filter(function (t) { - return ((t.target.ownerDocument === doc) && (type != 'touchcancel')); - })); - // touches on the same target - let targetTouches = doc.createTouchList(touches.filter(function (t) { - return ((t.target === target) && ((type != 'touchcancel') || (type != 'touchend'))); - })); - // Create changed touches - let changedTouches = doc.createTouchList(touch); - // Create the event object - let event = doc.createEvent('TouchEvent'); - event.initTouchEvent(type, - true, - true, - win, - 0, - false, false, false, false, - documentTouches, - targetTouches, - changedTouches); - target.dispatchEvent(event); -} - -/** - * Function to dispatch one set of actions - * @param touches represents all pending touches, batchIndex represents the batch we are dispatching right now - */ -function setDispatch(batches, touches, batchIndex=0) { - // check if all the sets have been fired - if (batchIndex >= batches.length) { - multiLast = {}; - return; - } - - // a set of actions need to be done - let batch = batches[batchIndex]; - // each action for some finger - let pack; - // the touch id for the finger (pack) - let touchId; - // command for the finger - let command; - // touch that will be created for the finger - let el; - let corx; - let cory; - let touch; - let lastTouch; - let touchIndex; - let waitTime = 0; - let maxTime = 0; - let c; - - // loop through the batch - batchIndex++; - for (let i = 0; i < batch.length; i++) { - pack = batch[i]; - touchId = pack[0]; - command = pack[1]; - - switch (command) { - case "press": - el = seenEls.get(pack[2], curContainer); - c = element.coordinates(el, pack[3], pack[4]); - touch = createATouch(el, c.x, c.y, touchId); - multiLast[touchId] = touch; - touches.push(touch); - emitMultiEvents("touchstart", touch, touches); - break; - - case "release": - touch = multiLast[touchId]; - // the index of the previous touch for the finger may change in the touches array - touchIndex = touches.indexOf(touch); - touches.splice(touchIndex, 1); - emitMultiEvents("touchend", touch, touches); - break; - - case "move": - el = seenEls.get(pack[2], curContainer); - c = element.coordinates(el); - touch = createATouch(multiLast[touchId].target, c.x, c.y, touchId); - touchIndex = touches.indexOf(lastTouch); - touches[touchIndex] = touch; - multiLast[touchId] = touch; - emitMultiEvents("touchmove", touch, touches); - break; - - case "moveByOffset": - el = multiLast[touchId].target; - lastTouch = multiLast[touchId]; - touchIndex = touches.indexOf(lastTouch); - let doc = el.ownerDocument; - let win = doc.defaultView; - // since x and y are relative to the last touch, therefore, it's relative to the position of the last touch - let clientX = lastTouch.clientX + pack[2], - clientY = lastTouch.clientY + pack[3]; - let pageX = clientX + win.pageXOffset, - pageY = clientY + win.pageYOffset; - let screenX = clientX + win.mozInnerScreenX, - screenY = clientY + win.mozInnerScreenY; - touch = doc.createTouch(win, el, touchId, pageX, pageY, screenX, screenY, clientX, clientY); - touches[touchIndex] = touch; - multiLast[touchId] = touch; - emitMultiEvents("touchmove", touch, touches); - break; - - case "wait": - if (typeof pack[2] != "undefined") { - waitTime = pack[2] * 1000; - if (waitTime > maxTime) { - maxTime = waitTime; - } - } - break; - } - } - - if (maxTime != 0) { - checkTimer.initWithCallback(function() { - setDispatch(batches, touches, batchIndex); - }, maxTime, Ci.nsITimer.TYPE_ONE_SHOT); - } else { - setDispatch(batches, touches, batchIndex); - } -} - -/** - * Start multi-action. - * - * @param {Number} maxLen - * Longest action chain for one finger. - */ -function multiAction(args, maxLen) { - // unwrap the original nested array - let commandArray = element.fromJson( - args, seenEls, curContainer.frame, curContainer.shadowRoot); - let concurrentEvent = []; - let temp; - for (let i = 0; i < maxLen; i++) { - let row = []; - for (let j = 0; j < commandArray.length; j++) { - if (typeof commandArray[j][i] != "undefined") { - // add finger id to the front of each action, i.e. [finger_id, action, element] - temp = commandArray[j][i]; - temp.unshift(j); - row.push(temp); - } - } - concurrentEvent.push(row); - } - - // now concurrent event is made of sets where each set contain a list of actions that need to be fired. - // note: each action belongs to a different finger - // pendingTouches keeps track of current touches that's on the screen - let pendingTouches = []; - setDispatch(concurrentEvent, pendingTouches); -} - -/** - * This implements the latter part of a get request (for the case we need to resume one - * when a remoteness update happens in the middle of a navigate request). This is most of - * of the work of a navigate request, but doesn't assume DOMContentLoaded is yet to fire. - * - * @param {function=} cleanupCallback - * Callback to execute when registered event handlers or observer notifications - * have to be cleaned-up. - * @param {number} command_id - * ID of the currently handled message between the driver and listener. - * @param {string=} lastSeenURL - * Last URL as seen before the navigation request got triggered. - * @param {number} pageTimeout - * Timeout in seconds the method has to wait for the page being finished loading. - * @param {number} startTime - * Unix timestap when the navitation request got triggred. - */ -function pollForReadyState(msg) { - let {cleanupCallback, command_id, lastSeenURL, pageTimeout, startTime} = msg.json; - - if (typeof startTime == "undefined") { - startTime = new Date().getTime(); - } - - if (typeof cleanupCallback == "undefined") { - cleanupCallback = () => {}; - } - - let endTime = startTime + pageTimeout; - - let checkLoad = function() { - navTimer.cancel(); - - let doc = curContainer.frame.document; - - if (pageTimeout === null || new Date().getTime() <= endTime) { - // Under some conditions (eg. for error pages) the pagehide event is fired - // even with a readyState complete for the formerly loaded page. - // To prevent race conditition for goBack and goForward we have to wait - // until the last seen page has been fully unloaded. - // TODO: Bug 1333458 has to improve this. - if (!doc.location || lastSeenURL && doc.location.href === lastSeenURL) { - navTimer.initWithCallback(checkLoad, 100, Ci.nsITimer.TYPE_ONE_SHOT); - - // document fully loaded - } else if (doc.readyState === "complete") { - cleanupCallback(); - sendOk(command_id); - - // document with an insecure cert - } else if (doc.readyState === "interactive" && - doc.baseURI.startsWith("about:certerror")) { - cleanupCallback(); - sendError(new InsecureCertificateError(), command_id); - - // we have reached an error url without requesting it - } else if (doc.readyState === "interactive" && - /about:.+(error)\?/.exec(doc.baseURI)) { - cleanupCallback(); - sendError(new UnknownError("Reached error page: " + doc.baseURI), command_id); - - // return early for about: urls - } else if (doc.readyState === "interactive" && doc.baseURI.startsWith("about:")) { - cleanupCallback(); - sendOk(command_id); - - // document not fully loaded - } else { - navTimer.initWithCallback(checkLoad, 100, Ci.nsITimer.TYPE_ONE_SHOT); - } - - } else { - cleanupCallback(); - sendError(new TimeoutError("Error loading page, timed out (checkLoad)"), command_id); - } - }; - - checkLoad(); -} - -/** - * Navigate to the given URL. The operation will be performed on the - * current browsing context, which means it handles the case where we - * navigate within an iframe. All other navigation is handled by the - * driver (in chrome space). - */ -function get(msg) { - let {pageTimeout, url, command_id} = msg.json; - - let startTime = new Date().getTime(); - - // We need to move to the top frame before navigating - sendSyncMessage("Marionette:switchedToFrame", {frameValue: null}); - curContainer.frame = content; - - let docShell = curContainer.frame - .document - .defaultView - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShell); - let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebProgress); - let sawLoad = false; - - let requestedURL; - let loadEventExpected = false; - try { - requestedURL = new URL(url).toString(); - let curURL = curContainer.frame.location; - loadEventExpected = navigate.isLoadEventExpected(curURL, requestedURL); - } catch (e) { - sendError(new InvalidArgumentError("Malformed URL: " + e.message), command_id); - return; - } - - // It's possible that a site we're being sent to will end up redirecting - // us before we end up on a page that fires DOMContentLoaded. We can ensure - // This loadListener ensures that we don't send a success signal back to - // the caller until we've seen the load of the requested URL attempted - // on this frame. - let loadListener = { - QueryInterface: XPCOMUtils.generateQI( - [Ci.nsIWebProgressListener, Ci.nsISupportsWeakReference]), - - onStateChange(webProgress, request, state, status) { - if (!(request instanceof Ci.nsIChannel)) { - return; - } - - const isDocument = state & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT; - const loadedURL = request.URI.spec; - - // We have to look at the originalURL because of about: pages, - // the loadedURL is what the about: page resolves to, and is - // not the one that was requested. - const originalURL = request.originalURI.spec; - const isRequestedURL = loadedURL == requestedURL || - originalURL == requestedURL; - - if (!isDocument || !isRequestedURL) { - return; - } - - // We started loading the requested document. This document - // might not be the one that ends up firing DOMContentLoaded - // (if it, for example, redirects), but because we've started - // loading this URL, we know that any future DOMContentLoaded's - // are fair game to tell the Marionette client about. - if (state & Ci.nsIWebProgressListener.STATE_START) { - sawLoad = true; - } - - // This indicates network stop or last request stop outside of - // loading the document. We hit this when DOMContentLoaded is - // not triggered, which is the case for image documents. - else if (state & Ci.nsIWebProgressListener.STATE_STOP && - content.document instanceof content.ImageDocument) { - pollForReadyState({json: { - command_id: command_id, - pageTimeout: pageTimeout, - startTime: startTime, - cleanupCallback: () => { - webProgress.removeProgressListener(loadListener); - removeEventListener("DOMContentLoaded", onDOMContentLoaded, false); - } - }}); - } - }, - - onLocationChange() {}, - onProgressChange() {}, - onStatusChange() {}, - onSecurityChange() {}, - }; - - webProgress.addProgressListener( - loadListener, Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT); - - // Prevent DOMContentLoaded events from frames from invoking this - // code, unless the event is coming from the frame associated with - // the current window (i.e. someone has used switch_to_frame). - onDOMContentLoaded = function onDOMContentLoaded(event) { - let frameEl = event.originalTarget.defaultView.frameElement; - let correctFrame = !frameEl || frameEl == curContainer.frame.frameElement; - - // If the page we're at fired DOMContentLoaded and appears - // to be the one we asked to load, then we definitely - // saw the load occur. We need this because for error - // pages, like about:neterror for unsupported protocols, - // we don't end up opening a channel that our - // WebProgressListener can monitor. - if (curContainer.frame.location == requestedURL) { - sawLoad = true; - } - - // We also need to make sure that if the requested URL is not about:blank - // the DOMContentLoaded we saw isn't for the initial about:blank of a newly - // created docShell. - let loadedRequestedURI = (requestedURL == "about:blank") || - docShell.hasLoadedNonBlankURI; - - if (correctFrame && sawLoad && loadedRequestedURI) { - pollForReadyState({json: { - command_id: command_id, - pageTimeout: pageTimeout, - startTime: startTime, - cleanupCallback: () => { - webProgress.removeProgressListener(loadListener); - removeEventListener("DOMContentLoaded", onDOMContentLoaded, false); - } - }}); - } - }; - - if (typeof pageTimeout != "undefined") { - let onTimeout = function() { - if (loadEventExpected) { - removeEventListener("DOMContentLoaded", onDOMContentLoaded, false); - } - webProgress.removeProgressListener(loadListener); - sendError(new TimeoutError("Error loading page, timed out (onDOMContentLoaded)"), command_id); - } - navTimer.initWithCallback(onTimeout, pageTimeout, Ci.nsITimer.TYPE_ONE_SHOT); - } - - if (loadEventExpected) { - addEventListener("DOMContentLoaded", onDOMContentLoaded, false); - } - curContainer.frame.location = requestedURL; - if (!loadEventExpected) { - sendOk(command_id); - } -} - -/** - * Cancel the polling and remove the event listener associated with a - * current navigation request in case we're interupted by an onbeforeunload - * handler and navigation doesn't complete. - */ -function cancelRequest() { - navTimer.cancel(); - if (onDOMContentLoaded) { - removeEventListener("DOMContentLoaded", onDOMContentLoaded, false); - } -} - -/** - * Get URL of the top-level browsing context. - */ -function getCurrentUrl() { - return content.location.href; -} - -/** - * Get the title of the current browsing context. - */ -function getTitle() { - return curContainer.frame.top.document.title; -} - -/** - * Get source of the current browsing context's DOM. - */ -function getPageSource() { - return curContainer.frame.document.documentElement.outerHTML; -} - -/** - * Wait for the current page to be unloaded after a navigation got triggered. - * - * @param {function} trigger - * Callback to execute which triggers a page navigation. - * @param {function} doneCallback - * Callback to execute when the current page has been unloaded. - * - * It receives a dictionary with the following items as argument: - * loading - Flag if a page load will follow. - * lastSeenURL - Last seen URL before the navigation request. - * startTime - Time when the navigation request has been triggered. - */ -function waitForPageUnloaded(trigger, doneCallback) { - let currentURL = curContainer.frame.location.href; - let start = new Date().getTime(); - - function handleEvent(event) { - // In case of a remoteness change it can happen that we are no longer able - // to access the document's location. In those cases ignore the event, - // but keep the code waiting, and assume in the driver that waiting for the - // page load is necessary. Bug 1333458 should improve things. - if (typeof event.originalTarget.location == "undefined") { - return; - } - - switch (event.type) { - case "hashchange": - removeEventListener("hashchange", handleEvent); - removeEventListener("pagehide", handleEvent); - removeEventListener("unload", handleEvent); - - doneCallback({loading: false, lastSeenURL: currentURL}); - break; - - case "pagehide": - case "unload": - if (event.originalTarget === curContainer.frame.document) { - removeEventListener("hashchange", handleEvent); - removeEventListener("pagehide", handleEvent); - removeEventListener("unload", handleEvent); - - doneCallback({loading: true, lastSeenURL: currentURL, startTime: start}); - } - break; - } - } - - addEventListener("hashchange", handleEvent, false); - addEventListener("pagehide", handleEvent, false); - addEventListener("unload", handleEvent, false); - - trigger(); -} - -/** - * Cause the browser to traverse one step backward in the joint history - * of the current browsing context. - * - * @param {number} command_id - * ID of the currently handled message between the driver and listener. - * @param {number} pageTimeout - * Timeout in milliseconds the method has to wait for the page being finished loading. - */ -function goBack(msg) { - let {command_id, pageTimeout} = msg.json; - - waitForPageUnloaded(() => { - curContainer.frame.history.back(); - }, pageLoadStatus => { - if (pageLoadStatus.loading) { - pollForReadyState({json: { - command_id: command_id, - lastSeenURL: pageLoadStatus.lastSeenURL, - pageTimeout: pageTimeout, - startTime: pageLoadStatus.startTime, - }}); - } else { - sendOk(command_id); - } - }); -} - -/** - * Cause the browser to traverse one step forward in the joint history - * of the current browsing context. - * - * @param {number} command_id - * ID of the currently handled message between the driver and listener. - * @param {number} pageTimeout - * Timeout in milliseconds the method has to wait for the page being finished loading. - */ -function goForward(msg) { - let {command_id, pageTimeout} = msg.json; - - waitForPageUnloaded(() => { - curContainer.frame.history.forward(); - }, pageLoadStatus => { - if (pageLoadStatus.loading) { - pollForReadyState({json: { - command_id: command_id, - lastSeenURL: pageLoadStatus.lastSeenURL, - pageTimeout: pageTimeout, - startTime: pageLoadStatus.startTime, - }}); - } else { - sendOk(command_id); - } - }); -} - -/** - * Refresh the page - */ -function refresh(msg) { - let command_id = msg.json.command_id; - curContainer.frame.location.reload(true); - let listen = function() { - removeEventListener("DOMContentLoaded", listen, false); - sendOk(command_id); - }; - addEventListener("DOMContentLoaded", listen, false); -} - -/** - * Find an element in the current browsing context's document using the - * given search strategy. - */ -function* findElementContent(strategy, selector, opts = {}) { - if (!SUPPORTED_STRATEGIES.has(strategy)) { - throw new InvalidSelectorError("Strategy not supported: " + strategy); - } - - opts.all = false; - if (opts.startNode) { - opts.startNode = seenEls.get(opts.startNode, curContainer); - } - - let el = yield element.find(curContainer, strategy, selector, opts); - let elRef = seenEls.add(el); - let webEl = element.makeWebElement(elRef); - return webEl; -} - -/** - * Find elements in the current browsing context's document using the - * given search strategy. - */ -function* findElementsContent(strategy, selector, opts = {}) { - if (!SUPPORTED_STRATEGIES.has(strategy)) { - throw new InvalidSelectorError("Strategy not supported: " + strategy); - } - - opts.all = true; - if (opts.startNode) { - opts.startNode = seenEls.get(opts.startNode, curContainer); - } - - let els = yield element.find(curContainer, strategy, selector, opts); - let elRefs = seenEls.addAll(els); - let webEls = elRefs.map(element.makeWebElement); - return webEls; -} - -/** Find and return the active element on the page. */ -function getActiveElement() { - let el = curContainer.frame.document.activeElement; - return element.toJson(el, seenEls); -} - -/** - * Send click event to element. - * - * @param {WebElement} id - * Reference to the web element to click. - */ -function clickElement(id) { - let el = seenEls.get(id, curContainer); - return interaction.clickElement( - el, - capabilities.get("moz:accessibilityChecks"), - capabilities.get("specificationLevel") >= 1); -} - -function getElementAttribute(id, name) { - let el = seenEls.get(id, curContainer); - if (element.isBooleanAttribute(el, name)) { - if (el.hasAttribute(name)) { - return "true"; - } else { - return null; - } - } else { - return el.getAttribute(name); - } -} - -function getElementProperty(id, name) { - let el = seenEls.get(id, curContainer); - return typeof el[name] != "undefined" ? el[name] : null; -} - -/** - * Get the text of this element. This includes text from child elements. - * - * @param {WebElement} id - * Reference to web element. - * - * @return {string} - * Text of element. - */ -function getElementText(id) { - let el = seenEls.get(id, curContainer); - return atom.getElementText(el, curContainer.frame); -} - -/** - * Get the tag name of an element. - * - * @param {WebElement} id - * Reference to web element. - * - * @return {string} - * Tag name of element. - */ -function getElementTagName(id) { - let el = seenEls.get(id, curContainer); - return el.tagName.toLowerCase(); -} - -/** - * Determine the element displayedness of the given web element. - * - * Also performs additional accessibility checks if enabled by session - * capability. - */ -function isElementDisplayed(id) { - let el = seenEls.get(id, curContainer); - return interaction.isElementDisplayed( - el, capabilities.get("moz:accessibilityChecks")); -} - -/** - * Retrieves the computed value of the given CSS property of the given - * web element. - * - * @param {String} id - * Web element reference. - * @param {String} prop - * The CSS property to get. - * - * @return {String} - * Effective value of the requested CSS property. - */ -function getElementValueOfCssProperty(id, prop) { - let el = seenEls.get(id, curContainer); - let st = curContainer.frame.document.defaultView.getComputedStyle(el, null); - return st.getPropertyValue(prop); -} - -/** - * Get the position and dimensions of the element. - * - * @param {WebElement} id - * Reference to web element. - * - * @return {Object.<string, number>} - * The x, y, width, and height properties of the element. - */ -function getElementRect(id) { - let el = seenEls.get(id, curContainer); - let clientRect = el.getBoundingClientRect(); - return { - x: clientRect.x + curContainer.frame.pageXOffset, - y: clientRect.y + curContainer.frame.pageYOffset, - width: clientRect.width, - height: clientRect.height - }; -} - -/** - * Check if element is enabled. - * - * @param {WebElement} id - * Reference to web element. - * - * @return {boolean} - * True if enabled, false otherwise. - */ -function isElementEnabled(id) { - let el = seenEls.get(id, curContainer); - return interaction.isElementEnabled( - el, capabilities.get("moz:accessibilityChecks")); -} - -/** - * Determines if the referenced element is selected or not. - * - * This operation only makes sense on input elements of the Checkbox- - * and Radio Button states, or option elements. - */ -function isElementSelected(id) { - let el = seenEls.get(id, curContainer); - return interaction.isElementSelected( - el, capabilities.get("moz:accessibilityChecks")); -} - -function* sendKeysToElement(id, val) { - let el = seenEls.get(id, curContainer); - if (el.type == "file") { - let path = val.join(""); - yield interaction.uploadFile(el, path); - } else if ((el.type == "date" || el.type == "time") && - Preferences.get("dom.forms.datetime")) { - yield interaction.setFormControlValue(el, val); - } else { - yield interaction.sendKeysToElement( - el, val, false, capabilities.get("moz:accessibilityChecks")); - } -} - -/** - * Clear the text of an element. - */ -function clearElement(id) { - try { - let el = seenEls.get(id, curContainer); - if (el.type == "file") { - el.value = null; - } else { - atom.clearElement(el, curContainer.frame); - } - } catch (e) { - // Bug 964738: Newer atoms contain status codes which makes wrapping - // this in an error prototype that has a status property unnecessary - if (e.name == "InvalidElementStateError") { - throw new InvalidElementStateError(e.message); - } else { - throw e; - } - } -} - -/** - * Switch the current context to the specified host's Shadow DOM. - * @param {WebElement} id - * Reference to web element. - */ -function switchToShadowRoot(id) { - if (!id) { - // If no host element is passed, attempt to find a parent shadow root or, if - // none found, unset the current shadow root - if (curContainer.shadowRoot) { - let parent; - try { - parent = curContainer.shadowRoot.host; - } catch (e) { - // There is a chance that host element is dead and we are trying to - // access a dead object. - curContainer.shadowRoot = null; - return; - } - while (parent && !(parent instanceof curContainer.frame.ShadowRoot)) { - parent = parent.parentNode; - } - curContainer.shadowRoot = parent; - } - return; - } - - let foundShadowRoot; - let hostEl = seenEls.get(id, curContainer); - foundShadowRoot = hostEl.shadowRoot; - if (!foundShadowRoot) { - throw new NoSuchElementError('Unable to locate shadow root: ' + id); - } - curContainer.shadowRoot = foundShadowRoot; -} - -/** - * Switch to the parent frame of the current Frame. If the frame is the top most - * is the current frame then no action will happen. - */ - function switchToParentFrame(msg) { - let command_id = msg.json.command_id; - curContainer.frame = curContainer.frame.parent; - let parentElement = seenEls.add(curContainer.frame); - - sendSyncMessage( - "Marionette:switchedToFrame", {frameValue: parentElement}); - - sendOk(msg.json.command_id); - } - -/** - * Switch to frame given either the server-assigned element id, - * its index in window.frames, or the iframe's name or id. - */ -function switchToFrame(msg) { - let command_id = msg.json.command_id; - function checkLoad() { - let errorRegex = /about:.+(error)|(blocked)\?/; - if (curContainer.frame.document.readyState == "complete") { - sendOk(command_id); - return; - } else if (curContainer.frame.document.readyState == "interactive" && - errorRegex.exec(curContainer.frame.document.baseURI)) { - sendError(new UnknownError("Error loading page"), command_id); - return; - } - checkTimer.initWithCallback(checkLoad, 100, Ci.nsITimer.TYPE_ONE_SHOT); - } - let foundFrame = null; - let frames = []; - let parWindow = null; - // Check of the curContainer.frame reference is dead - try { - frames = curContainer.frame.frames; - //Until Bug 761935 lands, we won't have multiple nested OOP iframes. We will only have one. - //parWindow will refer to the iframe above the nested OOP frame. - parWindow = curContainer.frame.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils).outerWindowID; - } catch (e) { - // We probably have a dead compartment so accessing it is going to make Firefox - // very upset. Let's now try redirect everything to the top frame even if the - // user has given us a frame since search doesnt look up. - msg.json.id = null; - msg.json.element = null; - } - - if ((msg.json.id === null || msg.json.id === undefined) && (msg.json.element == null)) { - // returning to root frame - sendSyncMessage("Marionette:switchedToFrame", { frameValue: null }); - - curContainer.frame = content; - if(msg.json.focus == true) { - curContainer.frame.focus(); - } - - checkTimer.initWithCallback(checkLoad, 100, Ci.nsITimer.TYPE_ONE_SHOT); - return; - } - - let id = msg.json.element; - if (seenEls.has(id)) { - let wantedFrame; - try { - wantedFrame = seenEls.get(id, curContainer); - } catch (e) { - sendError(e, command_id); - } - - if (frames.length > 0) { - for (let i = 0; i < frames.length; i++) { - // use XPCNativeWrapper to compare elements; see bug 834266 - if (XPCNativeWrapper(frames[i].frameElement) == XPCNativeWrapper(wantedFrame)) { - curContainer.frame = frames[i].frameElement; - foundFrame = i; - } - } - } - - if (foundFrame === null) { - // Either the frame has been removed or we have a OOP frame - // so lets just get all the iframes and do a quick loop before - // throwing in the towel - let iframes = curContainer.frame.document.getElementsByTagName("iframe"); - for (var i = 0; i < iframes.length; i++) { - if (XPCNativeWrapper(iframes[i]) == XPCNativeWrapper(wantedFrame)) { - curContainer.frame = iframes[i]; - foundFrame = i; - } - } - } - } - - if (foundFrame === null) { - if (typeof(msg.json.id) === 'number') { - try { - foundFrame = frames[msg.json.id].frameElement; - if (foundFrame !== null) { - curContainer.frame = foundFrame; - foundFrame = seenEls.add(curContainer.frame); - } - else { - // If foundFrame is null at this point then we have the top level browsing - // context so should treat it accordingly. - sendSyncMessage("Marionette:switchedToFrame", { frameValue: null}); - curContainer.frame = content; - if(msg.json.focus == true) { - curContainer.frame.focus(); - } - - checkTimer.initWithCallback(checkLoad, 100, Ci.nsITimer.TYPE_ONE_SHOT); - return; - } - } catch (e) { - // Since window.frames does not return OOP frames it will throw - // and we land up here. Let's not give up and check if there are - // iframes and switch to the indexed frame there - let iframes = curContainer.frame.document.getElementsByTagName("iframe"); - if (msg.json.id >= 0 && msg.json.id < iframes.length) { - curContainer.frame = iframes[msg.json.id]; - foundFrame = msg.json.id; - } - } - } - } - - if (foundFrame === null) { - sendError(new NoSuchFrameError("Unable to locate frame: " + (msg.json.id || msg.json.element)), command_id); - return true; - } - - // send a synchronous message to let the server update the currently active - // frame element (for getActiveFrame) - let frameValue = element.toJson( - curContainer.frame.wrappedJSObject, seenEls)[element.Key]; - sendSyncMessage("Marionette:switchedToFrame", {frameValue: frameValue}); - - let rv = null; - if (curContainer.frame.contentWindow === null) { - // The frame we want to switch to is a remote/OOP frame; - // notify our parent to handle the switch - curContainer.frame = content; - rv = {win: parWindow, frame: foundFrame}; - } else { - curContainer.frame = curContainer.frame.contentWindow; - if (msg.json.focus) { - curContainer.frame.focus(); - } - checkTimer.initWithCallback(checkLoad, 100, Ci.nsITimer.TYPE_ONE_SHOT); - } - - sendResponse(rv, command_id); -} - -function addCookie(cookie) { - cookies.add(cookie.name, cookie.value, cookie); -} - -/** - * Get all cookies for the current domain. - */ -function getCookies() { - let rv = []; - - for (let cookie of cookies) { - let expires = cookie.expires; - // session cookie, don't return an expiry - if (expires == 0) { - expires = null; - // date before epoch time, cap to epoch - } else if (expires == 1) { - expires = 0; - } - rv.push({ - 'name': cookie.name, - 'value': cookie.value, - 'path': cookie.path, - 'domain': cookie.host, - 'secure': cookie.isSecure, - 'httpOnly': cookie.httpOnly, - 'expiry': expires - }); - } - - return rv; -} - -/** - * Delete a cookie by name. - */ -function deleteCookie(name) { - cookies.delete(name); -} - -/** - * Delete all the visibile cookies on a page. - */ -function deleteAllCookies() { - for (let cookie of cookies) { - cookies.delete(cookie); - } -} - -function getAppCacheStatus(msg) { - sendResponse( - curContainer.frame.applicationCache.status, msg.json.command_id); -} - -/** - * Perform a screen capture in content context. - * - * Accepted values for |opts|: - * - * @param {UUID=} id - * Optional web element reference of an element to take a screenshot - * of. - * @param {boolean=} full - * True to take a screenshot of the entire document element. Is not - * considered if {@code id} is not defined. Defaults to true. - * @param {Array.<UUID>=} highlights - * Draw a border around the elements found by their web element - * references. - * @param {boolean=} scroll - * When |id| is given, scroll it into view before taking the - * screenshot. Defaults to true. - * - * @param {capture.Format} format - * Format to return the screenshot in. - * @param {Object.<string, ?>} opts - * Options. - * - * @return {string} - * Base64 encoded string or a SHA-256 hash of the screenshot. - */ -function takeScreenshot(format, opts = {}) { - let id = opts.id; - let full = !!opts.full; - let highlights = opts.highlights || []; - let scroll = !!opts.scroll; - - let highlightEls = highlights.map(ref => seenEls.get(ref, curContainer)); - - let canvas; - - // viewport - if (!id && !full) { - canvas = capture.viewport(curContainer.frame, highlightEls); - - // element or full document element - } else { - let el; - if (id) { - el = seenEls.get(id, curContainer); - if (scroll) { - element.scrollIntoView(el); - } - } else { - el = curContainer.frame.document.documentElement; - } - - canvas = capture.element(el, highlightEls); - } - - switch (format) { - case capture.Format.Base64: - return capture.toBase64(canvas); - - case capture.Format.Hash: - return capture.toHash(canvas); - - default: - throw new TypeError("Unknown screenshot format: " + format); - } -} - -// Call register self when we get loaded -registerSelf(); diff --git a/testing/marionette/logging.js b/testing/marionette/logging.js deleted file mode 100644 index d77adfb0c..000000000 --- a/testing/marionette/logging.js +++ /dev/null @@ -1,75 +0,0 @@ -/* 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/. */ - -"use strict"; - -this.EXPORTED_SYMBOLS = ["logging"]; - -this.logging = {}; - -/** Simple logger that is used in Simple Test harness tests. */ -logging.ContentLogger = class { - constructor() { - this.logs_ = []; - } - - /** - * Append a log entry. - * - * @param {string} message - * Log entry message. - * @param {string=} level - * Severity of entry. Defaults to "INFO". - */ - log(message, level = "INFO") { - let now = (new Date()).toString(); - this.logs_.push([level, message, now]); - } - - /** - * Append array of log entries. - * - * @param {Array.<Array<string, string, string>>} messages - * List of log entries, that are of the form severity, message, - * and date. - */ - addAll(messages) { - for (let message of messages) { - this.logs_.push(message); - } - } - - /** - * Gets current log entries and clears the cache. - * - * @return {Array.<Array<string, string, string>>} - * Log entries of the form severity, message, and date. - */ - get() { - let logs = this.logs_; - this.logs_ = []; - return logs; - } -}; - -/** - * Adapts an instance of ContentLogger for use in a sandbox. Is consumed - * by sandbox.augment. - */ -logging.Adapter = class { - constructor(logger = null) { - this.logger = logger; - } - - get exports() { - return new Map([["log", this.log.bind(this)]]); - } - - log(message, level = "INFO") { - dump(`MARIONETTE LOG: ${level}: ${message}\n`); - if (this.logger) { - this.logger.log(message, level); - } - } -}; diff --git a/testing/marionette/mach_commands.py b/testing/marionette/mach_commands.py deleted file mode 100644 index 6461bc16d..000000000 --- a/testing/marionette/mach_commands.py +++ /dev/null @@ -1,81 +0,0 @@ -# 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 __future__ import absolute_import, unicode_literals - -import os -import sys -import argparse - -from mozbuild.base import ( - MachCommandBase, - MachCommandConditions as conditions, -) - -from mach.decorators import ( - CommandArgument, - CommandProvider, - Command, -) - -def is_firefox_or_android(cls): - """Must have Firefox build or Android build.""" - return conditions.is_firefox(cls) or conditions.is_android(cls) - -def setup_marionette_argument_parser(): - from marionette_harness.runtests import MarionetteArguments - from mozlog.structured import commandline - parser = MarionetteArguments() - commandline.add_logging_group(parser) - return parser - -def run_marionette(tests, binary=None, topsrcdir=None, **kwargs): - from mozlog.structured import commandline - - from marionette_harness.runtests import ( - MarionetteTestRunner, - MarionetteHarness - ) - - parser = setup_marionette_argument_parser() - - if not tests: - tests = [os.path.join(topsrcdir, - 'testing/marionette/harness/marionette_harness/tests/unit-tests.ini')] - - args = argparse.Namespace(tests=tests) - - args.binary = binary - - for k, v in kwargs.iteritems(): - setattr(args, k, v) - - parser.verify_usage(args) - - args.logger = commandline.setup_logging("Marionette Unit Tests", - args, - {"mach": sys.stdout}) - failed = MarionetteHarness(MarionetteTestRunner, args=vars(args)).run() - if failed > 0: - return 1 - else: - return 0 - -@CommandProvider -class MachCommands(MachCommandBase): - @Command('marionette-test', category='testing', - description='Run a Marionette test (Check UI or the internal JavaScript using marionette).', - conditions=[is_firefox_or_android], - parser=setup_marionette_argument_parser, - ) - def run_marionette_test(self, tests, **kwargs): - if 'test_objects' in kwargs: - tests = [] - for obj in kwargs['test_objects']: - tests.append(obj['file_relpath']) - del kwargs['test_objects'] - - if not kwargs.get('binary') and conditions.is_firefox(self): - kwargs['binary'] = self.get_binary_path('app') - return run_marionette(tests, topsrcdir=self.topsrcdir, **kwargs) diff --git a/testing/marionette/mach_test_package_commands.py b/testing/marionette/mach_test_package_commands.py deleted file mode 100644 index 66d99d2a4..000000000 --- a/testing/marionette/mach_test_package_commands.py +++ /dev/null @@ -1,67 +0,0 @@ -# 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/. - -import argparse -import os -import sys -from functools import partial - -from mach.decorators import ( - CommandProvider, - Command, -) - -parser = None - - -def run_marionette(context, **kwargs): - from marionette.runtests import ( - MarionetteTestRunner, - MarionetteHarness - ) - from mozlog.structured import commandline - - - args = argparse.Namespace(**kwargs) - args.binary = args.binary or context.firefox_bin - args.e10s = context.mozharness_config.get('e10s', args.e10s) - - test_root = os.path.join(context.package_root, 'marionette', 'tests') - if not args.tests: - args.tests = [os.path.join(test_root, 'testing', 'marionette', 'harness', - 'marionette_harness', 'tests', 'unit-tests.ini')] - - normalize = partial(context.normalize_test_path, test_root) - args.tests = map(normalize, args.tests) - - commandline.add_logging_group(parser) - parser.verify_usage(args) - - args.logger = commandline.setup_logging("Marionette Unit Tests", - args, - {"mach": sys.stdout}) - status = MarionetteHarness(MarionetteTestRunner, args=vars(args)).run() - return 1 if status else 0 - - -def setup_marionette_argument_parser(): - from marionette.runner.base import BaseMarionetteArguments - global parser - parser = BaseMarionetteArguments() - return parser - - -@CommandProvider -class MachCommands(object): - - def __init__(self, context): - self.context = context - - @Command( - 'marionette-test', category='testing', - description='Run a Marionette test (Check UI or the internal JavaScript ' - 'using marionette).', - parser=setup_marionette_argument_parser) - def run_marionette_test(self, **kwargs): - return run_marionette(self.context, **kwargs) diff --git a/testing/marionette/marionette.eslintrc.js b/testing/marionette/marionette.eslintrc.js deleted file mode 100644 index 8e54bd5ff..000000000 --- a/testing/marionette/marionette.eslintrc.js +++ /dev/null @@ -1,8 +0,0 @@ -// Parent config file for all marionette files. -module.exports = { - // All globals made available in the test environment. - "globals": { - "ok": false, - "is": false, - } -}; diff --git a/testing/marionette/message.js b/testing/marionette/message.js deleted file mode 100644 index fd8ac4861..000000000 --- a/testing/marionette/message.js +++ /dev/null @@ -1,285 +0,0 @@ -/* 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/. */ - -"use strict"; - -var {utils: Cu} = Components; - -Cu.import("resource://gre/modules/Log.jsm"); -Cu.import("resource://gre/modules/Task.jsm"); - -Cu.import("chrome://marionette/content/error.js"); - -this.EXPORTED_SYMBOLS = [ - "Command", - "Message", - "MessageOrigin", - "Response", -]; - -const logger = Log.repository.getLogger("Marionette"); - -this.MessageOrigin = { - Client: 0, - Server: 1, -}; - -this.Message = {}; - -/** - * Converts a data packet into a Command or Response type. - * - * @param {Array.<number, number, ?, ?>} data - * A four element array where the elements, in sequence, signifies - * message type, message ID, method name or error, and parameters - * or result. - * - * @return {(Command,Response)} - * Based on the message type, a Command or Response instance. - * - * @throws {TypeError} - * If the message type is not recognised. - */ -Message.fromMsg = function (data) { - switch (data[0]) { - case Command.TYPE: - return Command.fromMsg(data); - - case Response.TYPE: - return Response.fromMsg(data); - - default: - throw new TypeError( - "Unrecognised message type in packet: " + JSON.stringify(data)); - } -}; - -/** - * A command is a request from the client to run a series of remote end - * steps and return a fitting response. - * - * The command can be synthesised from the message passed over the - * Marionette socket using the {@code fromMsg} function. The format of - * a message is: - * - * [type, id, name, params] - * - * where - * - * type: - * Must be zero (integer). Zero means that this message is a command. - * - * id: - * Number used as a sequence number. The server replies with a - * requested id. - * - * name: - * String representing the command name with an associated set of - * remote end steps. - * - * params: - * Object of command function arguments. The keys of this object - * must be strings, but the values can be arbitrary values. - * - * A command has an associated message {@code id} that prevents the - * dispatcher from sending responses in the wrong order. - * - * The command may also have optional error- and result handlers that - * are called when the client returns with a response. These are - * {@code function onerror({Object})}, {@code function onresult({Object})}, - * and {@code function onresult({Response})}. - * - * @param {number} msgId - * Message ID unique identifying this message. - * @param {string} name - * Command name. - * @param {Object<string, ?>} params - * Command parameters. - */ -this.Command = class { - constructor(msgId, name, params={}) { - this.id = msgId; - this.name = name; - this.parameters = params; - - this.onerror = null; - this.onresult = null; - - this.origin = MessageOrigin.Client; - this.sent = false; - } - - /** - * Calls the error- or result handler associated with this command. - * This function can be replaced with a custom response handler. - * - * @param {Response} resp - * The response to pass on to the result or error to the - * {@code onerror} or {@code onresult} handlers to. - */ - onresponse(resp) { - if (resp.error && this.onerror) { - this.onerror(resp.error); - } else if (resp.body && this.onresult) { - this.onresult(resp.body); - } - } - - toMsg() { - return [Command.TYPE, this.id, this.name, this.parameters]; - } - - toString() { - return "Command {id: " + this.id + ", " + - "name: " + JSON.stringify(this.name) + ", " + - "parameters: " + JSON.stringify(this.parameters) + "}" - } - - static fromMsg(msg) { - return new Command(msg[1], msg[2], msg[3]); - } -}; - -Command.TYPE = 0; - - -const validator = { - exclusionary: { - "capabilities": ["error", "value"], - "error": ["value", "sessionId", "capabilities"], - "sessionId": ["error", "value"], - "value": ["error", "sessionId", "capabilities"], - }, - - set: function (obj, prop, val) { - let tests = this.exclusionary[prop]; - if (tests) { - for (let t of tests) { - if (obj.hasOwnProperty(t)) { - throw new TypeError(`${t} set, cannot set ${prop}`); - } - } - } - - obj[prop] = val; - return true; - }, -}; - -/** - * The response body is exposed as an argument to commands. - * Commands can set fields on the body through defining properties. - * - * Setting properties invokes a validator that performs tests for - * mutually exclusionary fields on the input against the existing data - * in the body. - * - * For example setting the {@code error} property on the body when - * {@code value}, {@code sessionId}, or {@code capabilities} have been - * set previously will cause an error. - */ -this.ResponseBody = () => new Proxy({}, validator); - -/** - * Represents the response returned from the remote end after execution - * of its corresponding command. - * - * The response is a mutable object passed to each command for - * modification through the available setters. To send data in a response, - * you modify the body property on the response. The body property can - * also be replaced completely. - * - * The response is sent implicitly by CommandProcessor when a command - * has finished executing, and any modifications made subsequent to that - * will have no effect. - * - * @param {number} msgId - * Message ID tied to the corresponding command request this is a - * response for. - * @param {function(Response|Message)} respHandler - * Function callback called on sending the response. - */ -this.Response = class { - constructor(msgId, respHandler) { - this.id = msgId; - - this.error = null; - this.body = ResponseBody(); - - this.origin = MessageOrigin.Server; - this.sent = false; - - this.respHandler_ = respHandler; - } - - /** - * Sends response conditionally, given a predicate. - * - * @param {function(Response): boolean} predicate - * A predicate taking a Response object and returning a boolean. - */ - sendConditionally(predicate) { - if (predicate(this)) { - this.send(); - } - } - - /** - * Sends response using the response handler provided on construction. - * - * @throws {RangeError} - * If the response has already been sent. - */ - send() { - if (this.sent) { - throw new RangeError("Response has already been sent: " + this); - } - this.respHandler_(this); - this.sent = true; - } - - /** - * Send given Error to client. - * - * Turns the response into an error response, clears any previously - * set body data, and sends it using the response handler provided - * on construction. - * - * @param {Error} err - * The Error instance to send. - * - * @throws {Error} - * If the {@code error} is not a WebDriverError, the error is - * propagated. - */ - sendError(err) { - this.error = error.wrap(err).toJSON(); - this.body = null; - this.send(); - - // propagate errors which are implementation problems - if (!error.isWebDriverError(err)) { - throw err; - } - } - - toMsg() { - return [Response.TYPE, this.id, this.error, this.body]; - } - - toString() { - return "Response {id: " + this.id + ", " + - "error: " + JSON.stringify(this.error) + ", " + - "body: " + JSON.stringify(this.body) + "}"; - } - - static fromMsg(msg) { - let resp = new Response(msg[1], null); - resp.error = msg[2]; - resp.body = msg[3]; - return resp; - } -}; - -Response.TYPE = 1; diff --git a/testing/marionette/modal.js b/testing/marionette/modal.js deleted file mode 100644 index 3c1b46437..000000000 --- a/testing/marionette/modal.js +++ /dev/null @@ -1,113 +0,0 @@ -/* 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/. */ - -"use strict"; - -const {utils: Cu} = Components; - -Cu.import("resource://gre/modules/Services.jsm"); - -this.EXPORTED_SYMBOLS = ["modal"]; - -const isFirefox = () => Services.appinfo.name == "Firefox"; - -this.modal = {}; -modal = { - COMMON_DIALOG_LOADED: "common-dialog-loaded", - TABMODAL_DIALOG_LOADED: "tabmodal-dialog-loaded", - handlers: { - "common-dialog-loaded": new Set(), - "tabmodal-dialog-loaded": new Set() - } -}; - -/** - * Add handler that will be called when a global- or tab modal dialogue - * appears. - * - * This is achieved by installing observers for common- - * and tab modal loaded events. - * - * This function is a no-op if called on any other product than Firefox. - * - * @param {function(Object, string)} handler - * The handler to be called, which is passed the - * subject (e.g. ChromeWindow) and the topic (one of - * {@code modal.COMMON_DIALOG_LOADED} or - * {@code modal.TABMODAL_DIALOG_LOADED}. - */ -modal.addHandler = function (handler) { - if (!isFirefox()) { - return; - } - - Object.keys(this.handlers).map(topic => { - this.handlers[topic].add(handler); - Services.obs.addObserver(handler, topic, false); - }); -}; - -/** - * Remove modal dialogue handler by function reference. - * - * This function is a no-op if called on any other product than Firefox. - * - * @param {function} toRemove - * The handler previously passed to modal.addHandler which will now - * be removed. - */ -modal.removeHandler = function (toRemove) { - if (!isFirefox()) { - return; - } - - for (let topic of Object.keys(this.handlers)) { - let handlers = this.handlers[topic]; - for (let handler of handlers) { - if (handler == toRemove) { - Services.obs.removeObserver(handler, topic); - handlers.delete(handler); - } - } - } -}; - -/** - * Represents the current modal dialogue. - * - * @param {function(): browser.Context} curBrowserFn - * Function that returns the current |browser.Context|. - * @param {nsIWeakReference=} winRef - * A weak reference to the current |ChromeWindow|. - */ -modal.Dialog = class { - constructor(curBrowserFn, winRef = undefined) { - this.curBrowserFn_ = curBrowserFn; - this.win_ = winRef; - } - - get curBrowser_() { return this.curBrowserFn_(); } - - /** - * Returns the ChromeWindow associated with an open dialog window if - * it is currently attached to the DOM. - */ - get window() { - if (this.win_) { - let win = this.win_.get(); - if (win && win.parent) { - return win; - } - } - return null; - } - - get ui() { - let win = this.window; - if (win) { - return win.Dialog.ui; - } - return this.curBrowser_.getTabModalUI(); - } -}; diff --git a/testing/marionette/moz.build b/testing/marionette/moz.build deleted file mode 100644 index 7dbfcca37..000000000 --- a/testing/marionette/moz.build +++ /dev/null @@ -1,13 +0,0 @@ -# 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/. - -DIRS += ["components"] - -JAR_MANIFESTS += ["jar.mn"] -MARIONETTE_UNIT_MANIFESTS += ["harness/marionette_harness/tests/unit/unit-tests.ini"] -MARIONETTE_WEBAPI_MANIFESTS += ["harness/marionette_harness/tests/webapi-tests.ini"] -XPCSHELL_TESTS_MANIFESTS += ["unit.ini"] - -with Files("**"): - BUG_COMPONENT = ("Testing", "Marionette") diff --git a/testing/marionette/navigate.js b/testing/marionette/navigate.js deleted file mode 100644 index edbfa552a..000000000 --- a/testing/marionette/navigate.js +++ /dev/null @@ -1,119 +0,0 @@ -/* 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/. */ - -"use strict"; - -const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; - -Cu.importGlobalProperties(["URL"]); - -this.EXPORTED_SYMBOLS = ["navigate"]; - -this.navigate = {}; - -/** - * Determines if we expect to get a DOM load event (DOMContentLoaded) - * on navigating to the |future| URL. - * - * @param {string} current - * URL the browser is currently visiting. - * @param {string=} future - * Destination URL, if known. - * - * @return {boolean} - * Full page load would be expected if future is followed. - * - * @throws TypeError - * If |current| is not defined, or any of |current| or |future| - * are invalid URLs. - */ -navigate.isLoadEventExpected = function (current, future = undefined) { - if (typeof current == "undefined") { - throw TypeError("Expected at least one URL"); - } - - // assume we will go somewhere exciting - if (typeof future == "undefined") { - return true; - } - - let cur = new navigate.IdempotentURL(current); - let fut = new navigate.IdempotentURL(future); - - // assume javascript:<whatever> will modify current document - // but this is not an entirely safe assumption to make, - // considering it could be used to set window.location - if (fut.protocol == "javascript:") { - return false; - } - - // navigating to same url, but with any hash - if (cur.origin == fut.origin && - cur.pathname == fut.pathname && - fut.hash != "") { - return false; - } - - return true; -}; - -/** - * Sane URL implementation that normalises URL fragments (hashes) and - * path names for "data:" URLs, and makes them idempotent. - * - * At the time of writing this, the web is approximately 10 000 days (or - * ~27.39 years) old. One should think that by this point we would have - * solved URLs. The following code is prudent example that we have not. - * - * When a URL with a fragment identifier but no explicit name for the - * fragment is given, i.e. "#", the {@code hash} property a {@code URL} - * object computes is an empty string. This is incidentally the same as - * the default value of URLs without fragments, causing a lot of confusion. - * - * This means that the URL "http://a/#b" produces a hash of "#b", but that - * "http://a/#" produces "". This implementation rectifies this behaviour - * by returning the actual full fragment, which is "#". - * - * "data:" URLs that contain fragments, which if they have the same origin - * and path name are not meant to cause a page reload on navigation, - * confusingly adds the fragment to the {@code pathname} property. - * This implementation remedies this behaviour by trimming it off. - * - * The practical result of this is that while {@code URL} objects are - * not idempotent, the returned URL elements from this implementation - * guarantees that |url.hash == url.hash|. - * - * @param {string|URL} o - * Object to make an URL of. - * - * @return {navigate.IdempotentURL} - * Considered by some to be a somewhat saner URL. - * - * @throws TypeError - * If |o| is not a valid type or if is a string that cannot be parsed - * as a URL. - */ -navigate.IdempotentURL = function (o) { - let url = new URL(o); - - let hash = url.hash; - if (hash == "" && url.href[url.href.length - 1] == "#") { - hash = "#"; - } - - return { - hash: hash, - host: url.host, - hostname: url.hostname, - href: url.href, - origin: url.origin, - password: url.password, - pathname: url.pathname, - port: url.port, - protocol: url.protocol, - search: url.search, - searchParams: url.searchParams, - username: url.username, - }; -}; diff --git a/testing/marionette/proxy.js b/testing/marionette/proxy.js deleted file mode 100644 index 9d338d44a..000000000 --- a/testing/marionette/proxy.js +++ /dev/null @@ -1,376 +0,0 @@ -/* 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/. */ - -"use strict"; - -const {classes: Cc, interfaces: Ci, utils: Cu} = Components; - -Cu.import("chrome://marionette/content/error.js"); -Cu.import("chrome://marionette/content/modal.js"); - -this.EXPORTED_SYMBOLS = ["proxy"]; - -const uuidgen = Cc["@mozilla.org/uuid-generator;1"] - .getService(Ci.nsIUUIDGenerator); - -// Proxy handler that traps requests to get a property. Will prioritise -// properties that exist on the object's own prototype. -var ownPriorityGetterTrap = { - get: (obj, prop) => { - if (obj.hasOwnProperty(prop)) { - return obj[prop]; - } - return (...args) => obj.send(prop, args); - } -}; - -this.proxy = {}; - -/** - * Creates a transparent interface between the chrome- and content - * contexts. - * - * Calls to this object will be proxied via the message manager to a - * content frame script, and responses are returend as promises. - * - * The argument sequence is serialised and passed as an array, unless it - * consists of a single object type that isn't null, in which case it's - * passed literally. The latter specialisation is temporary to achieve - * backwards compatibility with listener.js. - * - * @param {function(): (nsIMessageSender|nsIMessageBroadcaster)} mmFn - * Closure function returning the current message manager. - * @param {function(string, Object, number)} sendAsyncFn - * Callback for sending async messages. - */ -proxy.toListener = function (mmFn, sendAsyncFn) { - let sender = new proxy.AsyncMessageChannel(mmFn, sendAsyncFn); - return new Proxy(sender, ownPriorityGetterTrap); -}; - -/** - * Provides a transparent interface between chrome- and content space. - * - * The AsyncMessageChannel is an abstraction of the message manager - * IPC architecture allowing calls to be made to any registered message - * listener in Marionette. The {@code #send(...)} method returns a promise - * that gets resolved when the message handler calls {@code .reply(...)}. - */ -proxy.AsyncMessageChannel = class { - constructor(mmFn, sendAsyncFn) { - this.sendAsync = sendAsyncFn; - // TODO(ato): Bug 1242595 - this.activeMessageId = null; - - this.mmFn_ = mmFn; - this.listeners_ = new Map(); - this.dialogueObserver_ = null; - } - - get mm() { - return this.mmFn_(); - } - - /** - * Send a message across the channel. The name of the function to - * call must be registered as a message listener. - * - * Usage: - * - * let channel = new AsyncMessageChannel( - * messageManager, sendAsyncMessage.bind(this)); - * let rv = yield channel.send("remoteFunction", ["argument"]); - * - * @param {string} name - * Function to call in the listener, e.g. for the message listener - * "Marionette:foo8", use "foo". - * @param {Array.<?>=}Â args - * Argument list to pass the function. If args has a single entry - * that is an object, we assume it's an old style dispatch, and - * the object will passed literally. - * - * @return {Promise} - * A promise that resolves to the result of the command. - * @throws {TypeError} - * If an unsupported reply type is received. - * @throws {WebDriverError} - * If an error is returned over the channel. - */ - send(name, args = []) { - let uuid = uuidgen.generateUUID().toString(); - // TODO(ato): Bug 1242595 - this.activeMessageId = uuid; - - return new Promise((resolve, reject) => { - let path = proxy.AsyncMessageChannel.makePath(uuid); - let cb = msg => { - this.activeMessageId = null; - - switch (msg.json.type) { - case proxy.AsyncMessageChannel.ReplyType.Ok: - case proxy.AsyncMessageChannel.ReplyType.Value: - resolve(msg.json.data); - break; - - case proxy.AsyncMessageChannel.ReplyType.Error: - let err = WebDriverError.fromJSON(msg.json.data); - reject(err); - break; - - default: - throw new TypeError( - `Unknown async response type: ${msg.json.type}`); - } - }; - - this.dialogueObserver_ = (subject, topic) => { - this.cancelAll(); - resolve(); - }; - - // start content message listener - // and install observers for global- and tab modal dialogues - this.addListener_(path, cb); - modal.addHandler(this.dialogueObserver_); - - // sendAsync is GeckoDriver#sendAsync - this.sendAsync(name, marshal(args), uuid); - }); - } - - /** - * Reply to an asynchronous request. - * - * Passing an WebDriverError prototype will cause the receiving channel - * to throw this error. - * - * Usage: - * - * let channel = proxy.AsyncMessageChannel( - * messageManager, sendAsyncMessage.bind(this)); - * - * // throws in requester: - * channel.reply(uuid, new WebDriverError()); - * - * // returns with value: - * channel.reply(uuid, "hello world!"); - * - * // returns with undefined: - * channel.reply(uuid); - * - * @param {UUID} uuid - * Unique identifier of the request. - * @param {?=} obj - * Message data to reply with. - */ - reply(uuid, obj = undefined) { - // TODO(ato): Eventually the uuid will be hidden in the dispatcher - // in listener, and passing it explicitly to this function will be - // unnecessary. - if (typeof obj == "undefined") { - this.sendReply_(uuid, proxy.AsyncMessageChannel.ReplyType.Ok); - } else if (error.isError(obj)) { - let err = error.wrap(obj); - this.sendReply_(uuid, proxy.AsyncMessageChannel.ReplyType.Error, err); - } else { - this.sendReply_(uuid, proxy.AsyncMessageChannel.ReplyType.Value, obj); - } - } - - sendReply_(uuid, type, data = undefined) { - const path = proxy.AsyncMessageChannel.makePath(uuid); - - let payload; - if (data && typeof data.toJSON == "function") { - payload = data.toJSON(); - } else { - payload = data; - } - - const msg = {type: type, data: payload}; - - // here sendAsync is actually the content frame's - // sendAsyncMessage(path, message) global - this.sendAsync(path, msg); - } - - /** - * Produces a path, or a name, for the message listener handler that - * listens for a reply. - * - * @param {UUID} uuid - * Unique identifier of the channel request. - * - * @return {string} - * Path to be used for nsIMessageListener.addMessageListener. - */ - static makePath(uuid) { - return "Marionette:asyncReply:" + uuid; - } - - /** - * Abort listening for responses, remove all modal dialogue handlers, - * and cancel any ongoing requests in the listener. - */ - cancelAll() { - this.removeAllListeners_(); - modal.removeHandler(this.dialogueObserver_); - // TODO(ato): It's not ideal to have listener specific behaviour here: - this.sendAsync("cancelRequest"); - } - - addListener_(path, callback) { - let autoRemover = msg => { - this.removeListener_(path); - modal.removeHandler(this.dialogueObserver_); - callback(msg); - }; - - this.mm.addMessageListener(path, autoRemover); - this.listeners_.set(path, autoRemover); - } - - removeListener_(path) { - if (!this.listeners_.has(path)) { - return true; - } - - let l = this.listeners_.get(path); - this.mm.removeMessageListener(path, l[1]); - return this.listeners_.delete(path); - } - - removeAllListeners_() { - let ok = true; - for (let [p, cb] of this.listeners_) { - ok |= this.removeListener_(p); - } - return ok; - } -}; -proxy.AsyncMessageChannel.ReplyType = { - Ok: 0, - Value: 1, - Error: 2, -}; - -/** - * A transparent content-to-chrome RPC interface where responses are - * presented as promises. - * - * @param {nsIFrameMessageManager} frameMessageManager - * The content frame's message manager, which itself is usually an - * implementor of. - */ -proxy.toChromeAsync = function (frameMessageManager) { - let sender = new AsyncChromeSender(frameMessageManager); - return new Proxy(sender, ownPriorityGetterTrap); -}; - -/** - * Sends asynchronous RPC messages to chrome space using a frame's - * sendAsyncMessage (nsIAsyncMessageSender) function. - * - * Example on how to use from a frame content script: - * - * let sender = new AsyncChromeSender(messageManager); - * let promise = sender.send("runEmulatorCmd", "my command"); - * let rv = yield promise; - */ -this.AsyncChromeSender = class { - constructor(frameMessageManager) { - this.mm = frameMessageManager; - } - - /** - * Call registered function in chrome context. - * - * @param {string} name - * Function to call in the chrome, e.g. for "Marionette:foo", use - * "foo". - * @param {?}Â args - * Argument list to pass the function. Must be JSON serialisable. - * - * @return {Promise} - * A promise that resolves to the value sent back. - */ - send(name, args) { - let uuid = uuidgen.generateUUID().toString(); - - let proxy = new Promise((resolve, reject) => { - let responseListener = msg => { - if (msg.json.id != uuid) { - return; - } - - this.mm.removeMessageListener( - "Marionette:listenerResponse", responseListener); - - if ("value" in msg.json) { - resolve(msg.json.value); - } else if ("error" in msg.json) { - reject(msg.json.error); - } else { - throw new TypeError( - `Unexpected response: ${msg.name} ${JSON.stringify(msg.json)}`); - } - }; - - let msg = {arguments: marshal(args), id: uuid}; - this.mm.addMessageListener( - "Marionette:listenerResponse", responseListener); - this.mm.sendAsyncMessage("Marionette:" + name, msg); - }); - - return proxy; - } -}; - -/** - * Creates a transparent interface from the content- to the chrome context. - * - * Calls to this object will be proxied via the frame's sendSyncMessage - * (nsISyncMessageSender) function. Since the message is synchronous, - * the return value is presented as a return value. - * - * Example on how to use from a frame content script: - * - * let chrome = proxy.toChrome(sendSyncMessage.bind(this)); - * let cookie = chrome.getCookie("foo"); - * - * @param {nsISyncMessageSender} sendSyncMessageFn - * The frame message manager's sendSyncMessage function. - */ -proxy.toChrome = function (sendSyncMessageFn) { - let sender = new proxy.SyncChromeSender(sendSyncMessageFn); - return new Proxy(sender, ownPriorityGetterTrap); -}; - -/** - * The SyncChromeSender sends synchronous RPC messages to the chrome - * context, using a frame's sendSyncMessage (nsISyncMessageSender) - * function. - * - * Example on how to use from a frame content script: - * - * let sender = new SyncChromeSender(sendSyncMessage.bind(this)); - * let res = sender.send("addCookie", cookie); - */ -proxy.SyncChromeSender = class { - constructor(sendSyncMessage) { - this.sendSyncMessage_ = sendSyncMessage; - } - - send(func, args) { - let name = "Marionette:" + func.toString(); - return this.sendSyncMessage_(name, marshal(args)); - } -}; - -var marshal = function (args) { - if (args.length == 1 && typeof args[0] == "object") { - return args[0]; - } - return args; -}; diff --git a/testing/marionette/puppeteer/.flake8 b/testing/marionette/puppeteer/.flake8 deleted file mode 100644 index ad0819adf..000000000 --- a/testing/marionette/puppeteer/.flake8 +++ /dev/null @@ -1,3 +0,0 @@ -[flake8] -max-line-length = 99 -exclude = __init__.py, diff --git a/testing/marionette/puppeteer/firefox/MANIFEST.in b/testing/marionette/puppeteer/firefox/MANIFEST.in deleted file mode 100644 index cf628b039..000000000 --- a/testing/marionette/puppeteer/firefox/MANIFEST.in +++ /dev/null @@ -1,2 +0,0 @@ -exclude MANIFEST.in -include requirements.txt diff --git a/testing/marionette/puppeteer/firefox/docs/Makefile b/testing/marionette/puppeteer/firefox/docs/Makefile deleted file mode 100644 index 86ab047db..000000000 --- a/testing/marionette/puppeteer/firefox/docs/Makefile +++ /dev/null @@ -1,177 +0,0 @@ -# Makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -PAPER = -BUILDDIR = _build - -# User-friendly check for sphinx-build -ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) -$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) -endif - -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . -# the i18n builder cannot share the environment and doctrees with the others -I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . - -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext - -help: - @echo "Please use \`make <target>' where <target> is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " singlehtml to make a single large HTML file" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " devhelp to make HTML files and a Devhelp project" - @echo " epub to make an epub" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run them through pdflatex" - @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" - @echo " text to make text files" - @echo " man to make manual pages" - @echo " texinfo to make Texinfo files" - @echo " info to make Texinfo files and run them through makeinfo" - @echo " gettext to make PO message catalogs" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " xml to make Docutils-native XML files" - @echo " pseudoxml to make pseudoxml-XML files for display purposes" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - -clean: - rm -rf $(BUILDDIR)/* - -html: - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -singlehtml: - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml - @echo - @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/FirefoxPuppeteer.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/FirefoxPuppeteer.qhc" - -devhelp: - $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp - @echo - @echo "Build finished." - @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/FirefoxPuppeteer" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/FirefoxPuppeteer" - @echo "# devhelp" - -epub: - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub - @echo - @echo "Build finished. The epub file is in $(BUILDDIR)/epub." - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make' in that directory to run these through (pdf)latex" \ - "(use \`make latexpdf' here to do that automatically)." - -latexpdf: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through pdflatex..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -latexpdfja: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through platex and dvipdfmx..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -text: - $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text - @echo - @echo "Build finished. The text files are in $(BUILDDIR)/text." - -man: - $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man - @echo - @echo "Build finished. The manual pages are in $(BUILDDIR)/man." - -texinfo: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo - @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." - @echo "Run \`make' in that directory to run these through makeinfo" \ - "(use \`make info' here to do that automatically)." - -info: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo "Running Texinfo files through makeinfo..." - make -C $(BUILDDIR)/texinfo info - @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." - -gettext: - $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale - @echo - @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." - -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." - -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." - -xml: - $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml - @echo - @echo "Build finished. The XML files are in $(BUILDDIR)/xml." - -pseudoxml: - $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml - @echo - @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/testing/marionette/puppeteer/firefox/docs/api/appinfo.rst b/testing/marionette/puppeteer/firefox/docs/api/appinfo.rst deleted file mode 100644 index e3e9842bf..000000000 --- a/testing/marionette/puppeteer/firefox/docs/api/appinfo.rst +++ /dev/null @@ -1,15 +0,0 @@ -.. py:currentmodule:: firefox_puppeteer.api.appinfo - -AppInfo -======= - -The appinfo class is a wrapper around the nsIXULAppInfo_ interface in -Firefox and provides access to a subset of its members. - -.. _nsIXULAppInfo: https://developer.mozilla.org/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIXULAppInfo - -AppInfo -------- - -.. autoclass:: AppInfo - :members: diff --git a/testing/marionette/puppeteer/firefox/docs/api/keys.rst b/testing/marionette/puppeteer/firefox/docs/api/keys.rst deleted file mode 100644 index 104b84e06..000000000 --- a/testing/marionette/puppeteer/firefox/docs/api/keys.rst +++ /dev/null @@ -1,12 +0,0 @@ -.. py:currentmodule:: firefox_puppeteer.api.keys - -Keys -==== - -Keys ----- - -.. autoclass:: Keys - :members: - :inherited-members: - :undoc-members: diff --git a/testing/marionette/puppeteer/firefox/docs/api/l10n.rst b/testing/marionette/puppeteer/firefox/docs/api/l10n.rst deleted file mode 100644 index dd332a63c..000000000 --- a/testing/marionette/puppeteer/firefox/docs/api/l10n.rst +++ /dev/null @@ -1,11 +0,0 @@ -.. py:currentmodule:: firefox_puppeteer.api.l10n - -Localization -============ - -Localization ------------- - -.. autoclass:: L10n - :members: - :undoc-members: diff --git a/testing/marionette/puppeteer/firefox/docs/api/places.rst b/testing/marionette/puppeteer/firefox/docs/api/places.rst deleted file mode 100644 index a469c1c6c..000000000 --- a/testing/marionette/puppeteer/firefox/docs/api/places.rst +++ /dev/null @@ -1,13 +0,0 @@ -.. py:currentmodule:: firefox_puppeteer.api.places - -Places -====== - -The Places class provides low-level access for several bookmark and history -related methods. - -Places ------- - -.. autoclass:: Places - :members: diff --git a/testing/marionette/puppeteer/firefox/docs/api/security.rst b/testing/marionette/puppeteer/firefox/docs/api/security.rst deleted file mode 100644 index eb9799fe0..000000000 --- a/testing/marionette/puppeteer/firefox/docs/api/security.rst +++ /dev/null @@ -1,13 +0,0 @@ -.. py:currentmodule:: firefox_puppeteer.api.security - -Security -=========== - -The Security class gives access to various helper methods, which assist in working with -certificates or accessing specific security related information. - -Security --------- - -.. autoclass:: Security - :members: diff --git a/testing/marionette/puppeteer/firefox/docs/api/software_update.rst b/testing/marionette/puppeteer/firefox/docs/api/software_update.rst deleted file mode 100644 index ce9f41016..000000000 --- a/testing/marionette/puppeteer/firefox/docs/api/software_update.rst +++ /dev/null @@ -1,30 +0,0 @@ -.. py:currentmodule:: firefox_puppeteer.api.software_update - -SoftwareUpdate -============== - -The SoftwareUpdate class provides helpers for update tests. - -SoftwareUpdate --------------- - -.. autoclass:: SoftwareUpdate - :members: - -ActiveUpdate ------------- - -.. autoclass:: ActiveUpdate - :members: - -MARChannels ------------ - -.. autoclass:: MARChannels - :members: - -UpdateChannel -------------- - -.. autoclass:: UpdateChannel - :members: diff --git a/testing/marionette/puppeteer/firefox/docs/api/utils.rst b/testing/marionette/puppeteer/firefox/docs/api/utils.rst deleted file mode 100644 index b654d24ec..000000000 --- a/testing/marionette/puppeteer/firefox/docs/api/utils.rst +++ /dev/null @@ -1,12 +0,0 @@ -.. py:currentmodule:: firefox_puppeteer.api.utils - -Utils -=========== - -The Utils class gives access to various helper methods. - -Utils ------ - -.. autoclass:: Utils - :members: diff --git a/testing/marionette/puppeteer/firefox/docs/conf.py b/testing/marionette/puppeteer/firefox/docs/conf.py deleted file mode 100644 index 9af133855..000000000 --- a/testing/marionette/puppeteer/firefox/docs/conf.py +++ /dev/null @@ -1,270 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Firefox Puppeteer documentation build configuration file, created by -# sphinx-quickstart on Thu Nov 20 10:35:33 2014. -# -# This file is execfile()d with the current directory set to its -# containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -import os - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# sys.path.insert(0, os.path.abspath('.')) - -# -- General configuration ------------------------------------------------ - -# If your documentation needs a minimal Sphinx version, state it here. -# needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.todo', -] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The encoding of source files. -# source_encoding = 'utf-8-sig' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'Firefox Puppeteer' -copyright = u'2014-2015, Mozilla' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -version = '0.1' -# The full version, including alpha/beta/rc tags. -release = '0.1' - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -# today = '' -# Else, today_fmt is used as the format for a strftime call. -# today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = ['_build'] - -# The reST default role (used for this markup: `text`) to use for all -# documents. -# default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -# add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -# add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -# show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# A list of ignored prefixes for module index sorting. -# modindex_common_prefix = [] - -# If true, keep warnings as "system message" paragraphs in the built documents. -# keep_warnings = False - - -# -- Options for HTML output ---------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -html_theme = 'default' - -on_rtd = os.environ.get('READTHEDOCS', None) == 'True' - -if not on_rtd: - try: - import sphinx_rtd_theme - html_theme = 'sphinx_rtd_theme' - html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] - except ImportError: - pass - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -# html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. -# html_theme_path = [] - -# The name for this set of Sphinx documents. If None, it defaults to -# "<project> v<release> documentation". -# html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -# html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -# html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -# html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# Add any extra paths that contain custom files (such as robots.txt or -# .htaccess) here, relative to this directory. These files are copied -# directly to the root of the documentation. -# html_extra_path = [] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -# html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -# html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -# html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -# html_additional_pages = {} - -# If false, no module index is generated. -# html_domain_indices = True - -# If false, no index is generated. -# html_use_index = True - -# If true, the index is split into individual pages for each letter. -# html_split_index = False - -# If true, links to the reST sources are added to the pages. -# html_show_sourcelink = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -# html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -# html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a <link> tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -# html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -# html_file_suffix = None - -# Output file base name for HTML help builder. -htmlhelp_basename = 'FirefoxPuppeteerdoc' - - -# -- Options for LaTeX output --------------------------------------------- - -latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - # 'papersize': 'letterpaper', - - # The font size ('10pt', '11pt' or '12pt'). - # 'pointsize': '10pt', - - # Additional stuff for the LaTeX preamble. - # 'preamble': '', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - ('index', 'FirefoxPuppeteer.tex', u'Firefox Puppeteer Documentation', - u'Mozilla', 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -# latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -# latex_use_parts = False - -# If true, show page references after internal links. -# latex_show_pagerefs = False - -# If true, show URL addresses after external links. -# latex_show_urls = False - -# Documents to append as an appendix to all manuals. -# latex_appendices = [] - -# If false, no module index is generated. -# latex_domain_indices = True - - -# -- Options for manual page output --------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - ('index', 'firefoxpuppeteer', u'Firefox Puppeteer Documentation', - [u'Mozilla'], 1) -] - -# If true, show URL addresses after external links. -# man_show_urls = False - - -# -- Options for Texinfo output ------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - ('index', 'FirefoxPuppeteer', u'Firefox Puppeteer Documentation', - u'Mozilla', 'FirefoxPuppeteer', 'One line description of project.', - 'Miscellaneous'), -] - -# Documents to append as an appendix to all manuals. -# texinfo_appendices = [] - -# If false, no module index is generated. -# texinfo_domain_indices = True - -# How to display URL addresses: 'footnote', 'no', or 'inline'. -# texinfo_show_urls = 'footnote' - -# If true, do not generate a @detailmenu in the "Top" node's menu. -# texinfo_no_detailmenu = False diff --git a/testing/marionette/puppeteer/firefox/docs/index.rst b/testing/marionette/puppeteer/firefox/docs/index.rst deleted file mode 100644 index d77ac5968..000000000 --- a/testing/marionette/puppeteer/firefox/docs/index.rst +++ /dev/null @@ -1,85 +0,0 @@ -.. py:currentmodule:: firefox_puppeteer - -Firefox Puppeteer -================= - -`Firefox Puppeteer`_ is a library built on top of the `Marionette Python client`_. -It aims to make automation of Firefox's browser UI simpler. It does **not** -make sense to use Firefox Puppeteer if: - -* You are manipulating something other than Firefox (like Firefox OS) -* You are only manipulating elements in content scope (like a webpage) - -Roughly speaking, Firefox Puppeteer provides a library to manipulate each -visual section of Firefox's browser UI. For example, there are different -libraries for the tab bar, the navigation bar, etc. - -.. _Firefox Puppeteer: http://firefox-puppeteer.readthedocs.io/ -.. _Marionette Python client: http://marionette-client.readthedocs.org/ - -Installation ------------- - -For end-users Firefox Puppeteer can be easily installed as a `Python package`_ -from PyPI. If you want to contribute to the project we propose that you clone -the `mozilla-central`_ repository and run the following commands:: - -$ cd testing/puppeteer/firefox -$ python setup.py develop - -In both cases all necessary files including all dependencies will be installed. - -.. _Python package: https://pypi.python.org/pypi/firefox-puppeteer -.. _mozilla-central: https://hg.mozilla.org/mozilla-central - -Versioning ----------- - -Puppeteer versions as regularly released from the Python source code, will follow -a specific versioning schema. It means the major version number will always -be identical with the supported Firefox version. Minor releases - the second part -of the version number - are done throughout the life-cycle of a Firefox version -when Puppeteer itself needs API changes for back-end and front-end modules. The -last part of the version number is the patch level, and is only used for bugfix -releases without any API changes. - -Examples: - - firefox_puppeteer_45.0.0 - First release for Firefox 45.0 and Firefox 45.xESR - firefox_puppeteer_46.2.0 - Second release for Firefox 46.0 caused by API changes - firefox_puppeteer_47.0.1 - First bugfix release for the new Firefox 47.0 support - - -Libraries ---------- - -The following libraries are currently implemented. More will be added in the -future. Each library is available from an instance of the FirefoxTestCase class. - -.. toctree:: - - ui/about_window/window - ui/deck - ui/menu - ui/pageinfo/window - ui/browser/notifications - ui/browser/tabbar - ui/browser/toolbars - ui/browser/window - ui/update_wizard/dialog - ui/windows - api/appinfo - api/keys - api/l10n - api/places - api/security - api/software_update - api/utils - - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` diff --git a/testing/marionette/puppeteer/firefox/docs/make.bat b/testing/marionette/puppeteer/firefox/docs/make.bat deleted file mode 100644 index 12a739016..000000000 --- a/testing/marionette/puppeteer/firefox/docs/make.bat +++ /dev/null @@ -1,242 +0,0 @@ -@ECHO OFF
-
-REM Command file for Sphinx documentation
-
-if "%SPHINXBUILD%" == "" (
- set SPHINXBUILD=sphinx-build
-)
-set BUILDDIR=_build
-set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
-set I18NSPHINXOPTS=%SPHINXOPTS% .
-if NOT "%PAPER%" == "" (
- set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
- set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
-)
-
-if "%1" == "" goto help
-
-if "%1" == "help" (
- :help
- echo.Please use `make ^<target^>` where ^<target^> is one of
- echo. html to make standalone HTML files
- echo. dirhtml to make HTML files named index.html in directories
- echo. singlehtml to make a single large HTML file
- echo. pickle to make pickle files
- echo. json to make JSON files
- echo. htmlhelp to make HTML files and a HTML help project
- echo. qthelp to make HTML files and a qthelp project
- echo. devhelp to make HTML files and a Devhelp project
- echo. epub to make an epub
- echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
- echo. text to make text files
- echo. man to make manual pages
- echo. texinfo to make Texinfo files
- echo. gettext to make PO message catalogs
- echo. changes to make an overview over all changed/added/deprecated items
- echo. xml to make Docutils-native XML files
- echo. pseudoxml to make pseudoxml-XML files for display purposes
- echo. linkcheck to check all external links for integrity
- echo. doctest to run all doctests embedded in the documentation if enabled
- goto end
-)
-
-if "%1" == "clean" (
- for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
- del /q /s %BUILDDIR%\*
- goto end
-)
-
-
-%SPHINXBUILD% 2> nul
-if errorlevel 9009 (
- echo.
- echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
- echo.installed, then set the SPHINXBUILD environment variable to point
- echo.to the full path of the 'sphinx-build' executable. Alternatively you
- echo.may add the Sphinx directory to PATH.
- echo.
- echo.If you don't have Sphinx installed, grab it from
- echo.http://sphinx-doc.org/
- exit /b 1
-)
-
-if "%1" == "html" (
- %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The HTML pages are in %BUILDDIR%/html.
- goto end
-)
-
-if "%1" == "dirhtml" (
- %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
- goto end
-)
-
-if "%1" == "singlehtml" (
- %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
- goto end
-)
-
-if "%1" == "pickle" (
- %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; now you can process the pickle files.
- goto end
-)
-
-if "%1" == "json" (
- %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; now you can process the JSON files.
- goto end
-)
-
-if "%1" == "htmlhelp" (
- %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; now you can run HTML Help Workshop with the ^
-.hhp project file in %BUILDDIR%/htmlhelp.
- goto end
-)
-
-if "%1" == "qthelp" (
- %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; now you can run "qcollectiongenerator" with the ^
-.qhcp project file in %BUILDDIR%/qthelp, like this:
- echo.^> qcollectiongenerator %BUILDDIR%\qthelp\FirefoxPuppeteer.qhcp
- echo.To view the help file:
- echo.^> assistant -collectionFile %BUILDDIR%\qthelp\FirefoxPuppeteer.ghc
- goto end
-)
-
-if "%1" == "devhelp" (
- %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished.
- goto end
-)
-
-if "%1" == "epub" (
- %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The epub file is in %BUILDDIR%/epub.
- goto end
-)
-
-if "%1" == "latex" (
- %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
- goto end
-)
-
-if "%1" == "latexpdf" (
- %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
- cd %BUILDDIR%/latex
- make all-pdf
- cd %BUILDDIR%/..
- echo.
- echo.Build finished; the PDF files are in %BUILDDIR%/latex.
- goto end
-)
-
-if "%1" == "latexpdfja" (
- %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
- cd %BUILDDIR%/latex
- make all-pdf-ja
- cd %BUILDDIR%/..
- echo.
- echo.Build finished; the PDF files are in %BUILDDIR%/latex.
- goto end
-)
-
-if "%1" == "text" (
- %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The text files are in %BUILDDIR%/text.
- goto end
-)
-
-if "%1" == "man" (
- %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The manual pages are in %BUILDDIR%/man.
- goto end
-)
-
-if "%1" == "texinfo" (
- %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
- goto end
-)
-
-if "%1" == "gettext" (
- %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
- goto end
-)
-
-if "%1" == "changes" (
- %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
- if errorlevel 1 exit /b 1
- echo.
- echo.The overview file is in %BUILDDIR%/changes.
- goto end
-)
-
-if "%1" == "linkcheck" (
- %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
- if errorlevel 1 exit /b 1
- echo.
- echo.Link check complete; look for any errors in the above output ^
-or in %BUILDDIR%/linkcheck/output.txt.
- goto end
-)
-
-if "%1" == "doctest" (
- %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
- if errorlevel 1 exit /b 1
- echo.
- echo.Testing of doctests in the sources finished, look at the ^
-results in %BUILDDIR%/doctest/output.txt.
- goto end
-)
-
-if "%1" == "xml" (
- %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The XML files are in %BUILDDIR%/xml.
- goto end
-)
-
-if "%1" == "pseudoxml" (
- %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
- goto end
-)
-
-:end
diff --git a/testing/marionette/puppeteer/firefox/docs/ui/about_window/window.rst b/testing/marionette/puppeteer/firefox/docs/ui/about_window/window.rst deleted file mode 100644 index 43f9132f1..000000000 --- a/testing/marionette/puppeteer/firefox/docs/ui/about_window/window.rst +++ /dev/null @@ -1,59 +0,0 @@ -About Window -============ - -AboutWindow --------------- - -.. autoclass:: firefox_puppeteer.ui.about_window.window.AboutWindow - :members: - :inherited-members: - -Deck ----- - -.. autoclass:: firefox_puppeteer.ui.about_window.deck.Deck - :members: - :inherited-members: - -ApplyPanel ----------- - -.. autoclass:: firefox_puppeteer.ui.about_window.deck.ApplyPanel - :members: - :inherited-members: - -CheckForUpdatesPanel --------------------- - -.. autoclass:: firefox_puppeteer.ui.about_window.deck.CheckForUpdatesPanel - :members: - :inherited-members: - -CheckingForUpdatesPanel ------------------------ - -.. autoclass:: firefox_puppeteer.ui.about_window.deck.CheckingForUpdatesPanel - :members: - :inherited-members: - -DownloadAndInstallPanel ------------------------ - -.. autoclass:: firefox_puppeteer.ui.about_window.deck.DownloadAndInstallPanel - :members: - :inherited-members: - -DownloadFailedPanel -------------------- - -.. autoclass:: firefox_puppeteer.ui.about_window.deck.DownloadFailedPanel - :members: - :inherited-members: - -DownloadingPanel ----------------- - -.. autoclass:: firefox_puppeteer.ui.about_window.deck.DownloadingPanel - :members: - :inherited-members: - diff --git a/testing/marionette/puppeteer/firefox/docs/ui/browser/notifications.rst b/testing/marionette/puppeteer/firefox/docs/ui/browser/notifications.rst deleted file mode 100644 index 9ef44502c..000000000 --- a/testing/marionette/puppeteer/firefox/docs/ui/browser/notifications.rst +++ /dev/null @@ -1,44 +0,0 @@ -.. py:currentmodule:: firefox_puppeteer.ui.browser.notifications - -Notifications -============= - -AddOnInstallBlockedNotification -------------------------------- - -.. autoclass:: AddOnInstallBlockedNotification - :members: - :inherited-members: - :show-inheritance: - -AddOnInstallConfirmationNotification ------------------------------------- - -.. autoclass:: AddOnInstallConfirmationNotification - :members: - :inherited-members: - :show-inheritance: - -AddOnInstallCompleteNotification --------------------------------- - -.. autoclass:: AddOnInstallCompleteNotification - :members: - :inherited-members: - :show-inheritance: - -AddOnInstallFailedNotification ------------------------------- - -.. autoclass:: AddOnInstallFailedNotification - :members: - :inherited-members: - :show-inheritance: - -AddOnProgressNotification -------------------------- - -.. autoclass:: AddOnProgressNotification - :members: - :inherited-members: - :show-inheritance: diff --git a/testing/marionette/puppeteer/firefox/docs/ui/browser/tabbar.rst b/testing/marionette/puppeteer/firefox/docs/ui/browser/tabbar.rst deleted file mode 100644 index 1ac431a99..000000000 --- a/testing/marionette/puppeteer/firefox/docs/ui/browser/tabbar.rst +++ /dev/null @@ -1,22 +0,0 @@ -.. py:currentmodule:: firefox_puppeteer.ui.browser.tabbar - -Tabbar -====== - -TabBar ------- - -.. autoclass:: TabBar - :members: - -Tab ---- - -.. autoclass:: Tab - :members: - -MenuPanel ----------- - -.. autoclass:: MenuPanel - :members: diff --git a/testing/marionette/puppeteer/firefox/docs/ui/browser/toolbars.rst b/testing/marionette/puppeteer/firefox/docs/ui/browser/toolbars.rst deleted file mode 100644 index ade4c799f..000000000 --- a/testing/marionette/puppeteer/firefox/docs/ui/browser/toolbars.rst +++ /dev/null @@ -1,28 +0,0 @@ -.. py:currentmodule:: firefox_puppeteer.ui.browser.toolbars - -Toolbars -======== - -NavBar ------- - -.. autoclass:: NavBar - :members: - -LocationBar ------------ - -.. autoclass:: LocationBar - :members: - -AutocompleteResults -------------------- - -.. autoclass:: AutocompleteResults - :members: - -IdentityPopup -------------- - -.. autoclass:: IdentityPopup - :members: diff --git a/testing/marionette/puppeteer/firefox/docs/ui/browser/window.rst b/testing/marionette/puppeteer/firefox/docs/ui/browser/window.rst deleted file mode 100644 index c12405f4d..000000000 --- a/testing/marionette/puppeteer/firefox/docs/ui/browser/window.rst +++ /dev/null @@ -1,11 +0,0 @@ -.. py:currentmodule:: firefox_puppeteer.ui.browser.window - -BrowserWindow -============= - -BrowserWindow -------------- - -.. autoclass:: BrowserWindow - :members: - :inherited-members: diff --git a/testing/marionette/puppeteer/firefox/docs/ui/deck.rst b/testing/marionette/puppeteer/firefox/docs/ui/deck.rst deleted file mode 100644 index 7f8bdef61..000000000 --- a/testing/marionette/puppeteer/firefox/docs/ui/deck.rst +++ /dev/null @@ -1,9 +0,0 @@ -Deck -===== - -Panel ------- - -.. autoclass:: firefox_puppeteer.ui.deck.Panel - :members: - :inherited-members: diff --git a/testing/marionette/puppeteer/firefox/docs/ui/menu.rst b/testing/marionette/puppeteer/firefox/docs/ui/menu.rst deleted file mode 100644 index f6ecf0f42..000000000 --- a/testing/marionette/puppeteer/firefox/docs/ui/menu.rst +++ /dev/null @@ -1,10 +0,0 @@ -.. py:currentmodule:: firefox_puppeteer.ui.menu - -Menu -==== - -Menu Bar --------- - -.. autoclass:: MenuBar - :members: diff --git a/testing/marionette/puppeteer/firefox/docs/ui/pageinfo/window.rst b/testing/marionette/puppeteer/firefox/docs/ui/pageinfo/window.rst deleted file mode 100644 index 0725db045..000000000 --- a/testing/marionette/puppeteer/firefox/docs/ui/pageinfo/window.rst +++ /dev/null @@ -1,30 +0,0 @@ -Page Info Window -================ - -PageInfoWindow --------------- - -.. autoclass:: firefox_puppeteer.ui.pageinfo.window.PageInfoWindow - :members: - :inherited-members: - -Deck ----- - -.. autoclass:: firefox_puppeteer.ui.pageinfo.deck.Deck - :members: - :inherited-members: - -PageInfoPanel -------------- - -.. autoclass:: firefox_puppeteer.ui.pageinfo.deck.PageInfoPanel - :members: - :inherited-members: - -SecurityPanel -------------- - -.. autoclass:: firefox_puppeteer.ui.pageinfo.deck.SecurityPanel - :members: - :inherited-members: diff --git a/testing/marionette/puppeteer/firefox/docs/ui/update_wizard/dialog.rst b/testing/marionette/puppeteer/firefox/docs/ui/update_wizard/dialog.rst deleted file mode 100644 index 8b70101e4..000000000 --- a/testing/marionette/puppeteer/firefox/docs/ui/update_wizard/dialog.rst +++ /dev/null @@ -1,128 +0,0 @@ -Update Wizard Dialog -==================== - -UpdateWizardDialog ------------------- - -.. autoclass:: firefox_puppeteer.ui.update_wizard.dialog.UpdateWizardDialog - :members: - :inherited-members: - -Wizard ------- - -.. autoclass:: firefox_puppeteer.ui.update_wizard.wizard.Wizard - :members: - :inherited-members: - -CheckingPanel -------------- - -.. autoclass:: firefox_puppeteer.ui.update_wizard.wizard.CheckingPanel - :members: - :inherited-members: - -DownloadingPanel ----------------- - -.. autoclass:: firefox_puppeteer.ui.update_wizard.wizard.DownloadingPanel - :members: - :inherited-members: - -DummyPanel ----------- - -.. autoclass:: firefox_puppeteer.ui.update_wizard.wizard.DummyPanel - :members: - :inherited-members: - -ErrorPatchingPanel ------------------- - -.. autoclass:: firefox_puppeteer.ui.update_wizard.wizard.ErrorPatchingPanel - :members: - :inherited-members: - -ErrorPanel ----------- - -.. autoclass:: firefox_puppeteer.ui.update_wizard.wizard.ErrorPanel - :members: - :inherited-members: - -ErrorExtraPanel ---------------- - -.. autoclass:: firefox_puppeteer.ui.update_wizard.wizard.ErrorExtraPanel - :members: - :inherited-members: - -FinishedPanel -------------- - -.. autoclass:: firefox_puppeteer.ui.update_wizard.wizard.FinishedPanel - :members: - :inherited-members: - -FinishedBackgroundPanel ------------------------ - -.. autoclass:: firefox_puppeteer.ui.update_wizard.wizard.FinishedBackgroundPanel - :members: - :inherited-members: - -IncompatibleCheckPanel ----------------------- - -.. autoclass:: firefox_puppeteer.ui.update_wizard.wizard.IncompatibleCheckPanel - :members: - :inherited-members: - -IncompatibleListPanel ---------------------- - -.. autoclass:: firefox_puppeteer.ui.update_wizard.wizard.IncompatibleListPanel - :members: - :inherited-members: - -InstalledPanel --------------- - -.. autoclass:: firefox_puppeteer.ui.update_wizard.wizard.InstalledPanel - :members: - :inherited-members: - -LicensePanel ------------- - -.. autoclass:: firefox_puppeteer.ui.update_wizard.wizard.LicensePanel - :members: - :inherited-members: - -ManualUpdatePanel ------------------ - -.. autoclass:: firefox_puppeteer.ui.update_wizard.wizard.ManualUpdatePanel - :members: - :inherited-members: - -NoUpdatesFoundPanel -------------------- - -.. autoclass:: firefox_puppeteer.ui.update_wizard.wizard.NoUpdatesFoundPanel - :members: - :inherited-members: - -PluginUpdatesFoundPanel ------------------------ - -.. autoclass:: firefox_puppeteer.ui.update_wizard.wizard.PluginUpdatesFoundPanel - :members: - :inherited-members: - -UpdatesFoundBasicPanel ----------------------- - -.. autoclass:: firefox_puppeteer.ui.update_wizard.wizard.UpdatesFoundBasicPanel - :members: - :inherited-members: diff --git a/testing/marionette/puppeteer/firefox/docs/ui/windows.rst b/testing/marionette/puppeteer/firefox/docs/ui/windows.rst deleted file mode 100644 index 258a743c9..000000000 --- a/testing/marionette/puppeteer/firefox/docs/ui/windows.rst +++ /dev/null @@ -1,16 +0,0 @@ -.. py:currentmodule:: firefox_puppeteer.ui.windows - -Windows -======= - -Windows -------- - -.. autoclass:: Windows - :members: - -BaseWindow ----------- - -.. autoclass:: BaseWindow - :members: diff --git a/testing/marionette/puppeteer/firefox/firefox_puppeteer/__init__.py b/testing/marionette/puppeteer/firefox/firefox_puppeteer/__init__.py deleted file mode 100644 index bc6605102..000000000 --- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# 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 firefox_puppeteer.mixins import PuppeteerMixin -from firefox_puppeteer.puppeteer import Puppeteer - - -__version__ = '52.1.0' diff --git a/testing/marionette/puppeteer/firefox/firefox_puppeteer/api/__init__.py b/testing/marionette/puppeteer/firefox/firefox_puppeteer/api/__init__.py deleted file mode 100644 index e69de29bb..000000000 --- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/api/__init__.py +++ /dev/null diff --git a/testing/marionette/puppeteer/firefox/firefox_puppeteer/api/appinfo.py b/testing/marionette/puppeteer/firefox/firefox_puppeteer/api/appinfo.py deleted file mode 100644 index 13d32c15b..000000000 --- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/api/appinfo.py +++ /dev/null @@ -1,45 +0,0 @@ -# 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 firefox_puppeteer.base import BaseLib - - -class AppInfo(BaseLib): - """This class provides access to various attributes of AppInfo. - - For more details on AppInfo, visit: - https://developer.mozilla.org/en-US/docs/Mozilla/QA/Mozmill_tests/Shared_Modules/UtilsAPI/appInfo - """ - - def __getattr__(self, attr): - with self.marionette.using_context('chrome'): - value = self.marionette.execute_script(""" - Components.utils.import("resource://gre/modules/Services.jsm"); - - return Services.appinfo[arguments[0]]; - """, script_args=[attr]) - - if value is not None: - return value - else: - raise AttributeError('{} has no attribute {}'.format(self.__class__.__name__, - attr)) - - @property - def locale(self): - with self.marionette.using_context('chrome'): - return self.marionette.execute_script(""" - return Components.classes["@mozilla.org/chrome/chrome-registry;1"] - .getService(Components.interfaces.nsIXULChromeRegistry) - .getSelectedLocale("global"); - """) - - @property - def user_agent(self): - with self.marionette.using_context('chrome'): - return self.marionette.execute_script(""" - return Components.classes["@mozilla.org/network/protocol;1?name=http"] - .getService(Components.interfaces.nsIHttpProtocolHandler) - .userAgent; - """) diff --git a/testing/marionette/puppeteer/firefox/firefox_puppeteer/api/keys.py b/testing/marionette/puppeteer/firefox/firefox_puppeteer/api/keys.py deleted file mode 100644 index 2c5a1e523..000000000 --- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/api/keys.py +++ /dev/null @@ -1,17 +0,0 @@ -# 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/. - -import marionette_driver - - -class Keys(marionette_driver.keys.Keys): - """Proxy to marionette's keys with an "accel" provided for convenience - testing across platforms.""" - - def __init__(self, marionette): - self.isDarwin = marionette.session_capabilities['platformName'] == 'darwin' - - @property - def ACCEL(self): - return self.META if self.isDarwin else self.CONTROL diff --git a/testing/marionette/puppeteer/firefox/firefox_puppeteer/api/l10n.py b/testing/marionette/puppeteer/firefox/firefox_puppeteer/api/l10n.py deleted file mode 100644 index f7f52918c..000000000 --- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/api/l10n.py +++ /dev/null @@ -1,125 +0,0 @@ -# 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/. - -# ----------------- -# DEPRECATED module -# ----------------- -# Replace its use in tests when Firefox 45 ESR support ends with -# marionette_driver.localization.L10n - -import copy - -from marionette_driver.errors import ( - NoSuchElementException, - UnknownCommandException, -) -from marionette_driver.localization import L10n as L10nMarionette - -from firefox_puppeteer.base import BaseLib - - -class L10n(BaseLib): - """An API which allows Marionette to handle localized content. - - .. deprecated:: 52.2.0 - Use the localization module from :py:mod:`marionette_driver` instead. - - The `localization`_ of UI elements in Gecko based applications is done via - entities and properties. For static values entities are used, which are located - in .dtd files. Whereby for dynamically updated content the values come from - .property files. Both types of elements can be identifed via a unique id, - and the translated content retrieved. - - .. _localization: https://mzl.la/2eUMjyF - """ - - def __init__(self, marionette): - super(L10n, self).__init__(marionette) - - self._l10nMarionette = L10nMarionette(self.marionette) - - def localize_entity(self, dtd_urls, entity_id): - """Returns the localized string for the specified DTD entity id. - - To find the entity all given DTD files will be searched for the id. - - :param dtd_urls: A list of dtd files to search. - :param entity_id: The id to retrieve the value from. - - :returns: The localized string for the requested entity. - - :raises NoSuchElementException: When entity id is not found in dtd_urls. - """ - # Add xhtml11.dtd to prevent missing entity errors with XHTML files - try: - return self._l10nMarionette.localize_entity(dtd_urls, entity_id) - except UnknownCommandException: - dtds = copy.copy(dtd_urls) - dtds.append("resource:///res/dtd/xhtml11.dtd") - - dtd_refs = '' - for index, item in enumerate(dtds): - dtd_id = 'dtd_%s' % index - dtd_refs += '<!ENTITY %% %s SYSTEM "%s">%%%s;' % \ - (dtd_id, item, dtd_id) - - contents = """<?xml version="1.0"?> - <!DOCTYPE elem [%s]> - - <elem id="entity">&%s;</elem>""" % (dtd_refs, entity_id) - - with self.marionette.using_context('chrome'): - value = self.marionette.execute_script(""" - var parser = Components.classes["@mozilla.org/xmlextras/domparser;1"] - .createInstance(Components.interfaces.nsIDOMParser); - var doc = parser.parseFromString(arguments[0], "text/xml"); - var node = doc.querySelector("elem[id='entity']"); - - return node ? node.textContent : null; - """, script_args=[contents]) - - if not value: - raise NoSuchElementException('DTD Entity not found: %s' % entity_id) - - return value - - def localize_property(self, property_urls, property_id): - """Returns the localized string for the specified property id. - - To find the property all given property files will be searched for - the id. - - :param property_urls: A list of property files to search. - :param property_id: The id to retrieve the value from. - - :returns: The localized string for the requested entity. - - :raises NoSuchElementException: When property id is not found in - property_urls. - """ - try: - return self._l10nMarionette.localize_property(property_urls, property_id) - except UnknownCommandException: - with self.marionette.using_context('chrome'): - value = self.marionette.execute_script(""" - let property = null; - let property_id = arguments[1]; - - arguments[0].some(aUrl => { - let bundle = Services.strings.createBundle(aUrl); - - try { - property = bundle.GetStringFromName(property_id); - return true; - } - catch (ex) { } - }); - - return property; - """, script_args=[property_urls, property_id]) - - if not value: - raise NoSuchElementException('Property not found: %s' % property_id) - - return value diff --git a/testing/marionette/puppeteer/firefox/firefox_puppeteer/api/places.py b/testing/marionette/puppeteer/firefox/firefox_puppeteer/api/places.py deleted file mode 100644 index fadc2c19b..000000000 --- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/api/places.py +++ /dev/null @@ -1,150 +0,0 @@ -# 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 collections import namedtuple -from time import sleep - -from marionette_driver.errors import MarionetteException - -from firefox_puppeteer.base import BaseLib - - -class Places(BaseLib): - """Low-level access to several bookmark and history related actions.""" - - BookmarkFolders = namedtuple('bookmark_folders', - ['root', 'menu', 'toolbar', 'unfiled']) - bookmark_folders = BookmarkFolders('root________', 'menu________', - 'toolbar_____', 'unfiled_____') - - # Bookmark related helpers # - - def is_bookmarked(self, url): - """Check if the given URL is bookmarked. - - :param url: The URL to Check - - :returns: True, if the URL is a bookmark - """ - return self.marionette.execute_async_script(""" - Components.utils.import("resource://gre/modules/PlacesUtils.jsm"); - - PlacesUtils.bookmarks.fetch({url: arguments[0]}).then(bm => { - marionetteScriptFinished(bm != null); - }); - """, script_args=[url]) - - def get_folder_ids_for_url(self, url): - """Retrieve the folder ids where the given URL has been bookmarked in. - - :param url: URL of the bookmark - - :returns: List of folder ids - """ - return self.marionette.execute_async_script(""" - Components.utils.import("resource://gre/modules/PlacesUtils.jsm"); - - let folderGuids = [] - - function onResult(bm) { - folderGuids.push(bm.parentGuid); - } - - PlacesUtils.bookmarks.fetch({url: arguments[0]}, onResult).then(() => { - marionetteScriptFinished(folderGuids); - }); - """, script_args=[url]) - - def is_bookmark_star_button_ready(self): - """Check if the status of the star-button is not updating. - - :returns: True, if the button is ready - """ - return self.marionette.execute_script(""" - let button = window.BookmarkingUI; - - return button.status !== button.STATUS_UPDATING; - """) - - def restore_default_bookmarks(self): - """Restore the default bookmarks for the current profile.""" - retval = self.marionette.execute_async_script(""" - Components.utils.import("resource://gre/modules/BookmarkHTMLUtils.jsm"); - - // Default bookmarks.html file is stored inside omni.jar, - // so get it via a resource URI - let defaultBookmarks = 'chrome://browser/locale/bookmarks.html'; - - // Trigger the import of the default bookmarks - BookmarkHTMLUtils.importFromURL(defaultBookmarks, true) - .then(() => marionetteScriptFinished(true)) - .catch(() => marionetteScriptFinished(false)); - """, script_timeout=10000) - - if not retval: - raise MarionetteException("Restore Default Bookmarks failed") - - # Browser history related helpers # - - def get_all_urls_in_history(self): - """Retrieve any URLs which have been stored in the history.""" - return self.marionette.execute_script(""" - Components.utils.import("resource://gre/modules/PlacesUtils.jsm"); - - let options = PlacesUtils.history.getNewQueryOptions(); - let root = PlacesUtils.history.executeQuery( - PlacesUtils.history.getNewQuery(), options).root; - let urls = []; - - root.containerOpen = true; - for (let i = 0; i < root.childCount; i++) { - urls.push(root.getChild(i).uri) - } - root.containerOpen = false; - - return urls; - """) - - def remove_all_history(self): - """Remove all history items.""" - retval = self.marionette.execute_async_script(""" - Components.utils.import("resource://gre/modules/PlacesUtils.jsm"); - - PlacesUtils.history.clear() - .then(() => marionetteScriptFinished(true)) - .catch(() => marionetteScriptFinished(false)); - """, script_timeout=10000) - - if not retval: - raise MarionetteException("Removing all history failed") - - def wait_for_visited(self, urls, callback): - """Wait until all passed-in urls have been visited. - - :param urls: List of URLs which need to be visited and indexed - - :param callback: Method to execute which triggers loading of the URLs - """ - # Bug 1121691: Needs observer handling support with callback first - # Until then we have to wait about 4s to ensure the page has been indexed - callback() - sleep(4) - - # Plugin related helpers # - - def clear_plugin_data(self): - """Clear any kind of locally stored data from plugins.""" - self.marionette.execute_script(""" - let host = Components.classes["@mozilla.org/plugin/host;1"] - .getService(Components.interfaces.nsIPluginHost); - let tags = host.getPluginTags(); - - tags.forEach(aTag => { - try { - host.clearSiteData(aTag, null, Components.interfaces.nsIPluginHost - .FLAG_CLEAR_ALL, -1); - } catch (ex) { - } - }); - """) diff --git a/testing/marionette/puppeteer/firefox/firefox_puppeteer/api/security.py b/testing/marionette/puppeteer/firefox/firefox_puppeteer/api/security.py deleted file mode 100644 index 7f6532a08..000000000 --- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/api/security.py +++ /dev/null @@ -1,68 +0,0 @@ -# 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/. - -import re - -from firefox_puppeteer.base import BaseLib -from firefox_puppeteer.errors import NoCertificateError - - -class Security(BaseLib): - """Low-level access to security (SSL, TLS) related information.""" - - # Security related helpers # - - def get_address_from_certificate(self, certificate): - """Retrieves the address of the organization from the certificate information. - - The returned address may be `None` in case of no address found, or a dictionary - with the following entries: `city`, `country`, `postal_code`, `state`, `street`. - - :param certificate: A JSON object representing the certificate, which can usually be - retrieved via the current tab: `self.browser.tabbar.selected_tab.certificate`. - - :returns: Address details as dictionary - """ - regex = re.compile('.*?L=(?P<city>.+?),ST=(?P<state>.+?),C=(?P<country>.+?)' - ',postalCode=(?P<postal_code>.+?),STREET=(?P<street>.+?)' - ',serial') - results = regex.search(certificate['subjectName']) - - return results.groupdict() if results else results - - def get_certificate_for_page(self, tab_element): - """The SSL certificate assiciated with the loaded web page in the given tab. - - :param tab_element: The inner tab DOM element. - - :returns: Certificate details as JSON object. - """ - cert = self.marionette.execute_script(""" - var securityUI = arguments[0].linkedBrowser.securityUI; - var status = securityUI.QueryInterface(Components.interfaces.nsISSLStatusProvider) - .SSLStatus; - - return status ? status.serverCert : null; - """, script_args=[tab_element]) - - uri = self.marionette.execute_script(""" - return arguments[0].linkedBrowser.currentURI.spec; - """, script_args=[tab_element]) - - if cert is None: - raise NoCertificateError('No certificate found for "{}"'.format(uri)) - - return cert - - def get_domain_from_common_name(self, common_name): - """Retrieves the domain associated with a page's security certificate from the common name. - - :param certificate: A string containing the certificate's common name, which can usually - be retrieved like so: `certificate['commonName']`. - - :returns: Domain as string - """ - return self.marionette.execute_script(""" - return Services.eTLD.getBaseDomainFromHost(arguments[0]); - """, script_args=[common_name]) diff --git a/testing/marionette/puppeteer/firefox/firefox_puppeteer/api/software_update.py b/testing/marionette/puppeteer/firefox/firefox_puppeteer/api/software_update.py deleted file mode 100644 index 0b90af0d8..000000000 --- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/api/software_update.py +++ /dev/null @@ -1,411 +0,0 @@ -# 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/. - -import ConfigParser -import os -import re - -import mozinfo - -from firefox_puppeteer.base import BaseLib -from firefox_puppeteer.api.appinfo import AppInfo - - -class ActiveUpdate(BaseLib): - - def __getattr__(self, attr): - value = self.marionette.execute_script(""" - let ums = Components.classes['@mozilla.org/updates/update-manager;1'] - .getService(Components.interfaces.nsIUpdateManager); - return ums.activeUpdate[arguments[0]]; - """, script_args=[attr]) - - if value: - return value - else: - raise AttributeError('{} has no attribute {}'.format(self.__class__.__name__, - attr)) - - @property - def exists(self): - """Checks if there is an active update. - - :returns: True if there is an active update - """ - active_update = self.marionette.execute_script(""" - let ums = Components.classes['@mozilla.org/updates/update-manager;1'] - .getService(Components.interfaces.nsIUpdateManager); - return ums.activeUpdate; - """) - - return bool(active_update) - - def get_patch_at(self, patch_index): - """Use nsIUpdate.getPatchAt to return a patch from an update. - - :returns: JSON data for an nsIUpdatePatch object - """ - return self.marionette.execute_script(""" - let ums = Components.classes['@mozilla.org/updates/update-manager;1'] - .getService(Components.interfaces.nsIUpdateManager); - return ums.activeUpdate.getPatchAt(arguments[0]); - """, script_args=[patch_index]) - - @property - def patch_count(self): - """Get the patchCount from the active update. - - :returns: The patch count - """ - return self.marionette.execute_script(""" - let ums = Components.classes['@mozilla.org/updates/update-manager;1'] - .getService(Components.interfaces.nsIUpdateManager); - return ums.activeUpdate.patchCount; - """) - - @property - def selected_patch(self): - """Get the selected patch for the active update. - - :returns: JSON data for the selected patch - """ - return self.marionette.execute_script(""" - let ums = Components.classes['@mozilla.org/updates/update-manager;1'] - .getService(Components.interfaces.nsIUpdateManager); - return ums.activeUpdate.selectedPatch; - """) - - -class MARChannels(BaseLib): - """Class to handle the allowed MAR channels as listed in update-settings.ini.""" - INI_SECTION = 'Settings' - INI_OPTION = 'ACCEPTED_MAR_CHANNEL_IDS' - - class MARConfigParser(ConfigParser.ConfigParser): - """INI parser which allows to read and write MAR config files. - - Virtually identical to the original method, but delimit keys and values - with '=' instead of ' = ' - """ - - def write(self, fp): - """Write an .ini-format representation of the configuration state.""" - if self._defaults: - fp.write("[%s]\n" % ConfigParser.DEFAULTSECT) - for (key, value) in self._defaults.items(): - fp.write("%s=%s\n" % (key, str(value).replace('\n', '\n\t'))) - fp.write("\n") - for section in self._sections: - fp.write("[%s]\n" % section) - for (key, value) in self._sections[section].items(): - if key == "__name__": - continue - if (value is not None) or (self._optcre == self.OPTCRE): - key = "=".join((key, str(value).replace('\n', '\n\t'))) - fp.write("%s\n" % (key)) - fp.write("\n") - - def __init__(self, marionette): - BaseLib.__init__(self, marionette) - - self.config_file_path = self.marionette.execute_script(""" - Components.utils.import('resource://gre/modules/Services.jsm'); - - let file = Services.dirsvc.get('GreD', Components.interfaces.nsIFile); - file.append('update-settings.ini'); - - return file.path; - """) - - self.config = self.MARConfigParser() - self.config.optionxform = str - - @property - def channels(self): - """The currently accepted MAR channels. - - :returns: A set of channel names - """ - # Make sure to always read the current file contents - self.config.read(self.config_file_path) - - return set(self.config.get(self.INI_SECTION, self.INI_OPTION).split(',')) - - @channels.setter - def channels(self, channels): - """Set the accepted MAR channels. - - :param channels: A set of channel names - """ - self.config.set(self.INI_SECTION, self.INI_OPTION, ','.join(channels)) - with open(self.config_file_path, 'wb') as configfile: - self.config.write(configfile) - - def add_channels(self, channels): - """Add additional MAR channels. - - :param channels: A set of channel names to add - """ - self.channels = self.channels | set(channels) - - def remove_channels(self, channels): - """Remove MAR channels. - - :param channels: A set of channel names to remove - """ - self.channels = self.channels - set(channels) - - -class SoftwareUpdate(BaseLib): - """The SoftwareUpdate API adds support for an easy access to the update process.""" - PREF_APP_DISTRIBUTION = 'distribution.id' - PREF_APP_DISTRIBUTION_VERSION = 'distribution.version' - PREF_APP_UPDATE_CHANNEL = 'app.update.channel' - PREF_APP_UPDATE_URL = 'app.update.url' - PREF_APP_UPDATE_URL_OVERRIDE = 'app.update.url.override' - PREF_DISABLED_ADDONS = 'extensions.disabledAddons' - - def __init__(self, marionette): - BaseLib.__init__(self, marionette) - - self.app_info = AppInfo(marionette) - - self._mar_channels = MARChannels(marionette) - self._active_update = ActiveUpdate(marionette) - - @property - def ABI(self): - """Get the customized ABI for the update service. - - :returns: ABI version - """ - abi = self.app_info.XPCOMABI - if mozinfo.isMac: - abi += self.marionette.execute_script(""" - let macutils = Components.classes['@mozilla.org/xpcom/mac-utils;1'] - .getService(Components.interfaces.nsIMacUtils); - if (macutils.isUniversalBinary) { - return '-u-' + macutils.architecturesInBinary; - } - return ''; - """) - - return abi - - @property - def active_update(self): - """ Holds a reference to an :class:`ActiveUpdate` object.""" - return self._active_update - - @property - def allowed(self): - """Check if the user has permissions to run the software update - - :returns: Status if the user has the permissions - """ - return self.marionette.execute_script(""" - let aus = Components.classes['@mozilla.org/updates/update-service;1'] - .getService(Components.interfaces.nsIApplicationUpdateService); - return aus.canCheckForUpdates && aus.canApplyUpdates; - """) - - @property - def build_info(self): - """Return information of the current build version - - :returns: A dictionary of build information - """ - update_url = self.get_update_url(True) - - return { - 'buildid': self.app_info.appBuildID, - 'channel': self.update_channel, - 'disabled_addons': self.marionette.get_pref(self.PREF_DISABLED_ADDONS), - 'locale': self.app_info.locale, - 'mar_channels': self.mar_channels.channels, - 'update_url': update_url, - 'update_snippet': self.get_update_snippet(update_url), - 'user_agent': self.app_info.user_agent, - 'version': self.app_info.version - } - - @property - def is_complete_update(self): - """Return true if the offered update is a complete update - - :returns: True if the offered update is a complete update - """ - # Throw when isCompleteUpdate is called without an update. This should - # never happen except if the test is incorrectly written. - assert self.active_update.exists, 'An active update has been found' - - patch_count = self.active_update.patch_count - assert patch_count == 1 or patch_count == 2,\ - 'An update must have one or two patches included' - - # Ensure Partial and Complete patches produced have unique urls - if patch_count == 2: - patch0_url = self.active_update.get_patch_at(0)['URL'] - patch1_url = self.active_update.get_patch_at(1)['URL'] - assert patch0_url != patch1_url,\ - 'Partial and Complete download URLs are different' - - return self.active_update.selected_patch['type'] == 'complete' - - @property - def mar_channels(self): - """ Holds a reference to a :class:`MARChannels` object.""" - return self._mar_channels - - @property - def os_version(self): - """Returns information about the OS version - - :returns: The OS version - """ - return self.marionette.execute_script(""" - Components.utils.import("resource://gre/modules/Services.jsm"); - - let osVersion; - try { - osVersion = Services.sysinfo.getProperty("name") + " " + - Services.sysinfo.getProperty("version"); - } - catch (ex) { - } - - if (osVersion) { - try { - osVersion += " (" + Services.sysinfo.getProperty("secondaryLibrary") + ")"; - } - catch (e) { - // Not all platforms have a secondary widget library, - // so an error is nothing to worry about. - } - osVersion = encodeURIComponent(osVersion); - } - return osVersion; - """) - - @property - def patch_info(self): - """ Returns information of the active update in the queue.""" - info = {'channel': self.update_channel} - - if (self.active_update.exists): - info['buildid'] = self.active_update.buildID - info['is_complete'] = self.is_complete_update - info['size'] = self.active_update.selected_patch['size'] - info['type'] = self.update_type - info['url_mirror'] = \ - self.active_update.selected_patch['finalURL'] or 'n/a' - info['version'] = self.active_update.appVersion - - return info - - @property - def staging_directory(self): - """ Returns the path to the updates staging directory.""" - return self.marionette.execute_script(""" - let aus = Components.classes['@mozilla.org/updates/update-service;1'] - .getService(Components.interfaces.nsIApplicationUpdateService); - return aus.getUpdatesDirectory().path; - """) - - @property - def update_channel(self): - """Return the currently used update channel.""" - return self.marionette.get_pref(self.PREF_APP_UPDATE_CHANNEL, - default_branch=True) - - @update_channel.setter - def update_channel(self, channel): - """Set the update channel to be used for update checks. - - :param channel: New update channel to use - - """ - writer = UpdateChannelWriter(self.marionette) - writer.set_channel(channel) - - @property - def update_type(self): - """Returns the type of the active update.""" - return self.active_update.type - - def force_fallback(self): - """Update the update.status file and set the status to 'failed:6'""" - with open(os.path.join(self.staging_directory, 'update.status'), 'w') as f: - f.write('failed: 6\n') - - def get_update_snippet(self, update_url): - """Retrieve contents of the update snippet. - - :param update_url: URL to the update snippet - """ - snippet = None - try: - import urllib2 - response = urllib2.urlopen(update_url) - snippet = response.read() - except Exception: - pass - - return snippet - - def get_update_url(self, force=False): - """Retrieve the AUS update URL the update snippet is retrieved from. - - :param force: Boolean flag to force an update check - - :returns: The URL of the update snippet - """ - url = self.marionette.get_pref(self.PREF_APP_UPDATE_URL_OVERRIDE) - if not url: - url = self.marionette.get_pref(self.PREF_APP_UPDATE_URL) - - # Format the URL by replacing placeholders - url = self.marionette.execute_script(""" - Components.utils.import("resource://gre/modules/UpdateUtils.jsm") - return UpdateUtils.formatUpdateURL(arguments[0]); - """, script_args=[url]) - - if force: - if '?' in url: - url += '&' - else: - url += '?' - url += 'force=1' - - return url - - -class UpdateChannelWriter(BaseLib): - """Class to handle the update channel as listed in channel-prefs.js""" - REGEX_UPDATE_CHANNEL = re.compile(r'("app\.update\.channel", ")([^"].*)(?=")') - - def __init__(self, *args, **kwargs): - BaseLib.__init__(self, *args, **kwargs) - - self.file_path = self.marionette.execute_script(""" - Components.utils.import('resource://gre/modules/Services.jsm'); - - let file = Services.dirsvc.get('PrfDef', Components.interfaces.nsIFile); - file.append('channel-prefs.js'); - - return file.path; - """) - - def set_channel(self, channel): - """Set default update channel. - - :param channel: New default update channel - """ - with open(self.file_path) as f: - file_contents = f.read() - - new_content = re.sub( - self.REGEX_UPDATE_CHANNEL, r'\g<1>' + channel, file_contents) - with open(self.file_path, 'w') as f: - f.write(new_content) diff --git a/testing/marionette/puppeteer/firefox/firefox_puppeteer/api/utils.py b/testing/marionette/puppeteer/firefox/firefox_puppeteer/api/utils.py deleted file mode 100644 index 2b4ef0766..000000000 --- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/api/utils.py +++ /dev/null @@ -1,140 +0,0 @@ -# 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.errors import MarionetteException - -from firefox_puppeteer.base import BaseLib - - -class Utils(BaseLib): - """Low-level access to utility actions.""" - - def __init__(self, *args, **kwargs): - super(Utils, self).__init__(*args, **kwargs) - - self._permissions = Permissions(self.marionette) - - @property - def permissions(self): - """Handling of various permissions for hosts. - - :returns: Instance of the Permissions class. - """ - return self._permissions - - def compare_version(self, a, b): - """Compare two version strings. - - :param a: The first version. - :param b: The second version. - - :returns: -1 if a is smaller than b, 0 if equal, and 1 if larger. - """ - return self.marionette.execute_script(""" - Components.utils.import("resource://gre/modules/Services.jsm"); - return Services.vc.compare(arguments[0], arguments[1]); - """, script_args=[a, b]) - - def sanitize(self, data_type): - """Sanitize user data, including cache, cookies, offlineApps, history, formdata, - downloads, passwords, sessions, siteSettings. - - Usage: - sanitize(): Clears all user data. - sanitize({ "sessions": True }): Clears only session user data. - - more: https://dxr.mozilla.org/mozilla-central/source/browser/base/content/sanitize.js - - :param data_type: optional, Information specifying data to be sanitized - """ - - with self.marionette.using_context('chrome'): - result = self.marionette.execute_async_script(""" - Components.utils.import("resource://gre/modules/Services.jsm"); - - var data_type = arguments[0]; - - var data_type = (typeof data_type === "undefined") ? {} : { - cache: data_type.cache || false, - cookies: data_type.cookies || false, - downloads: data_type.downloads || false, - formdata: data_type.formdata || false, - history: data_type.history || false, - offlineApps: data_type.offlineApps || false, - passwords: data_type.passwords || false, - sessions: data_type.sessions || false, - siteSettings: data_type.siteSettings || false - }; - - // Load the sanitize script - var tempScope = {}; - Components.classes["@mozilla.org/moz/jssubscript-loader;1"] - .getService(Components.interfaces.mozIJSSubScriptLoader) - .loadSubScript("chrome://browser/content/sanitize.js", tempScope); - - // Instantiate the Sanitizer - var s = new tempScope.Sanitizer(); - s.prefDomain = "privacy.cpd."; - var itemPrefs = Services.prefs.getBranch(s.prefDomain); - - // Apply options for what to sanitize - for (var pref in data_type) { - itemPrefs.setBoolPref(pref, data_type[pref]); - }; - - // Sanitize and wait for the promise to resolve - var finished = false; - s.sanitize().then(() => { - for (let pref in data_type) { - itemPrefs.clearUserPref(pref); - }; - marionetteScriptFinished(true); - }, aError => { - for (let pref in data_type) { - itemPrefs.clearUserPref(pref); - }; - marionetteScriptFinished(false); - }); - """, script_args=[data_type]) - - if not result: - raise MarionetteException('Sanitizing of profile data failed.') - - -class Permissions(BaseLib): - - def add(self, host, permission): - """Add a permission for web host. - - Permissions include safe-browsing, install, geolocation, and others described here: - https://dxr.mozilla.org/mozilla-central/source/browser/modules/SitePermissions.jsm#144 - and elsewhere. - - :param host: The web host whose permission will be added. - :param permission: The type of permission to be added. - """ - with self.marionette.using_context('chrome'): - self.marionette.execute_script(""" - Components.utils.import("resource://gre/modules/Services.jsm"); - let uri = Services.io.newURI(arguments[0], null, null); - Services.perms.add(uri, arguments[1], - Components.interfaces.nsIPermissionManager.ALLOW_ACTION); - """, script_args=[host, permission]) - - def remove(self, host, permission): - """Remove a permission for web host. - - Permissions include safe-browsing, install, geolocation, and others described here: - https://dxr.mozilla.org/mozilla-central/source/browser/modules/SitePermissions.jsm#144 - and elsewhere. - - :param host: The web host whose permission will be removed. - :param permission: The type of permission to be removed. - """ - with self.marionette.using_context('chrome'): - self.marionette.execute_script(""" - Components.utils.import("resource://gre/modules/Services.jsm"); - let uri = Services.io.newURI(arguments[0], null, null); - Services.perms.remove(uri, arguments[1]); - """, script_args=[host, permission]) diff --git a/testing/marionette/puppeteer/firefox/firefox_puppeteer/base.py b/testing/marionette/puppeteer/firefox/firefox_puppeteer/base.py deleted file mode 100644 index 8c805e2b5..000000000 --- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/base.py +++ /dev/null @@ -1,14 +0,0 @@ -# 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/. - - -class BaseLib(object): - """A base class that handles lazily setting the "client" class attribute.""" - - def __init__(self, marionette): - self._marionette = marionette - - @property - def marionette(self): - return self._marionette diff --git a/testing/marionette/puppeteer/firefox/firefox_puppeteer/decorators.py b/testing/marionette/puppeteer/firefox/firefox_puppeteer/decorators.py deleted file mode 100644 index 1cdb64b00..000000000 --- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/decorators.py +++ /dev/null @@ -1,35 +0,0 @@ -# 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 functools import wraps -from importlib import import_module - - -class use_class_as_property(object): - """ - This decorator imports a library module and sets an instance - of the associated class as an attribute on the Puppeteer - object and returns it. - - Note: return value of the wrapped function is ignored. - """ - def __init__(self, lib): - self.lib = lib - self.mod_name, self.cls_name = self.lib.rsplit('.', 1) - - def __call__(self, func): - @property - @wraps(func) - def _(cls, *args, **kwargs): - tag = '_{}_{}'.format(self.mod_name, self.cls_name) - prop = getattr(cls, tag, None) - - if not prop: - module = import_module('.{}'.format(self.mod_name), - 'firefox_puppeteer') - prop = getattr(module, self.cls_name)(cls.marionette) - setattr(cls, tag, prop) - func(cls, *args, **kwargs) - return prop - return _ diff --git a/testing/marionette/puppeteer/firefox/firefox_puppeteer/errors.py b/testing/marionette/puppeteer/firefox/firefox_puppeteer/errors.py deleted file mode 100644 index a518422e9..000000000 --- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/errors.py +++ /dev/null @@ -1,21 +0,0 @@ -# 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.errors import MarionetteException - - -class NoCertificateError(MarionetteException): - pass - - -class UnexpectedWindowTypeError(MarionetteException): - pass - - -class UnknownTabError(MarionetteException): - pass - - -class UnknownWindowError(MarionetteException): - pass diff --git a/testing/marionette/puppeteer/firefox/firefox_puppeteer/mixins.py b/testing/marionette/puppeteer/firefox/firefox_puppeteer/mixins.py deleted file mode 100644 index 645aa40a3..000000000 --- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/mixins.py +++ /dev/null @@ -1,101 +0,0 @@ -# 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 firefox_puppeteer.puppeteer import Puppeteer -from firefox_puppeteer.ui.browser.window import BrowserWindow - - -class PuppeteerMixin(object): - """Mix-in class for Firefox specific API modules exposed to test scope. - - It also provides common set-up and tear-down code for Firefox tests. - - Child test case classes are expected to also subclass MarionetteTestCase such - that PuppeteerMixin is followed by MarionetteTestCase. This will insert the - Puppeteer mixin before the MarionetteTestCase into the MRO. - - example: - `class MyTestCase(PuppeteerMixin, MarionetteTestCase)` - - The key role of MarionetteTestCase is to set self.marionette appropriately - in `setUp()`. Any TestCase class that satisfies this requirement is - compatible with this class. - - If you're extending the inheritance tree further to make specialized - TestCases, favour the use of super() as opposed to explicit calls to a - parent class. - - """ - def _check_and_fix_leaked_handles(self): - handle_count = len(self.marionette.window_handles) - url = [] - - try: - # Verify the existence of leaked tabs and print their URLs. - if self._start_handle_count < handle_count: - message = ('A test must not leak window handles. This test started with ' - '%s open top level browsing contexts, but ended with %s.' - ' Remaining Tabs URLs:') % (self._start_handle_count, handle_count) - with self.marionette.using_context('content'): - for tab in self.marionette.window_handles: - if tab not in self._init_tab_handles: - url.append(' %s' % self.marionette.get_url()) - self.assertListEqual(self._init_tab_handles, self.marionette.window_handles, - message + ','.join(url)) - finally: - # For clean-up make sure we work on a proper browser window - if not self.browser or self.browser.closed: - # Find a proper replacement browser window - # TODO: We have to make this less error prone in case no browser is open. - self.browser = self.puppeteer.windows.switch_to( - lambda win: type(win) is BrowserWindow) - - # Ensure to close all the remaining chrome windows to give following - # tests a proper start condition and make them not fail. - self.puppeteer.windows.close_all([self.browser]) - self.browser.focus() - - # Also close all remaining tabs - self.browser.tabbar.close_all_tabs([self.browser.tabbar.tabs[0]]) - self.browser.tabbar.tabs[0].switch_to() - - def restart(self, **kwargs): - """Restart Firefox and re-initialize data. - - :param flags: Specific restart flags for Firefox - """ - if kwargs.get('clean'): - self.marionette.restart(clean=True) - else: - self.marionette.restart(in_app=True) - - # Ensure that we always have a valid browser instance available - self.browser = self.puppeteer.windows.switch_to(lambda win: type(win) is BrowserWindow) - - def setUp(self, *args, **kwargs): - super(PuppeteerMixin, self).setUp(*args, **kwargs) - - self._start_handle_count = len(self.marionette.window_handles) - self._init_tab_handles = self.marionette.window_handles - self.marionette.set_context('chrome') - - self.puppeteer = Puppeteer(self.marionette) - self.browser = self.puppeteer.windows.current - self.browser.focus() - - with self.marionette.using_context(self.marionette.CONTEXT_CONTENT): - # Bug 1312674 - Navigating to about:blank twice can cause a hang in - # Marionette. So try to always have a known default page loaded. - self.marionette.navigate('about:') - - def tearDown(self, *args, **kwargs): - self.marionette.set_context('chrome') - - try: - # This code should be run after all other tearDown code - # so that in case of a failure, further tests will not run - # in a state that is more inconsistent than necessary. - self._check_and_fix_leaked_handles() - finally: - super(PuppeteerMixin, self).tearDown(*args, **kwargs) diff --git a/testing/marionette/puppeteer/firefox/firefox_puppeteer/puppeteer.py b/testing/marionette/puppeteer/firefox/firefox_puppeteer/puppeteer.py deleted file mode 100644 index 1894a414b..000000000 --- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/puppeteer.py +++ /dev/null @@ -1,84 +0,0 @@ -# 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 decorators import use_class_as_property - - -class Puppeteer(object): - """The puppeteer class is used to expose additional API and UI libraries. - - Each library can be referenced by its puppeteer name as a member of a - Puppeteer instance. For example, the `current_window` member of the - `Browser` class can be accessed via `puppeteer.browser.current_window`. - """ - - def __init__(self, marionette): - self._marionette = marionette - - @property - def marionette(self): - return self._marionette - - @use_class_as_property('api.appinfo.AppInfo') - def appinfo(self): - """ - Provides access to members of the appinfo api. - - See the :class:`~api.appinfo.AppInfo` reference. - """ - - @use_class_as_property('api.keys.Keys') - def keys(self): - """ - Provides a definition of control keys to use with keyboard shortcuts. - For example, keys.CONTROL or keys.ALT. - See the :class:`~api.keys.Keys` reference. - """ - - @use_class_as_property('api.places.Places') - def places(self): - """Provides low-level access to several bookmark and history related actions. - - See the :class:`~api.places.Places` reference. - """ - - @use_class_as_property('api.utils.Utils') - def utils(self): - """Provides an api for interacting with utility actions. - - See the :class:`~api.utils.Utils` reference. - """ - - @property - def platform(self): - """Returns the lowercased platform name. - - :returns: Platform name - """ - return self.marionette.session_capabilities['platformName'] - - @use_class_as_property('api.prefs.Preferences') - def prefs(self): - """ - Provides an api for setting and inspecting preferences, as see in - about:config. - - See the :class:`~api.prefs.Preferences` reference. - """ - - @use_class_as_property('api.security.Security') - def security(self): - """ - Provides an api for accessing security related properties and helpers. - - See the :class:`~api.security.Security` reference. - """ - - @use_class_as_property('ui.windows.Windows') - def windows(self): - """ - Provides shortcuts to the top-level windows. - - See the :class:`~ui.window.Windows` reference. - """ diff --git a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/__init__.py b/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/__init__.py deleted file mode 100644 index e69de29bb..000000000 --- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/__init__.py +++ /dev/null diff --git a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/about_window/__init__.py b/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/about_window/__init__.py deleted file mode 100644 index e69de29bb..000000000 --- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/about_window/__init__.py +++ /dev/null diff --git a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/about_window/deck.py b/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/about_window/deck.py deleted file mode 100644 index 9d8d90603..000000000 --- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/about_window/deck.py +++ /dev/null @@ -1,174 +0,0 @@ -# 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 - -from firefox_puppeteer.ui.base import UIBaseLib -from firefox_puppeteer.ui.deck import Panel - - -class Deck(UIBaseLib): - - def _create_panel_for_id(self, panel_id): - """Creates an instance of :class:`Panel` for the specified panel id. - - :param panel_id: The ID of the panel to create an instance of. - - :returns: :class:`Panel` instance - """ - mapping = {'apply': ApplyPanel, - 'checkForUpdates': CheckForUpdatesPanel, - 'checkingForUpdates': CheckingForUpdatesPanel, - 'downloadAndInstall': DownloadAndInstallPanel, - 'downloadFailed': DownloadFailedPanel, - 'downloading': DownloadingPanel, - 'noUpdatesFound': NoUpdatesFoundPanel, - } - - panel = self.element.find_element(By.ID, panel_id) - return mapping.get(panel_id, Panel)(self.marionette, self.window, panel) - - # Properties for visual elements of the deck # - - @property - def apply(self): - """The :class:`ApplyPanel` instance for the apply panel. - - :returns: :class:`ApplyPanel` instance. - """ - return self._create_panel_for_id('apply') - - @property - def check_for_updates(self): - """The :class:`CheckForUpdatesPanel` instance for the check for updates panel. - - :returns: :class:`CheckForUpdatesPanel` instance. - """ - return self._create_panel_for_id('checkForUpdates') - - @property - def checking_for_updates(self): - """The :class:`CheckingForUpdatesPanel` instance for the checking for updates panel. - - :returns: :class:`CheckingForUpdatesPanel` instance. - """ - return self._create_panel_for_id('checkingForUpdates') - - @property - def download_and_install(self): - """The :class:`DownloadAndInstallPanel` instance for the download and install panel. - - :returns: :class:`DownloadAndInstallPanel` instance. - """ - return self._create_panel_for_id('downloadAndInstall') - - @property - def download_failed(self): - """The :class:`DownloadFailedPanel` instance for the download failed panel. - - :returns: :class:`DownloadFailedPanel` instance. - """ - return self._create_panel_for_id('downloadFailed') - - @property - def downloading(self): - """The :class:`DownloadingPanel` instance for the downloading panel. - - :returns: :class:`DownloadingPanel` instance. - """ - return self._create_panel_for_id('downloading') - - @property - def no_updates_found(self): - """The :class:`NoUpdatesFoundPanel` instance for the no updates found panel. - - :returns: :class:`NoUpdatesFoundPanel` instance. - """ - return self._create_panel_for_id('noUpdatesFound') - - @property - def panels(self): - """List of all the :class:`Panel` instances of the current deck. - - :returns: List of :class:`Panel` instances. - """ - panels = self.marionette.execute_script(""" - let deck = arguments[0]; - let panels = []; - - for (let index = 0; index < deck.children.length; index++) { - if (deck.children[index].id) { - panels.push(deck.children[index].id); - } - } - - return panels; - """, script_args=[self.element]) - - return [self._create_panel_for_id(panel) for panel in panels] - - @property - def selected_index(self): - """The index of the currently selected panel. - - :return: Index of the selected panel. - """ - return int(self.element.get_property('selectedIndex')) - - @property - def selected_panel(self): - """A :class:`Panel` instance of the currently selected panel. - - :returns: :class:`Panel` instance. - """ - return self.panels[self.selected_index] - - -class ApplyPanel(Panel): - - @property - def button(self): - """The DOM element which represents the Update button. - - :returns: Reference to the button element. - """ - return self.element.find_element(By.ID, 'updateButton') - - -class CheckForUpdatesPanel(Panel): - - @property - def button(self): - """The DOM element which represents the Check for Updates button. - - :returns: Reference to the button element. - """ - return self.element.find_element(By.ID, 'checkForUpdatesButton') - - -class CheckingForUpdatesPanel(Panel): - pass - - -class DownloadAndInstallPanel(Panel): - - @property - def button(self): - """The DOM element which represents the Download button. - - :returns: Reference to the button element. - """ - return self.element.find_element(By.ID, 'downloadAndInstallButton') - - -class DownloadFailedPanel(Panel): - pass - - -class DownloadingPanel(Panel): - pass - - -class NoUpdatesFoundPanel(Panel): - pass diff --git a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/about_window/window.py b/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/about_window/window.py deleted file mode 100644 index 25037a471..000000000 --- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/about_window/window.py +++ /dev/null @@ -1,32 +0,0 @@ -# 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 - -from firefox_puppeteer.ui.about_window.deck import Deck -from firefox_puppeteer.ui.windows import BaseWindow, Windows - - -class AboutWindow(BaseWindow): - """Representation of the About window.""" - window_type = 'Browser:About' - - dtds = [ - 'chrome://branding/locale/brand.dtd', - 'chrome://browser/locale/aboutDialog.dtd', - ] - - @property - def deck(self): - """The :class:`Deck` instance which represents the deck. - - :returns: Reference to the deck. - """ - self.switch_to() - - deck = self.window_element.find_element(By.ID, 'updateDeck') - return Deck(self.marionette, self, deck) - - -Windows.register_window(AboutWindow.window_type, AboutWindow) diff --git a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/base.py b/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/base.py deleted file mode 100644 index 622568df9..000000000 --- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/base.py +++ /dev/null @@ -1,54 +0,0 @@ -# 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.marionette import HTMLElement - -from firefox_puppeteer.base import BaseLib -from firefox_puppeteer.ui.windows import BaseWindow - - -class UIBaseLib(BaseLib): - """A base class for all UI element wrapper classes inside a chrome window.""" - - def __init__(self, marionette, window, element): - super(UIBaseLib, self).__init__(marionette) - - assert isinstance(window, BaseWindow) - assert isinstance(element, HTMLElement) - - self._window = window - self._element = element - - @property - def element(self): - """Returns the reference to the underlying DOM element. - - :returns: Reference to the DOM element - """ - return self._element - - @property - def window(self): - """Returns the reference to the chrome window. - - :returns: :class:`BaseWindow` instance of the chrome window. - """ - return self._window - - -class DOMElement(HTMLElement): - """ - Class that inherits from HTMLElement and provides a way for subclasses to - expose new api's. - """ - - def __new__(cls, element): - instance = object.__new__(cls) - instance.__dict__ = element.__dict__.copy() - setattr(instance, 'inner', element) - - return instance - - def __init__(self, element): - pass diff --git a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/browser/__init__.py b/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/browser/__init__.py deleted file mode 100644 index c580d191c..000000000 --- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/browser/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# 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/. diff --git a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/browser/notifications.py b/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/browser/notifications.py deleted file mode 100644 index 2cf67ce7f..000000000 --- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/browser/notifications.py +++ /dev/null @@ -1,116 +0,0 @@ -# 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 abc import ABCMeta - -from marionette_driver import By - -from firefox_puppeteer.ui.base import UIBaseLib - - -class BaseNotification(UIBaseLib): - """Abstract base class for any kind of notification.""" - - __metaclass__ = ABCMeta - - @property - def close_button(self): - """Provide access to the close button. - - :returns: The close button. - """ - return self.element.find_element(By.ANON_ATTRIBUTE, - {'anonid': 'closebutton'}) - - @property - def label(self): - """Provide access to the notification label. - - :returns: The notification label. - """ - return self.element.get_attribute('label') - - @property - def origin(self): - """Provide access to the notification origin. - - :returns: The notification origin. - """ - return self.element.get_attribute('origin') - - def close(self, force=False): - """Close the notification. - - :param force: Optional, if True force close the notification. - Defaults to False. - """ - if force: - self.marionette.execute_script('arguments[0].click()', - script_args=[self.close_button]) - else: - self.close_button.click() - - self.window.wait_for_notification(None) - - -class AddOnInstallBlockedNotification(BaseNotification): - """Add-on install blocked notification.""" - - @property - def allow_button(self): - """Provide access to the allow button. - - :returns: The allow button. - """ - return self.element.find_element( - By.ANON_ATTRIBUTE, {'anonid': 'button'}).find_element( - By.ANON_ATTRIBUTE, {'anonid': 'button'}) - - -class AddOnInstallConfirmationNotification(BaseNotification): - """Add-on install confirmation notification.""" - - @property - def addon_name(self): - """Provide access to the add-on name. - - :returns: The add-on name. - """ - label = self.element.find_element( - By.CSS_SELECTOR, '#addon-install-confirmation-content label') - return label.get_attribute('value') - - def cancel_button(self): - """Provide access to the cancel button. - - :returns: The cancel button. - """ - return self.element.find_element( - By.ID, 'addon-install-confirmation-cancel') - - def install_button(self): - """Provide access to the install button. - - :returns: The install button. - """ - return self.element.find_element( - By.ID, 'addon-install-confirmation-accept') - - -class AddOnInstallCompleteNotification(BaseNotification): - """Add-on install complete notification.""" - - pass - - -class AddOnInstallFailedNotification(BaseNotification): - """Add-on install failed notification.""" - - pass - - -class AddOnProgressNotification(BaseNotification): - """Add-on progress notification.""" - - pass diff --git a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/browser/tabbar.py b/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/browser/tabbar.py deleted file mode 100644 index 4fec98d99..000000000 --- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/browser/tabbar.py +++ /dev/null @@ -1,388 +0,0 @@ -# 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)) diff --git a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/browser/toolbars.py b/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/browser/toolbars.py deleted file mode 100644 index d490e488f..000000000 --- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/browser/toolbars.py +++ /dev/null @@ -1,641 +0,0 @@ -# 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, keys, Wait - -from firefox_puppeteer.ui.base import UIBaseLib - - -class NavBar(UIBaseLib): - """Provides access to the DOM elements contained in the - navigation bar as well as the location bar.""" - - def __init__(self, *args, **kwargs): - super(NavBar, self).__init__(*args, **kwargs) - - self._locationbar = None - - @property - def back_button(self): - """Provides access to the DOM element back button in the navbar. - - :returns: Reference to the back button. - """ - return self.marionette.find_element(By.ID, 'back-button') - - @property - def forward_button(self): - """Provides access to the DOM element forward button in the navbar. - - :returns: Reference to the forward button. - """ - return self.marionette.find_element(By.ID, 'forward-button') - - @property - def home_button(self): - """Provides access to the DOM element home button in the navbar. - - :returns: Reference to the home button element - """ - return self.marionette.find_element(By.ID, 'home-button') - - @property - def locationbar(self): - """Provides access to the DOM elements contained in the - locationbar. - - See the :class:`LocationBar` reference. - """ - if not self._locationbar: - urlbar = self.marionette.find_element(By.ID, 'urlbar') - self._locationbar = LocationBar(self.marionette, self.window, urlbar) - - return self._locationbar - - @property - def menu_button(self): - """Provides access to the DOM element menu button in the navbar. - - :returns: Reference to the menu button element. - """ - return self.marionette.find_element(By.ID, 'PanelUI-menu-button') - - @property - def toolbar(self): - """The DOM element which represents the navigation toolbar. - - :returns: Reference to the navigation toolbar. - """ - return self.element - - -class LocationBar(UIBaseLib): - """Provides access to and methods for the DOM elements contained in the - locationbar (the text area of the ui that typically displays the current url).""" - - def __init__(self, *args, **kwargs): - super(LocationBar, self).__init__(*args, **kwargs) - - self._autocomplete_results = None - self._identity_popup = None - - @property - def autocomplete_results(self): - """Provides access to and methods for the location bar - autocomplete results. - - See the :class:`AutocompleteResults` reference.""" - if not self._autocomplete_results: - popup = self.marionette.find_element(By.ID, 'PopupAutoCompleteRichResult') - self._autocomplete_results = AutocompleteResults(self.marionette, - self.window, popup) - - return self._autocomplete_results - - def clear(self): - """Clears the contents of the url bar (via the DELETE shortcut).""" - self.focus('shortcut') - self.urlbar.send_keys(keys.Keys.DELETE) - Wait(self.marionette).until( - lambda _: self.value == '', - message='Contents of location bar could not be cleared.') - - def close_context_menu(self): - """Closes the Location Bar context menu by a key event.""" - # TODO: This method should be implemented via the menu API. - self.contextmenu.send_keys(keys.Keys.ESCAPE) - - @property - def connection_icon(self): - """ Provides access to the urlbar connection icon. - - :returns: Reference to the connection icon element. - """ - return self.marionette.find_element(By.ID, 'connection-icon') - - @property - def contextmenu(self): - """Provides access to the urlbar context menu. - - :returns: Reference to the urlbar context menu. - """ - # TODO: This method should be implemented via the menu API. - parent = self.urlbar.find_element(By.ANON_ATTRIBUTE, {'anonid': 'textbox-input-box'}) - return parent.find_element(By.ANON_ATTRIBUTE, {'anonid': 'input-box-contextmenu'}) - - @property - def focused(self): - """Checks the focus state of the location bar. - - :returns: `True` if focused, otherwise `False` - """ - return self.urlbar.get_attribute('focused') == 'true' - - @property - def identity_icon(self): - """ Provides access to the urlbar identity icon. - - :returns: Reference to the identity icon element. - """ - return self.marionette.find_element(By.ID, 'identity-icon') - - def focus(self, event='click'): - """Focus the location bar according to the provided event. - - :param eventt: The event to synthesize in order to focus the urlbar - (one of `click` or `shortcut`). - """ - if event == 'click': - self.urlbar.click() - elif event == 'shortcut': - cmd_key = self.window.localize_entity('openCmd.commandkey') - self.window.send_shortcut(cmd_key, accel=True) - else: - raise ValueError("An unknown event type was passed: %s" % event) - - Wait(self.marionette).until( - lambda _: self.focused, - message='Location bar has not be focused.') - - def get_contextmenu_entry(self, action): - """Retrieves the urlbar context menu entry corresponding - to the given action. - - :param action: The action corresponding to the retrieved value. - :returns: Reference to the urlbar contextmenu entry. - """ - # TODO: This method should be implemented via the menu API. - entries = self.contextmenu.find_elements(By.CSS_SELECTOR, 'menuitem') - filter_on = 'cmd_%s' % action - found = [e for e in entries if e.get_attribute('cmd') == filter_on] - return found[0] if len(found) else None - - @property - def history_drop_marker(self): - """Provides access to the history drop marker. - - :returns: Reference to the history drop marker. - """ - return self.urlbar.find_element(By.ANON_ATTRIBUTE, {'anonid': 'historydropmarker'}) - - @property - def identity_box(self): - """The DOM element which represents the identity box. - - :returns: Reference to the identity box. - """ - return self.marionette.find_element(By.ID, 'identity-box') - - @property - def identity_country_label(self): - """The DOM element which represents the identity icon country label. - - :returns: Reference to the identity icon country label. - """ - return self.marionette.find_element(By.ID, 'identity-icon-country-label') - - @property - def identity_organization_label(self): - """The DOM element which represents the identity icon label. - - :returns: Reference to the identity icon label. - """ - return self.marionette.find_element(By.ID, 'identity-icon-label') - - @property - def identity_popup(self): - """Provides utility members for accessing and manipulating the - identity popup. - - See the :class:`IdentityPopup` reference. - """ - if not self._identity_popup: - popup = self.marionette.find_element(By.ID, 'identity-popup') - self._identity_popup = IdentityPopup(self.marionette, - self.window, popup) - - return self._identity_popup - - def load_url(self, url): - """Load the specified url in the location bar by synthesized - keystrokes. - - :param url: The url to load. - """ - self.clear() - self.focus('shortcut') - self.urlbar.send_keys(url + keys.Keys.ENTER) - - @property - def notification_popup(self): - """Provides access to the DOM element notification popup. - - :returns: Reference to the notification popup. - """ - return self.marionette.find_element(By.ID, "notification-popup") - - def open_identity_popup(self): - """Open the identity popup.""" - self.identity_box.click() - Wait(self.marionette).until( - lambda _: self.identity_popup.is_open, - message='Identity popup has not been opened.') - - @property - def reload_button(self): - """Provides access to the DOM element reload button. - - :returns: Reference to the reload button. - """ - return self.marionette.find_element(By.ID, 'urlbar-reload-button') - - def reload_url(self, trigger='button', force=False): - """Reload the currently open page. - - :param trigger: The event type to use to cause the reload (one of - `shortcut`, `shortcut2`, or `button`). - :param force: Whether to cause a forced reload. - """ - # TODO: The force parameter is ignored for the moment. Use - # mouse event modifiers or actions when they're ready. - # Bug 1097705 tracks this feature in marionette. - if trigger == 'button': - self.reload_button.click() - elif trigger == 'shortcut': - cmd_key = self.window.localize_entity('reloadCmd.commandkey') - self.window.send_shortcut(cmd_key) - elif trigger == 'shortcut2': - self.window.send_shortcut(keys.Keys.F5) - - @property - def stop_button(self): - """Provides access to the DOM element stop button. - - :returns: Reference to the stop button. - """ - return self.marionette.find_element(By.ID, 'urlbar-stop-button') - - @property - def urlbar(self): - """Provides access to the DOM element urlbar. - - :returns: Reference to the url bar. - """ - return self.marionette.find_element(By.ID, 'urlbar') - - @property - def urlbar_input(self): - """Provides access to the urlbar input element. - - :returns: Reference to the urlbar input. - """ - return self.urlbar.find_element(By.ANON_ATTRIBUTE, {'anonid': 'input'}) - - @property - def value(self): - """Provides access to the currently displayed value of the urlbar. - - :returns: The urlbar value. - """ - return self.urlbar.get_property('value') - - -class AutocompleteResults(UIBaseLib): - """Wraps DOM elements and methods for interacting with autocomplete results.""" - - def close(self, force=False): - """Closes the urlbar autocomplete popup. - - :param force: If true, the popup is closed by its own hide function, - otherwise a key event is sent to close the popup. - """ - if not self.is_open: - return - - if force: - self.marionette.execute_script(""" - arguments[0].hidePopup(); - """, script_args=[self.element]) - else: - self.element.send_keys(keys.Keys.ESCAPE) - - Wait(self.marionette).until( - lambda _: not self.is_open, - message='Autocomplete popup has not been closed.') - - def get_matching_text(self, result, match_type): - """Returns an array of strings of the matching text within an autocomplete - result in the urlbar. - - :param result: The result to inspect for matches. - :param match_type: The type of match to search for (one of `title` or `url`). - """ - - if match_type not in ('title', 'url'): - raise ValueError('match_type provided must be one of' - '"title" or "url", not %s' % match_type) - - # Search for nodes of the given type with emphasized text - emphasized_nodes = result.find_elements( - By.ANON_ATTRIBUTE, - {'class': 'ac-emphasize-text ac-emphasize-text-%s' % match_type} - ) - - return [node.get_property('textContent') for node in emphasized_nodes] - - @property - def visible_results(self): - """Supplies the list of visible autocomplete result nodes. - - :returns: The list of visible results. - """ - match_count = self.element.get_property('_matchCount') - - return self.marionette.execute_script(""" - let rv = []; - let node = arguments[0]; - let count = arguments[1]; - - for (let i = 0; i < count; ++i) { - rv.push(node.getItemAtIndex(i)); - } - - return rv; - """, script_args=[self.results, match_count]) - - @property - def is_open(self): - """Returns whether this popup is currently open. - - :returns: True when the popup is open, otherwise false. - """ - return self.element.get_property('state') == 'open' - - @property - def is_complete(self): - """Returns when this popup is open and autocomplete results are complete. - - :returns: True, when autocomplete results have been populated. - """ - return self.marionette.execute_script(""" - Components.utils.import("resource://gre/modules/Services.jsm"); - - let win = Services.focus.activeWindow; - if (win) { - return win.gURLBar.controller.searchStatus >= - Components.interfaces.nsIAutoCompleteController.STATUS_COMPLETE_NO_MATCH; - } - - return null; - """) - - @property - def results(self): - """ - :returns: The autocomplete result container node. - """ - return self.element.find_element(By.ANON_ATTRIBUTE, - {'anonid': 'richlistbox'}) - - @property - def selected_index(self): - """Provides the index of the selected item in the autocomplete list. - - :returns: The index. - """ - return self.results.get_property('selectedIndex') - - -class IdentityPopup(UIBaseLib): - """Wraps DOM elements and methods for interacting with the identity popup.""" - - def __init__(self, *args, **kwargs): - super(IdentityPopup, self).__init__(*args, **kwargs) - - self._view = None - - @property - def is_open(self): - """Returns whether this popup is currently open. - - :returns: True when the popup is open, otherwise false. - """ - return self.element.get_property('state') == 'open' - - def close(self, force=False): - """Closes the identity popup by hitting the escape key. - - :param force: Optional, If `True` force close the popup. - Defaults to `False` - """ - if not self.is_open: - return - - if force: - self.marionette.execute_script(""" - arguments[0].hidePopup(); - """, script_args=[self.element]) - else: - self.element.send_keys(keys.Keys.ESCAPE) - - Wait(self.marionette).until( - lambda _: not self.is_open, - message='Identity popup has not been closed.') - - @property - def view(self): - """Provides utility members for accessing and manipulating the - identity popup's multi view. - - See the :class:`IdentityPopupMultiView` reference. - """ - if not self._view: - view = self.marionette.find_element(By.ID, 'identity-popup-multiView') - self._view = IdentityPopupMultiView(self.marionette, self.window, view) - - return self._view - - -class IdentityPopupMultiView(UIBaseLib): - - def _create_view_for_id(self, view_id): - """Creates an instance of :class:`IdentityPopupView` for the specified view id. - - :param view_id: The ID of the view to create an instance of. - - :returns: :class:`IdentityPopupView` instance - """ - mapping = {'identity-popup-mainView': IdentityPopupMainView, - 'identity-popup-securityView': IdentityPopupSecurityView, - } - - view = self.marionette.find_element(By.ID, view_id) - return mapping.get(view_id, IdentityPopupView)(self.marionette, self.window, view) - - @property - def main(self): - """The DOM element which represents the main view. - - :returns: Reference to the main view. - """ - return self._create_view_for_id('identity-popup-mainView') - - @property - def security(self): - """The DOM element which represents the security view. - - :returns: Reference to the security view. - """ - return self._create_view_for_id('identity-popup-securityView') - - -class IdentityPopupView(UIBaseLib): - - @property - def selected(self): - """Checks if the view is selected. - - :return: `True` if the view is selected. - """ - return self.element.get_attribute('current') == 'true' - - -class IdentityPopupMainView(IdentityPopupView): - - @property - def selected(self): - """Checks if the view is selected. - - :return: `True` if the view is selected. - """ - return self.marionette.execute_script(""" - return arguments[0].panelMultiView.getAttribute('viewtype') == 'main'; - """, script_args=[self.element]) - - @property - def expander(self): - """The DOM element which represents the expander button for the security content. - - :returns: Reference to the identity popup expander button. - """ - return self.element.find_element(By.CLASS_NAME, 'identity-popup-expander') - - @property - def host(self): - """The DOM element which represents the identity-popup content host. - - :returns: Reference to the identity-popup content host. - """ - return self.element.find_element(By.CLASS_NAME, 'identity-popup-headline host') - - @property - def insecure_connection_label(self): - """The DOM element which represents the identity popup insecure connection label. - - :returns: Reference to the identity-popup insecure connection label. - """ - return self.element.find_element(By.CLASS_NAME, 'identity-popup-connection-not-secure') - - @property - def internal_connection_label(self): - """The DOM element which represents the identity popup internal connection label. - - :returns: Reference to the identity-popup internal connection label. - """ - return self.element.find_element(By.CSS_SELECTOR, 'description[when-connection=chrome]') - - @property - def permissions(self): - """The DOM element which represents the identity-popup permissions content. - - :returns: Reference to the identity-popup permissions. - """ - return self.element.find_element(By.ID, 'identity-popup-permissions-content') - - @property - def secure_connection_label(self): - """The DOM element which represents the identity popup secure connection label. - - :returns: Reference to the identity-popup secure connection label. - """ - return self.element.find_element(By.CLASS_NAME, 'identity-popup-connection-secure') - - -class IdentityPopupSecurityView(IdentityPopupView): - - @property - def disable_mixed_content_blocking_button(self): - """The DOM element which represents the disable mixed content blocking button. - - :returns: Reference to the disable mixed content blocking button. - """ - return self.element.find_element(By.CSS_SELECTOR, - 'button[when-mixedcontent=active-blocked]') - - @property - def enable_mixed_content_blocking_button(self): - """The DOM element which represents the enable mixed content blocking button. - - :returns: Reference to the enable mixed content blocking button. - """ - return self.element.find_element(By.CSS_SELECTOR, - 'button[when-mixedcontent=active-loaded]') - - @property - def host(self): - """The DOM element which represents the identity-popup content host. - - :returns: Reference to the identity-popup content host. - """ - return self.element.find_element(By.CLASS_NAME, 'identity-popup-headline host') - - @property - def insecure_connection_label(self): - """The DOM element which represents the identity popup insecure connection label. - - :returns: Reference to the identity-popup insecure connection label. - """ - return self.element.find_element(By.CLASS_NAME, 'identity-popup-connection-not-secure') - - @property - def more_info_button(self): - """The DOM element which represents the identity-popup more info button. - - :returns: Reference to the identity-popup more info button. - """ - label = self.window.localize_entity('identity.moreInfoLinkText2') - - return self.element.find_element(By.CSS_SELECTOR, u'button[label="{}"]'.format(label)) - - @property - def owner(self): - """The DOM element which represents the identity-popup content owner. - - :returns: Reference to the identity-popup content owner. - """ - return self.element.find_element(By.ID, 'identity-popup-content-owner') - - @property - def owner_location(self): - """The DOM element which represents the identity-popup content supplemental. - - :returns: Reference to the identity-popup content supplemental. - """ - return self.element.find_element(By.ID, 'identity-popup-content-supplemental') - - @property - def secure_connection_label(self): - """The DOM element which represents the identity popup secure connection label. - - :returns: Reference to the identity-popup secure connection label. - """ - return self.element.find_element(By.CLASS_NAME, 'identity-popup-connection-secure') - - @property - def verifier(self): - """The DOM element which represents the identity-popup content verifier. - - :returns: Reference to the identity-popup content verifier. - """ - return self.element.find_element(By.ID, 'identity-popup-content-verifier') diff --git a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/browser/window.py b/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/browser/window.py deleted file mode 100644 index 728a2fd20..000000000 --- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/browser/window.py +++ /dev/null @@ -1,260 +0,0 @@ -# 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 - -from firefox_puppeteer.ui.about_window.window import AboutWindow -from firefox_puppeteer.ui.browser.notifications import ( - AddOnInstallBlockedNotification, - AddOnInstallConfirmationNotification, - AddOnInstallCompleteNotification, - AddOnInstallFailedNotification, - AddOnProgressNotification, - BaseNotification) -from firefox_puppeteer.ui.browser.tabbar import TabBar -from firefox_puppeteer.ui.browser.toolbars import NavBar -from firefox_puppeteer.ui.pageinfo.window import PageInfoWindow -from firefox_puppeteer.ui.windows import BaseWindow, Windows - - -class BrowserWindow(BaseWindow): - """Representation of a browser window.""" - - window_type = 'navigator:browser' - - dtds = [ - 'chrome://branding/locale/brand.dtd', - 'chrome://browser/locale/aboutPrivateBrowsing.dtd', - 'chrome://browser/locale/browser.dtd', - 'chrome://browser/locale/netError.dtd', - ] - - properties = [ - 'chrome://branding/locale/brand.properties', - 'chrome://branding/locale/browserconfig.properties', - 'chrome://browser/locale/browser.properties', - 'chrome://browser/locale/preferences/preferences.properties', - 'chrome://global/locale/browser.properties', - ] - - def __init__(self, *args, **kwargs): - super(BrowserWindow, self).__init__(*args, **kwargs) - - self._navbar = None - self._tabbar = None - - @property - def default_homepage(self): - """The default homepage as used by the current locale. - - :returns: The default homepage for the current locale. - """ - return self.marionette.get_pref('browser.startup.homepage', - value_type='nsIPrefLocalizedString') - - @property - def is_private(self): - """Returns True if this is a Private Browsing window.""" - self.switch_to() - - with self.marionette.using_context('chrome'): - return self.marionette.execute_script(""" - Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm"); - - let chromeWindow = arguments[0].ownerDocument.defaultView; - return PrivateBrowsingUtils.isWindowPrivate(chromeWindow); - """, script_args=[self.window_element]) - - @property - def navbar(self): - """Provides access to the navigation bar. This is the toolbar containing - the back, forward and home buttons. It also contains the location bar. - - See the :class:`~ui.browser.toolbars.NavBar` reference. - """ - self.switch_to() - - if not self._navbar: - navbar = self.window_element.find_element(By.ID, 'nav-bar') - self._navbar = NavBar(self.marionette, self, navbar) - - return self._navbar - - @property - def notification(self): - """Provides access to the currently displayed notification.""" - - notifications_map = { - 'addon-install-blocked-notification': AddOnInstallBlockedNotification, - 'addon-install-confirmation-notification': AddOnInstallConfirmationNotification, - 'addon-install-complete-notification': AddOnInstallCompleteNotification, - 'addon-install-failed-notification': AddOnInstallFailedNotification, - 'addon-progress-notification': AddOnProgressNotification, - } - - try: - notification = self.window_element.find_element( - By.CSS_SELECTOR, '#notification-popup popupnotification') - - notification_id = notification.get_attribute('id') - return notifications_map.get(notification_id, BaseNotification)( - self.marionette, self, notification) - - except NoSuchElementException: - return None # no notification is displayed - - def wait_for_notification(self, notification_class=BaseNotification, - timeout=5): - """Waits for the specified notification to be displayed. - - :param notification_class: Optional, the notification class to wait for. - If `None` is specified it will wait for any notification to be closed. - Defaults to `BaseNotification`. - :param timeout: Optional, how long to wait for the expected notification. - Defaults to 5 seconds. - """ - wait = Wait(self.marionette, timeout=timeout) - - if notification_class: - if notification_class is BaseNotification: - message = 'No notification was shown.' - else: - message = '{0} was not shown.'.format(notification_class.__name__) - wait.until( - lambda _: isinstance(self.notification, notification_class), - message=message) - else: - message = 'Unexpected notification shown.' - wait.until( - lambda _: self.notification is None, - message='Unexpected notification shown.') - - @property - def tabbar(self): - """Provides access to the tab bar. - - See the :class:`~ui.browser.tabbar.TabBar` reference. - """ - self.switch_to() - - if not self._tabbar: - tabbrowser = self.window_element.find_element(By.ID, 'tabbrowser-tabs') - self._tabbar = TabBar(self.marionette, self, tabbrowser) - - return self._tabbar - - def close(self, trigger='menu', force=False): - """Closes the current browser window by using the specified trigger. - - :param trigger: Optional, method to close the current browser window. This can - be a string with one of `menu` or `shortcut`, or a callback which gets triggered - with the current :class:`BrowserWindow` as parameter. Defaults to `menu`. - - :param force: Optional, forces the closing of the window by using the Gecko API. - Defaults to `False`. - """ - def callback(win): - # Prepare action which triggers the opening of the browser window - if callable(trigger): - trigger(win) - elif trigger == 'menu': - self.menubar.select_by_id('file-menu', 'menu_closeWindow') - elif trigger == 'shortcut': - win.send_shortcut(win.localize_entity('closeCmd.key'), - accel=True, shift=True) - else: - raise ValueError('Unknown closing method: "%s"' % trigger) - - BaseWindow.close(self, callback, force) - - def get_final_url(self, url): - """Loads the page at `url` and returns the resulting url. - - This function enables testing redirects. - - :param url: The url to test. - :returns: The resulting loaded url. - """ - with self.marionette.using_context('content'): - self.marionette.navigate(url) - return self.marionette.get_url() - - def open_browser(self, trigger='menu', is_private=False): - """Opens a new browser window by using the specified trigger. - - :param trigger: Optional, method in how to open the new browser window. This can - be a string with one of `menu` or `shortcut`, or a callback which gets triggered - with the current :class:`BrowserWindow` as parameter. Defaults to `menu`. - - :param is_private: Optional, if True the new window will be a private browsing one. - - :returns: :class:`BrowserWindow` instance for the new browser window. - """ - def callback(win): - # Prepare action which triggers the opening of the browser window - if callable(trigger): - trigger(win) - elif trigger == 'menu': - menu_id = 'menu_newPrivateWindow' if is_private else 'menu_newNavigator' - self.menubar.select_by_id('file-menu', menu_id) - elif trigger == 'shortcut': - cmd_key = 'privateBrowsingCmd.commandkey' if is_private else 'newNavigatorCmd.key' - win.send_shortcut(win.localize_entity(cmd_key), - accel=True, shift=is_private) - else: - raise ValueError('Unknown opening method: "%s"' % trigger) - - return BaseWindow.open_window(self, callback, BrowserWindow) - - def open_about_window(self, trigger='menu'): - """Opens the about window by using the specified trigger. - - :param trigger: Optional, method in how to open the new browser window. This can - either the string `menu` or a callback which gets triggered - with the current :class:`BrowserWindow` as parameter. Defaults to `menu`. - - :returns: :class:`AboutWindow` instance of the opened window. - """ - def callback(win): - # Prepare action which triggers the opening of the browser window - if callable(trigger): - trigger(win) - elif trigger == 'menu': - self.menubar.select_by_id('helpMenu', 'aboutName') - else: - raise ValueError('Unknown opening method: "%s"' % trigger) - - return BaseWindow.open_window(self, callback, AboutWindow) - - def open_page_info_window(self, trigger='menu'): - """Opens the page info window by using the specified trigger. - - :param trigger: Optional, method in how to open the new browser window. This can - be a string with one of `menu` or `shortcut`, or a callback which gets triggered - with the current :class:`BrowserWindow` as parameter. Defaults to `menu`. - - :returns: :class:`PageInfoWindow` instance of the opened window. - """ - def callback(win): - # Prepare action which triggers the opening of the browser window - if callable(trigger): - trigger(win) - elif trigger == 'menu': - self.menubar.select_by_id('tools-menu', 'menu_pageInfo') - elif trigger == 'shortcut': - if win.marionette.session_capabilities['platformName'] == 'windows_nt': - raise ValueError('Page info shortcut not available on Windows.') - win.send_shortcut(win.localize_entity('pageInfoCmd.commandkey'), - accel=True) - elif trigger == 'context_menu': - # TODO: Add once we can do right clicks - pass - else: - raise ValueError('Unknown opening method: "%s"' % trigger) - - return BaseWindow.open_window(self, callback, PageInfoWindow) - - -Windows.register_window(BrowserWindow.window_type, BrowserWindow) diff --git a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/deck.py b/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/deck.py deleted file mode 100644 index acc6d2458..000000000 --- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/deck.py +++ /dev/null @@ -1,17 +0,0 @@ -# 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 firefox_puppeteer.ui.base import UIBaseLib - - -class Panel(UIBaseLib): - - def __eq__(self, other): - return self.element.get_attribute('id') == other.element.get_attribute('id') - - def __ne__(self, other): - return self.element.get_attribute('id') != other.element.get_attribute('id') - - def __str__(self): - return self.element.get_attribute('id') diff --git a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/menu.py b/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/menu.py deleted file mode 100644 index 8251daa01..000000000 --- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/menu.py +++ /dev/null @@ -1,110 +0,0 @@ -# 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 -from marionette_driver.errors import NoSuchElementException - -from firefox_puppeteer.base import BaseLib -from firefox_puppeteer.ui.base import DOMElement - - -class MenuBar(BaseLib): - """Wraps the menubar DOM element inside a browser window.""" - - @property - def menus(self): - """A list of :class:`MenuElement` instances corresponding to - the top level menus in the menubar. - - :returns: A list of :class:`MenuElement` instances. - """ - menus = (self.marionette.find_element(By.ID, 'main-menubar') - .find_elements(By.TAG_NAME, 'menu')) - return [self.MenuElement(menu) for menu in menus] - - def get_menu(self, label): - """Get a :class:`MenuElement` instance corresponding to the specified label. - - :param label: The label of the menu, e.g., **File** or **View**. - :returns: A :class:`MenuElement` instance. - """ - menu = [m for m in self.menus if m.get_attribute('label') == label] - - if not menu: - raise NoSuchElementException('Could not find a menu with ' - 'label "{}"'.format(label)) - - return menu[0] - - def get_menu_by_id(self, menu_id): - """Get a :class:`MenuElement` instance corresponding to the specified - ID. - - :param menu_id: The ID of the menu, e.g., **file-menu** or **view-menu**. - :returns: A :class:`MenuElement` instance. - """ - menu = [m for m in self.menus if m.get_attribute('id') == menu_id] - - if not menu: - raise NoSuchElementException('Could not find a menu with ' - 'id "{}"'.format(menu_id)) - - return menu[0] - - def select(self, label, item): - """Select an item in a menu. - - :param label: The label of the menu, e.g., **File** or **View**. - :param item: The label of the item in the menu, e.g., **New Tab**. - """ - return self.get_menu(label).select(item) - - def select_by_id(self, menu_id, item_id): - """Select an item in a menu. - - :param menu_id: The ID of the menu, e.g. **file-menu** or **view-menu**. - :param item_id: The ID of the item in the menu, e.g. **menu_newNavigatorTab**. - """ - return self.get_menu_by_id(menu_id).select_by_id(item_id) - - class MenuElement(DOMElement): - """Wraps a menu element DOM element.""" - - @property - def items(self): - """A list of menuitem DOM elements within this :class:`MenuElement` instance. - - :returns: A list of items in the menu. - """ - return (self.find_element(By.TAG_NAME, 'menupopup') - .find_elements(By.TAG_NAME, 'menuitem')) - - def select(self, label): - """Click on a menu item within this menu. - - :param label: The label of the menu item, e.g., **New Tab**. - """ - item = [l for l in self.items if l.get_attribute('label') == label] - - if not item: - message = ("Item labeled '{}' not found in the '{}' menu" - .format(label, self.get_attribute('label'))) - raise NoSuchElementException(message) - - return item[0].click() - - def select_by_id(self, menu_item_id): - """Click on a menu item within this menu. - - :param menu_item_id: The ID of the menu item, e.g. **menu_newNavigatorTab**. - """ - item = [l for l in self.items if l.get_attribute('id') == - menu_item_id] - - if not item: - message = ("Item with ID '{}' not found in the '{}' menu" - .format(menu_item_id, self.get_attribute('id'))) - raise NoSuchElementException(message) - - return item[0].click() diff --git a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/pageinfo/__init__.py b/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/pageinfo/__init__.py deleted file mode 100644 index e69de29bb..000000000 --- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/pageinfo/__init__.py +++ /dev/null diff --git a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/pageinfo/deck.py b/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/pageinfo/deck.py deleted file mode 100644 index 0f2a2167a..000000000 --- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/pageinfo/deck.py +++ /dev/null @@ -1,204 +0,0 @@ -# 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 firefox_puppeteer.ui.base import UIBaseLib -from firefox_puppeteer.ui.deck import Panel - - -class Deck(UIBaseLib): - - def _create_panel_for_id(self, panel_id): - """Creates an instance of :class:`Panel` for the specified panel id. - - :param panel_id: The ID of the panel to create an instance of. - - :returns: :class:`Panel` instance - """ - mapping = {'feedPanel': FeedPanel, - 'generalPanel': GeneralPanel, - 'mediaPanel': MediaPanel, - 'permPanel': PermissionsPanel, - 'securityPanel': SecurityPanel - } - - panel = self.element.find_element(By.ID, panel_id) - return mapping.get(panel_id, Panel)(self.marionette, self.window, panel) - - # Properties for visual elements of the deck # - - @property - def feed(self): - """The :class:`FeedPanel` instance for the feed panel. - - :returns: :class:`FeedPanel` instance. - """ - return self._create_panel_for_id('feedPanel') - - @property - def general(self): - """The :class:`GeneralPanel` instance for the general panel. - - :returns: :class:`GeneralPanel` instance. - """ - return self._create_panel_for_id('generalPanel') - - @property - def media(self): - """The :class:`MediaPanel` instance for the media panel. - - :returns: :class:`MediaPanel` instance. - """ - return self._create_panel_for_id('mediaPanel') - - @property - def panels(self): - """List of all the :class:`Panel` instances of the current deck. - - :returns: List of :class:`Panel` instances. - """ - panels = self.marionette.execute_script(""" - let deck = arguments[0]; - let panels = []; - - for (let index = 0; index < deck.children.length; index++) { - if (deck.children[index].id) { - panels.push(deck.children[index].id); - } - } - - return panels; - """, script_args=[self.element]) - - return [self._create_panel_for_id(panel) for panel in panels] - - @property - def permissions(self): - """The :class:`PermissionsPanel` instance for the permissions panel. - - :returns: :class:`PermissionsPanel` instance. - """ - return self._create_panel_for_id('permPanel') - - @property - def security(self): - """The :class:`SecurityPanel` instance for the security panel. - - :returns: :class:`SecurityPanel` instance. - """ - return self._create_panel_for_id('securityPanel') - - # Properties for helpers when working with the deck # - - @property - def selected_index(self): - """The index of the currently selected panel. - - :return: Index of the selected panel. - """ - return int(self.element.get_property('selectedIndex')) - - @property - def selected_panel(self): - """A :class:`Panel` instance of the currently selected panel. - - :returns: :class:`Panel` instance. - """ - return self.panels[self.selected_index] - - # Methods for helpers when working with the deck # - - def select(self, panel): - """Selects the specified panel via the tab element. - - :param panel: The panel to select. - - :returns: :class:`Panel` instance of the selected panel. - """ - panel.tab.click() - Wait(self.marionette).until( - lambda _: self.selected_panel == panel, - message='Panel with ID "%s" could not be selected.' % panel) - - return panel - - -class PageInfoPanel(Panel): - - @property - def tab(self): - """The DOM element which represents the corresponding tab element at the top. - - :returns: Reference to the tab element. - """ - name = self.element.get_property('id').split('Panel')[0] - return self.window.window_element.find_element(By.ID, name + 'Tab') - - -class FeedPanel(PageInfoPanel): - pass - - -class GeneralPanel(PageInfoPanel): - pass - - -class MediaPanel(PageInfoPanel): - pass - - -class PermissionsPanel(PageInfoPanel): - pass - - -class SecurityPanel(PageInfoPanel): - - @property - def domain(self): - """The DOM element which represents the domain textbox. - - :returns: Reference to the textbox element. - """ - return self.element.find_element(By.ID, 'security-identity-domain-value') - - @property - def owner(self): - """The DOM element which represents the owner textbox. - - :returns: Reference to the textbox element. - """ - return self.element.find_element(By.ID, 'security-identity-owner-value') - - @property - def verifier(self): - """The DOM element which represents the verifier textbox. - - :returns: Reference to the textbox element. - """ - return self.element.find_element(By.ID, 'security-identity-verifier-value') - - @property - def view_certificate(self): - """The DOM element which represents the view certificate button. - - :returns: Reference to the button element. - """ - return self.element.find_element(By.ID, 'security-view-cert') - - @property - def view_cookies(self): - """The DOM element which represents the view cookies button. - - :returns: Reference to the button element. - """ - return self.element.find_element(By.ID, 'security-view-cookies') - - @property - def view_passwords(self): - """The DOM element which represents the view passwords button. - - :returns: Reference to the button element. - """ - return self.element.find_element(By.ID, 'security-view-password') diff --git a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/pageinfo/window.py b/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/pageinfo/window.py deleted file mode 100644 index 070f39f79..000000000 --- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/pageinfo/window.py +++ /dev/null @@ -1,61 +0,0 @@ -# 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 - -from firefox_puppeteer.ui.pageinfo.deck import Deck -from firefox_puppeteer.ui.windows import BaseWindow, Windows - - -class PageInfoWindow(BaseWindow): - """Representation of a page info window.""" - - window_type = 'Browser:page-info' - - dtds = [ - 'chrome://browser/locale/pageInfo.dtd', - ] - - properties = [ - 'chrome://browser/locale/browser.properties', - 'chrome://browser/locale/pageInfo.properties', - 'chrome://pippki/locale/pippki.properties', - ] - - @property - def deck(self): - """The :class:`Deck` instance which represents the deck. - - :returns: Reference to the deck. - """ - deck = self.window_element.find_element(By.ID, 'mainDeck') - return Deck(self.marionette, self, deck) - - def close(self, trigger='shortcut', force=False): - """Closes the current page info window by using the specified trigger. - - :param trigger: Optional, method to close the current window. This can - be a string with one of `menu` (OS X only) or `shortcut`, or a callback - which gets triggered with the current :class:`PageInfoWindow` as parameter. - Defaults to `shortcut`. - - :param force: Optional, forces the closing of the window by using the Gecko API. - Defaults to `False`. - """ - def callback(win): - # Prepare action which triggers the opening of the browser window - if callable(trigger): - trigger(win) - elif trigger == 'menu': - self.menubar.select_by_id('file-menu', 'menu_close') - elif trigger == 'shortcut': - win.send_shortcut(win.localize_entity('closeWindow.key'), - accel=True) - else: - raise ValueError('Unknown closing method: "%s"' % trigger) - - super(PageInfoWindow, self).close(callback, force) - - -Windows.register_window(PageInfoWindow.window_type, PageInfoWindow) diff --git a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/update_wizard/__init__.py b/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/update_wizard/__init__.py deleted file mode 100644 index 020996694..000000000 --- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/update_wizard/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# 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 dialog import UpdateWizardDialog diff --git a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/update_wizard/dialog.py b/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/update_wizard/dialog.py deleted file mode 100644 index 19435b211..000000000 --- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/update_wizard/dialog.py +++ /dev/null @@ -1,46 +0,0 @@ -# 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 firefox_puppeteer.ui.update_wizard.wizard import Wizard -from firefox_puppeteer.ui.windows import BaseWindow, Windows - - -# Bug 1143020 - Subclass from BaseDialog ui class with possible wizard mixin -class UpdateWizardDialog(BaseWindow): - """Representation of the old Software Update Wizard Dialog.""" - window_type = 'Update:Wizard' - - dtds = [ - 'chrome://branding/locale/brand.dtd', - 'chrome://mozapps/locale/update/updates.dtd', - ] - - properties = [ - 'chrome://branding/locale/brand.properties', - 'chrome://mozapps/locale/update/updates.properties', - ] - - @property - def wizard(self): - """The :class:`Wizard` instance which represents the wizard. - - :returns: Reference to the wizard. - """ - # The deck is also the root element - wizard = self.marionette.find_element(By.ID, 'updates') - return Wizard(self.marionette, self, wizard) - - def select_next_page(self): - """Clicks on "Next" button, and waits for the next page to show up.""" - current_panel = self.wizard.selected_panel - - self.wizard.next_button.click() - Wait(self.marionette).until( - lambda _: self.wizard.selected_panel != current_panel, - message='Next panel has not been selected.') - - -Windows.register_window(UpdateWizardDialog.window_type, UpdateWizardDialog) diff --git a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/update_wizard/wizard.py b/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/update_wizard/wizard.py deleted file mode 100644 index 9687ac917..000000000 --- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/update_wizard/wizard.py +++ /dev/null @@ -1,291 +0,0 @@ -# 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 firefox_puppeteer.ui.base import UIBaseLib -from firefox_puppeteer.ui.deck import Panel - - -class Wizard(UIBaseLib): - - def __init__(self, *args, **kwargs): - super(Wizard, self).__init__(*args, **kwargs) - - Wait(self.marionette).until( - lambda _: self.selected_panel, - message='No panel has been selected by default.') - - def _create_panel_for_id(self, panel_id): - """Creates an instance of :class:`Panel` for the specified panel id. - - :param panel_id: The ID of the panel to create an instance of. - - :returns: :class:`Panel` instance - """ - mapping = {'checking': CheckingPanel, - 'downloading': DownloadingPanel, - 'dummy': DummyPanel, - 'errorpatching': ErrorPatchingPanel, - 'errors': ErrorPanel, - 'errorextra': ErrorExtraPanel, - 'finished': FinishedPanel, - 'finishedBackground': FinishedBackgroundPanel, - 'manualUpdate': ManualUpdatePanel, - 'noupdatesfound': NoUpdatesFoundPanel, - 'updatesfoundbasic': UpdatesFoundBasicPanel, - - # TODO: Remove once we no longer support version Firefox 45.0ESR - 'incompatibleCheck': IncompatibleCheckPanel, - 'incompatibleList': IncompatibleListPanel, - } - - panel = self.element.find_element(By.ID, panel_id) - return mapping.get(panel_id, Panel)(self.marionette, self.window, panel) - - # Properties for visual buttons of the wizard # - - @property - def _buttons(self): - return self.element.find_element(By.ANON_ATTRIBUTE, {'anonid': 'Buttons'}) - - @property - def cancel_button(self): - return self._buttons.find_element(By.ANON_ATTRIBUTE, {'dlgtype': 'cancel'}) - - @property - def extra1_button(self): - return self._buttons.find_element(By.ANON_ATTRIBUTE, {'dlgtype': 'extra1'}) - - @property - def extra2_button(self): - return self._buttons.find_element(By.ANON_ATTRIBUTE, {'dlgtype': 'extra2'}) - - @property - def previous_button(self): - return self._buttons.find_element(By.ANON_ATTRIBUTE, {'dlgtype': 'back'}) - - @property - def finish_button(self): - return self._buttons.find_element(By.ANON_ATTRIBUTE, {'dlgtype': 'finish'}) - - @property - def next_button(self): - return self._buttons.find_element(By.ANON_ATTRIBUTE, {'dlgtype': 'next'}) - - # Properties for visual panels of the wizard # - - @property - def checking(self): - """The checking for updates panel. - - :returns: :class:`CheckingPanel` instance. - """ - return self._create_panel_for_id('checking') - - @property - def downloading(self): - """The downloading panel. - - :returns: :class:`DownloadingPanel` instance. - """ - return self._create_panel_for_id('downloading') - - @property - def dummy(self): - """The dummy panel. - - :returns: :class:`DummyPanel` instance. - """ - return self._create_panel_for_id('dummy') - - @property - def error_patching(self): - """The error patching panel. - - :returns: :class:`ErrorPatchingPanel` instance. - """ - return self._create_panel_for_id('errorpatching') - - @property - def error(self): - """The errors panel. - - :returns: :class:`ErrorPanel` instance. - """ - return self._create_panel_for_id('errors') - - @property - def error_extra(self): - """The error extra panel. - - :returns: :class:`ErrorExtraPanel` instance. - """ - return self._create_panel_for_id('errorextra') - - @property - def finished(self): - """The finished panel. - - :returns: :class:`FinishedPanel` instance. - """ - return self._create_panel_for_id('finished') - - @property - def finished_background(self): - """The finished background panel. - - :returns: :class:`FinishedBackgroundPanel` instance. - """ - return self._create_panel_for_id('finishedBackground') - - @property - def incompatible_check(self): - """The incompatible check panel. - - :returns: :class:`IncompatibleCheckPanel` instance. - """ - return self._create_panel_for_id('incompatibleCheck') - - @property - def incompatible_list(self): - """The incompatible list panel. - - :returns: :class:`IncompatibleListPanel` instance. - """ - return self._create_panel_for_id('incompatibleList') - - @property - def manual_update(self): - """The manual update panel. - - :returns: :class:`ManualUpdatePanel` instance. - """ - return self._create_panel_for_id('manualUpdate') - - @property - def no_updates_found(self): - """The no updates found panel. - - :returns: :class:`NoUpdatesFoundPanel` instance. - """ - return self._create_panel_for_id('noupdatesfound') - - @property - def updates_found_basic(self): - """The updates found panel. - - :returns: :class:`UpdatesFoundPanel` instance. - """ - return self._create_panel_for_id('updatesfoundbasic') - - @property - def panels(self): - """List of all the available :class:`Panel` instances. - - :returns: List of :class:`Panel` instances. - """ - panels = self.marionette.execute_script(""" - let wizard = arguments[0]; - let panels = []; - - for (let index = 0; index < wizard.children.length; index++) { - if (wizard.children[index].id) { - panels.push(wizard.children[index].id); - } - } - - return panels; - """, script_args=[self.element]) - - return [self._create_panel_for_id(panel) for panel in panels] - - @property - def selected_index(self): - """The index of the currently selected panel. - - :return: Index of the selected panel. - """ - return int(self.element.get_property('pageIndex')) - - @property - def selected_panel(self): - """A :class:`Panel` instance of the currently selected panel. - - :returns: :class:`Panel` instance. - """ - return self._create_panel_for_id(self.element.get_attribute('currentpageid')) - - -class CheckingPanel(Panel): - - @property - def progress(self): - """The DOM element which represents the progress meter. - - :returns: Reference to the progress element. - """ - return self.element.find_element(By.ID, 'checkingProgress') - - -class DownloadingPanel(Panel): - - @property - def progress(self): - """The DOM element which represents the progress meter. - - :returns: Reference to the progress element. - """ - return self.element.find_element(By.ID, 'downloadProgress') - - -class DummyPanel(Panel): - pass - - -class ErrorPatchingPanel(Panel): - pass - - -class ErrorPanel(Panel): - pass - - -class ErrorExtraPanel(Panel): - pass - - -class FinishedPanel(Panel): - pass - - -class FinishedBackgroundPanel(Panel): - pass - - -class IncompatibleCheckPanel(Panel): - - @property - def progress(self): - """The DOM element which represents the progress meter. - - :returns: Reference to the progress element. - """ - return self.element.find_element(By.ID, 'incompatibleCheckProgress') - - -class IncompatibleListPanel(Panel): - pass - - -class ManualUpdatePanel(Panel): - pass - - -class NoUpdatesFoundPanel(Panel): - pass - - -class UpdatesFoundBasicPanel(Panel): - pass diff --git a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/windows.py b/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/windows.py deleted file mode 100644 index 9248a3935..000000000 --- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/windows.py +++ /dev/null @@ -1,435 +0,0 @@ -# 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 NoSuchWindowException -from marionette_driver.keys import Keys - -import firefox_puppeteer.errors as errors - -from firefox_puppeteer.api.l10n import L10n -from firefox_puppeteer.base import BaseLib -from firefox_puppeteer.decorators import use_class_as_property - - -class Windows(BaseLib): - - # Used for registering the different windows with this class to avoid - # circular dependencies with BaseWindow - windows_map = {} - - @property - def all(self): - """Retrieves a list of all open chrome windows. - - :returns: List of :class:`BaseWindow` instances corresponding to the - windows in `marionette.chrome_window_handles`. - """ - return [self.create_window_instance(handle) for handle in - self.marionette.chrome_window_handles] - - @property - def current(self): - """Retrieves the currently selected chrome window. - - :returns: The :class:`BaseWindow` for the currently active window. - """ - return self.create_window_instance(self.marionette.current_chrome_window_handle) - - @property - def focused_chrome_window_handle(self): - """Returns the currently focused chrome window handle. - - :returns: The `window handle` of the focused chrome window. - """ - def get_active_handle(mn): - with self.marionette.using_context('chrome'): - return self.marionette.execute_script(""" - Components.utils.import("resource://gre/modules/Services.jsm"); - - let win = Services.focus.activeWindow; - if (win) { - return win.QueryInterface(Components.interfaces.nsIInterfaceRequestor) - .getInterface(Components.interfaces.nsIDOMWindowUtils) - .outerWindowID.toString(); - } - - return null; - """) - - # In case of `None` being returned no window is currently active. This can happen - # when a focus change action is currently happening. So lets wait until it is done. - return Wait(self.marionette).until(get_active_handle, - message='No focused window has been found.') - - def close(self, handle): - """Closes the chrome window with the given handle. - - :param handle: The handle of the chrome window. - """ - self.switch_to(handle) - - # TODO: Maybe needs to wait as handled via an observer - return self.marionette.close_chrome_window() - - def close_all(self, exceptions=None): - """Closes all open chrome windows. - - There is an optional `exceptions` list, which can be used to exclude - specific chrome windows from being closed. - - :param exceptions: Optional, list of :class:`BaseWindow` instances not to close. - """ - windows_to_keep = exceptions or [] - - # Get handles of windows_to_keep - handles_to_keep = [entry.handle for entry in windows_to_keep] - - # Find handles to close and close them all - handles_to_close = set(self.marionette.chrome_window_handles) - set(handles_to_keep) - for handle in handles_to_close: - self.close(handle) - - def create_window_instance(self, handle, expected_class=None): - """Creates a :class:`BaseWindow` instance for the given chrome window. - - :param handle: The handle of the chrome window. - :param expected_class: Optional, check for the correct window class. - """ - current_handle = self.marionette.current_chrome_window_handle - window = None - - with self.marionette.using_context('chrome'): - try: - # Retrieve window type to determine the type of chrome window - if handle != self.marionette.current_chrome_window_handle: - self.switch_to(handle) - window_type = self.marionette.get_window_type() - finally: - # Ensure to switch back to the original window - if handle != current_handle: - self.switch_to(current_handle) - - if window_type in self.windows_map: - window = self.windows_map[window_type](self.marionette, handle) - else: - window = BaseWindow(self.marionette, handle) - - if expected_class is not None and type(window) is not expected_class: - raise errors.UnexpectedWindowTypeError('Expected window "%s" but got "%s"' % - (expected_class, type(window))) - - # Before continuing ensure the chrome window has been completed loading - Wait(self.marionette).until( - lambda _: self.loaded(handle), - message='Chrome window with handle "%s" did not finish loading.' % handle) - - return window - - def focus(self, handle): - """Focuses the chrome window with the given handle. - - :param handle: The handle of the chrome window. - """ - self.switch_to(handle) - - with self.marionette.using_context('chrome'): - self.marionette.execute_script(""" window.focus(); """) - - Wait(self.marionette).until( - lambda _: handle == self.focused_chrome_window_handle, - message='Focus has not been set to chrome window handle "%s".' % handle) - - def loaded(self, handle): - """Check if the chrome window with the given handle has been completed loading. - - :param handle: The handle of the chrome window. - - :returns: True, if the chrome window has been loaded. - """ - with self.marionette.using_context('chrome'): - return self.marionette.execute_script(""" - Components.utils.import("resource://gre/modules/Services.jsm"); - - let win = Services.wm.getOuterWindowWithId(Number(arguments[0])); - return win.document.readyState == 'complete'; - """, script_args=[handle]) - - def switch_to(self, target): - """Switches context to the specified chrome window. - - :param target: The window to switch to. `target` can be a `handle` or a - callback that returns True in the context of the desired - window. - - :returns: Instance of the selected :class:`BaseWindow`. - """ - target_handle = None - - if target in self.marionette.chrome_window_handles: - target_handle = target - elif callable(target): - current_handle = self.marionette.current_chrome_window_handle - - # switches context if callback for a chrome window returns `True`. - for handle in self.marionette.chrome_window_handles: - self.marionette.switch_to_window(handle) - window = self.create_window_instance(handle) - if target(window): - target_handle = handle - break - - # if no handle has been found switch back to original window - if not target_handle: - self.marionette.switch_to_window(current_handle) - - if target_handle is None: - raise NoSuchWindowException("No window found for '{}'" - .format(target)) - - # only switch if necessary - if target_handle != self.marionette.current_chrome_window_handle: - self.marionette.switch_to_window(target_handle) - - return self.create_window_instance(target_handle) - - @classmethod - def register_window(cls, window_type, window_class): - """Registers a chrome window with this class so that this class may in - turn create the appropriate window instance later on. - - :param window_type: The type of window. - :param window_class: The constructor of the window - """ - cls.windows_map[window_type] = window_class - - -class BaseWindow(BaseLib): - """Base class for any kind of chrome window.""" - - # l10n class attributes will be set by each window class individually - dtds = [] - properties = [] - - def __init__(self, marionette, window_handle): - super(BaseWindow, self).__init__(marionette) - - self._l10n = L10n(self.marionette) - self._windows = Windows(self.marionette) - - if window_handle not in self.marionette.chrome_window_handles: - raise errors.UnknownWindowError('Window with handle "%s" does not exist' % - window_handle) - self._handle = window_handle - - def __eq__(self, other): - return self.handle == other.handle - - @property - def closed(self): - """Returns closed state of the chrome window. - - :returns: True if the window has been closed. - """ - return self.handle not in self.marionette.chrome_window_handles - - @property - def focused(self): - """Returns `True` if the chrome window is focused. - - :returns: True if the window is focused. - """ - self.switch_to() - - return self.handle == self._windows.focused_chrome_window_handle - - @property - def handle(self): - """Returns the `window handle` of the chrome window. - - :returns: `window handle`. - """ - return self._handle - - @property - def loaded(self): - """Checks if the window has been fully loaded. - - :returns: True, if the window is loaded. - """ - self._windows.loaded(self.handle) - - @use_class_as_property('ui.menu.MenuBar') - def menubar(self): - """Provides access to the menu bar, for example, the **File** menu. - - See the :class:`~ui.menu.MenuBar` reference. - """ - - @property - def window_element(self): - """Returns the inner DOM window element. - - :returns: DOM window element. - """ - self.switch_to() - - return self.marionette.find_element(By.CSS_SELECTOR, ':root') - - def close(self, callback=None, force=False): - """Closes the current chrome window. - - If this is the last remaining window, the Marionette session is ended. - - :param callback: Optional, function to trigger the window to open. It is - triggered with the current :class:`BaseWindow` as parameter. - Defaults to `window.open()`. - - :param force: Optional, forces the closing of the window by using the Gecko API. - Defaults to `False`. - """ - self.switch_to() - - # Bug 1121698 - # For more stable tests register an observer topic first - prev_win_count = len(self.marionette.chrome_window_handles) - - handle = self.handle - if force or callback is None: - self._windows.close(handle) - else: - callback(self) - - # Bug 1121698 - # Observer code should let us ditch this wait code - Wait(self.marionette).until( - lambda m: len(m.chrome_window_handles) == prev_win_count - 1, - message='Chrome window with handle "%s" has not been closed.' % handle) - - def focus(self): - """Sets the focus to the current chrome window.""" - return self._windows.focus(self.handle) - - def localize_entity(self, entity_id): - """Returns the localized string for the specified DTD entity id. - - :param entity_id: The id to retrieve the value from. - - :returns: The localized string for the requested entity. - - :raises MarionetteException: When entity id is not found. - """ - return self._l10n.localize_entity(self.dtds, entity_id) - - def localize_property(self, property_id): - """Returns the localized string for the specified property id. - - :param property_id: The id to retrieve the value from. - - :returns: The localized string for the requested property. - - :raises MarionetteException: When property id is not found. - """ - return self._l10n.localize_property(self.properties, property_id) - - def open_window(self, callback=None, expected_window_class=None, focus=True): - """Opens a new top-level chrome window. - - :param callback: Optional, function to trigger the window to open. It is - triggered with the current :class:`BaseWindow` as parameter. - Defaults to `window.open()`. - :param expected_class: Optional, check for the correct window class. - :param focus: Optional, if true, focus the new window. - Defaults to `True`. - """ - # Bug 1121698 - # For more stable tests register an observer topic first - start_handles = self.marionette.chrome_window_handles - - self.switch_to() - with self.marionette.using_context('chrome'): - if callback is not None: - callback(self) - else: - self.marionette.execute_script(""" window.open(); """) - - # TODO: Needs to be replaced with observer handling code (bug 1121698) - def window_opened(mn): - return len(mn.chrome_window_handles) == len(start_handles) + 1 - Wait(self.marionette).until( - window_opened, - message='No new chrome window has been opened.') - - handles = self.marionette.chrome_window_handles - [new_handle] = list(set(handles) - set(start_handles)) - - assert new_handle is not None - - window = self._windows.create_window_instance(new_handle, expected_window_class) - - if focus: - window.focus() - - return window - - def send_shortcut(self, command_key, **kwargs): - """Sends a keyboard shortcut to the window. - - :param command_key: The key (usually a letter) to be pressed. - - :param accel: Optional, If `True`, the `Accel` modifier key is pressed. - This key differs between OS X (`Meta`) and Linux/Windows (`Ctrl`). Defaults to `False`. - - :param alt: Optional, If `True`, the `Alt` modifier key is pressed. Defaults to `False`. - - :param ctrl: Optional, If `True`, the `Ctrl` modifier key is pressed. Defaults to `False`. - - :param meta: Optional, If `True`, the `Meta` modifier key is pressed. Defaults to `False`. - - :param shift: Optional, If `True`, the `Shift` modifier key is pressed. - Defaults to `False`. - """ - - platform = self.marionette.session_capabilities['platformName'] - - keymap = { - 'accel': Keys.META if platform == 'darwin' else Keys.CONTROL, - 'alt': Keys.ALT, - 'cmd': Keys.COMMAND, - 'ctrl': Keys.CONTROL, - 'meta': Keys.META, - 'shift': Keys.SHIFT, - } - - # Append all to press modifier keys - keys = [] - for modifier in kwargs: - if modifier not in keymap: - raise KeyError('"%s" is not a known modifier' % modifier) - - if kwargs[modifier] is True: - keys.append(keymap[modifier]) - - # Bug 1125209 - Only lower-case command keys should be sent - keys.append(command_key.lower()) - - self.switch_to() - self.window_element.send_keys(*keys) - - def switch_to(self, focus=False): - """Switches the context to this chrome window. - - By default it will not focus the window. If that behavior is wanted, the - `focus` parameter can be used. - - :param focus: If `True`, the chrome window will be focused. - - :returns: Current window as :class:`BaseWindow` instance. - """ - if focus: - self._windows.focus(self.handle) - else: - self._windows.switch_to(self.handle) - - return self diff --git a/testing/marionette/puppeteer/firefox/requirements-docs.txt b/testing/marionette/puppeteer/firefox/requirements-docs.txt deleted file mode 100644 index 9b4aa5d0a..000000000 --- a/testing/marionette/puppeteer/firefox/requirements-docs.txt +++ /dev/null @@ -1,5 +0,0 @@ -sphinx -sphinx_rtd_theme - -# Required by Readthedocs to install the firefox-puppeteer package --e testing/marionette/puppeteer/firefox diff --git a/testing/marionette/puppeteer/firefox/requirements.txt b/testing/marionette/puppeteer/firefox/requirements.txt deleted file mode 100644 index bfacfffde..000000000 --- a/testing/marionette/puppeteer/firefox/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -marionette-driver >= 2.2.0 -mozinfo >= 0.8 diff --git a/testing/marionette/puppeteer/firefox/setup.py b/testing/marionette/puppeteer/firefox/setup.py deleted file mode 100644 index 5efb36a61..000000000 --- a/testing/marionette/puppeteer/firefox/setup.py +++ /dev/null @@ -1,36 +0,0 @@ -# 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/. - -import os -import re -from setuptools import setup, find_packages - -THIS_DIR = os.path.dirname(os.path.realpath(__name__)) - - -def read(*parts): - with open(os.path.join(THIS_DIR, *parts)) as f: - return f.read() - - -def get_version(): - return re.findall("__version__ = '([\d\.]+)'", - read('firefox_puppeteer', '__init__.py'), re.M)[0] - - -setup(name='firefox-puppeteer', - version=get_version(), - description="Firefox Puppeteer", - long_description='See http://firefox-puppeteer.readthedocs.org/', - classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers - keywords='mozilla', - author='Auto-tools', - author_email='tools-marionette@lists.mozilla.org', - url='https://wiki.mozilla.org/Auto-tools/Projects/Marionette/Puppeteer/Firefox', - license='MPL', - packages=find_packages(), - include_package_data=True, - zip_safe=False, - install_requires=read('requirements.txt').splitlines(), - ) diff --git a/testing/marionette/server.js b/testing/marionette/server.js deleted file mode 100644 index 0d8a7bf23..000000000 --- a/testing/marionette/server.js +++ /dev/null @@ -1,144 +0,0 @@ -/* 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/. */ - -"use strict"; - -var {Constructor: CC, classes: Cc, interfaces: Ci, utils: Cu} = Components; - -var loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader); -const ServerSocket = CC("@mozilla.org/network/server-socket;1", "nsIServerSocket", "initSpecialConnection"); - -Cu.import("resource://gre/modules/Log.jsm"); -Cu.import("resource://gre/modules/Preferences.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -Cu.import("chrome://marionette/content/dispatcher.js"); -Cu.import("chrome://marionette/content/driver.js"); -Cu.import("chrome://marionette/content/element.js"); -Cu.import("chrome://marionette/content/simpletest.js"); - -// Bug 1083711: Load transport.js as an SDK module instead of subscript -loader.loadSubScript("resource://devtools/shared/transport/transport.js"); - -const logger = Log.repository.getLogger("Marionette"); - -this.EXPORTED_SYMBOLS = ["MarionetteServer"]; - -const CONTENT_LISTENER_PREF = "marionette.contentListener"; -const MANAGE_OFFLINE_STATUS_PREF = "network.gonk.manage-offline-status"; - -/** - * Bootstraps Marionette and handles incoming client connections. - * - * Once started, it opens a TCP socket sporting the debugger transport - * protocol on the provided port. For every new client a Dispatcher is - * created. - * - * @param {number} port - * Port for server to listen to. - * @param {boolean} forceLocal - * Listen only to connections from loopback if true. If false, - * accept all connections. - */ -this.MarionetteServer = function (port, forceLocal) { - this.port = port; - this.forceLocal = forceLocal; - this.conns = {}; - this.nextConnId = 0; - this.alive = false; - this._acceptConnections = false; -}; - -/** - * Function produces a GeckoDriver. - * - * Determines application nameto initialise the driver with. - * - * @return {GeckoDriver} - * A driver instance. - */ -MarionetteServer.prototype.driverFactory = function() { - let appName = isMulet() ? "B2G" : Services.appinfo.name; - let bypassOffline = false; - - Preferences.set(CONTENT_LISTENER_PREF, false); - - if (bypassOffline) { - logger.debug("Bypassing offline status"); - Preferences.set(MANAGE_OFFLINE_STATUS_PREF, false); - Services.io.manageOfflineStatus = false; - Services.io.offline = false; - } - - return new GeckoDriver(appName, this); -}; - -MarionetteServer.prototype.__defineSetter__("acceptConnections", function (value) { - if (!value) { - logger.info("New connections will no longer be accepted"); - } else { - logger.info("New connections are accepted again"); - } - - this._acceptConnections = value; -}); - -MarionetteServer.prototype.start = function() { - if (this.alive) { - return; - } - let flags = Ci.nsIServerSocket.KeepWhenOffline; - if (this.forceLocal) { - flags |= Ci.nsIServerSocket.LoopbackOnly; - } - this.listener = new ServerSocket(this.port, flags, 1); - this.listener.asyncListen(this); - this.alive = true; - this._acceptConnections = true; -}; - -MarionetteServer.prototype.stop = function() { - if (!this.alive) { - return; - } - this.closeListener(); - this.alive = false; - this._acceptConnections = false; -}; - -MarionetteServer.prototype.closeListener = function() { - this.listener.close(); - this.listener = null; -}; - -MarionetteServer.prototype.onSocketAccepted = function ( - serverSocket, clientSocket) { - if (!this._acceptConnections) { - logger.warn("New connections are currently not accepted"); - return; - } - - let input = clientSocket.openInputStream(0, 0, 0); - let output = clientSocket.openOutputStream(0, 0, 0); - let transport = new DebuggerTransport(input, output); - let connId = "conn" + this.nextConnId++; - - let dispatcher = new Dispatcher(connId, transport, this.driverFactory.bind(this)); - dispatcher.onclose = this.onConnectionClosed.bind(this); - this.conns[connId] = dispatcher; - - logger.debug(`Accepted connection ${connId} from ${clientSocket.host}:${clientSocket.port}`); - dispatcher.sayHello(); - transport.ready(); -}; - -MarionetteServer.prototype.onConnectionClosed = function (conn) { - let id = conn.connId; - delete this.conns[id]; - logger.debug(`Closed connection ${id}`); -}; - -function isMulet() { - return Preferences.get("b2g.is_mulet", false); -} diff --git a/testing/marionette/session.js b/testing/marionette/session.js deleted file mode 100644 index 8bd16404f..000000000 --- a/testing/marionette/session.js +++ /dev/null @@ -1,457 +0,0 @@ -/* 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/. */ - -"use strict"; - -const {interfaces: Ci, utils: Cu} = Components; - -Cu.import("resource://gre/modules/Log.jsm"); -Cu.import("resource://gre/modules/Preferences.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -Cu.import("chrome://marionette/content/assert.js"); -Cu.import("chrome://marionette/content/error.js"); - -this.EXPORTED_SYMBOLS = ["session"]; - -const logger = Log.repository.getLogger("Marionette"); -const {pprint} = error; - -// Enable testing this module, as Services.appinfo.* is not available -// in xpcshell tests. -const appinfo = {name: "<missing>", version: "<missing>"}; -try { appinfo.name = Services.appinfo.name.toLowerCase(); } catch (e) {} -try { appinfo.version = Services.appinfo.version; } catch (e) {} - -/** State associated with a WebDriver session. */ -this.session = {}; - -/** Representation of WebDriver session timeouts. */ -session.Timeouts = class { - constructor () { - // disabled - this.implicit = 0; - // five mintues - this.pageLoad = 300000; - // 30 seconds - this.script = 30000; - } - - toString () { return "[object session.Timeouts]"; } - - toJSON () { - return { - "implicit": this.implicit, - "page load": this.pageLoad, - "script": this.script, - }; - } - - static fromJSON (json) { - assert.object(json); - let t = new session.Timeouts(); - - for (let [typ, ms] of Object.entries(json)) { - assert.positiveInteger(ms); - - switch (typ) { - case "implicit": - t.implicit = ms; - break; - - case "script": - t.script = ms; - break; - - case "page load": - t.pageLoad = ms; - break; - - default: - throw new InvalidArgumentError(); - } - } - - return t; - } -}; - -/** Enum of page loading strategies. */ -session.PageLoadStrategy = { - None: "none", - Eager: "eager", - Normal: "normal", -}; - -/** Proxy configuration object representation. */ -session.Proxy = class { - constructor() { - this.proxyType = null; - this.httpProxy = null; - this.httpProxyPort = null; - this.sslProxy = null; - this.sslProxyPort = null; - this.ftpProxy = null; - this.ftpProxyPort = null; - this.socksProxy = null; - this.socksProxyPort = null; - this.socksVersion = null; - this.proxyAutoconfigUrl = null; - } - - /** - * Sets Firefox proxy settings. - * - * @return {boolean} - * True if proxy settings were updated as a result of calling this - * function, or false indicating that this function acted as - * a no-op. - */ - init() { - switch (this.proxyType) { - case "manual": - Preferences.set("network.proxy.type", 1); - if (this.httpProxy && this.httpProxyPort) { - Preferences.set("network.proxy.http", this.httpProxy); - Preferences.set("network.proxy.http_port", this.httpProxyPort); - } - if (this.sslProxy && this.sslProxyPort) { - Preferences.set("network.proxy.ssl", this.sslProxy); - Preferences.set("network.proxy.ssl_port", this.sslProxyPort); - } - if (this.ftpProxy && this.ftpProxyPort) { - Preferences.set("network.proxy.ftp", this.ftpProxy); - Preferences.set("network.proxy.ftp_port", this.ftpProxyPort); - } - if (this.socksProxy) { - Preferences.set("network.proxy.socks", this.socksProxy); - Preferences.set("network.proxy.socks_port", this.socksProxyPort); - if (this.socksVersion) { - Preferences.set("network.proxy.socks_version", this.socksVersion); - } - } - return true; - - case "pac": - Preferences.set("network.proxy.type", 2); - Preferences.set("network.proxy.autoconfig_url", this.proxyAutoconfigUrl); - return true; - - case "autodetect": - Preferences.set("network.proxy.type", 4); - return true; - - case "system": - Preferences.set("network.proxy.type", 5); - return true; - - case "noproxy": - Preferences.set("network.proxy.type", 0); - return true; - - default: - return false; - } - } - - toString () { return "[object session.Proxy]"; } - - toJSON () { - return marshal({ - proxyType: this.proxyType, - httpProxy: this.httpProxy, - httpProxyPort: this.httpProxyPort , - sslProxy: this.sslProxy, - sslProxyPort: this.sslProxyPort, - ftpProxy: this.ftpProxy, - ftpProxyPort: this.ftpProxyPort, - socksProxy: this.socksProxy, - socksProxyPort: this.socksProxyPort, - socksProxyVersion: this.socksProxyVersion, - proxyAutoconfigUrl: this.proxyAutoconfigUrl, - }); - } - - static fromJSON (json) { - let p = new session.Proxy(); - if (typeof json == "undefined" || json === null) { - return p; - } - - assert.object(json); - - assert.in("proxyType", json); - p.proxyType = json.proxyType; - - if (json.proxyType == "manual") { - if (typeof json.httpProxy != "undefined") { - p.httpProxy = assert.string(json.httpProxy); - p.httpProxyPort = assert.positiveInteger(json.httpProxyPort); - } - - if (typeof json.sslProxy != "undefined") { - p.sslProxy = assert.string(json.sslProxy); - p.sslProxyPort = assert.positiveInteger(json.sslProxyPort); - } - - if (typeof json.ftpProxy != "undefined") { - p.ftpProxy = assert.string(json.ftpProxy); - p.ftpProxyPort = assert.positiveInteger(json.ftpProxyPort); - } - - if (typeof json.socksProxy != "undefined") { - p.socksProxy = assert.string(json.socksProxy); - p.socksProxyPort = assert.positiveInteger(json.socksProxyPort); - p.socksProxyVersion = assert.positiveInteger(json.socksProxyVersion); - } - } - - if (typeof json.proxyAutoconfigUrl != "undefined") { - p.proxyAutoconfigUrl = assert.string(json.proxyAutoconfigUrl); - } - - return p; - } -}; - -/** WebDriver session capabilities representation. */ -session.Capabilities = class extends Map { - constructor () { - super([ - // webdriver - ["browserName", appinfo.name], - ["browserVersion", appinfo.version], - ["platformName", Services.sysinfo.getProperty("name").toLowerCase()], - ["platformVersion", Services.sysinfo.getProperty("version")], - ["pageLoadStrategy", session.PageLoadStrategy.Normal], - ["acceptInsecureCerts", false], - ["timeouts", new session.Timeouts()], - ["proxy", new session.Proxy()], - - // features - ["rotatable", appinfo.name == "B2G"], - - // proprietary - ["specificationLevel", 0], - ["moz:processID", Services.appinfo.processID], - ["moz:profile", maybeProfile()], - ["moz:accessibilityChecks", false], - ]); - } - - set (key, value) { - if (key === "timeouts" && !(value instanceof session.Timeouts)) { - throw new TypeError(); - } else if (key === "proxy" && !(value instanceof session.Proxy)) { - throw new TypeError(); - } - - return super.set(key, value); - } - - toString() { return "[object session.Capabilities]"; } - - toJSON() { - return marshal(this); - } - - /** - * Unmarshal a JSON object representation of WebDriver capabilities. - * - * @param {Object.<string, ?>=} json - * WebDriver capabilities. - * @param {boolean=} merge - * If providing |json| with |desiredCapabilities| or - * |requiredCapabilities| fields, or both, it should be set to - * true to merge these before parsing. This indicates - * that the input provided is from a client and not from - * |session.Capabilities#toJSON|. - * - * @return {session.Capabilities} - * Internal representation of WebDriver capabilities. - */ - static fromJSON (json, {merge = false} = {}) { - if (typeof json == "undefined" || json === null) { - json = {}; - } - assert.object(json); - - if (merge) { - json = session.Capabilities.merge_(json); - } - return session.Capabilities.match_(json); - } - - // Processes capabilities as described by WebDriver. - static merge_ (json) { - for (let entry of [json.desiredCapabilities, json.requiredCapabilities]) { - if (typeof entry == "undefined" || entry === null) { - continue; - } - assert.object(entry, error.pprint`Expected ${entry} to be a capabilities object`); - } - - let desired = json.desiredCapabilities || {}; - let required = json.requiredCapabilities || {}; - - // One level deep union merge of desired- and required capabilities - // with preference on required - return Object.assign({}, desired, required); - } - - // Matches capabilities as described by WebDriver. - static match_ (caps = {}) { - let matched = new session.Capabilities(); - - const defined = v => typeof v != "undefined" && v !== null; - const wildcard = v => v === "*"; - - // Iff |actual| provides some value, or is a wildcard or an exact - // match of |expected|. This means it can be null or undefined, - // or "*", or "firefox". - function stringMatch (actual, expected) { - return !defined(actual) || (wildcard(actual) || actual === expected); - } - - for (let [k,v] of Object.entries(caps)) { - switch (k) { - case "browserName": - let bname = matched.get("browserName"); - if (!stringMatch(v, bname)) { - throw new TypeError( - pprint`Given browserName ${v}, but my name is ${bname}`); - } - break; - - // TODO(ato): bug 1326397 - case "browserVersion": - let bversion = matched.get("browserVersion"); - if (!stringMatch(v, bversion)) { - throw new TypeError( - pprint`Given browserVersion ${v}, ` + - pprint`but current version is ${bversion}`); - } - break; - - case "platformName": - let pname = matched.get("platformName"); - if (!stringMatch(v, pname)) { - throw new TypeError( - pprint`Given platformName ${v}, ` + - pprint`but current platform is ${pname}`); - } - break; - - // TODO(ato): bug 1326397 - case "platformVersion": - let pversion = matched.get("platformVersion"); - if (!stringMatch(v, pversion)) { - throw new TypeError( - pprint`Given platformVersion ${v}, ` + - pprint`but current platform version is ${pversion}`); - } - break; - - case "acceptInsecureCerts": - assert.boolean(v); - matched.set("acceptInsecureCerts", v); - break; - - case "pageLoadStrategy": - if (Object.values(session.PageLoadStrategy).includes(v)) { - matched.set("pageLoadStrategy", v); - } else { - throw new TypeError("Unknown page load strategy: " + v); - } - break; - - case "proxy": - let proxy = session.Proxy.fromJSON(v); - matched.set("proxy", proxy); - break; - - case "timeouts": - let timeouts = session.Timeouts.fromJSON(v); - matched.set("timeouts", timeouts); - break; - - case "specificationLevel": - assert.positiveInteger(v); - matched.set("specificationLevel", v); - break; - - case "moz:accessibilityChecks": - assert.boolean(v); - matched.set("moz:accessibilityChecks", v); - break; - } - } - - return matched; - } -}; - -// Specialisation of |JSON.stringify| that produces JSON-safe object -// literals, dropping empty objects and entries which values are undefined -// or null. Objects are allowed to produce their own JSON representations -// by implementing a |toJSON| function. -function marshal(obj) { - let rv = Object.create(null); - - function* iter(mapOrObject) { - if (mapOrObject instanceof Map) { - for (const [k,v] of mapOrObject) { - yield [k,v]; - } - } else { - for (const k of Object.keys(mapOrObject)) { - yield [k, mapOrObject[k]]; - } - } - } - - for (let [k,v] of iter(obj)) { - // Skip empty values when serialising to JSON. - if (typeof v == "undefined" || v === null) { - continue; - } - - // Recursively marshal objects that are able to produce their own - // JSON representation. - if (typeof v.toJSON == "function") { - v = marshal(v.toJSON()); - } - - // Or do the same for object literals. - else if (isObject(v)) { - v = marshal(v); - } - - // And finally drop (possibly marshaled) objects which have no - // entries. - if (!isObjectEmpty(v)) { - rv[k] = v; - } - } - - return rv; -} - -function isObject(obj) { - return Object.prototype.toString.call(obj) == "[object Object]"; -} - -function isObjectEmpty(obj) { - return isObject(obj) && Object.keys(obj).length === 0; -} - -// Services.dirsvc is not accessible from content frame scripts, -// but we should not panic about that. -function maybeProfile() { - try { - return Services.dirsvc.get("ProfD", Ci.nsIFile).path; - } catch (e) { - return "<protected>"; - } -} diff --git a/testing/marionette/simpletest.js b/testing/marionette/simpletest.js deleted file mode 100644 index b179d2a8c..000000000 --- a/testing/marionette/simpletest.js +++ /dev/null @@ -1,208 +0,0 @@ -/* 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/. */ - -"use strict"; - -const {utils: Cu} = Components; - -Cu.import("chrome://marionette/content/error.js"); - -this.EXPORTED_SYMBOLS = ["simpletest"]; - -this.simpletest = {}; - -/** - * The simpletest harness, exposed in the script evaluation sandbox. - */ -simpletest.Harness = class { - constructor(window, context, contentLogger, timeout, testName) { - this.window = window; - this.tests = []; - this.logger = contentLogger; - this.context = context; - this.timeout = timeout; - this.testName = testName; - this.TEST_UNEXPECTED_FAIL = "TEST-UNEXPECTED-FAIL"; - this.TEST_UNEXPECTED_PASS = "TEST-UNEXPECTED-PASS"; - this.TEST_PASS = "TEST-PASS"; - this.TEST_KNOWN_FAIL = "TEST-KNOWN-FAIL"; - } - - get exports() { - return new Map([ - ["ok", this.ok.bind(this)], - ["is", this.is.bind(this)], - ["isnot", this.isnot.bind(this)], - ["todo", this.todo.bind(this)], - ["log", this.log.bind(this)], - ["getLogs", this.getLogs.bind(this)], - ["generate_results", this.generate_results.bind(this)], - ["waitFor", this.waitFor.bind(this)], - ["TEST_PASS", this.TEST_PASS], - ["TEST_KNOWN_FAIL", this.TEST_KNOWN_FAIL], - ["TEST_UNEXPECTED_FAIL", this.TEST_UNEXPECTED_FAIL], - ["TEST_UNEXPECTED_PASS", this.TEST_UNEXPECTED_PASS], - ]); - } - - addTest(condition, name, passString, failString, diag, state) { - let test = { - result: !!condition, - name: name, - diag: diag, - state: state - }; - this.logResult( - test, - typeof passString == "undefined" ? this.TEST_PASS : passString, - typeof failString == "undefined" ? this.TEST_UNEXPECTED_FAIL : failString); - this.tests.push(test); - } - - ok(condition, name, passString, failString) { - let diag = `${this.repr(condition)} was ${!!condition}, expected true`; - this.addTest(condition, name, passString, failString, diag); - } - - is(a, b, name, passString, failString) { - let pass = (a == b); - let diag = pass ? this.repr(a) + " should equal " + this.repr(b) - : "got " + this.repr(a) + ", expected " + this.repr(b); - this.addTest(pass, name, passString, failString, diag); - } - - isnot(a, b, name, passString, failString) { - let pass = (a != b); - let diag = pass ? this.repr(a) + " should not equal " + this.repr(b) - : "didn't expect " + this.repr(a) + ", but got it"; - this.addTest(pass, name, passString, failString, diag); - } - - todo(condition, name, passString, failString) { - let diag = this.repr(condition) + " was expected false"; - this.addTest(!condition, - name, - typeof(passString) == "undefined" ? this.TEST_KNOWN_FAIL : passString, - typeof(failString) == "undefined" ? this.TEST_UNEXPECTED_FAIL : failString, - diag, - "todo"); - } - - log(msg, level) { - dump("MARIONETTE LOG: " + (level ? level : "INFO") + ": " + msg + "\n"); - if (this.logger) { - this.logger.log(msg, level); - } - } - - // TODO(ato): Suspect this isn't used anywhere - getLogs() { - if (this.logger) { - return this.logger.get(); - } - } - - generate_results() { - let passed = 0; - let failures = []; - let expectedFailures = []; - let unexpectedSuccesses = []; - for (let i in this.tests) { - let isTodo = (this.tests[i].state == "todo"); - if(this.tests[i].result) { - if (isTodo) { - expectedFailures.push({'name': this.tests[i].name, 'diag': this.tests[i].diag}); - } - else { - passed++; - } - } - else { - if (isTodo) { - unexpectedSuccesses.push({'name': this.tests[i].name, 'diag': this.tests[i].diag}); - } - else { - failures.push({'name': this.tests[i].name, 'diag': this.tests[i].diag}); - } - } - } - // Reset state in case this object is reused for more tests. - this.tests = []; - return { - passed: passed, - failures: failures, - expectedFailures: expectedFailures, - unexpectedSuccesses: unexpectedSuccesses, - }; - } - - logToFile(file) { - //TODO - } - - logResult(test, passString, failString) { - //TODO: dump to file - let resultString = test.result ? passString : failString; - let diagnostic = test.name + (test.diag ? " - " + test.diag : ""); - let msg = resultString + " | " + this.testName + " | " + diagnostic; - dump("MARIONETTE TEST RESULT:" + msg + "\n"); - } - - repr(o) { - if (typeof o == "undefined") { - return "undefined"; - } else if (o === null) { - return "null"; - } - - try { - if (typeof o.__repr__ == "function") { - return o.__repr__(); - } else if (typeof o.repr == "function" && o.repr !== arguments.callee) { - return o.repr(); - } - } catch (e) {} - - try { - if (typeof o.NAME === "string" && - (o.toString === Function.prototype.toString || o.toString === Object.prototype.toString)) { - return o.NAME; - } - } catch (e) {} - - let ostring; - try { - ostring = (o + ""); - } catch (e) { - return "[" + typeof(o) + "]"; - } - - if (typeof o == "function") { - o = ostring.replace(/^\s+/, ""); - let idx = o.indexOf("{"); - if (idx != -1) { - o = o.substr(0, idx) + "{...}"; - } - } - return ostring; - } - - waitFor(callback, test, timeout) { - if (test()) { - callback(); - return; - } - - let now = new Date(); - let deadline = (timeout instanceof Date) ? timeout : - new Date(now.valueOf() + (typeof timeout == "undefined" ? this.timeout : timeout)); - if (deadline <= now) { - dump("waitFor timeout: " + test.toString() + "\n"); - // the script will timeout here, so no need to raise a separate - // timeout exception - return; - } - this.window.setTimeout(this.waitFor.bind(this), 100, callback, test, deadline); - } -}; diff --git a/testing/marionette/test_action.js b/testing/marionette/test_action.js deleted file mode 100644 index 1c58ced76..000000000 --- a/testing/marionette/test_action.js +++ /dev/null @@ -1,627 +0,0 @@ -/* 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/. */ - -"use strict"; - -const {utils: Cu} = Components; - -Cu.import("chrome://marionette/content/action.js"); -Cu.import("chrome://marionette/content/element.js"); -Cu.import("chrome://marionette/content/error.js"); - -action.inputStateMap = new Map(); - -add_test(function test_createAction() { - Assert.throws(() => new action.Action(), InvalidArgumentError, - "Missing Action constructor args"); - Assert.throws(() => new action.Action(1, 2), InvalidArgumentError, - "Missing Action constructor args"); - Assert.throws( - () => new action.Action(1, 2, "sometype"), /Expected string/, "Non-string arguments."); - ok(new action.Action("id", "sometype", "sometype")); - - run_next_test(); -}); - -add_test(function test_defaultPointerParameters() { - let defaultParameters = {pointerType: action.PointerType.Mouse}; - deepEqual(action.PointerParameters.fromJson(), defaultParameters); - - run_next_test(); -}); - -add_test(function test_processPointerParameters() { - let check = (regex, message, arg) => checkErrors( - regex, action.PointerParameters.fromJson, [arg], message); - let parametersData; - for (let d of ["foo", "", "get", "Get"]) { - parametersData = {pointerType: d}; - let message = `parametersData: [pointerType: ${parametersData.pointerType}]`; - check(/Unknown pointerType/, message, parametersData); - } - parametersData.pointerType = "mouse"; //TODO "pen"; - deepEqual(action.PointerParameters.fromJson(parametersData), - {pointerType: "mouse"}); //TODO action.PointerType.Pen}); - - run_next_test(); -}); - -add_test(function test_processPointerUpDownAction() { - let actionItem = {type: "pointerDown"}; - let actionSequence = {type: "pointer", id: "some_id"}; - for (let d of [-1, "a"]) { - actionItem.button = d; - checkErrors( - /Expected 'button' \(.*\) to be >= 0/, action.Action.fromJson, [actionSequence, actionItem], - `button: ${actionItem.button}`); - } - actionItem.button = 5; - let act = action.Action.fromJson(actionSequence, actionItem); - equal(act.button, actionItem.button); - - run_next_test(); -}); - -add_test(function test_validateActionDurationAndCoordinates() { - let actionItem = {}; - let actionSequence = {id: "some_id"}; - let check = function (type, subtype, message = undefined) { - message = message || `duration: ${actionItem.duration}, subtype: ${subtype}`; - actionItem.type = subtype; - actionSequence.type = type; - checkErrors(/Expected '.*' \(.*\) to be >= 0/, - action.Action.fromJson, [actionSequence, actionItem], message); - }; - for (let d of [-1, "a"]) { - actionItem.duration = d; - check("none", "pause"); - check("pointer", "pointerMove"); - } - actionItem.duration = 5000; - for (let name of ["x", "y"]) { - actionItem[name] = "a"; - actionItem.type = "pointerMove"; - actionSequence.type = "pointer"; - checkErrors(/Expected '.*' \(.*\) to be an Integer/, - action.Action.fromJson, [actionSequence, actionItem], - `duration: ${actionItem.duration}, subtype: pointerMove`); - } - run_next_test(); -}); - -add_test(function test_processPointerMoveActionOriginValidation() { - let actionSequence = {type: "pointer", id: "some_id"}; - let actionItem = {duration: 5000, type: "pointerMove"}; - for (let d of [-1, {a: "blah"}, []]) { - actionItem.origin = d; - - checkErrors(/Expected \'origin\' to be a string or a web element reference/, - action.Action.fromJson, - [actionSequence, actionItem], - `actionItem.origin: (${getTypeString(d)})`); - } - - run_next_test(); -}); - -add_test(function test_processPointerMoveActionOriginStringValidation() { - let actionSequence = {type: "pointer", id: "some_id"}; - let actionItem = {duration: 5000, type: "pointerMove"}; - for (let d of ["a", "", "get", "Get"]) { - actionItem.origin = d; - checkErrors(/Unknown pointer-move origin/, - action.Action.fromJson, - [actionSequence, actionItem], - `actionItem.origin: ${d}`); - } - - run_next_test(); -}); - -add_test(function test_processPointerMoveActionElementOrigin() { - let actionSequence = {type: "pointer", id: "some_id"}; - let actionItem = {duration: 5000, type: "pointerMove"}; - actionItem.origin = {[element.Key]: "something"}; - let a = action.Action.fromJson(actionSequence, actionItem); - deepEqual(a.origin, actionItem.origin); - run_next_test(); -}); - -add_test(function test_processPointerMoveActionDefaultOrigin() { - let actionSequence = {type: "pointer", id: "some_id"}; - // origin left undefined - let actionItem = {duration: 5000, type: "pointerMove"}; - let a = action.Action.fromJson(actionSequence, actionItem); - deepEqual(a.origin, action.PointerOrigin.Viewport); - run_next_test(); -}); - -add_test(function test_processPointerMoveAction() { - let actionSequence = {id: "some_id", type: "pointer"}; - let actionItems = [ - { - duration: 5000, - type: "pointerMove", - origin: undefined, - x: undefined, - y: undefined, - }, - { - duration: undefined, - type: "pointerMove", - origin: {[element.Key]: "id", [element.LegacyKey]: "id"}, - x: undefined, - y: undefined, - }, - { - duration: 5000, - type: "pointerMove", - x: 0, - y: undefined, - origin: undefined, - }, - { - duration: 5000, - type: "pointerMove", - x: 1, - y: 2, - origin: undefined, - }, - ]; - for (let expected of actionItems) { - let actual = action.Action.fromJson(actionSequence, expected); - ok(actual instanceof action.Action); - equal(actual.duration, expected.duration); - equal(actual.x, expected.x); - equal(actual.y, expected.y); - - let origin = expected.origin; - if (typeof origin == "undefined") { - origin = action.PointerOrigin.Viewport; - } - deepEqual(actual.origin, origin); - - } - run_next_test(); -}); - -add_test(function test_computePointerDestinationViewport() { - let act = { type: "pointerMove", x: 100, y: 200, origin: "viewport"}; - let inputState = new action.InputState.Pointer(action.PointerType.Mouse); - // these values should not affect the outcome - inputState.x = "99"; - inputState.y = "10"; - let target = action.computePointerDestination(act, inputState); - equal(act.x, target.x); - equal(act.y, target.y); - - run_next_test(); -}); - -add_test(function test_computePointerDestinationPointer() { - let act = { type: "pointerMove", x: 100, y: 200, origin: "pointer"}; - let inputState = new action.InputState.Pointer(action.PointerType.Mouse); - inputState.x = 10; - inputState.y = 99; - let target = action.computePointerDestination(act, inputState); - equal(act.x + inputState.x, target.x); - equal(act.y + inputState.y, target.y); - - - run_next_test(); -}); - -add_test(function test_computePointerDestinationElement() { - // origin represents a web element - // using an object literal instead to test default case in computePointerDestination - let act = {type: "pointerMove", x: 100, y: 200, origin: {}}; - let inputState = new action.InputState.Pointer(action.PointerType.Mouse); - let elementCenter = {x: 10, y: 99}; - let target = action.computePointerDestination(act, inputState, elementCenter); - equal(act.x + elementCenter.x, target.x); - equal(act.y + elementCenter.y, target.y); - - Assert.throws( - () => action.computePointerDestination(act, inputState, {a: 1}), - InvalidArgumentError, - "Invalid element center coordinates."); - - Assert.throws( - () => action.computePointerDestination(act, inputState, undefined), - InvalidArgumentError, - "Undefined element center coordinates."); - - run_next_test(); -}); - -add_test(function test_processPointerAction() { - let actionSequence = { - type: "pointer", - id: "some_id", - parameters: { - pointerType: "mouse" //TODO "touch" - }, - }; - let actionItems = [ - { - duration: 2000, - type: "pause", - }, - { - type: "pointerMove", - duration: 2000, - }, - { - type: "pointerUp", - button: 1, - } - ]; - for (let expected of actionItems) { - let actual = action.Action.fromJson(actionSequence, expected); - equal(actual.type, actionSequence.type); - equal(actual.subtype, expected.type); - equal(actual.id, actionSequence.id); - if (expected.type === "pointerUp") { - equal(actual.button, expected.button); - } else { - equal(actual.duration, expected.duration); - } - if (expected.type !== "pause") { - equal(actual.pointerType, actionSequence.parameters.pointerType); - } - } - - run_next_test(); -}); - -add_test(function test_processPauseAction() { - let actionItem = {type: "pause", duration: 5000}; - let actionSequence = {id: "some_id"}; - for (let type of ["none", "key", "pointer"]) { - actionSequence.type = type; - let act = action.Action.fromJson(actionSequence, actionItem); - ok(act instanceof action.Action); - equal(act.type, type); - equal(act.subtype, actionItem.type); - equal(act.id, actionSequence.id); - equal(act.duration, actionItem.duration); - } - actionItem.duration = undefined; - let act = action.Action.fromJson(actionSequence, actionItem); - equal(act.duration, actionItem.duration); - - run_next_test(); -}); - -add_test(function test_processActionSubtypeValidation() { - let actionItem = {type: "dancing"}; - let actionSequence = {id: "some_id"}; - let check = function (regex) { - let message = `type: ${actionSequence.type}, subtype: ${actionItem.type}`; - checkErrors(regex, action.Action.fromJson, [actionSequence, actionItem], message); - }; - for (let type of ["none", "key", "pointer"]) { - actionSequence.type = type; - check(new RegExp(`Unknown subtype for ${type} action`)); - } - run_next_test(); -}); - -add_test(function test_processKeyActionUpDown() { - let actionSequence = {type: "key", id: "some_id"}; - let actionItem = {type: "keyDown"}; - - for (let v of [-1, undefined, [], ["a"], {length: 1}, null]) { - actionItem.value = v; - let message = `actionItem.value: (${getTypeString(v)})`; - Assert.throws(() => action.Action.fromJson(actionSequence, actionItem), - InvalidArgumentError, message); - Assert.throws(() => action.Action.fromJson(actionSequence, actionItem), - /Expected 'value' to be a string that represents single code point/, message); - } - - actionItem.value = "\uE004"; - let act = action.Action.fromJson(actionSequence, actionItem); - ok(act instanceof action.Action); - equal(act.type, actionSequence.type); - equal(act.subtype, actionItem.type); - equal(act.id, actionSequence.id); - equal(act.value, actionItem.value); - - run_next_test(); -}); - -add_test(function test_processInputSourceActionSequenceValidation() { - let actionSequence = {type: "swim", id: "some id"}; - let check = (message, regex) => checkErrors( - regex, action.Sequence.fromJson, [actionSequence], message); - check(`actionSequence.type: ${actionSequence.type}`, /Unknown action type/); - action.inputStateMap.clear(); - - actionSequence.type = "none"; - actionSequence.id = -1; - check(`actionSequence.id: ${getTypeString(actionSequence.id)}`, - /Expected 'id' to be a string/); - action.inputStateMap.clear(); - - actionSequence.id = undefined; - check(`actionSequence.id: ${getTypeString(actionSequence.id)}`, - /Expected 'id' to be defined/); - action.inputStateMap.clear(); - - actionSequence.id = "some_id"; - actionSequence.actions = -1; - check(`actionSequence.actions: ${getTypeString(actionSequence.actions)}`, - /Expected 'actionSequence.actions' to be an Array/); - action.inputStateMap.clear(); - - run_next_test(); -}); - -add_test(function test_processInputSourceActionSequence() { - let actionItem = { type: "pause", duration: 5}; - let actionSequence = { - type: "none", - id: "some id", - actions: [actionItem], - }; - let expectedAction = new action.Action(actionSequence.id, "none", actionItem.type); - expectedAction.duration = actionItem.duration; - let actions = action.Sequence.fromJson(actionSequence); - equal(actions.length, 1); - deepEqual(actions[0], expectedAction); - action.inputStateMap.clear(); - run_next_test(); -}); - -add_test(function test_processInputSourceActionSequencePointer() { - let actionItem = {type: "pointerDown", button: 1}; - let actionSequence = { - type: "pointer", - id: "9", - actions: [actionItem], - parameters: { - pointerType: "mouse" // TODO "pen" - }, - }; - let expectedAction = new action.Action( - actionSequence.id, actionSequence.type, actionItem.type); - expectedAction.pointerType = actionSequence.parameters.pointerType; - expectedAction.button = actionItem.button; - let actions = action.Sequence.fromJson(actionSequence); - equal(actions.length, 1); - deepEqual(actions[0], expectedAction); - action.inputStateMap.clear(); - run_next_test(); -}); - -add_test(function test_processInputSourceActionSequenceKey() { - let actionItem = {type: "keyUp", value: "a"}; - let actionSequence = { - type: "key", - id: "9", - actions: [actionItem], - }; - let expectedAction = new action.Action( - actionSequence.id, actionSequence.type, actionItem.type); - expectedAction.value = actionItem.value; - let actions = action.Sequence.fromJson(actionSequence); - equal(actions.length, 1); - deepEqual(actions[0], expectedAction); - action.inputStateMap.clear(); - run_next_test(); -}); - - -add_test(function test_processInputSourceActionSequenceInputStateMap() { - let id = "1"; - let actionItem = {type: "pause", duration: 5000}; - let actionSequence = { - type: "key", - id: id, - actions: [actionItem], - }; - let wrongInputState = new action.InputState.Null(); - action.inputStateMap.set(actionSequence.id, wrongInputState); - checkErrors(/to be mapped to/, action.Sequence.fromJson, [actionSequence], - `${actionSequence.type} using ${wrongInputState}`); - action.inputStateMap.clear(); - let rightInputState = new action.InputState.Key(); - action.inputStateMap.set(id, rightInputState); - let acts = action.Sequence.fromJson(actionSequence); - equal(acts.length, 1); - action.inputStateMap.clear(); - run_next_test(); -}); - -add_test(function test_processPointerActionInputStateMap() { - let actionItem = {type: "pointerDown"}; - let id = "1"; - let parameters = {pointerType: "mouse"}; - let a = new action.Action(id, "pointer", actionItem.type); - let wrongInputState = new action.InputState.Key(); - action.inputStateMap.set(id, wrongInputState); - checkErrors( - /to be mapped to InputState whose type is/, action.processPointerAction, - [id, parameters, a], - `type "pointer" with ${wrongInputState.type} in inputState`); - action.inputStateMap.clear(); - - // TODO - uncomment once pen is supported - //wrongInputState = new action.InputState.Pointer("pen"); - //action.inputStateMap.set(id, wrongInputState); - //checkErrors( - // /to be mapped to InputState whose subtype is/, action.processPointerAction, - // [id, parameters, a], - // `subtype ${parameters.pointerType} with ${wrongInputState.subtype} in inputState`); - //action.inputStateMap.clear(); - - let rightInputState = new action.InputState.Pointer("mouse"); - action.inputStateMap.set(id, rightInputState); - action.processPointerAction(id, parameters, a); - action.inputStateMap.clear(); - run_next_test(); -}); - -add_test(function test_createInputState() { - for (let kind in action.InputState) { - let state; - if (kind == "Pointer") { - state = new action.InputState[kind]("mouse"); - } else { - state = new action.InputState[kind](); - } - ok(state); - if (kind === "Null") { - equal(state.type, "none"); - } else { - equal(state.type, kind.toLowerCase()); - } - } - Assert.throws(() => new action.InputState.Pointer(), InvalidArgumentError, - "Missing InputState.Pointer constructor arg"); - Assert.throws(() => new action.InputState.Pointer("foo"), InvalidArgumentError, - "Invalid InputState.Pointer constructor arg"); - run_next_test(); -}); - -add_test(function test_extractActionChainValidation() { - for (let actions of [-1, "a", undefined, null]) { - let message = `actions: ${getTypeString(actions)}`; - Assert.throws(() => action.Chain.fromJson(actions), - InvalidArgumentError, message); - Assert.throws(() => action.Chain.fromJson(actions), - /Expected 'actions' to be an Array/, message); - } - run_next_test(); -}); - -add_test(function test_extractActionChainEmpty() { - deepEqual(action.Chain.fromJson([]), []); - run_next_test(); -}); - -add_test(function test_extractActionChain_oneTickOneInput() { - let actionItem = {type: "pause", duration: 5000}; - let actionSequence = { - type: "none", - id: "some id", - actions: [actionItem], - }; - let expectedAction = new action.Action(actionSequence.id, "none", actionItem.type); - expectedAction.duration = actionItem.duration; - let actionsByTick = action.Chain.fromJson([actionSequence]); - equal(1, actionsByTick.length); - equal(1, actionsByTick[0].length); - deepEqual(actionsByTick, [[expectedAction]]); - action.inputStateMap.clear(); - run_next_test(); -}); - -add_test(function test_extractActionChain_twoAndThreeTicks() { - let mouseActionItems = [ - { - type: "pointerDown", - button: 2, - }, - { - type: "pointerUp", - button: 2, - }, - ]; - let mouseActionSequence = { - type: "pointer", - id: "7", - actions: mouseActionItems, - parameters: { - pointerType: "mouse" //TODO "touch" - }, - }; - let keyActionItems = [ - { - type: "keyDown", - value: "a", - }, - { - type: "pause", - duration: 4, - }, - { - type: "keyUp", - value: "a", - }, - ]; - let keyActionSequence = { - type: "key", - id: "1", - actions: keyActionItems, - }; - let actionsByTick = action.Chain.fromJson([keyActionSequence, mouseActionSequence]); - // number of ticks is same as longest action sequence - equal(keyActionItems.length, actionsByTick.length); - equal(2, actionsByTick[0].length); - equal(2, actionsByTick[1].length); - equal(1, actionsByTick[2].length); - let expectedAction = new action.Action(keyActionSequence.id, "key", keyActionItems[2].type); - expectedAction.value = keyActionItems[2].value; - deepEqual(actionsByTick[2][0], expectedAction); - action.inputStateMap.clear(); - - // one empty action sequence - actionsByTick = action.Chain.fromJson( - [keyActionSequence, {type: "none", id: "some", actions: []}]); - equal(keyActionItems.length, actionsByTick.length); - equal(1, actionsByTick[0].length); - action.inputStateMap.clear(); - run_next_test(); -}); - -add_test(function test_computeTickDuration() { - let expected = 8000; - let tickActions = [ - {type: "none", subtype: "pause", duration: 5000}, - {type: "key", subtype: "pause", duration: 1000}, - {type: "pointer", subtype: "pointerMove", duration: 6000}, - // invalid because keyDown should not have duration, so duration should be ignored. - {type: "key", subtype: "keyDown", duration: 100000}, - {type: "pointer", subtype: "pause", duration: expected}, - {type: "pointer", subtype: "pointerUp"}, - ]; - equal(expected, action.computeTickDuration(tickActions)); - run_next_test(); -}); - -add_test(function test_computeTickDuration_empty() { - equal(0, action.computeTickDuration([])); - run_next_test(); -}); - -add_test(function test_computeTickDuration_noDurations() { - let tickActions = [ - // invalid because keyDown should not have duration, so duration should be ignored. - {type: "key", subtype: "keyDown", duration: 100000}, - // undefined duration permitted - {type: "none", subtype: "pause"}, - {type: "pointer", subtype: "pointerMove"}, - {type: "pointer", subtype: "pointerDown"}, - {type: "key", subtype: "keyUp"}, - ]; - - equal(0, action.computeTickDuration(tickActions)); - run_next_test(); -}); - - -// helpers -function getTypeString(obj) { - return Object.prototype.toString.call(obj); -}; - -function checkErrors(regex, func, args, message) { - if (typeof message == "undefined") { - message = `actionFunc: ${func.name}; args: ${args}`; - } - Assert.throws(() => func.apply(this, args), InvalidArgumentError, message); - Assert.throws(() => func.apply(this, args), regex, message); -}; diff --git a/testing/marionette/test_assert.js b/testing/marionette/test_assert.js deleted file mode 100644 index c14f2852e..000000000 --- a/testing/marionette/test_assert.js +++ /dev/null @@ -1,126 +0,0 @@ -/* 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/. */ - -"use strict"; - -const {utils: Cu} = Components; - -Cu.import("chrome://marionette/content/assert.js"); -Cu.import("chrome://marionette/content/error.js"); - -add_test(function test_session() { - assert.session({sessionId: "foo"}); - for (let typ of [null, undefined, ""]) { - Assert.throws(() => assert.session({sessionId: typ}), InvalidSessionIDError); - } - - run_next_test(); -}); - -add_test(function test_platforms() { - // at least one will fail - let raised; - for (let fn of [assert.firefox, assert.fennec, assert.b2g, assert.mobile]) { - try { - fn(); - } catch (e) { - raised = e; - } - } - ok(raised instanceof UnsupportedOperationError); - - run_next_test(); -}); - -add_test(function test_defined() { - assert.defined({}); - Assert.throws(() => assert.defined(undefined), InvalidArgumentError); - - run_next_test(); -}); - -add_test(function test_number() { - assert.number(1); - assert.number(0); - assert.number(-1); - assert.number(1.2); - for (let i of ["foo", "1", {}, [], NaN, Infinity, undefined]) { - Assert.throws(() => assert.number(i), InvalidArgumentError); - } - run_next_test(); -}); - -add_test(function test_integer() { - assert.integer(1); - assert.integer(0); - assert.integer(-1); - Assert.throws(() => assert.integer("foo"), InvalidArgumentError); - Assert.throws(() => assert.integer(1.2), InvalidArgumentError); - - run_next_test(); -}); - -add_test(function test_positiveInteger() { - assert.positiveInteger(1); - assert.positiveInteger(0); - Assert.throws(() => assert.positiveInteger(-1), InvalidArgumentError); - Assert.throws(() => assert.positiveInteger("foo"), InvalidArgumentError); - - run_next_test(); -}); - -add_test(function test_boolean() { - assert.boolean(true); - assert.boolean(false); - Assert.throws(() => assert.boolean("false"), InvalidArgumentError); - Assert.throws(() => assert.boolean(undefined), InvalidArgumentError); - - run_next_test(); -}); - -add_test(function test_string() { - assert.string("foo"); - assert.string(`bar`); - Assert.throws(() => assert.string(42), InvalidArgumentError); - - run_next_test(); -}); - -add_test(function test_object() { - assert.object({}); - assert.object(new Object()); - for (let typ of [42, "foo", true, null, undefined]) { - Assert.throws(() => assert.object(typ), InvalidArgumentError); - } - - run_next_test(); -}); - -add_test(function test_in() { - assert.in("foo", {foo: 42}); - for (let typ of [{}, 42, true, null, undefined]) { - Assert.throws(() => assert.in("foo", typ), InvalidArgumentError); - } - - run_next_test(); -}); - -add_test(function test_array() { - assert.array([]); - assert.array(new Array()); - Assert.throws(() => assert.array(42), InvalidArgumentError); - Assert.throws(() => assert.array({}), InvalidArgumentError); - - run_next_test(); -}); - -add_test(function test_that() { - equal(1, assert.that(n => n + 1)(1)); - Assert.throws(() => assert.that(() => false)()); - Assert.throws(() => assert.that(val => val)(false)); - Assert.throws(() => assert.that(val => val, "foo", SessionNotCreatedError)(false), - SessionNotCreatedError); - - run_next_test(); -}); diff --git a/testing/marionette/test_element.js b/testing/marionette/test_element.js deleted file mode 100644 index f565bd8c0..000000000 --- a/testing/marionette/test_element.js +++ /dev/null @@ -1,55 +0,0 @@ -/* 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/. */ - -const {utils: Cu} = Components; - -Cu.import("chrome://marionette/content/element.js"); - -let el = { - getBoundingClientRect: function() { - return { - top: 0, - left: 0, - width: 100, - height: 100, - }; - } -}; - -add_test(function test_coordinates() { - let p = element.coordinates(el); - ok(p.hasOwnProperty("x")); - ok(p.hasOwnProperty("y")); - equal("number", typeof p.x); - equal("number", typeof p.y); - - deepEqual({x: 50, y: 50}, element.coordinates(el)); - deepEqual({x: 10, y: 10}, element.coordinates(el, 10, 10)); - deepEqual({x: -5, y: -5}, element.coordinates(el, -5, -5)); - - Assert.throws(() => element.coordinates(null)); - - Assert.throws(() => element.coordinates(el, "string", undefined)); - Assert.throws(() => element.coordinates(el, undefined, "string")); - Assert.throws(() => element.coordinates(el, "string", "string")); - Assert.throws(() => element.coordinates(el, {}, undefined)); - Assert.throws(() => element.coordinates(el, undefined, {})); - Assert.throws(() => element.coordinates(el, {}, {})); - Assert.throws(() => element.coordinates(el, [], undefined)); - Assert.throws(() => element.coordinates(el, undefined, [])); - Assert.throws(() => element.coordinates(el, [], [])); - - run_next_test(); -}); - -add_test(function test_isWebElementReference() { - strictEqual(element.isWebElementReference({[element.Key]: "some_id"}), true); - strictEqual(element.isWebElementReference({[element.LegacyKey]: "some_id"}), true); - strictEqual(element.isWebElementReference( - {[element.LegacyKey]: "some_id", [element.Key]: "2"}), true); - strictEqual(element.isWebElementReference({}), false); - strictEqual(element.isWebElementReference({"key": "blah"}), false); - - run_next_test(); -}); diff --git a/testing/marionette/test_error.js b/testing/marionette/test_error.js deleted file mode 100644 index a905f02f0..000000000 --- a/testing/marionette/test_error.js +++ /dev/null @@ -1,431 +0,0 @@ -/* 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/. */ - -const {utils: Cu} = Components; - -Cu.import("chrome://marionette/content/error.js"); - -function notok(condition) { - ok(!(condition)); -} - -add_test(function test_isError() { - notok(error.isError(null)); - notok(error.isError([])); - notok(error.isError(new Date())); - - ok(error.isError(new Components.Exception())); - ok(error.isError(new Error())); - ok(error.isError(new EvalError())); - ok(error.isError(new InternalError())); - ok(error.isError(new RangeError())); - ok(error.isError(new ReferenceError())); - ok(error.isError(new SyntaxError())); - ok(error.isError(new TypeError())); - ok(error.isError(new URIError())); - ok(error.isError(new WebDriverError())); - ok(error.isError(new InvalidArgumentError())); - - run_next_test(); -}); - -add_test(function test_isWebDriverError() { - notok(error.isWebDriverError(new Components.Exception())); - notok(error.isWebDriverError(new Error())); - notok(error.isWebDriverError(new EvalError())); - notok(error.isWebDriverError(new InternalError())); - notok(error.isWebDriverError(new RangeError())); - notok(error.isWebDriverError(new ReferenceError())); - notok(error.isWebDriverError(new SyntaxError())); - notok(error.isWebDriverError(new TypeError())); - notok(error.isWebDriverError(new URIError())); - - ok(error.isWebDriverError(new WebDriverError())); - ok(error.isWebDriverError(new InvalidArgumentError())); - ok(error.isWebDriverError(new JavaScriptError())); - - run_next_test(); -}); - -add_test(function test_wrap() { - // webdriver-derived errors should not be wrapped - equal(error.wrap(new WebDriverError()).name, "WebDriverError"); - ok(error.wrap(new WebDriverError()) instanceof WebDriverError); - equal(error.wrap(new InvalidArgumentError()).name, "InvalidArgumentError"); - ok(error.wrap(new InvalidArgumentError()) instanceof WebDriverError); - ok(error.wrap(new InvalidArgumentError()) instanceof InvalidArgumentError); - - // JS errors should be wrapped in WebDriverError - equal(error.wrap(new Error()).name, "WebDriverError"); - ok(error.wrap(new Error()) instanceof WebDriverError); - equal(error.wrap(new EvalError()).name, "WebDriverError"); - equal(error.wrap(new InternalError()).name, "WebDriverError"); - equal(error.wrap(new RangeError()).name, "WebDriverError"); - equal(error.wrap(new ReferenceError()).name, "WebDriverError"); - equal(error.wrap(new SyntaxError()).name, "WebDriverError"); - equal(error.wrap(new TypeError()).name, "WebDriverError"); - equal(error.wrap(new URIError()).name, "WebDriverError"); - - // wrapped JS errors should retain their type - // as part of the message field - equal(error.wrap(new WebDriverError("foo")).message, "foo"); - equal(error.wrap(new TypeError("foo")).message, "TypeError: foo"); - - run_next_test(); -}); - -add_test(function test_stringify() { - equal("<unprintable error>", error.stringify()); - equal("<unprintable error>", error.stringify("foo")); - equal("[object Object]", error.stringify({})); - equal("[object Object]\nfoo", error.stringify({stack: "foo"})); - equal("Error: foo", error.stringify(new Error("foo")).split("\n")[0]); - equal("WebDriverError: foo", - error.stringify(new WebDriverError("foo")).split("\n")[0]); - equal("InvalidArgumentError: foo", - error.stringify(new InvalidArgumentError("foo")).split("\n")[0]); - - run_next_test(); -}); - -add_test(function test_pprint() { - equal('[object Object] {"foo":"bar"}', error.pprint`${{foo: "bar"}}`); - - equal("[object Number] 42", error.pprint`${42}`); - equal("[object Boolean] true", error.pprint`${true}`); - equal("[object Undefined] undefined", error.pprint`${undefined}`); - equal("[object Null] null", error.pprint`${null}`); - - let complexObj = {toJSON: () => "foo"}; - equal('[object Object] "foo"', error.pprint`${complexObj}`); - - let cyclic = {}; - cyclic.me = cyclic; - equal("[object Object] <cyclic object value>", error.pprint`${cyclic}`); - - let el = { - nodeType: 1, - localName: "input", - id: "foo", - classList: {length: 1}, - className: "bar baz", - }; - equal('<input id="foo" class="bar baz">', error.pprint`${el}`); - - run_next_test(); -}); - -add_test(function test_toJSON() { - let e0 = new WebDriverError(); - let e0s = e0.toJSON(); - equal(e0s.error, "webdriver error"); - equal(e0s.message, ""); - equal(e0s.stacktrace, e0.stack); - - let e1 = new WebDriverError("a"); - let e1s = e1.toJSON(); - equal(e1s.message, e1.message); - equal(e1s.stacktrace, e1.stack); - - let e2 = new JavaScriptError("first", "second", "third", "fourth"); - let e2s = e2.toJSON(); - equal(e2.status, e2s.error); - equal(e2.message, e2s.message); - ok(e2s.stacktrace.match(/second/)); - ok(e2s.stacktrace.match(/third/)); - ok(e2s.stacktrace.match(/fourth/)); - - run_next_test(); -}); - -add_test(function test_fromJSON() { - Assert.throws(() => WebDriverError.fromJSON({error: "foo"}), - /Not of WebDriverError descent/); - Assert.throws(() => WebDriverError.fromJSON({error: "Error"}), - /Not of WebDriverError descent/); - Assert.throws(() => WebDriverError.fromJSON({}), - /Undeserialisable error type/); - Assert.throws(() => WebDriverError.fromJSON(undefined), - /TypeError/); - - // stacks will be different - let e1 = new WebDriverError("1"); - let e1r = WebDriverError.fromJSON({error: "webdriver error", message: "1"}); - ok(e1r instanceof WebDriverError); - equal(e1r.name, e1.name); - equal(e1r.status, e1.status); - equal(e1r.message, e1.message); - - // stacks will be different - let e2 = new InvalidArgumentError("2"); - let e2r = WebDriverError.fromJSON({error: "invalid argument", message: "2"}); - ok(e2r instanceof WebDriverError); - ok(e2r instanceof InvalidArgumentError); - equal(e2r.name, e2.name); - equal(e2r.status, e2.status); - equal(e2r.message, e2.message); - - // test stacks - let e3j = {error: "no such element", message: "3", stacktrace: "4"}; - let e3r = WebDriverError.fromJSON(e3j); - ok(e3r instanceof WebDriverError); - ok(e3r instanceof NoSuchElementError); - equal(e3r.name, "NoSuchElementError"); - equal(e3r.status, e3j.error); - equal(e3r.message, e3j.message); - equal(e3r.stack, e3j.stacktrace); - - // parity with toJSON - let e4 = new JavaScriptError("first", "second", "third", "fourth"); - let e4s = e4.toJSON(); - deepEqual(e4, WebDriverError.fromJSON(e4s)); - - run_next_test(); -}); - -add_test(function test_WebDriverError() { - let err = new WebDriverError("foo"); - equal("WebDriverError", err.name); - equal("foo", err.message); - equal("webdriver error", err.status); - ok(err instanceof WebDriverError); - - run_next_test(); -}); - -add_test(function test_ElementClickInterceptedError() { - let otherEl = { - nodeType: 1, - localName: "a", - classList: [], - }; - let obscuredEl = { - nodeType: 1, - localName: "b", - classList: [], - ownerDocument: { - elementFromPoint: function (x, y) { - return otherEl; - }, - }, - style: { - pointerEvents: "auto", - } - }; - - let err1 = new ElementClickInterceptedError(obscuredEl, {x: 1, y: 2}); - equal("ElementClickInterceptedError", err1.name); - equal("Element <b> is not clickable at point (1,2) " + - "because another element <a> obscures it", - err1.message); - equal("element click intercepted", err1.status); - ok(err1 instanceof WebDriverError); - - obscuredEl.style.pointerEvents = "none"; - let err2 = new ElementClickInterceptedError(obscuredEl, {x: 1, y: 2}); - equal("Element <b> is not clickable at point (1,2) " + - "because it does not have pointer events enabled, " + - "and element <a> would receive the click instead", - err2.message); - - run_next_test(); -}); - -add_test(function test_ElementNotAccessibleError() { - let err = new ElementNotAccessibleError("foo"); - equal("ElementNotAccessibleError", err.name); - equal("foo", err.message); - equal("element not accessible", err.status); - ok(err instanceof WebDriverError); - - run_next_test(); -}); - -add_test(function test_ElementNotInteractableError() { - let err = new ElementNotInteractableError("foo"); - equal("ElementNotInteractableError", err.name); - equal("foo", err.message); - equal("element not interactable", err.status); - ok(err instanceof WebDriverError); - - run_next_test(); -}); - -add_test(function test_InvalidArgumentError() { - let err = new InvalidArgumentError("foo"); - equal("InvalidArgumentError", err.name); - equal("foo", err.message); - equal("invalid argument", err.status); - ok(err instanceof WebDriverError); - - run_next_test(); -}); - -add_test(function test_InvalidElementStateError() { - let err = new InvalidElementStateError("foo"); - equal("InvalidElementStateError", err.name); - equal("foo", err.message); - equal("invalid element state", err.status); - ok(err instanceof WebDriverError); - - run_next_test(); -}); - -add_test(function test_InvalidSelectorError() { - let err = new InvalidSelectorError("foo"); - equal("InvalidSelectorError", err.name); - equal("foo", err.message); - equal("invalid selector", err.status); - ok(err instanceof WebDriverError); - - run_next_test(); -}); - -add_test(function test_InvalidSessionIDError() { - let err = new InvalidSessionIDError("foo"); - equal("InvalidSessionIDError", err.name); - equal("foo", err.message); - equal("invalid session id", err.status); - ok(err instanceof WebDriverError); - - run_next_test(); -}); - -add_test(function test_JavaScriptError() { - let err = new JavaScriptError("foo"); - equal("JavaScriptError", err.name); - equal("foo", err.message); - equal("javascript error", err.status); - ok(err instanceof WebDriverError); - - equal("undefined", new JavaScriptError(undefined).message); - // TODO(ato): Bug 1240550 - //equal("funcname @file", new JavaScriptError("message", "funcname", "file").stack); - equal("funcname @file, line line", - new JavaScriptError("message", "funcname", "file", "line").stack); - - // TODO(ato): More exhaustive tests for JS stack computation - - run_next_test(); -}); - -add_test(function test_NoAlertOpenError() { - let err = new NoAlertOpenError("foo"); - equal("NoAlertOpenError", err.name); - equal("foo", err.message); - equal("no such alert", err.status); - ok(err instanceof WebDriverError); - - run_next_test(); -}); - -add_test(function test_NoSuchElementError() { - let err = new NoSuchElementError("foo"); - equal("NoSuchElementError", err.name); - equal("foo", err.message); - equal("no such element", err.status); - ok(err instanceof WebDriverError); - - run_next_test(); -}); - -add_test(function test_NoSuchFrameError() { - let err = new NoSuchFrameError("foo"); - equal("NoSuchFrameError", err.name); - equal("foo", err.message); - equal("no such frame", err.status); - ok(err instanceof WebDriverError); - - run_next_test(); -}); - -add_test(function test_NoSuchWindowError() { - let err = new NoSuchWindowError("foo"); - equal("NoSuchWindowError", err.name); - equal("foo", err.message); - equal("no such window", err.status); - ok(err instanceof WebDriverError); - - run_next_test(); -}); - -add_test(function test_ScriptTimeoutError() { - let err = new ScriptTimeoutError("foo"); - equal("ScriptTimeoutError", err.name); - equal("foo", err.message); - equal("script timeout", err.status); - ok(err instanceof WebDriverError); - - run_next_test(); -}); - -add_test(function test_SessionNotCreatedError() { - let err = new SessionNotCreatedError("foo"); - equal("SessionNotCreatedError", err.name); - equal("foo", err.message); - equal("session not created", err.status); - ok(err instanceof WebDriverError); - - run_next_test(); -}); - -add_test(function test_StaleElementReferenceError() { - let err = new StaleElementReferenceError("foo"); - equal("StaleElementReferenceError", err.name); - equal("foo", err.message); - equal("stale element reference", err.status); - ok(err instanceof WebDriverError); - - run_next_test(); -}); - -add_test(function test_TimeoutError() { - let err = new TimeoutError("foo"); - equal("TimeoutError", err.name); - equal("foo", err.message); - equal("timeout", err.status); - ok(err instanceof WebDriverError); - - run_next_test(); -}); - -add_test(function test_UnableToSetCookieError() { - let err = new UnableToSetCookieError("foo"); - equal("UnableToSetCookieError", err.name); - equal("foo", err.message); - equal("unable to set cookie", err.status); - ok(err instanceof WebDriverError); - - run_next_test(); -}); - -add_test(function test_UnknownCommandError() { - let err = new UnknownCommandError("foo"); - equal("UnknownCommandError", err.name); - equal("foo", err.message); - equal("unknown command", err.status); - ok(err instanceof WebDriverError); - - run_next_test(); -}); - -add_test(function test_UnknownError() { - let err = new UnknownError("foo"); - equal("UnknownError", err.name); - equal("foo", err.message); - equal("unknown error", err.status); - ok(err instanceof WebDriverError); - - run_next_test(); -}); - -add_test(function test_UnsupportedOperationError() { - let err = new UnsupportedOperationError("foo"); - equal("UnsupportedOperationError", err.name); - equal("foo", err.message); - equal("unsupported operation", err.status); - ok(err instanceof WebDriverError); - - run_next_test(); -}); diff --git a/testing/marionette/test_message.js b/testing/marionette/test_message.js deleted file mode 100644 index 2ef20a454..000000000 --- a/testing/marionette/test_message.js +++ /dev/null @@ -1,204 +0,0 @@ -/* 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/. */ - -const {utils: Cu} = Components; - -Cu.import("chrome://marionette/content/error.js"); -Cu.import("chrome://marionette/content/message.js"); - -add_test(function test_MessageOrigin() { - equal(0, MessageOrigin.Client); - equal(1, MessageOrigin.Server); - - run_next_test(); -}); - -add_test(function test_Message_fromMsg() { - let cmd = new Command(4, "foo"); - let resp = new Response(5, () => {}); - - ok(Message.fromMsg(cmd.toMsg()) instanceof Command); - ok(Message.fromMsg(resp.toMsg()) instanceof Response); - Assert.throws(() => Message.fromMsg([3, 4, 5, 6]), - /Unrecognised message type in packet/); - - run_next_test(); -}); - -add_test(function test_Command() { - let cmd = new Command(42, "foo", {bar: "baz"}); - equal(42, cmd.id); - equal("foo", cmd.name); - deepEqual({bar: "baz"}, cmd.parameters); - equal(null, cmd.onerror); - equal(null, cmd.onresult); - equal(MessageOrigin.Client, cmd.origin); - equal(false, cmd.sent); - - run_next_test(); -}); - -add_test(function test_Command_onresponse() { - let onerrorOk = false; - let onresultOk = false; - - let cmd = new Command(); - cmd.onerror = () => onerrorOk = true; - cmd.onresult = () => onresultOk = true; - - let errorResp = new Response(); - errorResp.error = new WebDriverError("foo"); - - let bodyResp = new Response(); - bodyResp.body = "bar"; - - cmd.onresponse(errorResp); - equal(true, onerrorOk); - equal(false, onresultOk); - - cmd.onresponse(bodyResp); - equal(true, onresultOk); - - run_next_test(); -}); - -add_test(function test_Command_fromMsg() { - let cmd = new Command(42, "bar", {bar: "baz"}); - let msg = cmd.toMsg(); - - equal(Command.TYPE, msg[0]); - equal(cmd.id, msg[1]); - equal(cmd.name, msg[2]); - equal(cmd.parameters, msg[3]); - - run_next_test(); -}); - -add_test(function test_Command_toString() { - let cmd = new Command(42, "foo", {bar: "baz"}); - equal(`Command {id: ${cmd.id}, ` + - `name: ${JSON.stringify(cmd.name)}, ` + - `parameters: ${JSON.stringify(cmd.parameters)}}`, - cmd.toString()); - - run_next_test(); -}); - -add_test(function test_Command_fromMsg() { - let c1 = new Command(42, "foo", {bar: "baz"}); - - let msg = c1.toMsg(); - let c2 = Command.fromMsg(msg); - - equal(c1.id, c2.id); - equal(c1.name, c2.name); - equal(c1.parameters, c2.parameters); - - run_next_test(); -}); - -add_test(function test_Command_TYPE() { - equal(0, Command.TYPE); - run_next_test(); -}); - -add_test(function test_Response() { - let handler = () => run_next_test(); - - let resp = new Response(42, handler); - equal(42, resp.id); - equal(null, resp.error); - ok("origin" in resp); - equal(MessageOrigin.Server, resp.origin); - equal(false, resp.sent); - equal(handler, resp.respHandler_); - - run_next_test(); -}); - -add_test(function test_Response_sendConditionally() { - let fired = false; - let resp = new Response(42, () => fired = true); - resp.sendConditionally(r => false); - equal(false, resp.sent); - equal(false, fired); - resp.sendConditionally(r => true); - equal(true, resp.sent); - equal(true, fired); - - run_next_test(); -}); - -add_test(function test_Response_send() { - let fired = false; - let resp = new Response(42, () => fired = true); - resp.send(); - equal(true, resp.sent); - equal(true, fired); - - run_next_test(); -}); - -add_test(function test_Response_sendError() { - let err = new WebDriverError(); - let resp = new Response(42, r => { - equal(err.toJSON().error, r.error.error); - equal(null, r.body); - equal(false, r.sent); - }); - - resp.sendError(err); - equal(true, resp.sent); - Assert.throws(() => resp.send(), /already been sent/); - - resp.sent = false; - Assert.throws(() => resp.sendError(new Error())); - - run_next_test(); -}); - -add_test(function test_Response_toMsg() { - let resp = new Response(42); - let msg = resp.toMsg(); - - equal(Response.TYPE, msg[0]); - equal(resp.id, msg[1]); - equal(resp.error, msg[2]); - equal(resp.body, msg[3]); - - run_next_test(); -}); - -add_test(function test_Response_toString() { - let resp = new Response(42); - resp.error = "foo"; - resp.body = "bar"; - - equal(`Response {id: ${resp.id}, ` + - `error: ${JSON.stringify(resp.error)}, ` + - `body: ${JSON.stringify(resp.body)}}`, - resp.toString()); - - run_next_test(); -}); - -add_test(function test_Response_fromMsg() { - let r1 = new Response(42); - r1.error = "foo"; - r1.body = "bar"; - - let msg = r1.toMsg(); - let r2 = Response.fromMsg(msg); - - equal(r1.id, r2.id); - equal(r1.error, r2.error); - equal(r1.body, r2.body); - - run_next_test(); -}); - -add_test(function test_Response_TYPE() { - equal(1, Response.TYPE); - run_next_test(); -}); diff --git a/testing/marionette/test_navigate.js b/testing/marionette/test_navigate.js deleted file mode 100644 index 6fe29ec65..000000000 --- a/testing/marionette/test_navigate.js +++ /dev/null @@ -1,67 +0,0 @@ -/* 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/. */ - -const {utils: Cu} = Components; - -Cu.importGlobalProperties(["URL"]); - -Cu.import("chrome://marionette/content/navigate.js"); - -add_test(function test_isLoadEventExpected() { - Assert.throws(() => navigate.isLoadEventExpected(undefined), - /Expected at least one URL/); - - equal(true, navigate.isLoadEventExpected("http://a/")); - equal(true, navigate.isLoadEventExpected("http://a/", "http://a/")); - equal(true, navigate.isLoadEventExpected("http://a/", "http://a/b")); - equal(true, navigate.isLoadEventExpected("http://a/", "http://b")); - equal(true, navigate.isLoadEventExpected("http://a/", "data:text/html;charset=utf-8,foo")); - equal(true, navigate.isLoadEventExpected("about:blank", "http://a/")); - equal(true, navigate.isLoadEventExpected("http://a/", "about:blank")); - equal(true, navigate.isLoadEventExpected("http://a/", "https://a/")); - - equal(false, navigate.isLoadEventExpected("http://a/", "javascript:whatever")); - equal(false, navigate.isLoadEventExpected("http://a/", "http://a/#")); - equal(false, navigate.isLoadEventExpected("http://a/", "http://a/#b")); - equal(false, navigate.isLoadEventExpected("http://a/#b", "http://a/#b")); - equal(false, navigate.isLoadEventExpected("http://a/#b", "http://a/#c")); - equal(false, navigate.isLoadEventExpected("data:text/html;charset=utf-8,foo", "data:text/html;charset=utf-8,foo#bar")); - equal(false, navigate.isLoadEventExpected("data:text/html;charset=utf-8,foo", "data:text/html;charset=utf-8,foo#")); - - run_next_test(); -}); - -add_test(function test_IdempotentURL() { - Assert.throws(() => new navigate.IdempotentURL(undefined)); - Assert.throws(() => new navigate.IdempotentURL(true)); - Assert.throws(() => new navigate.IdempotentURL({})); - Assert.throws(() => new navigate.IdempotentURL(42)); - - // propagated URL properties - let u1 = new URL("http://a/b"); - let u2 = new navigate.IdempotentURL(u1); - equal(u1.host, u2.host); - equal(u1.hostname, u2.hostname); - equal(u1.href, u2.href); - equal(u1.origin, u2.origin); - equal(u1.password, u2.password); - equal(u1.port, u2.port); - equal(u1.protocol, u2.protocol); - equal(u1.search, u2.search); - equal(u1.username, u2.username); - - // specialisations - equal("#b", new navigate.IdempotentURL("http://a/#b").hash); - equal("#", new navigate.IdempotentURL("http://a/#").hash); - equal("", new navigate.IdempotentURL("http://a/").hash); - equal("#bar", new navigate.IdempotentURL("data:text/html;charset=utf-8,foo#bar").hash); - equal("#", new navigate.IdempotentURL("data:text/html;charset=utf-8,foo#").hash); - equal("", new navigate.IdempotentURL("data:text/html;charset=utf-8,foo").hash); - - equal("/", new navigate.IdempotentURL("http://a/").pathname); - equal("/", new navigate.IdempotentURL("http://a/#b").pathname); - equal("text/html;charset=utf-8,foo", new navigate.IdempotentURL("data:text/html;charset=utf-8,foo#bar").pathname); - - run_next_test(); -}); diff --git a/testing/marionette/test_session.js b/testing/marionette/test_session.js deleted file mode 100644 index ef45573a7..000000000 --- a/testing/marionette/test_session.js +++ /dev/null @@ -1,370 +0,0 @@ -/* 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/. */ - -"use strict"; - -const {utils: Cu} = Components; - -Cu.import("resource://gre/modules/Preferences.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -Cu.import("chrome://marionette/content/error.js"); -Cu.import("chrome://marionette/content/session.js"); - -add_test(function test_Timeouts_ctor() { - let ts = new session.Timeouts(); - equal(ts.implicit, 0); - equal(ts.pageLoad, 300000); - equal(ts.script, 30000); - - run_next_test(); -}); - -add_test(function test_Timeouts_toString() { - equal(new session.Timeouts().toString(), "[object session.Timeouts]"); - - run_next_test(); -}); - -add_test(function test_Timeouts_toJSON() { - let ts = new session.Timeouts(); - deepEqual(ts.toJSON(), {"implicit": 0, "page load": 300000, "script": 30000}); - - run_next_test(); -}); - -add_test(function test_Timeouts_fromJSON() { - let json = { - "implicit": 10, - "page load": 20, - "script": 30, - }; - let ts = session.Timeouts.fromJSON(json); - equal(ts.implicit, json["implicit"]); - equal(ts.pageLoad, json["page load"]); - equal(ts.script, json["script"]); - - run_next_test(); -}); - -add_test(function test_PageLoadStrategy() { - equal(session.PageLoadStrategy.None, "none"); - equal(session.PageLoadStrategy.Eager, "eager"); - equal(session.PageLoadStrategy.Normal, "normal"); - - run_next_test(); -}); - -add_test(function test_Proxy_ctor() { - let p = new session.Proxy(); - let props = [ - "proxyType", - "httpProxy", - "httpProxyPort", - "sslProxy", - "sslProxyPort", - "ftpProxy", - "ftpProxyPort", - "socksProxy", - "socksProxyPort", - "socksVersion", - "proxyAutoconfigUrl", - ]; - for (let prop of props) { - ok(prop in p, `${prop} in ${JSON.stringify(props)}`); - equal(p[prop], null); - } - - run_next_test(); -}); - -add_test(function test_Proxy_init() { - let p = new session.Proxy(); - - // no changed made, and 5 (system) is default - equal(p.init(), false); - equal(Preferences.get("network.proxy.type"), 5); - - // pac - p.proxyType = "pac"; - p.proxyAutoconfigUrl = "http://localhost:1234"; - ok(p.init()); - - equal(Preferences.get("network.proxy.type"), 2); - equal(Preferences.get("network.proxy.autoconfig_url"), - "http://localhost:1234"); - - // autodetect - p = new session.Proxy(); - p.proxyType = "autodetect"; - ok(p.init()); - equal(Preferences.get("network.proxy.type"), 4); - - // system - p = new session.Proxy(); - p.proxyType = "system"; - ok(p.init()); - equal(Preferences.get("network.proxy.type"), 5); - - // noproxy - p = new session.Proxy(); - p.proxyType = "noproxy"; - ok(p.init()); - equal(Preferences.get("network.proxy.type"), 0); - - run_next_test(); -}); - -add_test(function test_Proxy_toString() { - equal(new session.Proxy().toString(), "[object session.Proxy]"); - - run_next_test(); -}); - -add_test(function test_Proxy_toJSON() { - let p = new session.Proxy(); - deepEqual(p.toJSON(), {}); - - p = new session.Proxy(); - p.proxyType = "manual"; - deepEqual(p.toJSON(), {proxyType: "manual"}); - - run_next_test(); -}); - -add_test(function test_Proxy_fromJSON() { - deepEqual({}, session.Proxy.fromJSON(undefined).toJSON()); - deepEqual({}, session.Proxy.fromJSON(null).toJSON()); - - for (let typ of [true, 42, "foo", []]) { - Assert.throws(() => session.Proxy.fromJSON(typ), InvalidArgumentError); - } - - // must contain proxyType - Assert.throws(() => session.Proxy.fromJSON({}), InvalidArgumentError); - deepEqual({proxyType: "foo"}, - session.Proxy.fromJSON({proxyType: "foo"}).toJSON()); - - // manual - session.Proxy.fromJSON({proxyType: "manual"}); - - for (let proxy of ["httpProxy", "sslProxy", "ftpProxy", "socksProxy"]) { - let manual = {proxyType: "manual"}; - - for (let typ of [true, 42, [], {}, null]) { - manual[proxy] = typ; - Assert.throws(() => session.Proxy.fromJSON(manual), - InvalidArgumentError); - } - - manual[proxy] = "foo"; - Assert.throws(() => session.Proxy.fromJSON(manual), - InvalidArgumentError); - - for (let typ of ["bar", true, [], {}, null, undefined]) { - manual[proxy + "Port"] = typ; - Assert.throws(() => session.Proxy.fromJSON(manual), - InvalidArgumentError); - } - - manual[proxy] = "foo"; - manual[proxy + "Port"] = 1234; - - let expected = { - "proxyType": "manual", - [proxy]: "foo", - [proxy + "Port"]: 1234, - }; - - if (proxy == "socksProxy") { - manual.socksProxyVersion = 42; - expected.socksProxyVersion = 42; - } - deepEqual(expected, session.Proxy.fromJSON(manual).toJSON()); - } - - Assert.throws(() => session.Proxy.fromJSON( - {proxyType: "manual", socksProxy: "foo", socksProxyPort: 1234}), - InvalidArgumentError); - - run_next_test(); -}); - -add_test(function test_Capabilities_ctor() { - let caps = new session.Capabilities(); - ok(caps.has("browserName")); - ok(caps.has("browserVersion")); - ok(caps.has("platformName")); - ok(caps.has("platformVersion")); - equal(session.PageLoadStrategy.Normal, caps.get("pageLoadStrategy")); - equal(false, caps.get("acceptInsecureCerts")); - ok(caps.get("timeouts") instanceof session.Timeouts); - ok(caps.get("proxy") instanceof session.Proxy); - - ok(caps.has("rotatable")); - - equal(0, caps.get("specificationLevel")); - ok(caps.has("moz:processID")); - ok(caps.has("moz:profile")); - equal(false, caps.get("moz:accessibilityChecks")); - - run_next_test(); -}); - -add_test(function test_Capabilities_toString() { - equal("[object session.Capabilities]", new session.Capabilities().toString()); - - run_next_test(); -}); - -add_test(function test_Capabilities_toJSON() { - let caps = new session.Capabilities(); - let json = caps.toJSON(); - - equal(caps.get("browserName"), json.browserName); - equal(caps.get("browserVersion"), json.browserVersion); - equal(caps.get("platformName"), json.platformName); - equal(caps.get("platformVersion"), json.platformVersion); - equal(caps.get("pageLoadStrategy"), json.pageLoadStrategy); - equal(caps.get("acceptInsecureCerts"), json.acceptInsecureCerts); - deepEqual(caps.get("timeouts").toJSON(), json.timeouts); - equal(undefined, json.proxy); - - equal(caps.get("rotatable"), json.rotatable); - - equal(caps.get("specificationLevel"), json.specificationLevel); - equal(caps.get("moz:processID"), json["moz:processID"]); - equal(caps.get("moz:profile"), json["moz:profile"]); - equal(caps.get("moz:accessibilityChecks"), json["moz:accessibilityChecks"]); - - run_next_test(); -}); - -add_test(function test_Capabilities_fromJSON() { - const {fromJSON} = session.Capabilities; - - // plain - for (let typ of [{}, null, undefined]) { - ok(fromJSON(typ, {merge: true}).has("browserName")); - ok(fromJSON(typ, {merge: false}).has("browserName")); - } - for (let typ of [true, 42, "foo", []]) { - Assert.throws(() => - fromJSON(typ, {merge: true}), InvalidArgumentError); - Assert.throws(() => - fromJSON(typ, {merge: false}), InvalidArgumentError); - } - - // merging - let desired = {"moz:accessibilityChecks": false}; - let required = {"moz:accessibilityChecks": true}; - let matched = fromJSON( - {desiredCapabilities: desired, requiredCapabilities: required}, - {merge: true}); - ok(matched.has("moz:accessibilityChecks")); - equal(true, matched.get("moz:accessibilityChecks")); - - // desiredCapabilities/requriedCapabilities types - for (let typ of [undefined, null, {}]) { - ok(fromJSON({desiredCapabilities: typ}, {merge: true})); - ok(fromJSON({requiredCapabilities: typ}, {merge: true})); - } - for (let typ of [true, 42, "foo", []]) { - Assert.throws(() => fromJSON({desiredCapabilities: typ}, {merge: true})); - Assert.throws(() => fromJSON({requiredCapabilities: typ}, {merge: true})); - } - - // matching - let caps = new session.Capabilities(); - - ok(fromJSON({browserName: caps.get("browserName")})); - ok(fromJSON({browserName: null})); - ok(fromJSON({browserName: undefined})); - ok(fromJSON({browserName: "*"})); - Assert.throws(() => fromJSON({browserName: "foo"})); - - ok(fromJSON({browserVersion: caps.get("browserVersion")})); - ok(fromJSON({browserVersion: null})); - ok(fromJSON({browserVersion: undefined})); - ok(fromJSON({browserVersion: "*"})); - Assert.throws(() => fromJSON({browserVersion: "foo"})); - - ok(fromJSON({platformName: caps.get("platformName")})); - ok(fromJSON({platformName: null})); - ok(fromJSON({platformName: undefined})); - ok(fromJSON({platformName: "*"})); - Assert.throws(() => fromJSON({platformName: "foo"})); - - ok(fromJSON({platformVersion: caps.get("platformVersion")})); - ok(fromJSON({platformVersion: null})); - ok(fromJSON({platformVersion: undefined})); - ok(fromJSON({platformVersion: "*"})); - Assert.throws(() => fromJSON({platformVersion: "foo"})); - - caps = fromJSON({acceptInsecureCerts: true}); - equal(true, caps.get("acceptInsecureCerts")); - caps = fromJSON({acceptInsecureCerts: false}); - equal(false, caps.get("acceptInsecureCerts")); - Assert.throws(() => fromJSON({acceptInsecureCerts: "foo"})); - - for (let strategy of Object.values(session.PageLoadStrategy)) { - caps = fromJSON({pageLoadStrategy: strategy}); - equal(strategy, caps.get("pageLoadStrategy")); - } - Assert.throws(() => fromJSON({pageLoadStrategy: "foo"})); - - let proxyConfig = {proxyType: "manual"}; - caps = fromJSON({proxy: proxyConfig}); - equal("manual", caps.get("proxy").proxyType); - - let timeoutsConfig = {implicit: 123}; - caps = fromJSON({timeouts: timeoutsConfig}); - equal(123, caps.get("timeouts").implicit); - - equal(0, caps.get("specificationLevel")); - caps = fromJSON({specificationLevel: 123}); - equal(123, caps.get("specificationLevel")); - Assert.throws(() => fromJSON({specificationLevel: "foo"})); - Assert.throws(() => fromJSON({specificationLevel: -1})); - - caps = fromJSON({"moz:accessibilityChecks": true}); - equal(true, caps.get("moz:accessibilityChecks")); - caps = fromJSON({"moz:accessibilityChecks": false}); - equal(false, caps.get("moz:accessibilityChecks")); - Assert.throws(() => fromJSON({"moz:accessibilityChecks": "foo"})); - - run_next_test(); -}); - -// use session.Proxy.toJSON to test marshal -add_test(function test_marshal() { - let proxy = new session.Proxy(); - - // drop empty fields - deepEqual({}, proxy.toJSON()); - proxy.proxyType = "manual"; - deepEqual({proxyType: "manual"}, proxy.toJSON()); - proxy.proxyType = null; - deepEqual({}, proxy.toJSON()); - proxy.proxyType = undefined; - deepEqual({}, proxy.toJSON()); - - // iterate over object literals - proxy.proxyType = {foo: "bar"}; - deepEqual({proxyType: {foo: "bar"}}, proxy.toJSON()); - - // iterate over complex object that implement toJSON - proxy.proxyType = new session.Proxy(); - deepEqual({}, proxy.toJSON()); - proxy.proxyType.proxyType = "manual"; - deepEqual({proxyType: {proxyType: "manual"}}, proxy.toJSON()); - - // drop objects with no entries - proxy.proxyType = {foo: {}}; - deepEqual({}, proxy.toJSON()); - proxy.proxyType = {foo: new session.Proxy()}; - deepEqual({}, proxy.toJSON()); - - run_next_test(); -}); diff --git a/testing/marionette/unit.ini b/testing/marionette/unit.ini deleted file mode 100644 index 082106267..000000000 --- a/testing/marionette/unit.ini +++ /dev/null @@ -1,16 +0,0 @@ -# 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/. - -# xpcshell unit tests for Marionette - -[DEFAULT] -skip-if = appname == "thunderbird" - -[test_action.js] -[test_assert.js] -[test_element.js] -[test_error.js] -[test_message.js] -[test_navigate.js] -[test_session.js] |