summaryrefslogtreecommitdiffstats
path: root/testing/firefox-ui
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /testing/firefox-ui
parentInitial commit. (diff)
downloadfirefox-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')
-rw-r--r--testing/firefox-ui/harness/MANIFEST.in2
-rw-r--r--testing/firefox-ui/harness/firefox_ui_harness/__init__.py9
-rw-r--r--testing/firefox-ui/harness/firefox_ui_harness/arguments/__init__.py7
-rw-r--r--testing/firefox-ui/harness/firefox_ui_harness/arguments/base.py18
-rw-r--r--testing/firefox-ui/harness/firefox_ui_harness/cli_functional.py23
-rw-r--r--testing/firefox-ui/harness/firefox_ui_harness/runners/__init__.py7
-rw-r--r--testing/firefox-ui/harness/firefox_ui_harness/runners/base.py56
-rw-r--r--testing/firefox-ui/harness/requirements.txt4
-rw-r--r--testing/firefox-ui/harness/setup.py52
-rw-r--r--testing/firefox-ui/mach_commands.py118
-rw-r--r--testing/firefox-ui/moz.build9
-rw-r--r--testing/firefox-ui/resources/addons/extensions/webextension-signed.xpibin0 -> 4221 bytes
-rw-r--r--testing/firefox-ui/resources/addons/extensions/webextension-unsigned.xpibin0 -> 310 bytes
-rw-r--r--testing/firefox-ui/resources/cookies/cookie_single.html19
-rw-r--r--testing/firefox-ui/resources/images/firefox_favicon.icobin0 -> 1150 bytes
-rw-r--r--testing/firefox-ui/resources/images/mozilla_favicon.icobin0 -> 1406 bytes
-rw-r--r--testing/firefox-ui/resources/images/mozilla_logo.jpgbin0 -> 3298 bytes
-rw-r--r--testing/firefox-ui/resources/layout/mozilla.html49
-rw-r--r--testing/firefox-ui/resources/layout/mozilla_community.html61
-rw-r--r--testing/firefox-ui/resources/layout/mozilla_contribute.html74
-rw-r--r--testing/firefox-ui/resources/layout/mozilla_governance.html42
-rw-r--r--testing/firefox-ui/resources/layout/mozilla_grants.html76
-rw-r--r--testing/firefox-ui/resources/layout/mozilla_mission.html55
-rw-r--r--testing/firefox-ui/resources/layout/mozilla_organizations.html43
-rw-r--r--testing/firefox-ui/resources/layout/mozilla_projects.html64
-rw-r--r--testing/firefox-ui/resources/private_browsing/about.html14
-rw-r--r--testing/firefox-ui/resources/support.html23
-rw-r--r--testing/firefox-ui/resources/update/snippet_empty.xml7
-rw-r--r--testing/firefox-ui/tests/functional/manifest.ini3
-rw-r--r--testing/firefox-ui/tests/functional/safebrowsing/manifest.ini7
-rw-r--r--testing/firefox-ui/tests/functional/safebrowsing/test_initial_download.py144
-rw-r--r--testing/firefox-ui/tests/functional/safebrowsing/test_notification.py128
-rw-r--r--testing/firefox-ui/tests/functional/safebrowsing/test_warning_pages.py142
-rw-r--r--testing/firefox-ui/tests/functional/security/manifest.ini4
-rw-r--r--testing/firefox-ui/tests/functional/security/test_ssl_status_after_restart.py58
-rw-r--r--testing/firefox-ui/tests/functional/sessionstore/manifest.ini6
-rw-r--r--testing/firefox-ui/tests/functional/sessionstore/session_store_test_case.py400
-rw-r--r--testing/firefox-ui/tests/functional/sessionstore/test_restore_windows_after_restart_and_quit.py112
-rw-r--r--testing/firefox-ui/tests/functional/sessionstore/test_restore_windows_after_windows_shutdown.py67
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
new file mode 100644
index 0000000000..5363911af1
--- /dev/null
+++ b/testing/firefox-ui/resources/addons/extensions/webextension-signed.xpi
Binary files differ
diff --git a/testing/firefox-ui/resources/addons/extensions/webextension-unsigned.xpi b/testing/firefox-ui/resources/addons/extensions/webextension-unsigned.xpi
new file mode 100644
index 0000000000..cf0fad63b5
--- /dev/null
+++ b/testing/firefox-ui/resources/addons/extensions/webextension-unsigned.xpi
Binary files differ
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
new file mode 100644
index 0000000000..2c2f81768d
--- /dev/null
+++ b/testing/firefox-ui/resources/images/firefox_favicon.ico
Binary files differ
diff --git a/testing/firefox-ui/resources/images/mozilla_favicon.ico b/testing/firefox-ui/resources/images/mozilla_favicon.ico
new file mode 100644
index 0000000000..d44438903b
--- /dev/null
+++ b/testing/firefox-ui/resources/images/mozilla_favicon.ico
Binary files differ
diff --git a/testing/firefox-ui/resources/images/mozilla_logo.jpg b/testing/firefox-ui/resources/images/mozilla_logo.jpg
new file mode 100644
index 0000000000..231b385ee4
--- /dev/null
+++ b/testing/firefox-ui/resources/images/mozilla_logo.jpg
Binary files differ
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)