Backed out 8 changesets (bug 1470646) for linting failure on a CLOSED TREE
authorCoroiu Cristina <ccoroiu@mozilla.com>
Mon, 02 Jul 2018 17:56:27 +0300
changeset 479760 a33b4bcf0a74497279cae6a3dbc61fa271051b28
parent 479759 e55193f1ce01cdb173fb3de3331f05f90aa42d1b
child 479761 a146704d807d7c54c5b7c68474416a764e31c722
push id9719
push userffxbld-merge
push dateFri, 24 Aug 2018 17:49:46 +0000
treeherdermozilla-beta@719ec98fba77 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1470646
milestone63.0a1
backs out9b8d9f803b2596ec3a2021c2614f64a5e0e2df55
58e2010d28420b3f3b5464b15d769b29f611ece4
2b19429d778fc8fb76d0ddeaccf83ded7bb10013
e543f592454a2c126b4d16bff87b8d3ddd005c96
22469044267d15ef28e0fd29cf66fcddf6b66c8a
e5415b1e22f526bff9c750fcf84caf4a9f85d7e5
13e47fa99a3141bf76472087d63c7d3e02d197a0
42964d651f02bb0af3a3d01b5cff634a3c82ec35
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out 8 changesets (bug 1470646) for linting failure on a CLOSED TREE Backed out changeset 9b8d9f803b25 (bug 1470646) Backed out changeset 58e2010d2842 (bug 1470646) Backed out changeset 2b19429d778f (bug 1470646) Backed out changeset e543f592454a (bug 1470646) Backed out changeset 22469044267d (bug 1470646) Backed out changeset e5415b1e22f5 (bug 1470646) Backed out changeset 13e47fa99a31 (bug 1470646) Backed out changeset 42964d651f02 (bug 1470646)
testing/firefox-ui/tests/functional/sessionstore/session_store_test_case.py
testing/firefox-ui/tests/puppeteer/test_page_info_window.py
testing/marionette/capabilities.js
testing/marionette/driver.js
testing/marionette/format.js
testing/marionette/harness/marionette_harness/tests/unit/test_capabilities.py
testing/marionette/harness/marionette_harness/tests/unit/test_key_actions.py
testing/marionette/harness/marionette_harness/tests/unit/test_legacy_mouse_action.py
testing/marionette/harness/marionette_harness/tests/unit/test_mouse_action.py
testing/marionette/harness/marionette_harness/tests/unit/test_navigation.py
testing/marionette/harness/marionette_harness/tests/unit/test_switch_window_content.py
testing/marionette/harness/marionette_harness/tests/unit/test_typing.py
testing/marionette/harness/marionette_harness/tests/unit/test_window_maximize.py
testing/marionette/harness/marionette_harness/tests/unit/test_window_rect.py
testing/marionette/jar.mn
testing/marionette/listener.js
testing/marionette/puppeteer/firefox/firefox_puppeteer/api/keys.py
testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/browser/window.py
testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/windows.py
testing/marionette/session.js
testing/marionette/test/unit/test_capabilities.js
testing/marionette/test/unit/test_session.js
testing/marionette/test/unit/xpcshell.ini
testing/web-platform/meta/MANIFEST.json
testing/web-platform/tests/tools/wptrunner/wptrunner/wpttest.py
testing/web-platform/tests/webdriver/tests/actions/control_click.py
testing/web-platform/tests/webdriver/tests/actions/support/keys.py
testing/web-platform/tests/webdriver/tests/new_session/create_alwaysMatch.py
testing/web-platform/tests/webdriver/tests/set_window_rect/set.py
--- a/testing/firefox-ui/tests/functional/sessionstore/session_store_test_case.py
+++ b/testing/firefox-ui/tests/functional/sessionstore/session_store_test_case.py
@@ -1,285 +1,285 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-from firefox_puppeteer import PuppeteerMixin
-from marionette_harness import MarionetteTestCase
-
-
-class SessionStoreTestCase(PuppeteerMixin, MarionetteTestCase):
-
-    def setUp(self, startup_page=1, include_private=True, no_auto_updates=True):
-        super(SessionStoreTestCase, self).setUp()
-        # Each list element represents a window of tabs loaded at
-        # some testing URL
-        self.test_windows = set([
-            # Window 1. Note the comma after the absolute_url call -
-            # this is Python's way of declaring a 1 item tuple.
-            (self.marionette.absolute_url('layout/mozilla.html'), ),
-
-            # Window 2
-            (self.marionette.absolute_url('layout/mozilla_organizations.html'),
-             self.marionette.absolute_url('layout/mozilla_community.html')),
-
-            # Window 3
-            (self.marionette.absolute_url('layout/mozilla_governance.html'),
-             self.marionette.absolute_url('layout/mozilla_grants.html')),
-        ])
-
-        self.private_windows = set([
-            (self.marionette.absolute_url('layout/mozilla_mission.html'),
-             self.marionette.absolute_url('layout/mozilla_organizations.html')),
-
-            (self.marionette.absolute_url('layout/mozilla_projects.html'),
-             self.marionette.absolute_url('layout/mozilla_mission.html')),
-        ])
-
-        self.marionette.enforce_gecko_prefs({
-            # Set browser 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,
-        })
-
-        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.restart(clean=True)
-        finally:
-            super(SessionStoreTestCase, self).tearDown()
-
-    def open_windows(self, window_sets, is_private=False):
-        """ Opens a set of windows with tabs pointing at some
-        URLs.
-
-        @param window_sets (list)
-               A set of URL tuples. Each tuple within window_sets
-               represents a window, and each URL in the URL
-               tuples represents what will be loaded in a tab.
-
-               Note that if is_private is False, then the first
-               URL tuple will be opened in the current window, and
-               subequent tuples will be opened in new windows.
-
-               Example:
-
-               set(
-                   (self.marionette.absolute_url('layout/mozilla_1.html'),
-                    self.marionette.absolute_url('layout/mozilla_2.html')),
-
-                   (self.marionette.absolute_url('layout/mozilla_3.html'),
-                    self.marionette.absolute_url('layout/mozilla_4.html')),
-               )
-
-               This would take the currently open window, and load
-               mozilla_1.html and mozilla_2.html in new tabs. It would
-               then open a new, second window, and load tabs at
-               mozilla_3.html and mozilla_4.html.
-        @param is_private (boolean, optional)
-               Whether or not any new windows should be a private browsing
-               windows.
-        """
-
-        if (is_private):
-            win = self.browser.open_browser(is_private=True)
-            win.switch_to()
-        else:
-            win = self.browser
-
-        for index, urls in enumerate(window_sets):
-            if index > 0:
-                win = self.browser.open_browser(is_private=is_private)
-            win.switch_to()
-            self.open_tabs(win, urls)
-
-    def open_tabs(self, win, urls):
-        """ Opens a set of URLs inside a window in new tabs.
-
-        @param win (browser window)
-               The browser window to load the tabs in.
-        @param urls (tuple)
-               A tuple of URLs to load in this window. The
-               first URL will be loaded in the currently selected
-               browser tab. Subsequent URLs will be loaded in
-               new tabs.
-        """
-        # If there are any remaining URLs for this window,
-        # open some new tabs and navigate to them.
-        with self.marionette.using_context('content'):
-            if isinstance(urls, str):
-                self.marionette.navigate(urls)
-            else:
-                for index, url in enumerate(urls):
-                    if index > 0:
-                        with self.marionette.using_context('chrome'):
-                            win.tabbar.open_tab()
-                    self.marionette.navigate(url)
-
-    def convert_open_windows_to_set(self):
-        windows = self.puppeteer.windows.all
-
-        # There's no guarantee that Marionette will return us an
-        # iterator for the opened windows that will match the
-        # order within our window list. Instead, we'll convert
-        # the list of URLs within each open window to a set of
-        # tuples that will allow us to do a direct comparison
-        # while allowing the windows to be in any order.
-
-        opened_windows = set()
-        for win in windows:
-            urls = tuple()
-            for tab in win.tabbar.tabs:
-                urls = urls + tuple([tab.location])
-            opened_windows.add(urls)
-        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)
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from firefox_puppeteer import PuppeteerMixin
+from marionette_harness import MarionetteTestCase
+
+
+class SessionStoreTestCase(PuppeteerMixin, MarionetteTestCase):
+
+    def setUp(self, startup_page=1, include_private=True, no_auto_updates=True):
+        super(SessionStoreTestCase, self).setUp()
+        # Each list element represents a window of tabs loaded at
+        # some testing URL
+        self.test_windows = set([
+            # Window 1. Note the comma after the absolute_url call -
+            # this is Python's way of declaring a 1 item tuple.
+            (self.marionette.absolute_url('layout/mozilla.html'), ),
+
+            # Window 2
+            (self.marionette.absolute_url('layout/mozilla_organizations.html'),
+             self.marionette.absolute_url('layout/mozilla_community.html')),
+
+            # Window 3
+            (self.marionette.absolute_url('layout/mozilla_governance.html'),
+             self.marionette.absolute_url('layout/mozilla_grants.html')),
+        ])
+
+        self.private_windows = set([
+            (self.marionette.absolute_url('layout/mozilla_mission.html'),
+             self.marionette.absolute_url('layout/mozilla_organizations.html')),
+
+            (self.marionette.absolute_url('layout/mozilla_projects.html'),
+             self.marionette.absolute_url('layout/mozilla_mission.html')),
+        ])
+
+        self.marionette.enforce_gecko_prefs({
+            # Set browser 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,
+        })
+
+        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.restart(clean=True)
+        finally:
+            super(SessionStoreTestCase, self).tearDown()
+
+    def open_windows(self, window_sets, is_private=False):
+        """ Opens a set of windows with tabs pointing at some
+        URLs.
+
+        @param window_sets (list)
+               A set of URL tuples. Each tuple within window_sets
+               represents a window, and each URL in the URL
+               tuples represents what will be loaded in a tab.
+
+               Note that if is_private is False, then the first
+               URL tuple will be opened in the current window, and
+               subequent tuples will be opened in new windows.
+
+               Example:
+
+               set(
+                   (self.marionette.absolute_url('layout/mozilla_1.html'),
+                    self.marionette.absolute_url('layout/mozilla_2.html')),
+
+                   (self.marionette.absolute_url('layout/mozilla_3.html'),
+                    self.marionette.absolute_url('layout/mozilla_4.html')),
+               )
+
+               This would take the currently open window, and load
+               mozilla_1.html and mozilla_2.html in new tabs. It would
+               then open a new, second window, and load tabs at
+               mozilla_3.html and mozilla_4.html.
+        @param is_private (boolean, optional)
+               Whether or not any new windows should be a private browsing
+               windows.
+        """
+
+        if (is_private):
+            win = self.browser.open_browser(is_private=True)
+            win.switch_to()
+        else:
+            win = self.browser
+
+        for index, urls in enumerate(window_sets):
+            if index > 0:
+                win = self.browser.open_browser(is_private=is_private)
+            win.switch_to()
+            self.open_tabs(win, urls)
+
+    def open_tabs(self, win, urls):
+        """ Opens a set of URLs inside a window in new tabs.
+
+        @param win (browser window)
+               The browser window to load the tabs in.
+        @param urls (tuple)
+               A tuple of URLs to load in this window. The
+               first URL will be loaded in the currently selected
+               browser tab. Subsequent URLs will be loaded in
+               new tabs.
+        """
+        # If there are any remaining URLs for this window,
+        # open some new tabs and navigate to them.
+        with self.marionette.using_context('content'):
+            if isinstance(urls, str):
+                self.marionette.navigate(urls)
+            else:
+                for index, url in enumerate(urls):
+                    if index > 0:
+                        with self.marionette.using_context('chrome'):
+                            win.tabbar.open_tab()
+                    self.marionette.navigate(url)
+
+    def convert_open_windows_to_set(self):
+        windows = self.puppeteer.windows.all
+
+        # There's no guarantee that Marionette will return us an
+        # iterator for the opened windows that will match the
+        # order within our window list. Instead, we'll convert
+        # the list of URLs within each open window to a set of
+        # tuples that will allow us to do a direct comparison
+        # while allowing the windows to be in any order.
+
+        opened_windows = set()
+        for win in windows:
+            urls = tuple()
+            for tab in win.tabbar.tabs:
+                urls = urls + tuple([tab.location])
+            opened_windows.add(urls)
+        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_nt':
+            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)
--- a/testing/firefox-ui/tests/puppeteer/test_page_info_window.py
+++ b/testing/firefox-ui/tests/puppeteer/test_page_info_window.py
@@ -64,17 +64,17 @@ class TestPageInfoWindow(PuppeteerMixin,
 
         open_strategies = ('menu',
                            'shortcut',
                            opener,
                            )
 
         platformName = self.marionette.session_capabilities['platformName']
         for trigger in open_strategies:
-            if trigger == 'shortcut' and platformName == 'windows':
+            if trigger == 'shortcut' and platformName == 'windows_nt':
                 # The shortcut for page info window does not exist on windows.
                 self.assertRaises(ValueError, self.browser.open_page_info_window,
                                   trigger=trigger)
                 continue
 
             page_info = self.browser.open_page_info_window(trigger=trigger)
             self.assertEquals(page_info, self.puppeteer.windows.current)
             page_info.close()
@@ -88,14 +88,14 @@ class TestPageInfoWindow(PuppeteerMixin,
         # Close a tab by each trigger method
         close_strategies = ('menu',
                             'shortcut',
                             closer,
                             )
         platformName = self.marionette.session_capabilities['platformName']
         for trigger in close_strategies:
             # menu only works on OS X
-            if trigger == 'menu' and platformName != 'mac':
+            if trigger == 'menu' and platformName != 'darwin':
                 continue
 
             page_info = self.browser.open_page_info_window()
             page_info.close(trigger=trigger)
             self.assertTrue(page_info.closed)
--- a/testing/marionette/driver.js
+++ b/testing/marionette/driver.js
@@ -12,20 +12,16 @@ ChromeUtils.import("chrome://marionette/
 const {Addon} = ChromeUtils.import("chrome://marionette/content/addon.js", {});
 ChromeUtils.import("chrome://marionette/content/assert.js");
 ChromeUtils.import("chrome://marionette/content/atom.js");
 const {
   browser,
   Context,
   WindowState,
 } = ChromeUtils.import("chrome://marionette/content/browser.js", {});
-const {
-  Capabilities,
-  Timeouts,
-} = ChromeUtils.import("chrome://marionette/content/capabilities.js", {});
 ChromeUtils.import("chrome://marionette/content/capture.js");
 const {
   CertificateOverrideManager,
   InsecureSweepingOverride,
 } = ChromeUtils.import("chrome://marionette/content/cert.js", {});
 ChromeUtils.import("chrome://marionette/content/cookie.js");
 const {
   ChromeWebElement,
@@ -50,16 +46,17 @@ const {pprint} = ChromeUtils.import("chr
 ChromeUtils.import("chrome://marionette/content/interaction.js");
 ChromeUtils.import("chrome://marionette/content/l10n.js");
 ChromeUtils.import("chrome://marionette/content/legacyaction.js");
 const {Log} = ChromeUtils.import("chrome://marionette/content/log.js", {});
 ChromeUtils.import("chrome://marionette/content/modal.js");
 const {MarionettePrefs} = ChromeUtils.import("chrome://marionette/content/prefs.js", {});
 ChromeUtils.import("chrome://marionette/content/proxy.js");
 ChromeUtils.import("chrome://marionette/content/reftest.js");
+ChromeUtils.import("chrome://marionette/content/session.js");
 const {
   PollPromise,
   TimedPromise,
 } = ChromeUtils.import("chrome://marionette/content/sync.js", {});
 
 XPCOMUtils.defineLazyGetter(this, "logger", Log.get);
 XPCOMUtils.defineLazyGlobalGetters(this, ["URL"]);
 
@@ -142,17 +139,17 @@ this.GeckoDriver = function(appId, serve
   this._browserIds = new WeakMap();
 
   // Use content context by default
   this.context = Context.Content;
 
   this.sandboxes = new Sandboxes(() => this.getCurrentWindow());
   this.legacyactions = new legacyaction.Chain();
 
-  this.capabilities = new Capabilities();
+  this.capabilities = new session.Capabilities();
 
   this.mm = globalMessageManager;
   this.listener = proxy.toListener(
       this.sendAsync.bind(this), () => this.curBrowser);
 
   // points to an alert instance if a modal dialog is present
   this.dialog = null;
   this.dialogHandler = this.globalModalDialogHandler.bind(this);
@@ -701,17 +698,17 @@ GeckoDriver.prototype.listeningPromise =
  */
 GeckoDriver.prototype.newSession = async function(cmd) {
   if (this.sessionID) {
     throw new SessionNotCreatedError("Maximum number of active sessions");
   }
   this.sessionID = WebElement.generateUUID();
 
   try {
-    this.capabilities = Capabilities.fromJSON(cmd.parameters);
+    this.capabilities = session.Capabilities.fromJSON(cmd.parameters);
 
     if (!this.secureTLS) {
       logger.warn("TLS certificate errors will be ignored for this session");
       let acceptAllCerts = new InsecureSweepingOverride();
       CertificateOverrideManager.install(acceptAllCerts);
     }
 
     if (this.proxy.init()) {
@@ -1870,17 +1867,17 @@ GeckoDriver.prototype.getTimeouts = func
  *
  * @throws {InvalidArgumentError}
  *     If timeout type key is unknown, or the value provided with it is
  *     not an integer.
  */
 GeckoDriver.prototype.setTimeouts = function(cmd) {
   // merge with existing timeouts
   let merged = Object.assign(this.timeouts.toJSON(), cmd.parameters);
-  this.timeouts = Timeouts.fromJSON(merged);
+  this.timeouts = session.Timeouts.fromJSON(merged);
 };
 
 /** Single tap. */
 GeckoDriver.prototype.singleTap = async function(cmd) {
   assert.open(this.getCurrentWindow());
 
   let {id, x, y} = cmd.parameters;
   let webEl = WebElement.fromUUID(id, this.context);
@@ -2853,17 +2850,17 @@ GeckoDriver.prototype.deleteSession = fu
   }
 
   modal.removeHandler(this.dialogHandler);
 
   this.sandboxes.clear();
   CertificateOverrideManager.uninstall();
 
   this.sessionID = null;
-  this.capabilities = new Capabilities();
+  this.capabilities = new session.Capabilities();
 };
 
 /**
  * Takes a screenshot of a web element, current frame, or viewport.
  *
  * The screen capture is returned as a lossless PNG image encoded as
  * a base 64 string.
  *
--- a/testing/marionette/format.js
+++ b/testing/marionette/format.js
@@ -7,41 +7,42 @@
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 const {Log} = ChromeUtils.import("chrome://marionette/content/log.js", {});
 
 XPCOMUtils.defineLazyGetter(this, "log", Log.get);
 
 this.EXPORTED_SYMBOLS = ["pprint", "truncate"];
 
-const ELEMENT_NODE = 1;
 const MAX_STRING_LENGTH = 250;
 
 /**
  * Pretty-print values passed to template strings.
  *
- * Usage::
+ * Usage:
  *
+ * <pre><code>
  *     const {pprint} = Cu.import("chrome://marionette/content/error.js", {});
  *     let bool = {value: true};
  *     pprint`Expected boolean, got ${bool}`;
  *     => 'Expected boolean, got [object Object] {"value": true}'
  *
  *     let htmlElement = document.querySelector("input#foo");
  *     pprint`Expected element ${htmlElement}`;
  *     => 'Expected element <input id="foo" class="bar baz" type="input">'
  *
  *     pprint`Current window: ${window}`;
  *     => '[object Window https://www.mozilla.org/]'
+ * </code></pre>
  */
 function pprint(ss, ...values) {
   function pretty(val) {
     let proto = Object.prototype.toString.call(val);
-    if (typeof val == "object" && val !== null &&
-        "nodeType" in val && val.nodeType === ELEMENT_NODE) {
+
+    if (val && val.nodeType === 1) {
       return prettyElement(val);
     } else if (["[object Window]", "[object ChromeWindow]"].includes(proto)) {
       return prettyWindowGlobal(val);
     } else if (proto == "[object Attr]") {
       return prettyAttr(val);
     }
     return prettyObject(val);
   }
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_capabilities.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_capabilities.py
@@ -16,27 +16,18 @@ class TestCapabilities(MarionetteTestCas
         with self.marionette.using_context("chrome"):
             self.appinfo = self.marionette.execute_script("""
                 return {
                   name: Services.appinfo.name,
                   version: Services.appinfo.version,
                   processID: Services.appinfo.processID,
                 }
                 """)
-            self.os_name = self.marionette.execute_script("""
-                let name = Services.sysinfo.getProperty("name");
-                switch (name) {
-                  case "Windows_NT":
-                    return "windows";
-                  case "Darwin":
-                    return "mac";
-                  default:
-                    return name.toLowerCase();
-                }
-                """)
+            self.os_name = self.marionette.execute_script(
+                "return Services.sysinfo.getProperty('name')").lower()
             self.os_version = self.marionette.execute_script(
                 "return Services.sysinfo.getProperty('version')")
 
     def get_fennec_profile(self):
         profile = self.marionette.instance.runner.device.app_ctx.remote_profile
         if self.caps["moz:profile"].lower() != profile.lower():
             # mozdevice may be using a symlink and readlink is not
             # universally available (missing from sdk 18).
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_key_actions.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_key_actions.py
@@ -16,17 +16,17 @@ from marionette_harness import Marionett
 def inline(doc):
     return "data:text/html;charset=utf-8,{}".format(urllib.quote(doc))
 
 
 class TestKeyActions(WindowManagerMixin, MarionetteTestCase):
 
     def setUp(self):
         super(TestKeyActions, self).setUp()
-        if self.marionette.session_capabilities["platformName"] == "mac":
+        if self.marionette.session_capabilities["platformName"] == "darwin":
             self.mod_key = Keys.META
         else:
             self.mod_key = Keys.CONTROL
         test_html = self.marionette.absolute_url("keyboard.html")
         self.marionette.navigate(test_html)
         self.reporter_element = self.marionette.find_element(By.ID, "keyReporter")
         self.reporter_element.click()
         self.key_action = Actions(self.marionette)
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_legacy_mouse_action.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_legacy_mouse_action.py
@@ -17,17 +17,17 @@ def inline(doc):
     return "data:text/html;charset=utf-8,{}".format(urllib.quote(doc))
 
 
 class BaseLegacyMouseAction(MarionetteTestCase):
 
     def setUp(self):
         super(BaseLegacyMouseAction, self).setUp()
 
-        if self.marionette.session_capabilities["platformName"] == "mac":
+        if self.marionette.session_capabilities["platformName"] == "darwin":
             self.mod_key = Keys.META
         else:
             self.mod_key = Keys.CONTROL
 
         self.action = Actions(self.marionette)
 
 
 class TestLegacyMouseAction(BaseLegacyMouseAction):
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_mouse_action.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_mouse_action.py
@@ -60,17 +60,17 @@ class Actions(object):
         return self
 
 
 class BaseMouseAction(MarionetteTestCase):
 
     def setUp(self):
         super(BaseMouseAction, self).setUp()
 
-        if self.marionette.session_capabilities["platformName"] == "mac":
+        if self.marionette.session_capabilities["platformName"] == "darwin":
             self.mod_key = Keys.META
         else:
             self.mod_key = Keys.CONTROL
 
         self.action = Actions(self.marionette)
 
     def tearDown(self):
         super(BaseMouseAction, self).tearDown()
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_navigation.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_navigation.py
@@ -45,17 +45,17 @@ class BaseNavigationTestCase(WindowManag
         self.test_page_file_url = "file:///{}".format(file_path)
         self.test_page_frameset = self.marionette.absolute_url("frameset.html")
         self.test_page_insecure = self.fixtures.where_is("test.html", on="https")
         self.test_page_not_remote = "about:robots"
         self.test_page_push_state = self.marionette.absolute_url("navigation_pushstate.html")
         self.test_page_remote = self.marionette.absolute_url("test.html")
         self.test_page_slow_resource = self.marionette.absolute_url("slow_resource.html")
 
-        if self.marionette.session_capabilities["platformName"] == "mac":
+        if self.marionette.session_capabilities["platformName"] == "darwin":
             self.mod_key = Keys.META
         else:
             self.mod_key = Keys.CONTROL
 
         def open_with_link():
             link = self.marionette.find_element(By.ID, "new-blank-tab")
             link.click()
 
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_switch_window_content.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_switch_window_content.py
@@ -10,17 +10,17 @@ from marionette_driver.keys import Keys
 from marionette_harness import MarionetteTestCase, skip_if_mobile, WindowManagerMixin
 
 
 class TestSwitchToWindowContent(WindowManagerMixin, MarionetteTestCase):
 
     def setUp(self):
         super(TestSwitchToWindowContent, self).setUp()
 
-        if self.marionette.session_capabilities["platformName"] == "mac":
+        if self.marionette.session_capabilities["platformName"] == "darwin":
             self.mod_key = Keys.META
         else:
             self.mod_key = Keys.CONTROL
 
         self.empty_page = self.marionette.absolute_url("empty.html")
         self.test_page = self.marionette.absolute_url("windowHandles.html")
 
         self.selected_tab_index = self.get_selected_tab_index()
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_typing.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_typing.py
@@ -17,17 +17,17 @@ def inline(doc):
     return "data:text/html;charset=utf-8,{}".format(urllib.quote(doc))
 
 
 class TypingTestCase(MarionetteTestCase):
 
     def setUp(self):
         super(TypingTestCase, self).setUp()
 
-        if self.marionette.session_capabilities["platformName"] == "mac":
+        if self.marionette.session_capabilities["platformName"] == "darwin":
             self.mod_key = Keys.META
         else:
             self.mod_key = Keys.CONTROL
 
 
 class TestTypingChrome(TypingTestCase):
 
     def setUp(self):
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_window_maximize.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_window_maximize.py
@@ -26,17 +26,17 @@ class TestWindowMaximize(MarionetteTestC
 
         self.original_size = actual
 
     def tearDown(self):
         self.marionette.set_window_rect(
             width=self.original_size["width"], height=self.original_size["height"])
 
     def assert_window_maximized(self, actual, delta=None):
-        if self.marionette.session_capabilities["platformName"] == "windows":
+        if self.marionette.session_capabilities["platformName"] == "windows_nt":
             delta = 16
         else:
             delta = 22
 
         self.assertGreaterEqual(
             actual["width"], self.max["width"] - delta,
             msg="Window width is not within {delta} px of availWidth: "
                 "current width {current} should be greater than or equal to max width {max}"
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_window_rect.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_window_rect.py
@@ -181,23 +181,23 @@ class TestWindowRect(MarionetteTestCase)
         elif os == "linux":
             # certain WMs prohibit windows from being moved off-screen
             self.assertLessEqual(new_position["x"], 0)
             self.assertLessEqual(new_position["y"], 0)
 
         # On macOS, windows can only be moved off the screen on the
         # horizontal axis.  The system menu bar also blocks windows from
         # being moved to (0,0).
-        elif os == "mac":
+        elif os == "darwin":
             self.assertEqual(-8, new_position["x"])
             self.assertEqual(23, new_position["y"])
 
         # It turns out that Windows is the only platform on which the
         # window can be reliably positioned off-screen.
-        elif os == "windows":
+        elif os == "windows_nt":
             self.assertEqual(-8, new_position["x"])
             self.assertEqual(-8, new_position["y"])
 
     def test_resize_larger_than_screen(self):
         new_size = self.marionette.set_window_rect(
             width=self.max["width"] * 2, height=self.max["height"] * 2)
         actual_size = self.marionette.window_rect
 
--- a/testing/marionette/jar.mn
+++ b/testing/marionette/jar.mn
@@ -5,17 +5,16 @@
 marionette.jar:
 % content marionette %content/
   content/accessibility.js (accessibility.js)
   content/action.js (action.js)
   content/addon.js (addon.js)
   content/assert.js (assert.js)
   content/atom.js (atom.js)
   content/browser.js (browser.js)
-  content/capabilities.js (capabilities.js)
   content/capture.js (capture.js)
   content/cert.js (cert.js)
   content/cookie.js (cookie.js)
   content/dom.js (dom.js)
   content/driver.js (driver.js)
   content/element.js (element.js)
   content/error.js (error.js)
   content/evaluate.js (evaluate.js)
@@ -30,16 +29,17 @@ marionette.jar:
   content/modal.js (modal.js)
   content/navigate.js (navigate.js)
   content/packets.js (packets.js)
   content/prefs.js (prefs.js)
   content/proxy.js (proxy.js)
   content/reftest.js (reftest.js)
   content/reftest.xul (reftest.xul)
   content/server.js (server.js)
+  content/session.js (session.js)
   content/stream-utils.js (stream-utils.js)
   content/sync.js (sync.js)
   content/transport.js (transport.js)
 #ifdef ENABLE_TESTS
   content/test2.xul (chrome/test2.xul)
   content/test_anonymous_content.xul (chrome/test_anonymous_content.xul)
   content/test_dialog.dtd (chrome/test_dialog.dtd)
   content/test_dialog.properties (chrome/test_dialog.properties)
--- a/testing/marionette/listener.js
+++ b/testing/marionette/listener.js
@@ -12,20 +12,16 @@ const winUtil = content.QueryInterface(C
 
 ChromeUtils.import("resource://gre/modules/FileUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 ChromeUtils.import("chrome://marionette/content/accessibility.js");
 ChromeUtils.import("chrome://marionette/content/action.js");
 ChromeUtils.import("chrome://marionette/content/atom.js");
-const {
-  Capabilities,
-  PageLoadStrategy,
-} = ChromeUtils.import("chrome://marionette/content/capabilities.js", {});
 ChromeUtils.import("chrome://marionette/content/capture.js");
 const {
   element,
   WebElement,
 } = ChromeUtils.import("chrome://marionette/content/element.js", {});
 const {
   ElementNotInteractableError,
   InsecureCertificateError,
@@ -40,16 +36,17 @@ ChromeUtils.import("chrome://marionette/
 ChromeUtils.import("chrome://marionette/content/event.js");
 const {ContentEventObserverService} = ChromeUtils.import("chrome://marionette/content/dom.js", {});
 const {pprint, truncate} = ChromeUtils.import("chrome://marionette/content/format.js", {});
 ChromeUtils.import("chrome://marionette/content/interaction.js");
 ChromeUtils.import("chrome://marionette/content/legacyaction.js");
 const {Log} = ChromeUtils.import("chrome://marionette/content/log.js", {});
 ChromeUtils.import("chrome://marionette/content/navigate.js");
 ChromeUtils.import("chrome://marionette/content/proxy.js");
+ChromeUtils.import("chrome://marionette/content/session.js");
 
 XPCOMUtils.defineLazyGetter(this, "logger", () => Log.getWithPrefix(outerWindowID));
 XPCOMUtils.defineLazyGlobalGetters(this, ["URL"]);
 
 let {outerWindowID} = winUtil;
 let curContainer = {frame: content, shadowRoot: null};
 
 // Listen for click event to indicate one click has happened, so actions
@@ -68,17 +65,17 @@ const SUPPORTED_STRATEGIES = new Set([
   element.Strategy.PartialLinkText,
   element.Strategy.TagName,
   element.Strategy.XPath,
 ]);
 
 Object.defineProperty(this, "capabilities", {
   get() {
     let payload = sendSyncMessage("Marionette:WebDriver:GetCapabilities");
-    return Capabilities.fromJSON(payload[0]);
+    return session.Capabilities.fromJSON(payload[0]);
   },
   configurable: true,
 });
 
 let legacyactions = new legacyaction.Chain();
 
 // last touch for each fingerId
 let multiLast = {};
@@ -276,17 +273,18 @@ const loadListener = {
           finished = true;
 
         // Return early with a page load strategy of eager, and also
         // special-case about:blocked pages which should be treated as
         // non-error pages but do not raise a pageshow event. about:blank
         // is also treaded specifically here, because it gets temporary
         // loaded for new content processes, and we only want to rely on
         // complete loads for it.
-        } else if ((capabilities.get("pageLoadStrategy") === PageLoadStrategy.Eager &&
+        } else if ((capabilities.get("pageLoadStrategy") ===
+            session.PageLoadStrategy.Eager &&
             documentURI != "about:blank") ||
             /about:blocked\?/.exec(documentURI)) {
           this.stop();
           sendOk(this.commandID);
           finished = true;
         }
 
         break;
@@ -394,17 +392,18 @@ const loadListener = {
    * @param {string=} url
    *     Optional URL, which is used to check if a page load is expected.
    */
   navigate(trigger, commandID, timeout, loadEventExpected = true,
       useUnloadTimer = false) {
 
     // Only wait if the page load strategy is not `none`
     loadEventExpected = loadEventExpected &&
-        (capabilities.get("pageLoadStrategy") !== PageLoadStrategy.None);
+        (capabilities.get("pageLoadStrategy") !==
+        session.PageLoadStrategy.None);
 
     if (loadEventExpected) {
       let startTime = new Date().getTime();
       this.start(commandID, timeout, startTime, true);
     }
 
     return (async () => {
       await trigger();
--- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/api/keys.py
+++ b/testing/marionette/puppeteer/firefox/firefox_puppeteer/api/keys.py
@@ -3,19 +3,17 @@
 # You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from __future__ import absolute_import
 
 import marionette_driver
 
 
 class Keys(marionette_driver.keys.Keys):
-    """
-    Proxy to Marionette's keys with an "accel" provided for convenience
-    testing across platforms.
-    """
+    """Proxy to marionette's keys with an "accel" provided for convenience
+    testing across platforms."""
 
     def __init__(self, marionette):
-        self.is_mac = marionette.session_capabilities["platformName"] == "mac"
+        self.isDarwin = marionette.session_capabilities['platformName'] == 'darwin'
 
     @property
     def ACCEL(self):
-        return self.META if self.is_mac else self.CONTROL
+        return self.META if self.isDarwin else self.CONTROL
--- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/browser/window.py
+++ b/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/browser/window.py
@@ -241,17 +241,17 @@ class BrowserWindow(BaseWindow):
         """
         def callback(win):
             # Prepare action which triggers the opening of the browser window
             if callable(trigger):
                 trigger(win)
             elif trigger == 'menu':
                 self.menubar.select_by_id('tools-menu', 'menu_pageInfo')
             elif trigger == 'shortcut':
-                if win.marionette.session_capabilities['platformName'] == 'windows':
+                if win.marionette.session_capabilities['platformName'] == 'windows_nt':
                     raise ValueError('Page info shortcut not available on Windows.')
                 win.send_shortcut(win.localize_entity('pageInfoCmd.commandkey'),
                                   accel=True)
             elif trigger == 'context_menu':
                 # TODO: Add once we can do right clicks
                 pass
             else:
                 raise ValueError('Unknown opening method: "%s"' % trigger)
--- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/windows.py
+++ b/testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/windows.py
@@ -389,17 +389,17 @@ class BaseWindow(BaseLib):
 
         :param shift: Optional, If `True`, the `Shift` modifier key is pressed.
          Defaults to `False`.
         """
 
         platform = self.marionette.session_capabilities['platformName']
 
         keymap = {
-            'accel': Keys.META if platform == "mac" else Keys.CONTROL,
+            'accel': Keys.META if platform == 'darwin' else Keys.CONTROL,
             'alt': Keys.ALT,
             'cmd': Keys.COMMAND,
             'ctrl': Keys.CONTROL,
             'meta': Keys.META,
             'shift': Keys.SHIFT,
         }
 
         # Append all to press modifier keys
rename from testing/marionette/capabilities.js
rename to testing/marionette/session.js
--- a/testing/marionette/capabilities.js
+++ b/testing/marionette/session.js
@@ -13,55 +13,57 @@ const {
   InvalidArgumentError,
 } = ChromeUtils.import("chrome://marionette/content/error.js", {});
 const {
   pprint,
 } = ChromeUtils.import("chrome://marionette/content/format.js", {});
 
 XPCOMUtils.defineLazyGlobalGetters(this, ["URL"]);
 
-this.EXPORTED_SYMBOLS = [
-  "Capabilities",
-  "PageLoadStrategy",
-  "Proxy",
-  "Timeouts",
-];
+this.EXPORTED_SYMBOLS = ["session"];
 
 // Enable testing this module, as Services.appinfo.* is not available
 // in xpcshell tests.
 const appinfo = {name: "<missing>", version: "<missing>"};
 try { appinfo.name = Services.appinfo.name.toLowerCase(); } catch (e) {}
 try { appinfo.version = Services.appinfo.version; } catch (e) {}
 
+/**
+ * State associated with a WebDriver session.
+ *
+ * @namespace
+ */
+this.session = {};
+
 /** Representation of WebDriver session timeouts. */
-class Timeouts {
+session.Timeouts = class {
   constructor() {
     // disabled
     this.implicit = 0;
     // five mintues
     this.pageLoad = 300000;
     // 30 seconds
     this.script = 30000;
   }
 
-  toString() { return "[object Timeouts]"; }
+  toString() { return "[object session.Timeouts]"; }
 
   /** Marshals timeout durations to a JSON Object. */
   toJSON() {
     return {
       implicit: this.implicit,
       pageLoad: this.pageLoad,
       script: this.script,
     };
   }
 
   static fromJSON(json) {
     assert.object(json,
         pprint`Expected "timeouts" to be an object, got ${json}`);
-    let t = new Timeouts();
+    let t = new session.Timeouts();
 
     for (let [type, ms] of Object.entries(json)) {
       switch (type) {
         case "implicit":
           t.implicit = assert.positiveInteger(ms,
               pprint`Expected ${type} to be a positive integer, got ${ms}`);
           break;
 
@@ -77,40 +79,40 @@ class Timeouts {
 
         default:
           throw new InvalidArgumentError("Unrecognised timeout: " + type);
       }
     }
 
     return t;
   }
-}
+};
 
 /**
  * Enum of page loading strategies.
  *
  * @enum
  */
-const PageLoadStrategy = {
+session.PageLoadStrategy = {
   /** No page load strategy.  Navigation will return immediately. */
   None: "none",
   /**
    * Eager, causing navigation to complete when the document reaches
    * the <code>interactive</code> ready state.
    */
   Eager: "eager",
   /**
    * Normal, causing navigation to return when the document reaches the
    * <code>complete</code> ready state.
    */
   Normal: "normal",
 };
 
 /** Proxy configuration object representation. */
-class Proxy {
+session.Proxy = class {
   /** @class */
   constructor() {
     this.proxyType = null;
     this.ftpProxy = null;
     this.ftpProxyPort = null;
     this.httpProxy = null;
     this.httpProxyPort = null;
     this.noProxy = null;
@@ -250,17 +252,17 @@ class Proxy {
           url.hash != "") {
         throw new InvalidArgumentError(
             `${host} was not of the form host[:port]`);
       }
 
       return [hostname, port];
     }
 
-    let p = new Proxy();
+    let p = new session.Proxy();
     if (typeof json == "undefined" || json === null) {
       return p;
     }
 
     assert.object(json, pprint`Expected "proxy" to be an object, got ${json}`);
 
     assert.in("proxyType", json,
         pprint`Expected "proxyType" in "proxy" object, got ${json}`);
@@ -349,33 +351,33 @@ class Proxy {
       noProxy: excludes,
       sslProxy: toHost(this.sslProxy, this.sslProxyPort),
       socksProxy: toHost(this.socksProxy, this.socksProxyPort),
       socksVersion: this.socksVersion,
       proxyAutoconfigUrl: this.proxyAutoconfigUrl,
     });
   }
 
-  toString() { return "[object Proxy]"; }
-}
+  toString() { return "[object session.Proxy]"; }
+};
 
 /** WebDriver session capabilities representation. */
-class Capabilities extends Map {
+session.Capabilities = class extends Map {
   /** @class */
   constructor() {
     super([
       // webdriver
       ["browserName", appinfo.name],
       ["browserVersion", appinfo.version],
-      ["platformName", getWebDriverPlatformName()],
+      ["platformName", Services.sysinfo.getProperty("name").toLowerCase()],
       ["platformVersion", Services.sysinfo.getProperty("version")],
-      ["pageLoadStrategy", PageLoadStrategy.Normal],
+      ["pageLoadStrategy", session.PageLoadStrategy.Normal],
       ["acceptInsecureCerts", false],
-      ["timeouts", new Timeouts()],
-      ["proxy", new Proxy()],
+      ["timeouts", new session.Timeouts()],
+      ["proxy", new session.Proxy()],
 
       // features
       ["rotatable", appinfo.name == "B2G"],
 
       // proprietary
       ["moz:accessibilityChecks", false],
       ["moz:headless", Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo).isHeadless],
       ["moz:processID", Services.appinfo.processID],
@@ -387,91 +389,92 @@ class Capabilities extends Map {
 
   /**
    * @param {string} key
    *     Capability key.
    * @param {(string|number|boolean)} value
    *     JSON-safe capability value.
    */
   set(key, value) {
-    if (key === "timeouts" && !(value instanceof Timeouts)) {
+    if (key === "timeouts" && !(value instanceof session.Timeouts)) {
       throw new TypeError();
-    } else if (key === "proxy" && !(value instanceof Proxy)) {
+    } else if (key === "proxy" && !(value instanceof session.Proxy)) {
       throw new TypeError();
     }
 
     return super.set(key, value);
   }
 
-  toString() { return "[object Capabilities]"; }
+  /** @return {string} */
+  toString() { return "[object session.Capabilities]"; }
 
   /**
    * JSON serialisation of capabilities object.
    *
    * @return {Object.<string, ?>}
    */
   toJSON() {
     return marshal(this);
   }
 
   /**
    * Unmarshal a JSON object representation of WebDriver capabilities.
    *
    * @param {Object.<string, *>=} json
    *     WebDriver capabilities.
    *
-   * @return {Capabilities}
+   * @return {session.Capabilities}
    *     Internal representation of WebDriver capabilities.
    */
   static fromJSON(json) {
     if (typeof json == "undefined" || json === null) {
       json = {};
     }
     assert.object(json,
         pprint`Expected "capabilities" to be an object, got ${json}"`);
 
-    return Capabilities.match_(json);
+    return session.Capabilities.match_(json);
   }
 
   // Matches capabilities as described by WebDriver.
   static match_(json = {}) {
-    let matched = new Capabilities();
+    let matched = new session.Capabilities();
 
     for (let [k, v] of Object.entries(json)) {
       switch (k) {
         case "acceptInsecureCerts":
           assert.boolean(v,
               pprint`Expected ${k} to be a boolean, got ${v}`);
           matched.set("acceptInsecureCerts", v);
           break;
 
         case "pageLoadStrategy":
           if (v === null) {
-            matched.set("pageLoadStrategy", PageLoadStrategy.Normal);
+            matched.set("pageLoadStrategy", session.PageLoadStrategy.Normal);
           } else {
             assert.string(v,
                 pprint`Expected ${k} to be a string, got ${v}`);
 
-            if (Object.values(PageLoadStrategy).includes(v)) {
+            if (Object.values(session.PageLoadStrategy).includes(v)) {
               matched.set("pageLoadStrategy", v);
             } else {
               throw new InvalidArgumentError(
                   "Unknown page load strategy: " + v);
             }
           }
 
           break;
 
         case "proxy":
-          let proxy = Proxy.fromJSON(v);
+          let proxy = session.Proxy.fromJSON(v);
           matched.set("proxy", proxy);
           break;
 
         case "timeouts":
-          let timeouts = Timeouts.fromJSON(v);
+          let timeouts = session.Timeouts.fromJSON(v);
           matched.set("timeouts", timeouts);
           break;
 
         case "moz:accessibilityChecks":
           assert.boolean(v,
               pprint`Expected ${k} to be a boolean, got ${v}`);
           matched.set("moz:accessibilityChecks", v);
           break;
@@ -487,37 +490,17 @@ class Capabilities extends Map {
               pprint`Expected ${k} to be a boolean, got ${v}`);
           matched.set("moz:webdriverClick", v);
           break;
       }
     }
 
     return matched;
   }
-}
-
-this.Capabilities = Capabilities;
-this.PageLoadStrategy = PageLoadStrategy;
-this.Proxy = Proxy;
-this.Timeouts = Timeouts;
-
-function getWebDriverPlatformName() {
-  let name = Services.sysinfo.getProperty("name");
-
-  switch (name) {
-    case "Windows_NT":
-      return "windows";
-
-    case "Darwin":
-      return "mac";
-
-    default:
-      return name.toLowerCase();
-  }
-}
+};
 
 // Specialisation of |JSON.stringify| that produces JSON-safe object
 // literals, dropping empty objects and entries which values are undefined
 // or null.  Objects are allowed to produce their own JSON representations
 // by implementing a |toJSON| function.
 function marshal(obj) {
   let rv = Object.create(null);
 
rename from testing/marionette/test/unit/test_capabilities.js
rename to testing/marionette/test/unit/test_session.js
--- a/testing/marionette/test/unit/test_capabilities.js
+++ b/testing/marionette/test/unit/test_session.js
@@ -3,106 +3,101 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/Preferences.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 const {InvalidArgumentError} = ChromeUtils.import("chrome://marionette/content/error.js", {});
-const {
-  Capabilities,
-  PageLoadStrategy,
-  Proxy,
-  Timeouts,
-} = ChromeUtils.import("chrome://marionette/content/capabilities.js", {});
+ChromeUtils.import("chrome://marionette/content/session.js");
 
 add_test(function test_Timeouts_ctor() {
-  let ts = new Timeouts();
+  let ts = new session.Timeouts();
   equal(ts.implicit, 0);
   equal(ts.pageLoad, 300000);
   equal(ts.script, 30000);
 
   run_next_test();
 });
 
 add_test(function test_Timeouts_toString() {
-  equal(new Timeouts().toString(), "[object Timeouts]");
+  equal(new session.Timeouts().toString(), "[object session.Timeouts]");
 
   run_next_test();
 });
 
 add_test(function test_Timeouts_toJSON() {
-  let ts = new Timeouts();
+  let ts = new session.Timeouts();
   deepEqual(ts.toJSON(), {"implicit": 0, "pageLoad": 300000, "script": 30000});
 
   run_next_test();
 });
 
 add_test(function test_Timeouts_fromJSON() {
   let json = {
     implicit: 10,
     pageLoad: 20,
     script: 30,
   };
-  let ts = Timeouts.fromJSON(json);
+  let ts = session.Timeouts.fromJSON(json);
   equal(ts.implicit, json.implicit);
   equal(ts.pageLoad, json.pageLoad);
   equal(ts.script, json.script);
 
   run_next_test();
 });
 
 add_test(function test_Timeouts_fromJSON_unrecognised_field() {
   let json = {
     sessionId: "foobar",
     script: 42,
   };
   try {
-    Timeouts.fromJSON(json);
+    session.Timeouts.fromJSON(json);
   } catch (e) {
     equal(e.name, InvalidArgumentError.name);
     equal(e.message, "Unrecognised timeout: sessionId");
   }
 
   run_next_test();
 });
 
 add_test(function test_Timeouts_fromJSON_invalid_type() {
   try {
-    Timeouts.fromJSON({script: "foobar"});
+    session.Timeouts.fromJSON({script: "foobar"});
   } catch (e) {
     equal(e.name, InvalidArgumentError.name);
     equal(e.message, "Expected [object String] \"script\" to be a positive integer, got [object String] \"foobar\"");
   }
 
   run_next_test();
 });
 
 add_test(function test_Timeouts_fromJSON_bounds() {
   try {
-    Timeouts.fromJSON({script: -42});
+    session.Timeouts.fromJSON({script: -42});
   } catch (e) {
     equal(e.name, InvalidArgumentError.name);
     equal(e.message, "Expected [object String] \"script\" to be a positive integer, got [object Number] -42");
   }
 
   run_next_test();
 });
 
 add_test(function test_PageLoadStrategy() {
-  equal(PageLoadStrategy.None, "none");
-  equal(PageLoadStrategy.Eager, "eager");
-  equal(PageLoadStrategy.Normal, "normal");
+  equal(session.PageLoadStrategy.None, "none");
+  equal(session.PageLoadStrategy.Eager, "eager");
+  equal(session.PageLoadStrategy.Normal, "normal");
 
   run_next_test();
 });
 
 add_test(function test_Proxy_ctor() {
-  let p = new Proxy();
+  let p = new session.Proxy();
   let props = [
     "proxyType",
     "httpProxy",
     "sslProxy",
     "ftpProxy",
     "socksProxy",
     "socksVersion",
     "proxyAutoconfigUrl",
@@ -111,52 +106,52 @@ add_test(function test_Proxy_ctor() {
     ok(prop in p, `${prop} in ${JSON.stringify(props)}`);
     equal(p[prop], null);
   }
 
   run_next_test();
 });
 
 add_test(function test_Proxy_init() {
-  let p = new Proxy();
+  let p = new session.Proxy();
 
   // no changed made, and 5 (system) is default
   equal(p.init(), false);
   equal(Preferences.get("network.proxy.type"), 5);
 
   // pac
   p.proxyType = "pac";
   p.proxyAutoconfigUrl = "http://localhost:1234";
   ok(p.init());
 
   equal(Preferences.get("network.proxy.type"), 2);
   equal(Preferences.get("network.proxy.autoconfig_url"),
       "http://localhost:1234");
 
   // direct
-  p = new Proxy();
+  p = new session.Proxy();
   p.proxyType = "direct";
   ok(p.init());
   equal(Preferences.get("network.proxy.type"), 0);
 
   // autodetect
-  p = new Proxy();
+  p = new session.Proxy();
   p.proxyType = "autodetect";
   ok(p.init());
   equal(Preferences.get("network.proxy.type"), 4);
 
   // system
-  p = new Proxy();
+  p = new session.Proxy();
   p.proxyType = "system";
   ok(p.init());
   equal(Preferences.get("network.proxy.type"), 5);
 
   // manual
   for (let proxy of ["ftp", "http", "ssl", "socks"]) {
-    p = new Proxy();
+    p = new session.Proxy();
     p.proxyType = "manual";
     p.noProxy = ["foo", "bar"];
     p[`${proxy}Proxy`] = "foo";
     p[`${proxy}ProxyPort`] = 42;
     if (proxy === "socks") {
       p[`${proxy}Version`] = 4;
     }
 
@@ -166,50 +161,50 @@ add_test(function test_Proxy_init() {
     equal(Preferences.get(`network.proxy.${proxy}`), "foo");
     equal(Preferences.get(`network.proxy.${proxy}_port`), 42);
     if (proxy === "socks") {
       equal(Preferences.get(`network.proxy.${proxy}_version`), 4);
     }
   }
 
   // empty no proxy should reset default exclustions
-  p = new Proxy();
+  p = new session.Proxy();
   p.proxyType = "manual";
   p.noProxy = [];
   ok(p.init());
   equal(Preferences.get("network.proxy.no_proxies_on"), "");
 
   run_next_test();
 });
 
 add_test(function test_Proxy_toString() {
-  equal(new Proxy().toString(), "[object Proxy]");
+  equal(new session.Proxy().toString(), "[object session.Proxy]");
 
   run_next_test();
 });
 
 add_test(function test_Proxy_toJSON() {
-  let p = new Proxy();
+  let p = new session.Proxy();
   deepEqual(p.toJSON(), {});
 
   // autoconfig url
-  p = new Proxy();
+  p = new session.Proxy();
   p.proxyType = "pac";
   p.proxyAutoconfigUrl = "foo";
   deepEqual(p.toJSON(), {proxyType: "pac", proxyAutoconfigUrl: "foo"});
 
   // manual proxy
-  p = new Proxy();
+  p = new session.Proxy();
   p.proxyType = "manual";
   deepEqual(p.toJSON(), {proxyType: "manual"});
 
   for (let proxy of ["ftpProxy", "httpProxy", "sslProxy", "socksProxy"]) {
     let expected = {proxyType: "manual"};
 
-    p = new Proxy();
+    p = new session.Proxy();
     p.proxyType = "manual";
 
     if (proxy == "socksProxy") {
       p.socksVersion = 5;
       expected.socksVersion = 5;
     }
 
     // without port
@@ -231,69 +226,69 @@ add_test(function test_Proxy_toJSON() {
     p[proxy] = "2001:db8::1";
     p[`${proxy}Port`] = 42;
     expected[proxy] = "foo:42";
     expected[proxy] = "[2001:db8::1]:42";
     deepEqual(p.toJSON(), expected);
   }
 
   // noProxy: add brackets for IPv6 address
-  p = new Proxy();
+  p = new session.Proxy();
   p.proxyType = "manual";
   p.noProxy = ["2001:db8::1"];
   let expected = {proxyType: "manual", noProxy: "[2001:db8::1]"};
   deepEqual(p.toJSON(), expected);
 
   run_next_test();
 });
 
 add_test(function test_Proxy_fromJSON() {
-  let p = new Proxy();
-  deepEqual(p, Proxy.fromJSON(undefined));
-  deepEqual(p, Proxy.fromJSON(null));
+  let p = new session.Proxy();
+  deepEqual(p, session.Proxy.fromJSON(undefined));
+  deepEqual(p, session.Proxy.fromJSON(null));
 
   for (let typ of [true, 42, "foo", []]) {
-    Assert.throws(() => Proxy.fromJSON(typ), /InvalidArgumentError/);
+    Assert.throws(() => session.Proxy.fromJSON(typ), /InvalidArgumentError/);
   }
 
   // must contain a valid proxyType
-  Assert.throws(() => Proxy.fromJSON({}), /InvalidArgumentError/);
-  Assert.throws(() => Proxy.fromJSON({proxyType: "foo"}),
+  Assert.throws(() => session.Proxy.fromJSON({}), /InvalidArgumentError/);
+  Assert.throws(() => session.Proxy.fromJSON({proxyType: "foo"}),
       /InvalidArgumentError/);
 
   // autoconfig url
   for (let url of [true, 42, [], {}]) {
-    Assert.throws(() => Proxy.fromJSON(
+    Assert.throws(() => session.Proxy.fromJSON(
         {proxyType: "pac", proxyAutoconfigUrl: url}), /InvalidArgumentError/);
   }
 
-  p = new Proxy();
+  p = new session.Proxy();
   p.proxyType = "pac";
   p.proxyAutoconfigUrl = "foo";
   deepEqual(p,
-      Proxy.fromJSON({proxyType: "pac", proxyAutoconfigUrl: "foo"}));
+      session.Proxy.fromJSON({proxyType: "pac", proxyAutoconfigUrl: "foo"}));
 
   // manual proxy
-  p = new Proxy();
+  p = new session.Proxy();
   p.proxyType = "manual";
-  deepEqual(p, Proxy.fromJSON({proxyType: "manual"}));
+  deepEqual(p, session.Proxy.fromJSON({proxyType: "manual"}));
 
   for (let proxy of ["httpProxy", "sslProxy", "ftpProxy", "socksProxy"]) {
     let manual = {proxyType: "manual"};
 
     // invalid hosts
     for (let host of [true, 42, [], {}, null, "http://foo",
       "foo:-1", "foo:65536", "foo/test", "foo#42", "foo?foo=bar",
       "2001:db8::1"]) {
       manual[proxy] = host;
-      Assert.throws(() => Proxy.fromJSON(manual),
+      Assert.throws(() => session.Proxy.fromJSON(manual),
           /InvalidArgumentError/);
     }
 
-    p = new Proxy();
+    p = new session.Proxy();
     p.proxyType = "manual";
     if (proxy == "socksProxy") {
       manual.socksVersion = 5;
       p.socksVersion = 5;
     }
 
     let host_map = {
       "foo:1": {hostname: "foo", port: 1},
@@ -307,17 +302,17 @@ add_test(function test_Proxy_fromJSON() 
 
     // valid proxy hosts with port
     for (let host in host_map) {
       manual[proxy] = host;
 
       p[`${proxy}`] = host_map[host].hostname;
       p[`${proxy}Port`] = host_map[host].port;
 
-      deepEqual(p, Proxy.fromJSON(manual));
+      deepEqual(p, session.Proxy.fromJSON(manual));
     }
 
     // Without a port the default port of the scheme is used
     for (let host of ["foo", "foo:"]) {
       manual[proxy] = host;
 
       // For socks no default port is available
       p[proxy] = `foo`;
@@ -325,83 +320,82 @@ add_test(function test_Proxy_fromJSON() 
         p[`${proxy}Port`] = null;
       } else {
         let default_ports = {"ftpProxy": 21, "httpProxy": 80,
           "sslProxy": 443};
 
         p[`${proxy}Port`] = default_ports[proxy];
       }
 
-      deepEqual(p, Proxy.fromJSON(manual));
+      deepEqual(p, session.Proxy.fromJSON(manual));
     }
   }
 
   // missing required socks version
-  Assert.throws(() => Proxy.fromJSON(
+  Assert.throws(() => session.Proxy.fromJSON(
       {proxyType: "manual", socksProxy: "foo:1234"}),
       /InvalidArgumentError/);
 
   // noProxy: invalid settings
   for (let noProxy of [true, 42, {}, null, "foo",
       [true], [42], [{}], [null]]) {
-    Assert.throws(() => Proxy.fromJSON(
+    Assert.throws(() => session.Proxy.fromJSON(
         {proxyType: "manual", noProxy}),
         /InvalidArgumentError/);
   }
 
   // noProxy: valid settings
-  p = new Proxy();
+  p = new session.Proxy();
   p.proxyType = "manual";
   for (let noProxy of [[], ["foo"], ["foo", "bar"], ["127.0.0.1"]]) {
     let manual = {proxyType: "manual", "noProxy": noProxy};
     p.noProxy = noProxy;
-    deepEqual(p, Proxy.fromJSON(manual));
+    deepEqual(p, session.Proxy.fromJSON(manual));
   }
 
   // noProxy: IPv6 needs brackets removed
-  p = new Proxy();
+  p = new session.Proxy();
   p.proxyType = "manual";
   p.noProxy = ["2001:db8::1"];
   let manual = {proxyType: "manual", "noProxy": ["[2001:db8::1]"]};
-  deepEqual(p, Proxy.fromJSON(manual));
+  deepEqual(p, session.Proxy.fromJSON(manual));
 
   run_next_test();
 });
 
 add_test(function test_Capabilities_ctor() {
-  let caps = new Capabilities();
+  let caps = new session.Capabilities();
   ok(caps.has("browserName"));
   ok(caps.has("browserVersion"));
   ok(caps.has("platformName"));
-  ok(["linux", "mac", "windows", "android"].includes(caps.get("platformName")));
   ok(caps.has("platformVersion"));
-  equal(PageLoadStrategy.Normal, caps.get("pageLoadStrategy"));
+  equal(session.PageLoadStrategy.Normal, caps.get("pageLoadStrategy"));
   equal(false, caps.get("acceptInsecureCerts"));
-  ok(caps.get("timeouts") instanceof Timeouts);
-  ok(caps.get("proxy") instanceof Proxy);
+  ok(caps.get("timeouts") instanceof session.Timeouts);
+  ok(caps.get("proxy") instanceof session.Proxy);
 
   ok(caps.has("rotatable"));
 
   equal(false, caps.get("moz:accessibilityChecks"));
   ok(caps.has("moz:processID"));
   ok(caps.has("moz:profile"));
   equal(false, caps.get("moz:useNonSpecCompliantPointerOrigin"));
   equal(true, caps.get("moz:webdriverClick"));
 
   run_next_test();
 });
 
 add_test(function test_Capabilities_toString() {
-  equal("[object Capabilities]", new Capabilities().toString());
+  equal("[object session.Capabilities]", new session.Capabilities().toString());
 
   run_next_test();
 });
 
 add_test(function test_Capabilities_toJSON() {
-  let caps = new Capabilities();
+  let caps = new session.Capabilities();
   let json = caps.toJSON();
 
   equal(caps.get("browserName"), json.browserName);
   equal(caps.get("browserVersion"), json.browserVersion);
   equal(caps.get("platformName"), json.platformName);
   equal(caps.get("platformVersion"), json.platformVersion);
   equal(caps.get("pageLoadStrategy"), json.pageLoadStrategy);
   equal(caps.get("acceptInsecureCerts"), json.acceptInsecureCerts);
@@ -416,36 +410,36 @@ add_test(function test_Capabilities_toJS
   equal(caps.get("moz:useNonSpecCompliantPointerOrigin"),
       json["moz:useNonSpecCompliantPointerOrigin"]);
   equal(caps.get("moz:webdriverClick"), json["moz:webdriverClick"]);
 
   run_next_test();
 });
 
 add_test(function test_Capabilities_fromJSON() {
-  const {fromJSON} = Capabilities;
+  const {fromJSON} = session.Capabilities;
 
   // plain
   for (let typ of [{}, null, undefined]) {
     ok(fromJSON(typ).has("browserName"));
   }
   for (let typ of [true, 42, "foo", []]) {
     Assert.throws(() => fromJSON(typ), /InvalidArgumentError/);
   }
 
   // matching
-  let caps = new Capabilities();
+  let caps = new session.Capabilities();
 
   caps = fromJSON({acceptInsecureCerts: true});
   equal(true, caps.get("acceptInsecureCerts"));
   caps = fromJSON({acceptInsecureCerts: false});
   equal(false, caps.get("acceptInsecureCerts"));
   Assert.throws(() => fromJSON({acceptInsecureCerts: "foo"}), InvalidArgumentError);
 
-  for (let strategy of Object.values(PageLoadStrategy)) {
+  for (let strategy of Object.values(session.PageLoadStrategy)) {
     caps = fromJSON({pageLoadStrategy: strategy});
     equal(strategy, caps.get("pageLoadStrategy"));
   }
   Assert.throws(() => fromJSON({pageLoadStrategy: "foo"}), InvalidArgumentError);
 
   let proxyConfig = {proxyType: "manual"};
   caps = fromJSON({proxy: proxyConfig});
   equal("manual", caps.get("proxy").proxyType);
@@ -475,39 +469,39 @@ add_test(function test_Capabilities_from
   caps = fromJSON({"moz:webdriverClick": false});
   equal(false, caps.get("moz:webdriverClick"));
   Assert.throws(() => fromJSON({"moz:webdriverClick": "foo"}), InvalidArgumentError);
   Assert.throws(() => fromJSON({"moz:webdriverClick": 1}), InvalidArgumentError);
 
   run_next_test();
 });
 
-// use Proxy.toJSON to test marshal
+// use session.Proxy.toJSON to test marshal
 add_test(function test_marshal() {
-  let proxy = new Proxy();
+  let proxy = new session.Proxy();
 
   // drop empty fields
   deepEqual({}, proxy.toJSON());
   proxy.proxyType = "manual";
   deepEqual({proxyType: "manual"}, proxy.toJSON());
   proxy.proxyType = null;
   deepEqual({}, proxy.toJSON());
   proxy.proxyType = undefined;
   deepEqual({}, proxy.toJSON());
 
   // iterate over object literals
   proxy.proxyType = {foo: "bar"};
   deepEqual({proxyType: {foo: "bar"}}, proxy.toJSON());
 
   // iterate over complex object that implement toJSON
-  proxy.proxyType = new Proxy();
+  proxy.proxyType = new session.Proxy();
   deepEqual({}, proxy.toJSON());
   proxy.proxyType.proxyType = "manual";
   deepEqual({proxyType: {proxyType: "manual"}}, proxy.toJSON());
 
   // drop objects with no entries
   proxy.proxyType = {foo: {}};
   deepEqual({}, proxy.toJSON());
-  proxy.proxyType = {foo: new Proxy()};
+  proxy.proxyType = {foo: new session.Proxy()};
   deepEqual({}, proxy.toJSON());
 
   run_next_test();
 });
--- a/testing/marionette/test/unit/xpcshell.ini
+++ b/testing/marionette/test/unit/xpcshell.ini
@@ -3,19 +3,19 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 [DEFAULT]
 skip-if = appname == "thunderbird"
 
 [test_action.js]
 [test_assert.js]
 [test_browser.js]
-[test_capabilities.js]
 [test_cookie.js]
 [test_dom.js]
 [test_element.js]
 [test_error.js]
 [test_evaluate.js]
 [test_format.js]
 [test_message.js]
 [test_navigate.js]
 [test_prefs.js]
+[test_session.js]
 [test_sync.js]
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -619873,17 +619873,17 @@
    "da39a3ee5e6b4b0d3255bfef95601890afd80709",
    "support"
   ],
   "webdriver/tests/actions/conftest.py": [
    "f366a25d9d5e35a2897413a265398bc7a54f7c44",
    "support"
   ],
   "webdriver/tests/actions/control_click.py": [
-   "cbe7cc08b4a991da0cce1dfefd96b25be3b50138",
+   "341a8fbfe64f5231a91999768de0b44bba121122",
    "wdspec"
   ],
   "webdriver/tests/actions/key.py": [
    "0b70d98a4558427666abbe9629a6cf42d69e2597",
    "wdspec"
   ],
   "webdriver/tests/actions/key_shortcuts.py": [
    "dbe27dd0b1625169fc8cc2055f8fb49d5a4a78d2",
@@ -619917,17 +619917,17 @@
    "b021866b542bd6d02171084343f676090378e878",
    "wdspec"
   ],
   "webdriver/tests/actions/support/__init__.py": [
    "da39a3ee5e6b4b0d3255bfef95601890afd80709",
    "support"
   ],
   "webdriver/tests/actions/support/keys.py": [
-   "b06f684335c4ebb18ee8d0dd9e9b757f4cdcd52a",
+   "528ab8473914c14f9671d89b8a888d30162714ec",
    "support"
   ],
   "webdriver/tests/actions/support/mouse.py": [
    "bc7da107e5b897105dfa7405aa57cba7355854dc",
    "support"
   ],
   "webdriver/tests/actions/support/refine.py": [
    "0d244bffe67ef57be68aad99f1cbc7440ff80e27",
@@ -620313,17 +620313,17 @@
    "da39a3ee5e6b4b0d3255bfef95601890afd80709",
    "support"
   ],
   "webdriver/tests/new_session/conftest.py": [
    "72798e95422e70158c26b60aabc27a480bac8758",
    "support"
   ],
   "webdriver/tests/new_session/create_alwaysMatch.py": [
-   "d0100b96ccdcc99bccd7bd611bd9df2f51c4a728",
+   "b2783003e5e1a7d762a4340b33f65d6afd9b0e62",
    "wdspec"
   ],
   "webdriver/tests/new_session/create_firstMatch.py": [
    "a3f2156bea16ae9eb270794a2b5a0c1a39c3e631",
    "wdspec"
   ],
   "webdriver/tests/new_session/default_values.py": [
    "596830d45fc6c9f8a5ad1e91f79d580adf32ff54",
@@ -620381,17 +620381,17 @@
    "335f9e4ae5c406a90bf6a4431fefb629496ad767",
    "wdspec"
   ],
   "webdriver/tests/set_window_rect/__init__.py": [
    "da39a3ee5e6b4b0d3255bfef95601890afd80709",
    "support"
   ],
   "webdriver/tests/set_window_rect/set.py": [
-   "04161b59c5b144dd571dca3ef224595f9d4612f2",
+   "79de443bc251f7effaa7c2ef3b95695f22db01c6",
    "wdspec"
   ],
   "webdriver/tests/set_window_rect/user_prompts.py": [
    "4ed66d05835d3ab229cb90928e0ca2a15ba08c8a",
    "wdspec"
   ],
   "webdriver/tests/status/__init__.py": [
    "da39a3ee5e6b4b0d3255bfef95601890afd80709",
--- a/testing/web-platform/tests/tools/wptrunner/wptrunner/wpttest.py
+++ b/testing/web-platform/tests/tools/wptrunner/wptrunner/wpttest.py
@@ -402,17 +402,17 @@ class ReftestTest(Test):
 
 class WdspecTest(Test):
 
     result_cls = WdspecResult
     subtest_result_cls = WdspecSubtestResult
     test_type = "wdspec"
 
     default_timeout = 25
-    long_timeout = 180  # 3 minutes
+    long_timeout = 120
 
 
 manifest_test_cls = {"reftest": ReftestTest,
                      "testharness": TestharnessTest,
                      "manual": ManualTest,
                      "wdspec": WdspecTest}
 
 
--- a/testing/web-platform/tests/webdriver/tests/actions/control_click.py
+++ b/testing/web-platform/tests/webdriver/tests/actions/control_click.py
@@ -13,17 +13,17 @@ def test_control_click(session, test_act
     key_chain \
         .pause(0) \
         .key_down(modifier) \
         .pause(200) \
         .key_up(modifier)
     outer = session.find.css("#outer", all=False)
     mouse_chain.click(element=outer)
     session.actions.perform([key_chain.dict, mouse_chain.dict])
-    if os == "windows":
+    if os == "windows_nt":
         expected = [
             {"type": "mousemove"},
             {"type": "mousedown"},
             {"type": "mouseup"},
             {"type": "click"},
         ]
     else:
         expected = [
--- a/testing/web-platform/tests/webdriver/tests/actions/support/keys.py
+++ b/testing/web-platform/tests/webdriver/tests/actions/support/keys.py
@@ -10,27 +10,29 @@
 #
 # Unless required by applicable law or agreed to in writing,
 # software distributed under the License is distributed on an
 # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
 
-"""The Keys implementation."""
+"""
+The Keys implementation.
+"""
 
 from inspect import getmembers
 import sys
 
 
 class Keys(object):
     """
     Set of special keys codes.
 
-    See also https://w3c.github.io/webdriver/#h-keyboard-actions
+    See also https://w3c.github.io/webdriver/webdriver-spec.html#h-keyboard-actions
     """
 
     NULL = u"\ue000"
     CANCEL = u"\ue001"  # ^break
     HELP = u"\ue002"
     BACKSPACE = u"\ue003"
     TAB = u"\ue004"
     CLEAR = u"\ue005"
@@ -735,12 +737,12 @@ ALL_EVENTS = {
         "key": "ZenkakuHankaku",
         "location": 0,
         "meta": False,
         "shift": False,
         "value": u"\ue040",
     }
 }
 
-if sys.platform == "mac":
+if sys.platform == 'darwin':
     MODIFIER_KEY = Keys.META
 else:
     MODIFIER_KEY = Keys.CONTROL
--- a/testing/web-platform/tests/webdriver/tests/new_session/create_alwaysMatch.py
+++ b/testing/web-platform/tests/webdriver/tests/new_session/create_alwaysMatch.py
@@ -8,9 +8,8 @@ from tests.support.asserts import assert
 from tests.new_session.support.create import valid_data
 
 
 @pytest.mark.parametrize("key,value", flatten(product(*item) for item in valid_data))
 def test_valid(new_session, add_browser_capabilites, key, value):
     response, _ = new_session({"capabilities": {
         "alwaysMatch": add_browser_capabilites({key: value})}})
     assert_success(response)
-
--- a/testing/web-platform/tests/webdriver/tests/set_window_rect/set.py
+++ b/testing/web-platform/tests/webdriver/tests/set_window_rect/set.py
@@ -329,25 +329,25 @@ def test_negative_x_y(session):
         assert rect["x"] <= 0
         assert rect["y"] <= 0
         assert rect["width"] == original["width"]
         assert rect["height"] == original["height"]
 
     # On macOS, windows can only be moved off the screen on the
     # horizontal axis.  The system menu bar also blocks windows from
     # being moved to (0,0).
-    elif os == "mac":
+    elif os == "darwin":
         assert_success(response, {"x": -8,
                                   "y": 23,
                                   "width": original["width"],
                                   "height": original["height"]})
 
     # It turns out that Windows is the only platform on which the
     # window can be reliably positioned off-screen.
-    elif os == "windows":
+    elif os == "windows_nt":
         assert_success(response, {"x": -8,
                                   "y": -8,
                                   "width": original["width"],
                                   "height": original["height"]})
 
 
 def test_move_to_same_position(session):
     original_position = session.window.position