diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /testing/firefox-ui | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/firefox-ui')
39 files changed, 1903 insertions, 0 deletions
diff --git a/testing/firefox-ui/harness/MANIFEST.in b/testing/firefox-ui/harness/MANIFEST.in new file mode 100644 index 0000000000..cf628b039c --- /dev/null +++ b/testing/firefox-ui/harness/MANIFEST.in @@ -0,0 +1,2 @@ +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 new file mode 100644 index 0000000000..5479f9b2e3 --- /dev/null +++ b/testing/firefox-ui/harness/firefox_ui_harness/__init__.py @@ -0,0 +1,9 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.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 + +__version__ = "1.4.0" + +from . import cli_functional diff --git a/testing/firefox-ui/harness/firefox_ui_harness/arguments/__init__.py b/testing/firefox-ui/harness/firefox_ui_harness/arguments/__init__.py new file mode 100644 index 0000000000..fb802ee70f --- /dev/null +++ b/testing/firefox-ui/harness/firefox_ui_harness/arguments/__init__.py @@ -0,0 +1,7 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.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 + +from firefox_ui_harness.arguments.base import FirefoxUIArguments diff --git a/testing/firefox-ui/harness/firefox_ui_harness/arguments/base.py b/testing/firefox-ui/harness/firefox_ui_harness/arguments/base.py new file mode 100644 index 0000000000..e4c7c9caa4 --- /dev/null +++ b/testing/firefox-ui/harness/firefox_ui_harness/arguments/base.py @@ -0,0 +1,18 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.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 +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/cli_functional.py b/testing/firefox-ui/harness/firefox_ui_harness/cli_functional.py new file mode 100644 index 0000000000..c193297e56 --- /dev/null +++ b/testing/firefox-ui/harness/firefox_ui_harness/cli_functional.py @@ -0,0 +1,23 @@ +#!/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 __future__ import absolute_import +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/runners/__init__.py b/testing/firefox-ui/harness/firefox_ui_harness/runners/__init__.py new file mode 100644 index 0000000000..ade629b770 --- /dev/null +++ b/testing/firefox-ui/harness/firefox_ui_harness/runners/__init__.py @@ -0,0 +1,7 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.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 + +from firefox_ui_harness.runners.base import FirefoxUITestRunner diff --git a/testing/firefox-ui/harness/firefox_ui_harness/runners/base.py b/testing/firefox-ui/harness/firefox_ui_harness/runners/base.py new file mode 100644 index 0000000000..8d02800f9c --- /dev/null +++ b/testing/firefox-ui/harness/firefox_ui_harness/runners/base.py @@ -0,0 +1,56 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.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 +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" + + # low-noise log messages useful in tests + # TODO: should be moved to individual tests once bug 1386810 + # is fixed + moz_log = "" + if "MOZ_LOG" in os.environ: + moz_log = os.environ["MOZ_LOG"] + if len(moz_log) > 0: + moz_log += "," + moz_log += "UrlClassifierStreamUpdater:1" + os.environ["MOZ_LOG"] = moz_log + + 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/requirements.txt b/testing/firefox-ui/harness/requirements.txt new file mode 100644 index 0000000000..cfebcb1be5 --- /dev/null +++ b/testing/firefox-ui/harness/requirements.txt @@ -0,0 +1,4 @@ +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 new file mode 100644 index 0000000000..a24b17251a --- /dev/null +++ b/testing/firefox-ui/harness/setup.py @@ -0,0 +1,52 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.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 +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=[ + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 2 :: Only", + ], + # 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 + """, +) diff --git a/testing/firefox-ui/mach_commands.py b/testing/firefox-ui/mach_commands.py new file mode 100644 index 0000000000..56edef080e --- /dev/null +++ b/testing/firefox-ui/mach_commands.py @@ -0,0 +1,118 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.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 six +import logging +import os +import sys + +from mozbuild.base import ( + MachCommandBase, + MachCommandConditions as conditions, + BinaryNotFoundException, +) + +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 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() + + test_types = { + "functional": { + "default_tests": [ + os.path.join("functional", "manifest.ini"), + ], + "cli_module": firefox_ui_harness.cli_functional, + }, + } + + 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"] = kwargs.pop("log", None) + if not kwargs["logger"]: + kwargs["logger"] = commandline.setup_logging( + "Firefox UI - {} Tests".format(testtype), {"mach": sys.stdout} + ) + + args = Namespace() + + for k, v in six.iteritems(kwargs): + 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): + try: + kwargs["binary"] = kwargs["binary"] or self.get_binary_path("app") + except BinaryNotFoundException as e: + self.log( + logging.ERROR, + "firefox-ui-functional", + {"error": str(e)}, + "ERROR: {error}", + ) + self.log( + logging.INFO, "firefox-ui-functional", {"help": e.help()}, "{help}" + ) + return 1 + + return run_firefox_ui_test( + testtype="functional", topsrcdir=self.topsrcdir, **kwargs + ) diff --git a/testing/firefox-ui/moz.build b/testing/firefox-ui/moz.build new file mode 100644 index 0000000000..bfd7c9f009 --- /dev/null +++ b/testing/firefox-ui/moz.build @@ -0,0 +1,9 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.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"] + +with Files("**"): + BUG_COMPONENT = ("Testing", "Firefox UI Tests") + SCHEDULES.exclusive = ["firefox-ui"] diff --git a/testing/firefox-ui/resources/addons/extensions/webextension-signed.xpi b/testing/firefox-ui/resources/addons/extensions/webextension-signed.xpi Binary files differnew file mode 100644 index 0000000000..5363911af1 --- /dev/null +++ b/testing/firefox-ui/resources/addons/extensions/webextension-signed.xpi diff --git a/testing/firefox-ui/resources/addons/extensions/webextension-unsigned.xpi b/testing/firefox-ui/resources/addons/extensions/webextension-unsigned.xpi Binary files differnew file mode 100644 index 0000000000..cf0fad63b5 --- /dev/null +++ b/testing/firefox-ui/resources/addons/extensions/webextension-unsigned.xpi diff --git a/testing/firefox-ui/resources/cookies/cookie_single.html b/testing/firefox-ui/resources/cookies/cookie_single.html new file mode 100644 index 0000000000..fe6a44c6e0 --- /dev/null +++ b/testing/firefox-ui/resources/cookies/cookie_single.html @@ -0,0 +1,19 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.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 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 differnew file mode 100644 index 0000000000..2c2f81768d --- /dev/null +++ b/testing/firefox-ui/resources/images/firefox_favicon.ico diff --git a/testing/firefox-ui/resources/images/mozilla_favicon.ico b/testing/firefox-ui/resources/images/mozilla_favicon.ico Binary files differnew file mode 100644 index 0000000000..d44438903b --- /dev/null +++ b/testing/firefox-ui/resources/images/mozilla_favicon.ico diff --git a/testing/firefox-ui/resources/images/mozilla_logo.jpg b/testing/firefox-ui/resources/images/mozilla_logo.jpg Binary files differnew file mode 100644 index 0000000000..231b385ee4 --- /dev/null +++ b/testing/firefox-ui/resources/images/mozilla_logo.jpg diff --git a/testing/firefox-ui/resources/layout/mozilla.html b/testing/firefox-ui/resources/layout/mozilla.html new file mode 100644 index 0000000000..be87e406b1 --- /dev/null +++ b/testing/firefox-ui/resources/layout/mozilla.html @@ -0,0 +1,49 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.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 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 new file mode 100644 index 0000000000..d6b668bded --- /dev/null +++ b/testing/firefox-ui/resources/layout/mozilla_community.html @@ -0,0 +1,61 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.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 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 new file mode 100644 index 0000000000..b4c8f1a2f2 --- /dev/null +++ b/testing/firefox-ui/resources/layout/mozilla_contribute.html @@ -0,0 +1,74 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.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 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 new file mode 100644 index 0000000000..153f6a421b --- /dev/null +++ b/testing/firefox-ui/resources/layout/mozilla_governance.html @@ -0,0 +1,42 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.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 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 new file mode 100644 index 0000000000..141367334a --- /dev/null +++ b/testing/firefox-ui/resources/layout/mozilla_grants.html @@ -0,0 +1,76 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.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 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 new file mode 100644 index 0000000000..77059cc5c1 --- /dev/null +++ b/testing/firefox-ui/resources/layout/mozilla_mission.html @@ -0,0 +1,55 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.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 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 new file mode 100644 index 0000000000..e82b03a6fc --- /dev/null +++ b/testing/firefox-ui/resources/layout/mozilla_organizations.html @@ -0,0 +1,43 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.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 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 new file mode 100644 index 0000000000..ad3e596906 --- /dev/null +++ b/testing/firefox-ui/resources/layout/mozilla_projects.html @@ -0,0 +1,64 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.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 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 new file mode 100644 index 0000000000..e8022026ad --- /dev/null +++ b/testing/firefox-ui/resources/private_browsing/about.html @@ -0,0 +1,14 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.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 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/support.html b/testing/firefox-ui/resources/support.html new file mode 100644 index 0000000000..818aa97276 --- /dev/null +++ b/testing/firefox-ui/resources/support.html @@ -0,0 +1,23 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.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"> + <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/resources/update/snippet_empty.xml b/testing/firefox-ui/resources/update/snippet_empty.xml new file mode 100644 index 0000000000..8675c79066 --- /dev/null +++ b/testing/firefox-ui/resources/update/snippet_empty.xml @@ -0,0 +1,7 @@ +<?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/. --> + +<updates> +</updates> diff --git a/testing/firefox-ui/tests/functional/manifest.ini b/testing/firefox-ui/tests/functional/manifest.ini new file mode 100644 index 0000000000..8afe842897 --- /dev/null +++ b/testing/firefox-ui/tests/functional/manifest.ini @@ -0,0 +1,3 @@ +[include:safebrowsing/manifest.ini] +[include:security/manifest.ini] +[include:sessionstore/manifest.ini] diff --git a/testing/firefox-ui/tests/functional/safebrowsing/manifest.ini b/testing/firefox-ui/tests/functional/safebrowsing/manifest.ini new file mode 100644 index 0000000000..97116af83a --- /dev/null +++ b/testing/firefox-ui/tests/functional/safebrowsing/manifest.ini @@ -0,0 +1,7 @@ +[DEFAULT] +tags = remote + +[test_initial_download.py] +skip-if = debug || asan || (cc_type == "clang" && os == 'win') || (os == 'win' && bits == 64 && !debug && processor == "x86_64") # the GAPI key isn't available in debug or asan builds, bug 1526450 +[test_notification.py] +[test_warning_pages.py] diff --git a/testing/firefox-ui/tests/functional/safebrowsing/test_initial_download.py b/testing/firefox-ui/tests/functional/safebrowsing/test_initial_download.py new file mode 100644 index 0000000000..e77c9d95c7 --- /dev/null +++ b/testing/firefox-ui/tests/functional/safebrowsing/test_initial_download.py @@ -0,0 +1,144 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.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 + +import os + +from functools import reduce + +from marionette_driver import Wait +from marionette_harness import MarionetteTestCase + + +class TestSafeBrowsingInitialDownload(MarionetteTestCase): + + v2_file_extensions = [ + "vlpset", + "sbstore", + ] + + v4_file_extensions = [ + "vlpset", + "metadata", + ] + + 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.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, is_v4): + files = [] + + if is_v4: + my_file_extensions = self.v4_file_extensions + else: # v2 + my_file_extensions = self.v2_file_extensions + + for pref_name in self.prefs_download_lists: + base_names = self.marionette.get_pref(pref_name).split(",") + + # moztest- lists are not saved to disk + # pylint --py3k: W1639 + base_names = list( + filter(lambda x: not x.startswith("moztest-"), base_names) + ) + + for ext in my_file_extensions: + files.extend( + [ + "{name}.{ext}".format(name=f, ext=ext) + for f in base_names + if f and f.endswith("-proto") == is_v4 + ] + ) + + return set(sorted(files)) + + def setUp(self): + super(TestSafeBrowsingInitialDownload, self).setUp() + + self.safebrowsing_v2_files = self.get_safebrowsing_files(False) + if any( + f.startswith("goog-") or f.startswith("googpub-") + for f in self.safebrowsing_v2_files + ): + self.prefs_provider_update_time.update( + { + "browser.safebrowsing.provider.google.nextupdatetime": 1, + } + ) + + self.safebrowsing_v4_files = self.get_safebrowsing_files(True) + if any( + f.startswith("goog-") or f.startswith("googpub-") + for f in self.safebrowsing_v4_files + ): + self.prefs_provider_update_time.update( + { + "browser.safebrowsing.provider.google4.nextupdatetime": 1, + } + ) + + # 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" + ) + + def tearDown(self): + try: + # Restart with a fresh profile + self.marionette.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, + list(self.prefs_provider_update_time), + True, + ) + + try: + Wait(self.marionette, timeout=170).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_v2_files: + self.assertIn(f, files_on_disk_toplevel) + + if len(self.safebrowsing_v4_files) > 0: + files_on_disk_google4 = os.listdir( + os.path.join(self.safebrowsing_path, "google4") + ) + for f in self.safebrowsing_v4_files: + self.assertIn(f, files_on_disk_google4) diff --git a/testing/firefox-ui/tests/functional/safebrowsing/test_notification.py b/testing/firefox-ui/tests/functional/safebrowsing/test_notification.py new file mode 100644 index 0000000000..7a19becba1 --- /dev/null +++ b/testing/firefox-ui/tests/functional/safebrowsing/test_notification.py @@ -0,0 +1,128 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.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 + +import time + +from marionette_driver import By, expected, Wait +from marionette_harness import MarionetteTestCase, WindowManagerMixin + + +class TestSafeBrowsingNotificationBar(WindowManagerMixin, MarionetteTestCase): + def setUp(self): + super(TestSafeBrowsingNotificationBar, self).setUp() + + self.test_data = [ + # Unwanted software URL + {"unsafe_page": "https://www.itisatrap.org/firefox/unwanted.html"}, + # Phishing URL info + {"unsafe_page": "https://www.itisatrap.org/firefox/its-a-trap.html"}, + # Malware URL object + {"unsafe_page": "https://www.itisatrap.org/firefox/its-an-attack.html"}, + ] + + self.default_homepage = self.marionette.get_pref("browser.startup.homepage") + + 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) + + # Run this test in a new tab. + new_tab = self.open_tab() + self.marionette.switch_to_window(new_tab) + + def tearDown(self): + try: + self.marionette.clear_pref("browser.safebrowsing.phishing.enabled") + self.marionette.clear_pref("browser.safebrowsing.malware.enabled") + + self.remove_permission("https://www.itisatrap.org", "safe-browsing") + self.close_all_tabs() + finally: + super(TestSafeBrowsingNotificationBar, self).tearDown() + + def test_notification_bar(self): + for item in self.test_data: + unsafe_page = item["unsafe_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_link(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_link(unsafe_page) + self.check_x_button() + + def get_final_url(self, url): + self.marionette.navigate(url) + return self.marionette.get_url() + + def remove_permission(self, host, permission): + 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); + let principal = Services.scriptSecurityManager.createContentPrincipal(uri, {}); + Services.perms.removeFromPrincipal(principal, arguments[1]); + """, + script_args=[host, permission], + ) + + def check_ignore_warning_link(self, unsafe_page): + button = self.marionette.find_element(By.ID, "seeDetailsButton") + button.click() + time.sleep(1) + link = self.marionette.find_element(By.ID, "ignore_warning_link") + link.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.get_final_url(unsafe_page)) + + # Clean up here since the permission gets set in this function + self.remove_permission("https://www.itisatrap.org", "safe-browsing") + + def check_get_me_out_of_here_button(self): + with self.marionette.using_context("chrome"): + button = self.marionette.find_element( + By.ID, "tabbrowser-tabbox" + ).find_element(By.CSS_SELECTOR, 'button[label="Get me out of here!"]') + button.click() + + Wait(self.marionette, timeout=self.marionette.timeout.page_load).until( + lambda mn: self.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"): + button = ( + self.marionette.find_element(By.ID, "tabbrowser-tabbox") + .find_element( + By.CSS_SELECTOR, "notification[value=blocked-badware-page]" + ) + .find_element(By.CSS_SELECTOR, ".messageCloseButton") + ) + 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/safebrowsing/test_warning_pages.py b/testing/firefox-ui/tests/functional/safebrowsing/test_warning_pages.py new file mode 100644 index 0000000000..883d90c7a6 --- /dev/null +++ b/testing/firefox-ui/tests/functional/safebrowsing/test_warning_pages.py @@ -0,0 +1,142 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.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 + +import time + +from marionette_driver import By, expected, Wait +from marionette_harness import MarionetteTestCase, WindowManagerMixin + + +class TestSafeBrowsingWarningPages(WindowManagerMixin, 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.default_homepage = self.marionette.get_pref("browser.startup.homepage") + self.support_page = self.marionette.absolute_url("support.html?topic=") + + self.marionette.set_pref("app.support.baseURL", self.support_page) + 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) + + # Run this test in a new tab. + new_tab = self.open_tab() + self.marionette.switch_to_window(new_tab) + + def tearDown(self): + try: + self.marionette.clear_pref("app.support.baseURL") + self.marionette.clear_pref("browser.safebrowsing.malware.enabled") + self.marionette.clear_pref("browser.safebrowsing.phishing.enabled") + + self.remove_permission("https://www.itisatrap.org", "safe-browsing") + self.close_all_tabs() + finally: + super(TestSafeBrowsingWarningPages, self).tearDown() + + def test_warning_pages(self): + 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_link(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 get_final_url(self, url): + self.marionette.navigate(url) + return self.marionette.get_url() + + def remove_permission(self, host, permission): + 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); + let principal = Services.scriptSecurityManager.createContentPrincipal(uri, {}); + Services.perms.removeFromPrincipal(principal, arguments[1]); + """, + script_args=[host, permission], + ) + + def check_get_me_out_of_here_button(self, unsafe_page): + button = self.marionette.find_element(By.ID, "goBackButton") + button.click() + + Wait(self.marionette, timeout=self.marionette.timeout.page_load).until( + lambda mn: self.default_homepage in mn.get_url() + ) + + def check_report_link(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, "seeDetailsButton") + button.click() + link = self.marionette.find_element(By.ID, "firefox_support") + link.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.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, "seeDetailsButton") + button.click() + link = self.marionette.find_element(By.ID, "ignore_warning_link") + link.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.get_final_url(unsafe_page)) + + # Clean up by removing safe browsing permission for unsafe page + self.remove_permission("https://www.itisatrap.org", "safe-browsing") diff --git a/testing/firefox-ui/tests/functional/security/manifest.ini b/testing/firefox-ui/tests/functional/security/manifest.ini new file mode 100644 index 0000000000..e0993b122a --- /dev/null +++ b/testing/firefox-ui/tests/functional/security/manifest.ini @@ -0,0 +1,4 @@ +[DEFAULT] +tags = remote + +[test_ssl_status_after_restart.py] 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 new file mode 100644 index 0000000000..5941748ad3 --- /dev/null +++ b/testing/firefox-ui/tests/functional/security/test_ssl_status_after_restart.py @@ -0,0 +1,58 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.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 + +from marionette_driver import By, Wait +from marionette_harness import MarionetteTestCase, WindowManagerMixin + + +class TestSSLStatusAfterRestart(WindowManagerMixin, MarionetteTestCase): + def setUp(self): + super(TestSSLStatusAfterRestart, self).setUp() + self.marionette.set_context("chrome") + + self.test_url = "https://sha512.badssl.com/" + + # Set browser to restore previous session + self.marionette.set_pref("browser.startup.page", 3) + # Disable rcwn to make cache behavior deterministic + self.marionette.set_pref("network.http.rcwn.enable", False) + + def tearDown(self): + self.marionette.clear_pref("browser.startup.page") + self.marionette.clear_pref("network.http.rcwn.enable") + + super(TestSSLStatusAfterRestart, self).tearDown() + + def test_ssl_status_after_restart(self): + with self.marionette.using_context("content"): + self.marionette.navigate(self.test_url) + self.verify_certificate_status(self.test_url) + + self.marionette.restart(in_app=True) + + self.verify_certificate_status(self.test_url) + + def verify_certificate_status(self, url): + with self.marionette.using_context("content"): + Wait(self.marionette).until( + lambda _: self.marionette.get_url() == url, + message="Expected URL loaded", + ) + + identity_box = self.marionette.find_element(By.ID, "identity-box") + self.assertEqual(identity_box.get_attribute("pageproxystate"), "valid") + + class_list = self.marionette.execute_script( + """ + const names = []; + for (const name of arguments[0].classList) { + names.push(name); + } + return names; + """, + script_args=[identity_box], + ) + self.assertIn("verifiedDomain", class_list) diff --git a/testing/firefox-ui/tests/functional/sessionstore/manifest.ini b/testing/firefox-ui/tests/functional/sessionstore/manifest.ini new file mode 100644 index 0000000000..09e94968ee --- /dev/null +++ b/testing/firefox-ui/tests/functional/sessionstore/manifest.ini @@ -0,0 +1,6 @@ +[DEFAULT] +tags = local + +[test_restore_windows_after_restart_and_quit.py] +[test_restore_windows_after_windows_shutdown.py] +skip-if = os != "win" diff --git a/testing/firefox-ui/tests/functional/sessionstore/session_store_test_case.py b/testing/firefox-ui/tests/functional/sessionstore/session_store_test_case.py new file mode 100644 index 0000000000..658f80c512 --- /dev/null +++ b/testing/firefox-ui/tests/functional/sessionstore/session_store_test_case.py @@ -0,0 +1,400 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.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 + +from marionette_driver.keys import Keys +from marionette_harness import MarionetteTestCase, WindowManagerMixin + + +class SessionStoreTestCase(WindowManagerMixin, MarionetteTestCase): + def setUp( + self, + startup_page=1, + include_private=True, + no_auto_updates=True, + win_register_restart=False, + ): + super(SessionStoreTestCase, self).setUp() + self.marionette.set_context("chrome") + + platform = self.marionette.session_capabilities["platformName"] + self.accelKey = Keys.META if platform == "mac" else Keys.CONTROL + + # 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 restore previous session pref, + # depending on what the test requires. + "browser.startup.page": startup_page, + # 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": no_auto_updates, + # Whether to enable the register application restart mechanism. + "toolkit.winRegisterApplicationRestart": win_register_restart, + } + ) + + self.all_windows = self.test_windows.copy() + self.open_windows(self.test_windows) + + if include_private: + self.all_windows.update(self.private_windows) + self.open_windows(self.private_windows, is_private=True) + + def tearDown(self): + try: + # Create a fresh profile for subsequent tests. + self.marionette.restart(clean=True) + finally: + super(SessionStoreTestCase, self).tearDown() + + def open_windows(self, window_sets, is_private=False): + """Open 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.open_window(private=True) + self.marionette.switch_to_window(win) + else: + win = self.marionette.current_chrome_window_handle + + for index, urls in enumerate(window_sets): + if index > 0: + win = self.open_window(private=is_private) + self.marionette.switch_to_window(win) + self.open_tabs(win, urls) + + def open_tabs(self, win, urls): + """Open 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: + tab = self.open_tab() + self.marionette.switch_to_window(tab) + self.marionette.navigate(url) + + def get_urls_for_window(self, win): + orig_handle = self.marionette.current_chrome_window_handle + + try: + with self.marionette.using_context("chrome"): + self.marionette.switch_to_window(win) + return self.marionette.execute_script( + """ + return gBrowser.tabs.map(tab => { + return tab.linkedBrowser.currentURI.spec; + }); + """ + ) + finally: + self.marionette.switch_to_window(orig_handle) + + def convert_open_windows_to_set(self): + # 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 self.marionette.chrome_window_handles: + urls = tuple(self.get_urls_for_window(win)) + opened_windows.add(urls) + + return opened_windows + + def simulate_os_shutdown(self): + """Simulate an OS shutdown. + + :raises: Exception: if not supported on the current platform + :raises: WindowsError: if a Windows API call failed + """ + if self.marionette.session_capabilities["platformName"] != "windows": + raise Exception("Unsupported platform for simulate_os_shutdown") + + self._shutdown_with_windows_restart_manager(self.marionette.process_id) + + def _shutdown_with_windows_restart_manager(self, pid): + """Shut down a process using the Windows Restart Manager. + + When Windows shuts down, it uses a protocol including the + WM_QUERYENDSESSION and WM_ENDSESSION messages to give + applications a chance to shut down safely. The best way to + simulate this is via the Restart Manager, which allows a process + (such as an installer) to use the same mechanism to shut down + any other processes which are using registered resources. + + This function starts a Restart Manager session, registers the + process as a resource, and shuts down the process. + + :param pid: The process id (int) of the process to shutdown + + :raises: WindowsError: if a Windows API call fails + """ + import ctypes + from ctypes import Structure, POINTER, WINFUNCTYPE, windll, pointer, WinError + from ctypes.wintypes import HANDLE, DWORD, BOOL, WCHAR, UINT, ULONG, LPCWSTR + + # set up Windows SDK types + OpenProcess = windll.kernel32.OpenProcess + OpenProcess.restype = HANDLE + OpenProcess.argtypes = [ + DWORD, # dwDesiredAccess + BOOL, # bInheritHandle + DWORD, + ] # dwProcessId + PROCESS_QUERY_INFORMATION = 0x0400 + + class FILETIME(Structure): + _fields_ = [("dwLowDateTime", DWORD), ("dwHighDateTime", DWORD)] + + LPFILETIME = POINTER(FILETIME) + + GetProcessTimes = windll.kernel32.GetProcessTimes + GetProcessTimes.restype = BOOL + GetProcessTimes.argtypes = [ + HANDLE, # hProcess + LPFILETIME, # lpCreationTime + LPFILETIME, # lpExitTime + LPFILETIME, # lpKernelTime + LPFILETIME, + ] # lpUserTime + + ERROR_SUCCESS = 0 + + class RM_UNIQUE_PROCESS(Structure): + _fields_ = [("dwProcessId", DWORD), ("ProcessStartTime", FILETIME)] + + RmStartSession = windll.rstrtmgr.RmStartSession + RmStartSession.restype = DWORD + RmStartSession.argtypes = [ + POINTER(DWORD), # pSessionHandle + DWORD, # dwSessionFlags + POINTER(WCHAR), + ] # strSessionKey + + class GUID(ctypes.Structure): + _fields_ = [ + ("Data1", ctypes.c_ulong), + ("Data2", ctypes.c_ushort), + ("Data3", ctypes.c_ushort), + ("Data4", ctypes.c_ubyte * 8), + ] + + CCH_RM_SESSION_KEY = ctypes.sizeof(GUID) * 2 + + RmRegisterResources = windll.rstrtmgr.RmRegisterResources + RmRegisterResources.restype = DWORD + RmRegisterResources.argtypes = [ + DWORD, # dwSessionHandle + UINT, # nFiles + POINTER(LPCWSTR), # rgsFilenames + UINT, # nApplications + POINTER(RM_UNIQUE_PROCESS), # rgApplications + UINT, # nServices + POINTER(LPCWSTR), + ] # rgsServiceNames + + RM_WRITE_STATUS_CALLBACK = WINFUNCTYPE(None, UINT) + RmShutdown = windll.rstrtmgr.RmShutdown + RmShutdown.restype = DWORD + RmShutdown.argtypes = [ + DWORD, # dwSessionHandle + ULONG, # lActionFlags + RM_WRITE_STATUS_CALLBACK, + ] # fnStatus + + RmEndSession = windll.rstrtmgr.RmEndSession + RmEndSession.restype = DWORD + RmEndSession.argtypes = [DWORD] # dwSessionHandle + + # Get the info needed to uniquely identify the process + hProc = OpenProcess(PROCESS_QUERY_INFORMATION, False, pid) + if not hProc: + raise WinError() + + creationTime = FILETIME() + exitTime = FILETIME() + kernelTime = FILETIME() + userTime = FILETIME() + if not GetProcessTimes( + hProc, + pointer(creationTime), + pointer(exitTime), + pointer(kernelTime), + pointer(userTime), + ): + raise WinError() + + # Start the Restart Manager Session + dwSessionHandle = DWORD() + sessionKeyType = WCHAR * (CCH_RM_SESSION_KEY + 1) + sessionKey = sessionKeyType() + if RmStartSession(pointer(dwSessionHandle), 0, sessionKey) != ERROR_SUCCESS: + raise WinError() + + try: + UProcs_count = 1 + UProcsArrayType = RM_UNIQUE_PROCESS * UProcs_count + UProcs = UProcsArrayType(RM_UNIQUE_PROCESS(pid, creationTime)) + + # Register the process as a resource + if ( + RmRegisterResources( + dwSessionHandle, 0, None, UProcs_count, UProcs, 0, None + ) + != ERROR_SUCCESS + ): + raise WinError() + + # Shut down all processes using registered resources + if ( + RmShutdown( + dwSessionHandle, 0, ctypes.cast(None, RM_WRITE_STATUS_CALLBACK) + ) + != ERROR_SUCCESS + ): + raise WinError() + + finally: + RmEndSession(dwSessionHandle) + + def windows_shutdown_with_variety(self, restart_by_os, expect_restore): + """Test restoring windows after Windows shutdown. + + Opens a set of windows, both standard and private, with + some number of tabs in them. Once the tabs have loaded, shuts down + the browser with the Windows Restart Manager and restarts the browser. + + This specifically exercises the Windows synchronous shutdown mechanism, + which terminates the process in response to the Restart Manager's + WM_ENDSESSION message. + + If restart_by_os is True, the -os-restarted arg is passed when restarting, + simulating being automatically restarted by the Restart Manager. + + If expect_restore is True, this ensures that the standard tabs have been + restored, and that the private ones have not. Otherwise it ensures that + no tabs and windows have been restored. + """ + current_windows_set = self.convert_open_windows_to_set() + self.assertEqual( + current_windows_set, + self.all_windows, + msg="Not all requested windows have been opened. Expected {}, got {}.".format( + self.all_windows, current_windows_set + ), + ) + + self.marionette.quit(in_app=True, callback=lambda: self.simulate_os_shutdown()) + + saved_args = self.marionette.instance.app_args + try: + if restart_by_os: + self.marionette.instance.app_args = ["-os-restarted"] + + self.marionette.start_session() + self.marionette.set_context("chrome") + finally: + self.marionette.instance.app_args = saved_args + + current_windows_set = self.convert_open_windows_to_set() + if expect_restore: + self.assertEqual( + current_windows_set, + self.test_windows, + msg="""Non private browsing windows should have + been restored. Expected {}, got {}. + """.format( + self.test_windows, current_windows_set + ), + ) + else: + self.assertEqual( + len(self.marionette.chrome_window_handles), + 1, + msg="Windows from last session shouldn`t have been restored.", + ) + self.assertEqual( + len(self.marionette.window_handles), + 1, + msg="Tabs from last session shouldn`t have been restored.", + ) diff --git a/testing/firefox-ui/tests/functional/sessionstore/test_restore_windows_after_restart_and_quit.py b/testing/firefox-ui/tests/functional/sessionstore/test_restore_windows_after_restart_and_quit.py new file mode 100644 index 0000000000..b96e2cd3c1 --- /dev/null +++ b/testing/firefox-ui/tests/functional/sessionstore/test_restore_windows_after_restart_and_quit.py @@ -0,0 +1,112 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# add this directory to the path +from __future__ import absolute_import +import sys +import os + +sys.path.append(os.path.dirname(__file__)) + +from session_store_test_case import SessionStoreTestCase + + +class TestSessionStoreEnabledAllWindows(SessionStoreTestCase): + def setUp(self, include_private=True): + """Setup for the test, enabling session restore. + + :param include_private: Whether to open private windows. + """ + super(TestSessionStoreEnabledAllWindows, self).setUp( + include_private=include_private, startup_page=3 + ) + + def test_with_variety(self): + """Test opening and restoring both standard and private windows. + + 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. + """ + current_windows_set = self.convert_open_windows_to_set() + self.assertEqual( + current_windows_set, + self.all_windows, + msg="Not all requested windows have been opened. Expected {}, got {}.".format( + self.all_windows, current_windows_set + ), + ) + + self.marionette.quit(in_app=True) + self.marionette.start_session() + self.marionette.set_context("chrome") + + current_windows_set = self.convert_open_windows_to_set() + self.assertEqual( + current_windows_set, + self.test_windows, + msg="""Non private browsing windows should have + been restored. Expected {}, got {}. + """.format( + self.test_windows, current_windows_set + ), + ) + + +class TestSessionStoreEnabledNoPrivateWindows(TestSessionStoreEnabledAllWindows): + def setUp(self): + super(TestSessionStoreEnabledNoPrivateWindows, self).setUp( + include_private=False + ) + + +class TestSessionStoreDisabled(SessionStoreTestCase): + def test_no_restore_with_quit(self): + current_windows_set = self.convert_open_windows_to_set() + self.assertEqual( + current_windows_set, + self.all_windows, + msg="Not all requested windows have been opened. Expected {}, got {}.".format( + self.all_windows, current_windows_set + ), + ) + + self.marionette.quit(in_app=True) + self.marionette.start_session() + self.marionette.set_context("chrome") + + self.assertEqual( + len(self.marionette.chrome_window_handles), + 1, + msg="Windows from last session shouldn`t have been restored.", + ) + self.assertEqual( + len(self.marionette.window_handles), + 1, + msg="Tabs from last session shouldn`t have been restored.", + ) + + def test_restore_with_restart(self): + current_windows_set = self.convert_open_windows_to_set() + self.assertEqual( + current_windows_set, + self.all_windows, + msg="Not all requested windows have been opened. Expected {}, got {}.".format( + self.all_windows, current_windows_set + ), + ) + + self.marionette.restart(in_app=True) + + current_windows_set = self.convert_open_windows_to_set() + self.assertEqual( + current_windows_set, + self.test_windows, + msg="""Non private browsing windows should have + been restored. Expected {}, got {}. + """.format( + self.test_windows, current_windows_set + ), + ) diff --git a/testing/firefox-ui/tests/functional/sessionstore/test_restore_windows_after_windows_shutdown.py b/testing/firefox-ui/tests/functional/sessionstore/test_restore_windows_after_windows_shutdown.py new file mode 100644 index 0000000000..7c5bf449e4 --- /dev/null +++ b/testing/firefox-ui/tests/functional/sessionstore/test_restore_windows_after_windows_shutdown.py @@ -0,0 +1,67 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# add this directory to the path +from __future__ import absolute_import +import sys +import os + +sys.path.append(os.path.dirname(__file__)) + +from session_store_test_case import SessionStoreTestCase + +# We test the following combinations with simulated Windows shutdown: +# - Start page = restore session (expect restore in all cases) +# - RAR (toolkit.winRegisterApplicationRestart) disabled +# - RAR enabled, restarted manually +# +# - Start page = home +# - RAR disabled (no restore) +# - RAR enabled: +# - restarted by OS (restore) +# - restarted manually (no restore) + + +class TestWindowsShutdown(SessionStoreTestCase): + def setUp(self): + super(TestWindowsShutdown, self).setUp(startup_page=3, no_auto_updates=False) + + def test_with_variety(self): + """Test session restore selected by user.""" + self.windows_shutdown_with_variety(restart_by_os=False, expect_restore=True) + + +class TestWindowsShutdownRegisterRestart(SessionStoreTestCase): + def setUp(self): + super(TestWindowsShutdownRegisterRestart, self).setUp( + startup_page=3, no_auto_updates=False, win_register_restart=True + ) + + def test_manual_restart(self): + """Test that restore tabs works in case of register restart failure.""" + self.windows_shutdown_with_variety(restart_by_os=False, expect_restore=True) + + +class TestWindowsShutdownNormal(SessionStoreTestCase): + def setUp(self): + super(TestWindowsShutdownNormal, self).setUp(no_auto_updates=False) + + def test_with_variety(self): + """Test that windows are not restored on a normal restart.""" + self.windows_shutdown_with_variety(restart_by_os=False, expect_restore=False) + + +class TestWindowsShutdownForcedSessionRestore(SessionStoreTestCase): + def setUp(self): + super(TestWindowsShutdownForcedSessionRestore, self).setUp( + no_auto_updates=False, win_register_restart=True + ) + + def test_os_restart(self): + """Test that register application restart restores the session.""" + self.windows_shutdown_with_variety(restart_by_os=True, expect_restore=True) + + def test_manual_restart(self): + """Test that OS shutdown is ignored on manual start.""" + self.windows_shutdown_with_variety(restart_by_os=False, expect_restore=False) |