Merge inbound to mozilla-central. a=merge
authorTiberius Oros <toros@mozilla.com>
Thu, 11 Oct 2018 00:57:47 +0300
changeset 499031 9ba60bd4482561db2a86f7ebcee1f0b66b32746b
parent 499005 389e356499dfff9e5411d0fbf8fb9db6c6d2d0b6 (current diff)
parent 499030 edeba530712124ce1b30693042d4c2296319300f (diff)
child 499032 2f85c2b55cd2f1b241f27b7ad8b9dfe42dfc139c
child 499075 c8ac5fc2e323999eefcb33ee1bb39db27ed3f0a2
child 499092 ca79a952c3905158d163f06a0132b69857901be2
push id1864
push userffxbld-merge
push dateMon, 03 Dec 2018 15:51:40 +0000
treeherdermozilla-release@f040763d99ad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone64.0a1
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
Merge inbound to mozilla-central. a=merge
devtools/client/debugger/test/mochitest/addon1.xpi
devtools/client/debugger/test/mochitest/addon2.xpi
devtools/client/debugger/test/mochitest/browser_dbg_WorkerTargetActor.attach.js
devtools/client/debugger/test/mochitest/browser_dbg_WorkerTargetActor.attachThread.js
devtools/client/debugger/test/mochitest/browser_dbg_chrome-create.js
devtools/client/debugger/test/mochitest/browser_dbg_chrome-debugging.js
devtools/client/debugger/test/mochitest/browser_dbg_global-method-override.js
devtools/client/debugger/test/mochitest/browser_dbg_globalactor.js
devtools/client/debugger/test/mochitest/browser_dbg_listaddons.js
devtools/client/debugger/test/mochitest/browser_dbg_listtabs-01.js
devtools/client/debugger/test/mochitest/browser_dbg_listtabs-02.js
devtools/client/debugger/test/mochitest/browser_dbg_listtabs-03.js
devtools/client/debugger/test/mochitest/browser_dbg_multiple-windows.js
devtools/client/debugger/test/mochitest/browser_dbg_navigation.js
devtools/client/debugger/test/mochitest/browser_dbg_target-scoped-actor-01.js
devtools/client/debugger/test/mochitest/browser_dbg_target-scoped-actor-02.js
devtools/client/debugger/test/mochitest/browser_dbg_worker-console-01.js
devtools/client/debugger/test/mochitest/browser_dbg_worker-console-02.js
devtools/client/debugger/test/mochitest/browser_dbg_worker-console-03.js
devtools/client/debugger/test/mochitest/browser_dbg_worker-console-04.js
devtools/client/debugger/test/mochitest/doc_global-method-override.html
dom/media/MediaManager.cpp
--- a/.cron.yml
+++ b/.cron.yml
@@ -114,16 +114,19 @@ jobs:
           by-project:
               # No default branch
               mozilla-beta:
                   - {hour: 7, minute: 0}
                   - {hour: 19, minute: 0}
               mozilla-release:
                   - {hour: 7, minute: 0}
                   - {hour: 19, minute: 0}
+              mozilla-esr60:
+                  - {hour: 7, minute: 0}
+                  - {hour: 19, minute: 0}
 
     - name: periodic-update
       job:
           type: decision-task
           treeherder-symbol: Nfile
           target-tasks-method: file_update
       run-on-projects:
           - mozilla-central
--- a/browser/base/content/test/performance/browser.ini
+++ b/browser/base/content/test/performance/browser.ini
@@ -18,16 +18,17 @@ skip-if = asan || debug || (os == 'win' 
 skip-if = !debug
 [browser_startup.js]
 [browser_startup_content.js]
 skip-if = !e10s
 [browser_startup_flicker.js]
 run-if = debug || devedition || nightly_build # Requires startupRecorder.js, which isn't shipped everywhere by default
 [browser_tabclose_grow.js]
 [browser_tabclose.js]
+skip-if = (os == 'win' && bits == 32) # Bug 1488537
 [browser_tabopen.js]
 skip-if = (verify && (os == 'mac'))
 [browser_tabopen_squeeze.js]
 [browser_tabstrip_overflow_underflow.js]
 skip-if = (verify && !debug && (os == 'win'))
 [browser_tabswitch.js]
 [browser_toolbariconcolor_restyles.js]
 [browser_urlbar_keyed_search.js]
--- a/build/moz.configure/util.configure
+++ b/build/moz.configure/util.configure
@@ -17,16 +17,17 @@ def configure_error(message):
     '''Raise a programming error and terminate configure.
     Primarily for use in moz.configure templates to sanity check
     their inputs from moz.configure usage.'''
     raise ConfigureError(message)
 
 
 # A wrapper to obtain a process' output and return code.
 # Returns a tuple (retcode, stdout, stderr).
+@imports('os')
 @imports(_from='__builtin__', _import='unicode')
 @imports('subprocess')
 @imports(_from='mozbuild.shellutil', _import='quote')
 def get_cmd_output(*args, **kwargs):
     # subprocess on older Pythons can't handle unicode keys or values in
     # environment dicts. Normalize automagically so callers don't have to
     # deal with this.
     if 'env' in kwargs:
@@ -39,17 +40,23 @@ def get_cmd_output(*args, **kwargs):
                 v = v.encode('utf-8', 'strict')
 
             normalized_env[k] = v
 
         kwargs['env'] = normalized_env
 
     log.debug('Executing: `%s`', quote(*args))
     proc = subprocess.Popen(args, stdout=subprocess.PIPE,
-                            stderr=subprocess.PIPE, **kwargs)
+                            stderr=subprocess.PIPE,
+                            # On Python 2 on Windows, close_fds prevents the
+                            # process from inheriting stdout/stderr.
+                            # Elsewhere, it simply prevents it from inheriting
+                            # extra file descriptors, which is what we want.
+                            close_fds=os.name != 'nt',
+                            **kwargs)
     stdout, stderr = proc.communicate()
     return proc.wait(), stdout, stderr
 
 
 # A wrapper to obtain a process' output that returns the output generated
 # by running the given command if it exits normally, and streams that
 # output to log.debug and calls die or the given error callback if it
 # does not.
--- a/devtools/client/debugger/new/test/mochitest/browser.ini
+++ b/devtools/client/debugger/new/test/mochitest/browser.ini
@@ -619,16 +619,17 @@ support-files =
   examples/doc-sourcemaps2.html
   examples/doc-sourcemaps3.html
   examples/doc-sourcemap-bogus.html
   examples/doc-sources.html
   examples/doc-strict.html
   examples/doc-pause-points.html
   examples/doc-return-values.html
   examples/doc-wasm-sourcemaps.html
+  examples/doc_global-method-override.html
   examples/asm.js
   examples/async.js
   examples/bogus-map.js
   examples/entry.js
   examples/exceptions.js
   examples/long.js
   examples/math.min.js
   examples/nested/nested-source.js
@@ -681,16 +682,18 @@ skip-if = (verify && !debug && (os == 'l
 skip-if = (os == "win" && ccov) # Bug 1424154
 [browser_dbg-debugger-buttons.js]
 [browser_dbg-editor-gutter.js]
 [browser_dbg-editor-select.js]
 [browser_dbg-editor-highlight.js]
 [browser_dbg-ember-quickstart.js]
 [browser_dbg-expressions.js]
 [browser_dbg-expressions-error.js]
+[browser_dbg_global-method-override.js]
+skip-if = e10s && debug
 [browser_dbg-iframes.js]
 [browser_dbg-inline-cache.js]
 [browser_dbg-keyboard-navigation.js]
 [browser_dbg-keyboard-shortcuts.js]
 skip-if = os == "linux" # bug 1351952
 [browser_dbg-layout-changes.js]
 [browser_dbg-outline.js]
 skip-if = verify
rename from devtools/client/debugger/test/mochitest/browser_dbg_global-method-override.js
rename to devtools/client/debugger/new/test/mochitest/browser_dbg_global-method-override.js
--- a/devtools/client/debugger/test/mochitest/browser_dbg_global-method-override.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg_global-method-override.js
@@ -1,26 +1,18 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that scripts that override properties of the global object, like
- * toString don't break the debugger. The test page used to cause the debugger
+ * toString, don't break the debugger. The test page used to cause the debugger
  * to throw when trying to attach to the thread actor.
  */
 
-const TAB_URL = EXAMPLE_URL + "doc_global-method-override.html";
+"use strict";
 
-function test() {
-  let options = {
-    source: TAB_URL,
-    line: 1
-  };
-  initDebugger(TAB_URL, options).then(([aTab, aPanel]) => {
-    let gDebugger = aPanel.panelWin;
-    ok(gDebugger, "Should have a debugger available.");
-    is(gDebugger.gThreadClient.state, "attached", "Debugger should be attached.");
-
-    closeDebuggerAndFinish(aPanel);
-  });
-}
+add_task(async function() {
+  const dbg = await initDebugger("doc_global-method-override.html");
+  ok(dbg, "Should have a debugger available.");
+  is(dbg.toolbox.threadClient.state, "attached", "Debugger should be attached.");
+});
rename from devtools/client/debugger/test/mochitest/doc_global-method-override.html
rename to devtools/client/debugger/new/test/mochitest/examples/doc_global-method-override.html
--- a/devtools/client/debugger/test/mochitest/browser.ini
+++ b/devtools/client/debugger/test/mochitest/browser.ini
@@ -1,18 +1,16 @@
 # Tests in this directory are split into two manifests (this and browser2.ini)
 # to facilitate better chunking; see bug 1294489.
 
 [DEFAULT]
 tags = devtools
 subsuite = devtools
 skip-if = (os == 'linux' && debug && bits == 32)
 support-files =
-  addon1.xpi
-  addon2.xpi
   addon4.xpi
   addon5.xpi
   addon-webext-contentscript.xpi
   addon-source/browser_dbg_addon5/*
   code_binary_search.coffee
   code_binary_search.js
   code_binary_search.map
   code_breakpoints-break-on-last-line-of-script-on-reload.js
@@ -65,17 +63,16 @@ support-files =
   doc_empty-tab-02.html
   doc_event-listeners-01.html
   doc_event-listeners-02.html
   doc_event-listeners-03.html
   doc_frame-parameters.html
   doc_function-display-name.html
   doc_function-jump.html
   doc_function-search.html
-  doc_global-method-override.html
   doc_iframes.html
   doc_included-script.html
   doc_inline-debugger-statement.html
   doc_inline-script.html
   doc_large-array-buffer.html
   doc_listworkers-tab.html
   doc_map-set.html
   doc_minified.html
@@ -142,20 +139,16 @@ skip-if = e10s && debug
 [browser_dbg_breakpoints-eval.js]
 skip-if = e10s && debug
 [browser_dbg_breakpoints-new-script.js]
 skip-if = e10s && debug
 [browser_dbg_breakpoints-other-tabs.js]
 skip-if = e10s && debug
 [browser_dbg_bug-896139.js]
 skip-if = e10s && debug
-[browser_dbg_chrome-create.js]
-skip-if = (e10s && debug) || (verify && os == "linux") # Exit code mismatch with verify
-[browser_dbg_chrome-debugging.js]
-skip-if = e10s && debug
 [browser_dbg_clean-exit.js]
 skip-if = true # Bug 1044985 (racy test)
 [browser_dbg_closure-inspection.js]
 skip-if = e10s && debug
 [browser_dbg_conditional-breakpoints-01.js]
 skip-if = e10s && debug
 [browser_dbg_conditional-breakpoints-02.js]
 skip-if = e10s && debug
@@ -188,32 +181,17 @@ skip-if = e10s && debug
 [browser_dbg_event-listeners-01.js]
 skip-if = e10s && debug
 [browser_dbg_event-listeners-02.js]
 skip-if = e10s && debug
 [browser_dbg_event-listeners-03.js]
 skip-if = e10s && debug
 [browser_dbg_file-reload.js]
 skip-if = e10s && debug
-[browser_dbg_global-method-override.js]
-skip-if = e10s && debug
-[browser_dbg_globalactor.js]
-skip-if = e10s
 [browser_dbg_host-layout.js]
 skip-if = e10s && debug
 [browser_dbg_jump-to-function-definition.js]
 skip-if = e10s && debug
 [browser_dbg_iframes.js]
 skip-if = e10s # TODO
 [browser_dbg_interrupts.js]
 skip-if = e10s && debug
-[browser_dbg_listaddons.js]
-skip-if = e10s && debug
-tags = addons
-[browser_dbg_listtabs-01.js]
-[browser_dbg_listtabs-02.js]
-skip-if = true # Never worked for remote frames, needs a mock DebuggerServerConnection
-[browser_dbg_listtabs-03.js]
-skip-if = e10s && debug
 [browser_dbg_listworkers.js]
-[browser_dbg_multiple-windows.js]
-[browser_dbg_navigation.js]
-skip-if = e10s && debug
--- a/devtools/client/debugger/test/mochitest/browser2.ini
+++ b/devtools/client/debugger/test/mochitest/browser2.ini
@@ -1,18 +1,16 @@
 # Tests in this directory are split into two manifests (this and browser.ini)
 # to facilitate better chunking; see bug 1294489.
 
 [DEFAULT]
 tags = devtools
 subsuite = devtools
 skip-if = (os == 'linux' && debug && bits == 32)
 support-files =
-  addon1.xpi
-  addon2.xpi
   addon4.xpi
   addon5.xpi
   addon-webext-contentscript.xpi
   addon-source/browser_dbg_addon5/*
   code_binary_search.coffee
   code_binary_search.js
   code_binary_search.map
   code_breakpoints-break-on-last-line-of-script-on-reload.js
@@ -65,17 +63,16 @@ support-files =
   doc_empty-tab-02.html
   doc_event-listeners-01.html
   doc_event-listeners-02.html
   doc_event-listeners-03.html
   doc_frame-parameters.html
   doc_function-display-name.html
   doc_function-jump.html
   doc_function-search.html
-  doc_global-method-override.html
   doc_iframes.html
   doc_included-script.html
   doc_inline-debugger-statement.html
   doc_inline-script.html
   doc_large-array-buffer.html
   doc_listworkers-tab.html
   doc_map-set.html
   doc_minified.html
@@ -154,33 +151,19 @@ uses-unsafe-cpows = true
 [browser_dbg_sources-iframe-reload.js]
 uses-unsafe-cpows = true
 skip-if = (os == "linux" && debug && bits == 64) #Bug 1455225, disable on Linux x64 debug for frequent failures
 [browser_dbg_sources-bookmarklet.js]
 uses-unsafe-cpows = true
 skip-if = e10s && debug
 [browser_dbg_split-console-paused-reload.js]
 skip-if = true # Bug 1288348 - previously e10s && debug
-[browser_dbg_target-scoped-actor-01.js]
-[browser_dbg_target-scoped-actor-02.js]
 [browser_dbg_terminate-on-tab-close.js]
 uses-unsafe-cpows = true
 skip-if = e10s && debug
-[browser_dbg_worker-console-01.js]
-skip-if = true # bug 1368569
-[browser_dbg_worker-console-02.js]
-skip-if = e10s && debug
-[browser_dbg_worker-console-03.js]
-skip-if = debug # bug 1334683
-[browser_dbg_worker-console-04.js]
-skip-if = e10s && debug
 [browser_dbg_worker-source-map.js]
 uses-unsafe-cpows = true
 skip-if = e10s && debug
 [browser_dbg_worker-window.js]
 skip-if = (e10s && debug) || true # Bug 1486974
-[browser_dbg_WorkerTargetActor.attach.js]
-skip-if = e10s && debug
-[browser_dbg_WorkerTargetActor.attachThread.js]
-skip-if = e10s && debug
 [browser_dbg_split-console-keypress.js]
 uses-unsafe-cpows = true
 skip-if = (debug || os == "linux") # Bug 1214439
deleted file mode 100644
--- a/devtools/client/debugger/test/mochitest/browser_dbg_WorkerTargetActor.attachThread.js
+++ /dev/null
@@ -1,100 +0,0 @@
-var TAB_URL = EXAMPLE_URL + "doc_WorkerTargetActor.attachThread-tab.html";
-var WORKER_URL = "code_WorkerTargetActor.attachThread-worker.js";
-
-function test() {
-  Task.spawn(function* () {
-    DebuggerServer.init();
-    DebuggerServer.registerAllActors();
-
-    let client1 = new DebuggerClient(DebuggerServer.connectPipe());
-    yield connect(client1);
-    let client2 = new DebuggerClient(DebuggerServer.connectPipe());
-    yield connect(client2);
-
-    let tab = yield addTab(TAB_URL);
-    let { tabs: tabs1 } = yield listTabs(client1);
-    let [, tabClient1] = yield attachTarget(client1, findTab(tabs1, TAB_URL));
-    let { tabs: tabs2 } = yield listTabs(client2);
-    let [, tabClient2] = yield attachTarget(client2, findTab(tabs2, TAB_URL));
-
-    yield listWorkers(tabClient1);
-    yield listWorkers(tabClient2);
-    yield createWorkerInTab(tab, WORKER_URL);
-    let { workers: workers1 } = yield listWorkers(tabClient1);
-    let [, workerClient1] = yield attachWorker(tabClient1,
-                                               findWorker(workers1, WORKER_URL));
-    let { workers: workers2 } = yield listWorkers(tabClient2);
-    let [, workerClient2] = yield attachWorker(tabClient2,
-                                               findWorker(workers2, WORKER_URL));
-
-    let location = { line: 5 };
-
-    let [, threadClient1] = yield attachThread(workerClient1);
-    let sources1 = yield getSources(threadClient1);
-    let sourceClient1 = threadClient1.source(findSource(sources1,
-                                                        EXAMPLE_URL + WORKER_URL));
-    let [, breakpointClient1] = yield setBreakpoint(sourceClient1, location);
-    yield resume(threadClient1);
-
-    let [, threadClient2] = yield attachThread(workerClient2);
-    let sources2 = yield getSources(threadClient2);
-    let sourceClient2 = threadClient2.source(findSource(sources2,
-                                                        EXAMPLE_URL + WORKER_URL));
-    let [, breakpointClient2] = yield setBreakpoint(sourceClient2, location);
-    yield resume(threadClient2);
-
-    let packet = yield source(sourceClient1);
-    let text = (yield new Promise(function (resolve) {
-      let request = new XMLHttpRequest();
-      request.open("GET", EXAMPLE_URL + WORKER_URL, true);
-      request.send();
-      request.onload = function () {
-        resolve(request.responseText);
-      };
-    }));
-    is(packet.source, text);
-
-    postMessageToWorkerInTab(tab, WORKER_URL, "ping");
-    yield Promise.all([
-      waitForPause(threadClient1).then((packet) => {
-        is(packet.type, "paused");
-        let why = packet.why;
-        is(why.type, "breakpoint");
-        is(why.actors.length, 1);
-        is(why.actors[0], breakpointClient1.actor);
-        let frame = packet.frame;
-        let where = frame.where;
-        is(where.source.actor, sourceClient1.actor);
-        is(where.line, location.line);
-        let variables = frame.environment.bindings.variables;
-        is(variables.a.value, 1);
-        is(variables.b.value.type, "undefined");
-        is(variables.c.value.type, "undefined");
-        return resume(threadClient1);
-      }),
-      waitForPause(threadClient2).then((packet) => {
-        is(packet.type, "paused");
-        let why = packet.why;
-        is(why.type, "breakpoint");
-        is(why.actors.length, 1);
-        is(why.actors[0], breakpointClient2.actor);
-        let frame = packet.frame;
-        let where = frame.where;
-        is(where.source.actor, sourceClient2.actor);
-        is(where.line, location.line);
-        let variables = frame.environment.bindings.variables;
-        is(variables.a.value, 1);
-        is(variables.b.value.type, "undefined");
-        is(variables.c.value.type, "undefined");
-        return resume(threadClient2);
-      }),
-    ]);
-
-    terminateWorkerInTab(tab, WORKER_URL);
-    yield waitForWorkerClose(workerClient1);
-    yield waitForWorkerClose(workerClient2);
-    yield close(client1);
-    yield close(client2);
-    finish();
-  });
-}
deleted file mode 100644
--- a/devtools/client/debugger/test/mochitest/browser_dbg_chrome-create.js
+++ /dev/null
@@ -1,70 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests that a chrome debugger can be created in a new process.
- */
-
-// There are shutdown issues for which multiple rejections are left uncaught.
-// See bug 1018184 for resolving these issues.
-const { PromiseTestUtils } = scopedCuImport("resource://testing-common/PromiseTestUtils.jsm");
-PromiseTestUtils.whitelistRejectionsGlobally(/File closed/);
-PromiseTestUtils.whitelistRejectionsGlobally(/NS_ERROR_FAILURE/);
-
-var gProcess;
-
-function test() {
-  // Windows XP and 8.1 test slaves are terribly slow at this test.
-  requestLongerTimeout(5);
-  Services.prefs.setBoolPref("devtools.chrome.enabled", true);
-  Services.prefs.setBoolPref("devtools.debugger.remote-enabled", true);
-
-  initChromeDebugger(aOnClose).then(aProcess => {
-    gProcess = aProcess;
-
-    info("Starting test...");
-    performTest();
-  });
-}
-
-function performTest() {
-  ok(gProcess._dbgProcess,
-    "The remote debugger process wasn't created properly!");
-  ok(gProcess._dbgProcess.exitCode == null,
-    "The remote debugger process isn't running!");
-  is(typeof gProcess._dbgProcess.pid, "number",
-    "The remote debugger process doesn't have a pid (?!)");
-
-  info("process location: " + gProcess._dbgProcess.location);
-  info("process pid: " + gProcess._dbgProcess.pid);
-  info("process name: " + gProcess._dbgProcess.processName);
-  info("process sig: " + gProcess._dbgProcess.processSignature);
-
-  ok(gProcess._dbgProfilePath,
-    "The remote debugger profile wasn't created properly!");
-  is(gProcess._dbgProfilePath, OS.Path.join(OS.Constants.Path.profileDir, "chrome_debugger_profile"),
-     "The remote debugger profile isn't where we expect it!");
-
-  info("profile path: " + gProcess._dbgProfilePath);
-
-  gProcess.close();
-}
-
-function aOnClose() {
-  is(gProcess._dbgProcess.exitCode, (Services.appinfo.OS == "WINNT" ? -9 : -15),
-    "The remote debugger process didn't die cleanly.");
-
-  info("process exit value: " + gProcess._dbgProcess.exitValue);
-
-  info("profile path: " + gProcess._dbgProfilePath);
-
-  finish();
-}
-
-registerCleanupFunction(function () {
-  Services.prefs.clearUserPref("devtools.chrome.enabled");
-  Services.prefs.clearUserPref("devtools.debugger.remote-enabled");
-  gProcess = null;
-});
deleted file mode 100644
--- a/devtools/client/debugger/test/mochitest/browser_dbg_chrome-debugging.js
+++ /dev/null
@@ -1,89 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests that chrome debugging works.
- */
-
-const TAB_URL = EXAMPLE_URL + "doc_inline-debugger-statement.html";
-
-var gClient, gThreadClient;
-var gAttached = promise.defer();
-var gNewChromeSource = promise.defer();
-
-var { DevToolsLoader } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
-var customLoader = new DevToolsLoader();
-customLoader.invisibleToDebugger = true;
-var { DebuggerServer } = customLoader.require("devtools/server/main");
-
-function test() {
-  DebuggerServer.init();
-  DebuggerServer.registerAllActors();
-  DebuggerServer.allowChromeProcess = true;
-
-  let transport = DebuggerServer.connectPipe();
-  gClient = new DebuggerClient(transport);
-  gClient.connect().then(([aType, aTraits]) => {
-    is(aType, "browser",
-      "Root actor should identify itself as a browser.");
-
-    promise.all([gAttached.promise, gNewChromeSource.promise])
-      .then(resumeAndCloseConnection)
-      .then(finish)
-      .catch(aError => {
-        ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
-      });
-
-    testParentProcessTargetActor();
-  });
-}
-
-function testParentProcessTargetActor() {
-  gClient.getProcess().then(aResponse => {
-    let actor = aResponse.form.actor;
-    gClient.attachTarget(actor).then(([response, tabClient]) => {
-      tabClient.attachThread(null).then(([aResponse, aThreadClient]) => {
-        gThreadClient = aThreadClient;
-        gThreadClient.addListener("newSource", onNewSource);
-
-        if (aResponse.error) {
-          ok(false, "Couldn't attach to the chrome debugger.");
-          gAttached.reject();
-        } else {
-          ok(true, "Attached to the chrome debugger.");
-          gAttached.resolve();
-
-          // Ensure that a new chrome global will be created.
-          gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, "about:mozilla");
-        }
-      });
-    });
-  });
-}
-
-function onNewSource(aEvent, aPacket) {
-  if (aPacket.source.url.startsWith("chrome:")) {
-    ok(true, "Received a new chrome source: " + aPacket.source.url);
-
-    gThreadClient.removeListener("newSource", onNewSource);
-    gNewChromeSource.resolve();
-  }
-}
-
-function resumeAndCloseConnection() {
-  let deferred = promise.defer();
-  gThreadClient.resume(() => deferred.resolve(gClient.close()));
-  return deferred.promise;
-}
-
-registerCleanupFunction(function () {
-  gClient = null;
-  gThreadClient = null;
-  gAttached = null;
-  gNewChromeSource = null;
-
-  customLoader = null;
-  DebuggerServer = null;
-});
--- a/devtools/client/inspector/computed/test/browser.ini
+++ b/devtools/client/inspector/computed/test/browser.ini
@@ -14,16 +14,17 @@ support-files =
   !/devtools/client/inspector/test/shared-head.js
   !/devtools/client/shared/test/shared-head.js
   !/devtools/client/shared/test/telemetry-test-helpers.js
   !/devtools/client/shared/test/test-actor.js
   !/devtools/client/shared/test/test-actor-registry.js
 
 [browser_computed_browser-styles.js]
 [browser_computed_cycle_color.js]
+[browser_computed_default_tab.js]
 [browser_computed_getNodeInfo.js]
 [browser_computed_keybindings_01.js]
 [browser_computed_keybindings_02.js]
 [browser_computed_matched-selectors-toggle.js]
 [browser_computed_matched-selectors_01.js]
 [browser_computed_matched-selectors_02.js]
 [browser_computed_media-queries.js]
 [browser_computed_no-results-placeholder.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/computed/test/browser_computed_default_tab.js
@@ -0,0 +1,36 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that the computed view is initialized when the computed view is the default tab
+// for the inspector.
+
+const TEST_URI = `
+  <style type="text/css">
+    #matches {
+      color: #F00;
+    }
+  </style>
+  <span id="matches">Some styled text</span>
+`;
+
+add_task(async function() {
+  await pushPref("devtools.inspector.activeSidebar", "computedview");
+  await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  const {inspector, view} = await openComputedView();
+  await selectNode("#matches", inspector);
+  is(isPropertyVisible("color", view), true, "span #matches color property is visible");
+});
+
+function isPropertyVisible(name, view) {
+  info("Checking property visibility for " + name);
+  const propertyViews = view.propertyViews;
+  for (const propView of propertyViews) {
+    if (propView.name == name) {
+      return propView.visible;
+    }
+  }
+  return false;
+}
--- a/devtools/client/inspector/inspector.js
+++ b/devtools/client/inspector/inspector.js
@@ -750,29 +750,27 @@ Inspector.prototype = {
    * panel. Otherwise, we specify the default tab when handling the sidebar setup.
    *
    * @params {String} defaultTab
    *         Thie id of the default tab for the sidebar.
    */
   async addRuleView({ defaultTab = "ruleview", skipQueue = false } = {}) {
     const ruleViewSidebar = this.sidebarSplitBox.startPanelContainer;
 
-    if (this.is3PaneModeEnabled || defaultTab === "ruleview") {
-      // Force the rule view panel creation by calling getPanel
-      this.getPanel("ruleview");
-    }
-
     if (this.is3PaneModeEnabled) {
       // Convert to 3 pane mode by removing the rule view from the inspector sidebar
       // and adding the rule view to the middle (in landscape/horizontal mode) or
       // bottom-left (in portrait/vertical mode) panel.
       ruleViewSidebar.style.display = "block";
 
       this.setSidebarSplitBoxState();
 
+      // Force the rule view panel creation by calling getPanel
+      this.getPanel("ruleview");
+
       await this.sidebar.removeTab("ruleview");
 
       this.ruleViewSideBar.addExistingTab(
         "ruleview",
         INSPECTOR_L10N.getStr("inspector.sidebar.ruleViewTitle"),
         true);
 
       this.ruleViewSideBar.show();
@@ -861,16 +859,17 @@ Inspector.prototype = {
         collapsed: !this.is3PaneModeEnabled,
         collapsePaneTitle: INSPECTOR_L10N.getStr("inspector.hideThreePaneMode"),
         expandPaneTitle: INSPECTOR_L10N.getStr("inspector.showThreePaneMode"),
         onClick: this.onSidebarToggle,
       }
     };
 
     this.sidebar = new ToolSidebar(sidebar, this, "inspector", options);
+    this.sidebar.on("select", this.onSidebarSelect);
 
     const ruleSideBar = this.panelDoc.getElementById("inspector-rules-sidebar");
     this.ruleViewSideBar = new ToolSidebar(ruleSideBar, this, "inspector", {
       hideTabstripe: true
     });
 
     // defaultTab may also be an empty string or a tab id that doesn't exist anymore
     // (e.g. it was a tab registered by an addon that has been uninstalled).
@@ -981,17 +980,16 @@ Inspector.prototype = {
           }
         },
         defaultTab == changesId);
     }
 
     this.sidebar.addAllQueuedTabs();
 
     // Persist splitter state in preferences.
-    this.sidebar.on("select", this.onSidebarSelect);
     this.sidebar.on("show", this.onSidebarShown);
     this.sidebar.on("hide", this.onSidebarHidden);
     this.sidebar.on("destroy", this.onSidebarHidden);
 
     this.sidebar.show();
   },
 
   /**
rename from devtools/client/debugger/test/mochitest/addon1.xpi
rename to devtools/client/shared/test/addon1.xpi
rename from devtools/client/debugger/test/mochitest/addon2.xpi
rename to devtools/client/shared/test/addon2.xpi
--- a/devtools/client/shared/test/browser.ini
+++ b/devtools/client/shared/test/browser.ini
@@ -1,60 +1,77 @@
 [DEFAULT]
 tags = devtools
 subsuite = devtools
 support-files =
+  addon1.xpi
+  addon2.xpi
   browser_devices.json
+  code_WorkerTargetActor.attach-worker1.js
+  code_WorkerTargetActor.attach-worker2.js
+  code_WorkerTargetActor.attachThread-worker.js
+  code_frame-script.js
   doc_cubic-bezier-01.html
   doc_cubic-bezier-02.html
+  doc_empty-tab-01.html
+  doc_empty-tab-02.html
   doc_filter-editor-01.html
   doc_html_tooltip-02.xul
   doc_html_tooltip-03.xul
   doc_html_tooltip-04.xul
   doc_html_tooltip-05.xul
   doc_html_tooltip.xul
   doc_html_tooltip_arrow-01.xul
   doc_html_tooltip_arrow-02.xul
   doc_html_tooltip_doorhanger-01.xul
   doc_html_tooltip_doorhanger-02.xul
   doc_html_tooltip_hover.xul
   doc_html_tooltip_rtl.xul
   doc_inplace-editor_autocomplete_offset.xul
   doc_layoutHelpers-getBoxQuads.html
   doc_layoutHelpers.html
   doc_options-view.xul
+  doc_script-switching-01.html
+  doc_script-switching-02.html
   doc_spectrum.html
   doc_tableWidget_basic.html
   doc_tableWidget_keyboard_interaction.xul
   doc_tableWidget_mouse_interaction.xul
   doc_templater_basic.html
+  doc_WorkerTargetActor.attach-tab1.html
+  doc_WorkerTargetActor.attach-tab2.html
+  doc_WorkerTargetActor.attachThread-tab.html
   dummy.html
   frame-script-utils.js
   head.js
   helper_color_data.js
   helper_html_tooltip.js
   helper_inplace_editor.js
+  helper_workers.js
   leakhunt.js
   shared-head.js
   shared-redux-head.js
   telemetry-test-helpers.js
   test-actor-registry.js
   test-actor.js
+  testactors.js
   !/devtools/client/responsive.html/test/browser/devices.json
 
 [browser_autocomplete_popup.js]
 [browser_css_angle.js]
 [browser_css_color.js]
 [browser_cubic-bezier-01.js]
 [browser_cubic-bezier-02.js]
 [browser_cubic-bezier-03.js]
 [browser_cubic-bezier-04.js]
 [browser_cubic-bezier-05.js]
 [browser_cubic-bezier-06.js]
 [browser_cubic-bezier-07.js]
+[browser_dbg_globalactor.js]
+skip-if = e10s
 [browser_filter-editor-01.js]
 [browser_filter-editor-02.js]
 [browser_filter-editor-03.js]
 [browser_filter-editor-04.js]
 [browser_filter-editor-05.js]
 [browser_filter-editor-06.js]
 [browser_filter-editor-07.js]
 [browser_filter-editor-08.js]
@@ -199,8 +216,31 @@ skip-if = !e10s || os == "win" # RDM onl
 [browser_telemetry_toolboxtabs_webaudioeditor.js]
 [browser_telemetry_toolboxtabs_webconsole.js]
 [browser_treeWidget_basic.js]
 [browser_treeWidget_keyboard_interaction.js]
 [browser_treeWidget_mouse_interaction.js]
 [browser_devices.js]
 skip-if = verify
 [browser_theme_switching.js]
+[browser_dbg_listaddons.js]
+skip-if = e10s && debug
+tags = addons
+[browser_dbg_listtabs-01.js]
+[browser_dbg_listtabs-02.js]
+skip-if = true # Never worked for remote frames, needs a mock DebuggerServerConnection
+[browser_dbg_listtabs-03.js]
+skip-if = e10s && debug
+[browser_dbg_multiple-windows.js]
+[browser_dbg_navigation.js]
+skip-if = e10s && debug
+[browser_dbg_target-scoped-actor-01.js]
+[browser_dbg_target-scoped-actor-02.js]
+[browser_dbg_worker-console-01.js]
+skip-if = true # bug 1368569
+[browser_dbg_worker-console-02.js]
+skip-if = e10s && debug
+[browser_dbg_worker-console-03.js]
+skip-if = debug # bug 1334683
+[browser_dbg_worker-console-04.js]
+skip-if = e10s && debug
+[browser_dbg_WorkerTargetActor.attach.js]
+skip-if = e10s && debug
\ No newline at end of file
rename from devtools/client/debugger/test/mochitest/browser_dbg_WorkerTargetActor.attach.js
rename to devtools/client/shared/test/browser_dbg_WorkerTargetActor.attach.js
--- a/devtools/client/debugger/test/mochitest/browser_dbg_WorkerTargetActor.attach.js
+++ b/devtools/client/shared/test/browser_dbg_WorkerTargetActor.attach.js
@@ -1,29 +1,45 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Check to make sure that a worker can be attached to a toolbox
+// and that the console works.
+
+// Import helpers for the workers
+/* import-globals-from helper_workers.js */
+Services.scriptloader.loadSubScript(
+  "chrome://mochitests/content/browser/devtools/client/shared/test/helper_workers.js",
+  this);
+
 var MAX_TOTAL_VIEWERS = "browser.sessionhistory.max_total_viewers";
 
 var TAB1_URL = EXAMPLE_URL + "doc_WorkerTargetActor.attach-tab1.html";
 var TAB2_URL = EXAMPLE_URL + "doc_WorkerTargetActor.attach-tab2.html";
 var WORKER1_URL = "code_WorkerTargetActor.attach-worker1.js";
 var WORKER2_URL = "code_WorkerTargetActor.attach-worker2.js";
 
 function test() {
   Task.spawn(function* () {
-    let oldMaxTotalViewers = SpecialPowers.getIntPref(MAX_TOTAL_VIEWERS);
+    const oldMaxTotalViewers = SpecialPowers.getIntPref(MAX_TOTAL_VIEWERS);
     SpecialPowers.setIntPref(MAX_TOTAL_VIEWERS, 10);
 
     DebuggerServer.init();
     DebuggerServer.registerAllActors();
 
-    let client = new DebuggerClient(DebuggerServer.connectPipe());
+    const client = new DebuggerClient(DebuggerServer.connectPipe());
     yield connect(client);
 
-    let tab = yield addTab(TAB1_URL);
-    let { tabs } = yield listTabs(client);
-    let [, tabClient] = yield attachTarget(client, findTab(tabs, TAB1_URL));
+    const tab = yield addTab(TAB1_URL);
+    const { tabs } = yield listTabs(client);
+    const [, tabClient] = yield attachTarget(client, findTab(tabs, TAB1_URL));
     yield listWorkers(tabClient);
 
     // If a page still has pending network requests, it will not be moved into
     // the bfcache. Consequently, we cannot use waitForWorkerListChanged here,
     // because the worker is not guaranteed to have finished loading when it is
     // registered. Instead, we have to wait for the promise returned by
     // createWorker in the tab to be resolved.
     yield createWorkerInTab(tab, WORKER1_URL);
@@ -35,17 +51,17 @@ function test() {
     executeSoon(() => {
       BrowserTestUtils.loadURI(tab.linkedBrowser, TAB2_URL);
     });
     yield waitForWorkerClose(workerClient1);
     is(workerClient1.isClosed, true, "worker in tab 1 should be closed");
 
     yield createWorkerInTab(tab, WORKER2_URL);
     ({ workers } = yield listWorkers(tabClient));
-    let [, workerClient2] = yield attachWorker(tabClient,
+    const [, workerClient2] = yield attachWorker(tabClient,
                                                findWorker(workers, WORKER2_URL));
     is(workerClient2.isClosed, false, "worker in tab 2 should not be closed");
 
     executeSoon(() => {
       tab.linkedBrowser.goBack();
     });
     yield waitForWorkerClose(workerClient2);
     is(workerClient2.isClosed, true, "worker in tab 2 should be closed");
rename from devtools/client/debugger/test/mochitest/browser_dbg_globalactor.js
rename to devtools/client/shared/test/browser_dbg_globalactor.js
--- a/devtools/client/debugger/test/mochitest/browser_dbg_globalactor.js
+++ b/devtools/client/shared/test/browser_dbg_globalactor.js
@@ -2,17 +2,23 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Check extension-added global actor API.
  */
 
-const ACTORS_URL = CHROME_URL + "testactors.js";
+"use strict";
+
+var { DebuggerServer } = require("devtools/server/main");
+var { ActorRegistry } = require("devtools/server/actors/utils/actor-registry");
+var { DebuggerClient } = require("devtools/shared/client/debugger-client");
+
+const ACTORS_URL = EXAMPLE_URL + "testactors.js";
 
 add_task(async function() {
   DebuggerServer.init();
   DebuggerServer.registerAllActors();
 
   ActorRegistry.registerModule(ACTORS_URL, {
     prefix: "testOne",
     constructor: "TestActor1",
@@ -38,17 +44,17 @@ add_task(async function() {
   response = await client.request({ to: globalActor, type: "ping" });
   is(response.pong, "pong", "Actor should respond to requests.");
 
   // Make sure that lazily-created actors are created only once.
   let count = 0;
   for (const connID of Object.getOwnPropertyNames(DebuggerServer._connections)) {
     const conn = DebuggerServer._connections[connID];
     const actorPrefix = conn._prefix + "testOne";
-    for (let pool of conn._extraPools) {
+    for (const pool of conn._extraPools) {
       for (const actor of pool.poolChildren()) {
         if (actor.actorID.startsWith(actorPrefix)) {
           count++;
         }
       }
     }
   }
 
rename from devtools/client/debugger/test/mochitest/browser_dbg_listaddons.js
rename to devtools/client/shared/test/browser_dbg_listaddons.js
--- a/devtools/client/debugger/test/mochitest/browser_dbg_listaddons.js
+++ b/devtools/client/shared/test/browser_dbg_listaddons.js
@@ -1,110 +1,165 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+"use strict";
+
+var { DebuggerServer } = require("devtools/server/main");
+var { DebuggerClient } = require("devtools/shared/client/debugger-client");
+
+const chromeRegistry =
+  Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIChromeRegistry);
+const DEBUGGER_CHROME_URL = "chrome://mochitests/content/browser/devtools/client/shared/test/";
+const DEBUGGER_CHROME_URI = Services.io.newURI(DEBUGGER_CHROME_URL);
+
+var { AddonManager } = ChromeUtils.import("resource://gre/modules/AddonManager.jsm", {});
+
 /**
  * Make sure the listAddons request works as specified.
  */
 const ADDON1_ID = "jid1-oBAwBoE5rSecNg@jetpack";
 const ADDON1_PATH = "addon1.xpi";
 const ADDON2_ID = "jid1-qjtzNGV8xw5h2A@jetpack";
 const ADDON2_PATH = "addon2.xpi";
 
-var gAddon1, gAddon1Actor, gAddon2, gAddon2Actor, gClient;
+var gAddon1, gAddon1Actor, gAddon2, gClient;
 
 function test() {
   DebuggerServer.init();
   DebuggerServer.registerAllActors();
 
-  let transport = DebuggerServer.connectPipe();
+  const transport = DebuggerServer.connectPipe();
   gClient = new DebuggerClient(transport);
   gClient.connect().then(([aType, aTraits]) => {
     is(aType, "browser",
       "Root actor should identify itself as a browser.");
 
     promise.resolve(null)
       .then(testFirstAddon)
       .then(testSecondAddon)
       .then(testRemoveFirstAddon)
       .then(testRemoveSecondAddon)
       .then(() => gClient.close())
       .then(finish)
-      .catch(aError => {
-        ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
+      .catch(error => {
+        ok(false, "Got an error: " + error.message + "\n" + error.stack);
       });
   });
 }
 
 function testFirstAddon() {
   let addonListChanged = false;
   gClient.addOneTimeListener("addonListChanged", () => {
     addonListChanged = true;
   });
 
-  return addTemporaryAddon(ADDON1_PATH).then(aAddon => {
-    gAddon1 = aAddon;
+  return addTemporaryAddon(ADDON1_PATH).then(addon => {
+    gAddon1 = addon;
 
-    return getAddonActorForId(gClient, ADDON1_ID).then(aGrip => {
+    return getAddonActorForId(gClient, ADDON1_ID).then(grip => {
       ok(!addonListChanged, "Should not yet be notified that list of addons changed.");
-      ok(aGrip, "Should find an addon actor for addon1.");
-      gAddon1Actor = aGrip.actor;
+      ok(grip, "Should find an addon actor for addon1.");
+      gAddon1Actor = grip.actor;
     });
   });
 }
 
 function testSecondAddon() {
   let addonListChanged = false;
-  gClient.addOneTimeListener("addonListChanged", function () {
+  gClient.addOneTimeListener("addonListChanged", function() {
     addonListChanged = true;
   });
 
-  return addTemporaryAddon(ADDON2_PATH).then(aAddon => {
-    gAddon2 = aAddon;
+  return addTemporaryAddon(ADDON2_PATH).then(addon => {
+    gAddon2 = addon;
 
-    return getAddonActorForId(gClient, ADDON1_ID).then(aFirstGrip => {
-      return getAddonActorForId(gClient, ADDON2_ID).then(aSecondGrip => {
+    return getAddonActorForId(gClient, ADDON1_ID).then(fistGrip => {
+      return getAddonActorForId(gClient, ADDON2_ID).then(secondGrip => {
         ok(addonListChanged, "Should be notified that list of addons changed.");
-        is(aFirstGrip.actor, gAddon1Actor, "First addon's actor shouldn't have changed.");
-        ok(aSecondGrip, "Should find a addon actor for the second addon.");
-        gAddon2Actor = aSecondGrip.actor;
+        is(fistGrip.actor, gAddon1Actor, "First addon's actor shouldn't have changed.");
+        ok(secondGrip, "Should find a addon actor for the second addon.");
       });
     });
   });
 }
 
 function testRemoveFirstAddon() {
   let addonListChanged = false;
-  gClient.addOneTimeListener("addonListChanged", function () {
+  gClient.addOneTimeListener("addonListChanged", function() {
     addonListChanged = true;
   });
 
   return removeAddon(gAddon1).then(() => {
-    return getAddonActorForId(gClient, ADDON1_ID).then(aGrip => {
+    return getAddonActorForId(gClient, ADDON1_ID).then(grip => {
       ok(addonListChanged, "Should be notified that list of addons changed.");
-      ok(!aGrip, "Shouldn't find a addon actor for the first addon anymore.");
+      ok(!grip, "Shouldn't find a addon actor for the first addon anymore.");
     });
   });
 }
 
 function testRemoveSecondAddon() {
   let addonListChanged = false;
-  gClient.addOneTimeListener("addonListChanged", function () {
+  gClient.addOneTimeListener("addonListChanged", function() {
     addonListChanged = true;
   });
 
   return removeAddon(gAddon2).then(() => {
-    return getAddonActorForId(gClient, ADDON2_ID).then(aGrip => {
+    return getAddonActorForId(gClient, ADDON2_ID).then(grip => {
       ok(addonListChanged, "Should be notified that list of addons changed.");
-      ok(!aGrip, "Shouldn't find a addon actor for the second addon anymore.");
+      ok(!grip, "Shouldn't find a addon actor for the second addon anymore.");
     });
   });
 }
 
-registerCleanupFunction(function () {
+registerCleanupFunction(function() {
   gAddon1 = null;
   gAddon1Actor = null;
   gAddon2 = null;
-  gAddon2Actor = null;
   gClient = null;
 });
+
+function getAddonURIFromPath(path) {
+  const chromeURI = Services.io.newURI(path, null, DEBUGGER_CHROME_URI);
+  return chromeRegistry.convertChromeURL(chromeURI).QueryInterface(Ci.nsIFileURL);
+}
+
+function addTemporaryAddon(path) {
+  const addonFile = getAddonURIFromPath(path).file;
+  info("Installing addon: " + addonFile.path);
+
+  return AddonManager.installTemporaryAddon(addonFile);
+}
+
+function getAddonActorForId(client, addonId) {
+  info("Get addon actor for ID: " + addonId);
+  const deferred = promise.defer();
+
+  client.listAddons().then(response => {
+    const addonTargetActor = response.addons.filter(grip => grip.id == addonId).pop();
+    info("got addon actor for ID: " + addonId);
+    deferred.resolve(addonTargetActor);
+  });
+
+  return deferred.promise;
+}
+
+function removeAddon(addon) {
+  info("Removing addon.");
+
+  const deferred = promise.defer();
+
+  const listener = {
+    onUninstalled: function(uninstalledAddon) {
+      if (uninstalledAddon != addon) {
+        return;
+      }
+      AddonManager.removeAddonListener(listener);
+      deferred.resolve();
+    }
+  };
+  AddonManager.addAddonListener(listener);
+  addon.uninstall();
+
+  return deferred.promise;
+}
rename from devtools/client/debugger/test/mochitest/browser_dbg_listtabs-01.js
rename to devtools/client/shared/test/browser_dbg_listtabs-01.js
--- a/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-01.js
+++ b/devtools/client/shared/test/browser_dbg_listtabs-01.js
@@ -1,96 +1,112 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+"use strict";
+
 /**
  * Make sure the listTabs request works as specified.
  */
 
+var { DebuggerServer } = require("devtools/server/main");
+var { DebuggerClient } = require("devtools/shared/client/debugger-client");
+
 const TAB1_URL = EXAMPLE_URL + "doc_empty-tab-01.html";
 const TAB2_URL = EXAMPLE_URL + "doc_empty-tab-02.html";
 
 var gTab1, gTab1Actor, gTab2, gTab2Actor, gClient;
 
 function test() {
   DebuggerServer.init();
   DebuggerServer.registerAllActors();
 
-  let transport = DebuggerServer.connectPipe();
+  const transport = DebuggerServer.connectPipe();
   gClient = new DebuggerClient(transport);
   gClient.connect().then(([aType, aTraits]) => {
     is(aType, "browser",
       "Root actor should identify itself as a browser.");
 
     promise.resolve(null)
       .then(testFirstTab)
       .then(testSecondTab)
       .then(testRemoveTab)
       .then(testAttachRemovedTab)
       .then(() => gClient.close())
       .then(finish)
-      .catch(aError => {
-        ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
+      .catch(error => {
+        ok(false, "Got an error: " + error.message + "\n" + error.stack);
       });
   });
 }
 
 function testFirstTab() {
-  return addTab(TAB1_URL).then(aTab => {
-    gTab1 = aTab;
+  return addTab(TAB1_URL).then(tab => {
+    gTab1 = tab;
 
-    return getTargetActorForUrl(gClient, TAB1_URL).then(aGrip => {
-      ok(aGrip, "Should find a target actor for the first tab.");
-      gTab1Actor = aGrip.actor;
+    return getTargetActorForUrl(gClient, TAB1_URL).then(grip => {
+      ok(grip, "Should find a target actor for the first tab.");
+      gTab1Actor = grip.actor;
     });
   });
 }
 
 function testSecondTab() {
-  return addTab(TAB2_URL).then(aTab => {
-    gTab2 = aTab;
+  return addTab(TAB2_URL).then(tab => {
+    gTab2 = tab;
 
-    return getTargetActorForUrl(gClient, TAB1_URL).then(aFirstGrip => {
-      return getTargetActorForUrl(gClient, TAB2_URL).then(aSecondGrip => {
-        is(aFirstGrip.actor, gTab1Actor, "First tab's actor shouldn't have changed.");
-        ok(aSecondGrip, "Should find a target actor for the second tab.");
-        gTab2Actor = aSecondGrip.actor;
+    return getTargetActorForUrl(gClient, TAB1_URL).then(firstGrip => {
+      return getTargetActorForUrl(gClient, TAB2_URL).then(secondGrip => {
+        is(firstGrip.actor, gTab1Actor, "First tab's actor shouldn't have changed.");
+        ok(secondGrip, "Should find a target actor for the second tab.");
+        gTab2Actor = secondGrip.actor;
       });
     });
   });
 }
 
 function testRemoveTab() {
   return removeTab(gTab1).then(() => {
-    return getTargetActorForUrl(gClient, TAB1_URL).then(aGrip => {
-      ok(!aGrip, "Shouldn't find a target actor for the first tab anymore.");
+    return getTargetActorForUrl(gClient, TAB1_URL).then(grip => {
+      ok(!grip, "Shouldn't find a target actor for the first tab anymore.");
     });
   });
 }
 
 function testAttachRemovedTab() {
   return removeTab(gTab2).then(() => {
-    let deferred = promise.defer();
+    const deferred = promise.defer();
 
-    gClient.addListener("paused", (aEvent, aPacket) => {
+    gClient.addListener("paused", () => {
       ok(false, "Attaching to an exited target actor shouldn't generate a pause.");
       deferred.reject();
     });
 
-    gClient.request({ to: gTab2Actor, type: "attach" }, aResponse => {
-      is(aResponse.error, "connectionClosed",
+    gClient.request({ to: gTab2Actor, type: "attach" }, response => {
+      is(response.error, "connectionClosed",
          "Connection is gone since the tab was removed.");
       deferred.resolve();
     });
 
     return deferred.promise;
   });
 }
 
-registerCleanupFunction(function () {
+registerCleanupFunction(function() {
   gTab1 = null;
   gTab1Actor = null;
   gTab2 = null;
   gTab2Actor = null;
   gClient = null;
 });
+
+function getTargetActorForUrl(client, url) {
+  const deferred = promise.defer();
+
+  client.listTabs().then(response => {
+    const targetActor = response.tabs.filter(grip => grip.url == url).pop();
+    deferred.resolve(targetActor);
+  });
+
+  return deferred.promise;
+}
rename from devtools/client/debugger/test/mochitest/browser_dbg_listtabs-02.js
rename to devtools/client/shared/test/browser_dbg_listtabs-02.js
--- a/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-02.js
+++ b/devtools/client/shared/test/browser_dbg_listtabs-02.js
@@ -3,16 +3,17 @@
 
 "use strict";
 
 /**
  * Make sure the root actor's live tab list implementation works as specified.
  */
 
 var { BrowserTabList } = require("devtools/server/actors/webbrowser");
+var { DebuggerServer } = require("devtools/server/main");
 
 var gTestPage = "data:text/html;charset=utf-8," + encodeURIComponent(
   "<title>JS Debugger BrowserTabList test page</title><body>Yo.</body>");
 
 // The tablist object whose behavior we observe.
 var gTabList;
 var gFirstActor, gActorA;
 var gTabA, gTabB, gTabC;
@@ -49,62 +50,70 @@ function test() {
     .then(checkSingleTab)
     .then(finishUp);
 }
 
 function checkSingleTab() {
   return gTabList.getList().then(targetActors => {
     is(targetActors.length, 1, "initial tab list: contains initial tab");
     gFirstActor = targetActors[0];
-    is(gFirstActor.url, "about:blank", "initial tab list: initial tab URL is 'about:blank'");
+    is(
+      gFirstActor.url,
+      "about:blank",
+      "initial tab list: initial tab URL is 'about:blank'"
+    );
     is(gFirstActor.title, "New Tab", "initial tab list: initial tab title is 'New Tab'");
   });
 }
 
 function addTabA() {
-  return addTab(gTestPage).then(aTab => {
-    gTabA = aTab;
+  return addTab(gTestPage).then(tab => {
+    gTabA = tab;
   });
 }
 
 function testTabA() {
   is(onListChangedCount, 1, "onListChanged handler call count");
 
   return gTabList.getList().then(targetActors => {
     targetActors = new Set(targetActors);
     is(targetActors.size, 2, "gTabA opened: two tabs in list");
     ok(targetActors.has(gFirstActor), "gTabA opened: initial tab present");
 
     info("actors: " + [...targetActors].map(a => a.url));
     gActorA = [...targetActors].filter(a => a !== gFirstActor)[0];
     ok(gActorA.url.match(/^data:text\/html;/), "gTabA opened: new tab URL");
-    is(gActorA.title, "JS Debugger BrowserTabList test page", "gTabA opened: new tab title");
+    is(
+      gActorA.title,
+      "JS Debugger BrowserTabList test page",
+      "gTabA opened: new tab title"
+    );
   });
 }
 
 function addTabB() {
-  return addTab(gTestPage).then(aTab => {
-    gTabB = aTab;
+  return addTab(gTestPage).then(tab => {
+    gTabB = tab;
   });
 }
 
 function testTabB() {
   is(onListChangedCount, 2, "onListChanged handler call count");
 
   return gTabList.getList().then(targetActors => {
     targetActors = new Set(targetActors);
     is(targetActors.size, 3, "gTabB opened: three tabs in list");
   });
 }
 
 function removeTabA() {
-  let deferred = promise.defer();
+  const deferred = promise.defer();
 
-  once(gBrowser.tabContainer, "TabClose").then(aEvent => {
-    ok(!aEvent.detail.adoptedBy, "This was a normal tab close");
+  once(gBrowser.tabContainer, "TabClose").then(event => {
+    ok(!event.detail.adoptedBy, "This was a normal tab close");
 
     // Let the actor's TabClose handler finish first.
     executeSoon(deferred.resolve);
   }, false);
 
   removeTab(gTabA);
   return deferred.promise;
 }
@@ -115,40 +124,44 @@ function testTabClosed() {
   gTabList.getList().then(targetActors => {
     targetActors = new Set(targetActors);
     is(targetActors.size, 2, "gTabA closed: two tabs in list");
     ok(targetActors.has(gFirstActor), "gTabA closed: initial tab present");
 
     info("actors: " + [...targetActors].map(a => a.url));
     gActorA = [...targetActors].filter(a => a !== gFirstActor)[0];
     ok(gActorA.url.match(/^data:text\/html;/), "gTabA closed: new tab URL");
-    is(gActorA.title, "JS Debugger BrowserTabList test page", "gTabA closed: new tab title");
+    is(
+      gActorA.title,
+      "JS Debugger BrowserTabList test page",
+      "gTabA closed: new tab title"
+    );
   });
 }
 
 function addTabC() {
-  return addTab(gTestPage).then(aTab => {
-    gTabC = aTab;
+  return addTab(gTestPage).then(tab => {
+    gTabC = tab;
   });
 }
 
 function testTabC() {
   is(onListChangedCount, 4, "onListChanged handler call count");
 
   gTabList.getList().then(targetActors => {
     targetActors = new Set(targetActors);
     is(targetActors.size, 3, "gTabC opened: three tabs in list");
   });
 }
 
 function removeTabC() {
-  let deferred = promise.defer();
+  const deferred = promise.defer();
 
-  once(gBrowser.tabContainer, "TabClose").then(aEvent => {
-    ok(aEvent.detail.adoptedBy, "This was a tab closed by moving");
+  once(gBrowser.tabContainer, "TabClose").then(event => {
+    ok(event.detail.adoptedBy, "This was a tab closed by moving");
 
     // Let the actor's TabClose handler finish first.
     executeSoon(deferred.resolve);
   }, false);
 
   gNewWindow = gBrowser.replaceTabWithWindow(gTabC);
   return deferred.promise;
 }
@@ -159,25 +172,29 @@ function testNewWindow() {
   return gTabList.getList().then(targetActors => {
     targetActors = new Set(targetActors);
     is(targetActors.size, 3, "gTabC closed: three tabs in list");
     ok(targetActors.has(gFirstActor), "gTabC closed: initial tab present");
 
     info("actors: " + [...targetActors].map(a => a.url));
     gActorA = [...targetActors].filter(a => a !== gFirstActor)[0];
     ok(gActorA.url.match(/^data:text\/html;/), "gTabC closed: new tab URL");
-    is(gActorA.title, "JS Debugger BrowserTabList test page", "gTabC closed: new tab title");
+    is(
+      gActorA.title,
+      "JS Debugger BrowserTabList test page",
+      "gTabC closed: new tab title"
+    );
   });
 }
 
 function removeNewWindow() {
-  let deferred = promise.defer();
+  const deferred = promise.defer();
 
-  once(gNewWindow, "unload").then(aEvent => {
-    ok(!aEvent.detail, "This was a normal window close");
+  once(gNewWindow, "unload").then(event => {
+    ok(!event.detail, "This was a normal window close");
 
     // Let the actor's TabClose handler finish first.
     executeSoon(deferred.resolve);
   }, false);
 
   gNewWindow.close();
   return deferred.promise;
 }
@@ -188,25 +205,29 @@ function testWindowClosed() {
   return gTabList.getList().then(targetActors => {
     targetActors = new Set(targetActors);
     is(targetActors.size, 2, "gNewWindow closed: two tabs in list");
     ok(targetActors.has(gFirstActor), "gNewWindow closed: initial tab present");
 
     info("actors: " + [...targetActors].map(a => a.url));
     gActorA = [...targetActors].filter(a => a !== gFirstActor)[0];
     ok(gActorA.url.match(/^data:text\/html;/), "gNewWindow closed: new tab URL");
-    is(gActorA.title, "JS Debugger BrowserTabList test page", "gNewWindow closed: new tab title");
+    is(
+      gActorA.title,
+      "JS Debugger BrowserTabList test page",
+      "gNewWindow closed: new tab title"
+    );
   });
 }
 
 function removeTabB() {
-  let deferred = promise.defer();
+  const deferred = promise.defer();
 
-  once(gBrowser.tabContainer, "TabClose").then(aEvent => {
-    ok(!aEvent.detail.adoptedBy, "This was a normal tab close");
+  once(gBrowser.tabContainer, "TabClose").then(event => {
+    ok(!event.detail.adoptedBy, "This was a normal tab close");
 
     // Let the actor's TabClose handler finish first.
     executeSoon(deferred.resolve);
   }, false);
 
   removeTab(gTabB);
   return deferred.promise;
 }
rename from devtools/client/debugger/test/mochitest/browser_dbg_listtabs-03.js
rename to devtools/client/shared/test/browser_dbg_listtabs-03.js
--- a/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-03.js
+++ b/devtools/client/shared/test/browser_dbg_listtabs-03.js
@@ -1,59 +1,61 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+"use strict";
+
 /**
  * Make sure the listTabs request works as specified.
  */
 
+var { DebuggerServer } = require("devtools/server/main");
+var { DebuggerClient } = require("devtools/shared/client/debugger-client");
+var { Task } = require("devtools/shared/task");
+
 const TAB1_URL = EXAMPLE_URL + "doc_empty-tab-01.html";
 
-var gTab1, gTab1Actor, gTab2, gTab2Actor, gClient;
+var gClient;
 
 function test() {
   DebuggerServer.init();
   DebuggerServer.registerAllActors();
 
-  let transport = DebuggerServer.connectPipe();
+  const transport = DebuggerServer.connectPipe();
   gClient = new DebuggerClient(transport);
   gClient.connect().then(Task.async(function* ([aType, aTraits]) {
     is(aType, "browser",
       "Root actor should identify itself as a browser.");
-    let tab = yield addTab(TAB1_URL);
+    const tab = yield addTab(TAB1_URL);
 
     let { tabs } = yield gClient.listTabs();
     is(tabs.length, 2, "Should be two tabs");
-    let tabGrip = tabs.filter(a => a.url == TAB1_URL).pop();
+    const tabGrip = tabs.filter(a => a.url == TAB1_URL).pop();
     ok(tabGrip, "Should have an actor for the tab");
 
     let response = yield gClient.request({ to: tabGrip.actor, type: "attach" });
     is(response.type, "tabAttached", "Should have attached");
 
     response = yield gClient.listTabs();
     tabs = response.tabs;
 
     response = yield gClient.request({ to: tabGrip.actor, type: "detach" });
     is(response.type, "detached", "Should have detached");
 
-    let newGrip = tabs.filter(a => a.url == TAB1_URL).pop();
+    const newGrip = tabs.filter(a => a.url == TAB1_URL).pop();
     is(newGrip.actor, tabGrip.actor, "Should have the same actor for the same tab");
 
     response = yield gClient.request({ to: tabGrip.actor, type: "attach" });
     is(response.type, "tabAttached", "Should have attached");
     response = yield gClient.request({ to: tabGrip.actor, type: "detach" });
     is(response.type, "detached", "Should have detached");
 
     yield removeTab(tab);
     yield gClient.close();
     finish();
   }));
 }
 
-registerCleanupFunction(function () {
-  gTab1 = null;
-  gTab1Actor = null;
-  gTab2 = null;
-  gTab2Actor = null;
+registerCleanupFunction(function() {
   gClient = null;
 });
rename from devtools/client/debugger/test/mochitest/browser_dbg_multiple-windows.js
rename to devtools/client/shared/test/browser_dbg_multiple-windows.js
--- a/devtools/client/debugger/test/mochitest/browser_dbg_multiple-windows.js
+++ b/devtools/client/shared/test/browser_dbg_multiple-windows.js
@@ -1,18 +1,23 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+"use strict";
+
 /**
  * Make sure that the debugger attaches to the right tab when multiple windows
  * are open.
  */
 
+var { DebuggerServer } = require("devtools/server/main");
+var { DebuggerClient } = require("devtools/shared/client/debugger-client");
+
 const TAB1_URL = EXAMPLE_URL + "doc_script-switching-01.html";
 const TAB2_URL = EXAMPLE_URL + "doc_script-switching-02.html";
 
 add_task(async function() {
   DebuggerServer.init();
   DebuggerServer.registerAllActors();
 
   const transport = DebuggerServer.connectPipe();
@@ -82,21 +87,29 @@ async function testFocusFirst(client) {
 async function testRemoveTab(client, win, tab) {
   win.close();
 
   // give it time to close
   await new Promise(resolve => executeSoon(resolve));
   await continue_remove_tab(client, tab);
 }
 
-async function continue_remove_tab(client, tab)
-{
+async function continue_remove_tab(client, tab) {
   removeTab(tab);
 
   const response = await client.listTabs();
   // Verify that tabs are no longer included in listTabs.
   const foundTab1 = response.tabs.some(grip => grip.url == TAB1_URL);
   const foundTab2 = response.tabs.some(grip => grip.url == TAB2_URL);
   ok(!foundTab1, "Tab1 should be gone.");
   ok(!foundTab2, "Tab2 should be gone.");
 
   is(response.selected, 0, "The original tab is selected.");
 }
+
+function addWindow(url) {
+  info("Adding window: " + url);
+  return promise.resolve(getChromeWindow(window.open(url)));
+}
+
+function getChromeWindow(win) {
+  return win.docShell.rootTreeItem.domWindow;
+}
rename from devtools/client/debugger/test/mochitest/browser_dbg_navigation.js
rename to devtools/client/shared/test/browser_dbg_navigation.js
--- a/devtools/client/debugger/test/mochitest/browser_dbg_navigation.js
+++ b/devtools/client/shared/test/browser_dbg_navigation.js
@@ -1,73 +1,99 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+"use strict";
+
 /**
  * Check tab attach/navigation.
  */
 
+var { DebuggerServer } = require("devtools/server/main");
+var { DebuggerClient } = require("devtools/shared/client/debugger-client");
+
 const TAB1_URL = EXAMPLE_URL + "doc_empty-tab-01.html";
-const TAB2_URL = EXAMPLE_URL + "doc_empty-tab-02.html";
+const TAB2_FILE = "doc_empty-tab-02.html";
+const TAB2_URL = EXAMPLE_URL + TAB2_FILE;
 
 var gClient;
 
 function test() {
   DebuggerServer.init();
   DebuggerServer.registerAllActors();
 
-  let transport = DebuggerServer.connectPipe();
+  const transport = DebuggerServer.connectPipe();
   gClient = new DebuggerClient(transport);
   gClient.connect().then(([aType, aTraits]) => {
     is(aType, "browser",
       "Root actor should identify itself as a browser.");
 
     addTab(TAB1_URL)
       .then(() => attachTargetActorForUrl(gClient, TAB1_URL))
       .then(testNavigate)
       .then(testDetach)
       .then(finish)
-      .catch(aError => {
-        ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
+      .catch(error => {
+        ok(false, "Got an error: " + error.message + "\n" + error.stack);
       });
   });
 }
 
 function testNavigate([aGrip, aResponse]) {
-  let outstanding = [promise.defer(), promise.defer()];
+  const outstanding = [promise.defer(), promise.defer()];
 
-  gClient.addListener("tabNavigated", function onTabNavigated(aEvent, aPacket) {
-    is(aPacket.url, TAB2_URL,
+  gClient.addListener("tabNavigated", function onTabNavigated(event, packet) {
+    is(packet.url.split("/").pop(), TAB2_FILE,
       "Got a tab navigation notification.");
 
-    if (aPacket.state == "start") {
+    info(JSON.stringify(packet));
+    info(JSON.stringify(event));
+
+    if (packet.state == "start") {
       ok(true, "Tab started to navigate.");
       outstanding[0].resolve();
     } else {
       ok(true, "Tab finished navigating.");
       gClient.removeListener("tabNavigated", onTabNavigated);
       outstanding[1].resolve();
     }
   });
 
   BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TAB2_URL);
   return promise.all(outstanding.map(e => e.promise))
                 .then(() => aGrip.actor);
 }
 
-function testDetach(aActor) {
-  let deferred = promise.defer();
+function testDetach(actor) {
+  const deferred = promise.defer();
 
-  gClient.addOneTimeListener("tabDetached", (aType, aPacket) => {
+  gClient.addOneTimeListener("tabDetached", (type, packet) => {
     ok(true, "Got a tab detach notification.");
-    is(aPacket.from, aActor, "tab detach message comes from the expected actor");
+    is(packet.from, actor, "tab detach message comes from the expected actor");
     deferred.resolve(gClient.close());
   });
 
   removeTab(gBrowser.selectedTab);
   return deferred.promise;
 }
 
-registerCleanupFunction(function () {
+registerCleanupFunction(function() {
   gClient = null;
 });
+
+async function attachTargetActorForUrl(client, url) {
+  const grip = await getTargetActorForUrl(client, url);
+  const [ response ] = await client.attachTarget(grip.actor);
+  return [grip, response];
+}
+
+function getTargetActorForUrl(client, url) {
+  const deferred = promise.defer();
+
+  client.listTabs().then(response => {
+    const targetActor = response.tabs.filter(grip => grip.url == url).pop();
+    deferred.resolve(targetActor);
+  });
+
+  return deferred.promise;
+}
rename from devtools/client/debugger/test/mochitest/browser_dbg_target-scoped-actor-01.js
rename to devtools/client/shared/test/browser_dbg_target-scoped-actor-01.js
--- a/devtools/client/debugger/test/mochitest/browser_dbg_target-scoped-actor-01.js
+++ b/devtools/client/shared/test/browser_dbg_target-scoped-actor-01.js
@@ -1,18 +1,23 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+"use strict";
+
 /**
  * Check target-scoped actor lifetimes.
  */
 
-const ACTORS_URL = CHROME_URL + "testactors.js";
+var { DebuggerServer } = require("devtools/server/main");
+var { DebuggerClient } = require("devtools/shared/client/debugger-client");
+
+const ACTORS_URL = EXAMPLE_URL + "testactors.js";
 const TAB_URL = EXAMPLE_URL + "doc_empty-tab-01.html";
 
 add_task(async function test() {
   await addTab(TAB_URL);
 
   DebuggerServer.init();
   DebuggerServer.registerAllActors();
 
@@ -37,8 +42,25 @@ async function testTargetScopedActor(cli
   ok(grip.testOneActor,
     "Found the test target-scoped actor.");
   ok(grip.testOneActor.includes("testOne"),
     "testOneActor's actorPrefix should be used.");
 
   const response = await client.request({ to: grip.testOneActor, type: "ping" });
   is(response.pong, "pong", "Actor should respond to requests.");
 }
+
+async function attachTargetActorForUrl(client, url) {
+  const grip = await getTargetActorForUrl(client, url);
+  const [ response ] = await client.attachTarget(grip.actor);
+  return [grip, response];
+}
+
+function getTargetActorForUrl(client, url) {
+  const deferred = promise.defer();
+
+  client.listTabs().then(response => {
+    const targetActor = response.tabs.filter(grip => grip.url == url).pop();
+    deferred.resolve(targetActor);
+  });
+
+  return deferred.promise;
+}
rename from devtools/client/debugger/test/mochitest/browser_dbg_target-scoped-actor-02.js
rename to devtools/client/shared/test/browser_dbg_target-scoped-actor-02.js
--- a/devtools/client/debugger/test/mochitest/browser_dbg_target-scoped-actor-02.js
+++ b/devtools/client/shared/test/browser_dbg_target-scoped-actor-02.js
@@ -1,18 +1,23 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+"use strict";
+
 /**
  * Check target-scoped actor lifetimes.
  */
 
-const ACTORS_URL = CHROME_URL + "testactors.js";
+var { DebuggerServer } = require("devtools/server/main");
+var { DebuggerClient } = require("devtools/shared/client/debugger-client");
+
+const ACTORS_URL = EXAMPLE_URL + "testactors.js";
 const TAB_URL = EXAMPLE_URL + "doc_empty-tab-01.html";
 
 add_task(async function() {
   await addTab(TAB_URL);
 
   DebuggerServer.init();
   DebuggerServer.registerAllActors();
 
@@ -48,8 +53,25 @@ async function testTargetScopedActor(cli
 async function closeTab(client, grip) {
   await removeTab(gBrowser.selectedTab);
   await Assert.rejects(
     client.request({ to: grip.testOneActor, type: "ping" }),
     err => err.message === `'ping' active request packet to '${grip.testOneActor}' ` +
                            `can't be sent as the connection just closed.`,
     "testOneActor went away.");
 }
+
+async function attachTargetActorForUrl(client, url) {
+  const grip = await getTargetActorForUrl(client, url);
+  const [ response ] = await client.attachTarget(grip.actor);
+  return [grip, response];
+}
+
+function getTargetActorForUrl(client, url) {
+  const deferred = promise.defer();
+
+  client.listTabs().then(response => {
+    const targetActor = response.tabs.filter(grip => grip.url == url).pop();
+    deferred.resolve(targetActor);
+  });
+
+  return deferred.promise;
+}
rename from devtools/client/debugger/test/mochitest/browser_dbg_worker-console-01.js
rename to devtools/client/shared/test/browser_dbg_worker-console-01.js
--- a/devtools/client/debugger/test/mochitest/browser_dbg_worker-console-01.js
+++ b/devtools/client/shared/test/browser_dbg_worker-console-01.js
@@ -1,20 +1,33 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
 // Check to make sure that a worker can be attached to a toolbox
 // and that the console works.
 
+// Import helpers for the workers
+/* import-globals-from helper_workers.js */
+Services.scriptloader.loadSubScript(
+  "chrome://mochitests/content/browser/devtools/client/shared/test/helper_workers.js",
+  this);
+
 var TAB_URL = EXAMPLE_URL + "doc_WorkerTargetActor.attachThread-tab.html";
 var WORKER_URL = "code_WorkerTargetActor.attachThread-worker.js";
 
 add_task(async function testNormalExecution() {
-  let {client, tab, tabClient, workerClient, toolbox, gDebugger} =
+  const {client, tab, workerClient, toolbox} =
     await initWorkerDebugger(TAB_URL, WORKER_URL);
 
-  let jsterm = await getSplitConsole(toolbox);
-  let executed = await jsterm.execute("this.location.toString()");
+  const jsterm = await getSplitConsole(toolbox);
+  const executed = await jsterm.execute("this.location.toString()");
   ok(executed.textContent.includes(WORKER_URL),
       "Evaluating the global's location works");
 
   terminateWorkerInTab(tab, WORKER_URL);
   await waitForWorkerClose(workerClient);
   await gDevTools.closeToolbox(TargetFactory.forWorker(workerClient));
   await close(client);
   await removeTab(tab);
rename from devtools/client/debugger/test/mochitest/browser_dbg_worker-console-02.js
rename to devtools/client/shared/test/browser_dbg_worker-console-02.js
--- a/devtools/client/debugger/test/mochitest/browser_dbg_worker-console-02.js
+++ b/devtools/client/shared/test/browser_dbg_worker-console-02.js
@@ -1,58 +1,62 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
 // Check to make sure that a worker can be attached to a toolbox
 // and that the console works.
 
+// Import helpers for the workers
+/* import-globals-from helper_workers.js */
+Services.scriptloader.loadSubScript(
+  "chrome://mochitests/content/browser/devtools/client/shared/test/helper_workers.js",
+  this);
+
 var TAB_URL = EXAMPLE_URL + "doc_WorkerTargetActor.attachThread-tab.html";
 var WORKER_URL = "code_WorkerTargetActor.attachThread-worker.js";
 
 add_task(async function testWhilePaused() {
-  let {client, tab, tabClient, workerClient, toolbox, gDebugger} =
-    await initWorkerDebugger(TAB_URL, WORKER_URL);
-
-  let gTarget = gDebugger.gTarget;
-  let gResumeButton = gDebugger.document.getElementById("resume");
-  let gResumeKey = gDebugger.document.getElementById("resumeKey");
+  const dbg = await initWorkerDebugger(TAB_URL, WORKER_URL);
+  const {client, tab, workerClient, toolbox} = dbg;
 
   // Execute some basic math to make sure evaluations are working.
-  let jsterm = await getSplitConsole(toolbox);
+  const jsterm = await getSplitConsole(toolbox);
   let executed = await jsterm.execute("10000+1");
   ok(executed.textContent.includes("10001"), "Text for message appeared correct");
 
-  // Pause the worker by waiting for next execution and then sending a message to
-  // it from the main thread.
-  let oncePaused = gTarget.once("thread-paused");
-  EventUtils.sendMouseEvent({ type: "mousedown" }, gResumeButton, gDebugger);
-  once(gDebugger.gClient, "willInterrupt").then(() => {
+  await clickElement(dbg, "pause");
+  once(dbg.client, "willInterrupt").then(() => {
     info("Posting message to worker, then waiting for a pause");
     postMessageToWorkerInTab(tab, WORKER_URL, "ping");
   });
-  await oncePaused;
+  await waitForPaused(dbg);
 
-  let command1 = jsterm.execute("10000+2");
-  let command2 = jsterm.execute("10000+3");
-  let command3 = jsterm.execute("foobar"); // throw an error
+  const command1 = jsterm.execute("10000+2");
+  const command2 = jsterm.execute("10000+3");
+  const command3 = jsterm.execute("foobar"); // throw an error
 
   info("Trying to get the result of command1");
   executed = await command1;
   ok(executed.textContent.includes("10002"),
       "command1 executed successfully");
 
   info("Trying to get the result of command2");
   executed = await command2;
   ok(executed.textContent.includes("10003"),
       "command2 executed successfully");
 
   info("Trying to get the result of command3");
   executed = await command3;
   ok(executed.textContent.includes("ReferenceError: foobar is not defined"),
      "command3 executed successfully");
 
-  let onceResumed = gTarget.once("thread-resumed");
-  EventUtils.sendMouseEvent({ type: "mousedown" }, gResumeButton, gDebugger);
-  await onceResumed;
+  await resume(dbg);
 
   terminateWorkerInTab(tab, WORKER_URL);
   await waitForWorkerClose(workerClient);
   await gDevTools.closeToolbox(TargetFactory.forWorker(workerClient));
   await close(client);
   await removeTab(tab);
 });
rename from devtools/client/debugger/test/mochitest/browser_dbg_worker-console-03.js
rename to devtools/client/shared/test/browser_dbg_worker-console-03.js
--- a/devtools/client/debugger/test/mochitest/browser_dbg_worker-console-03.js
+++ b/devtools/client/shared/test/browser_dbg_worker-console-03.js
@@ -1,43 +1,50 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
 // Check to make sure that a worker can be attached to a toolbox
 // and that the console works.
 
+// Import helpers for the workers
+/* import-globals-from helper_workers.js */
+Services.scriptloader.loadSubScript(
+  "chrome://mochitests/content/browser/devtools/client/shared/test/helper_workers.js",
+  this);
+
 var TAB_URL = EXAMPLE_URL + "doc_WorkerTargetActor.attachThread-tab.html";
 var WORKER_URL = "code_WorkerTargetActor.attachThread-worker.js";
 
 // Test to see if creating the pause from the console works.
 add_task(async function testPausedByConsole() {
-  let {client, tab, tabClient, workerClient, toolbox, gDebugger} =
-    await initWorkerDebugger(TAB_URL, WORKER_URL);
+  const dbg = await initWorkerDebugger(TAB_URL, WORKER_URL);
+  const {client, tab, workerClient, toolbox} = dbg;
 
-  let gTarget = gDebugger.gTarget;
-  let gResumeButton = gDebugger.document.getElementById("resume");
-  let gResumeKey = gDebugger.document.getElementById("resumeKey");
-
-  let jsterm = await getSplitConsole(toolbox);
+  const jsterm = await getSplitConsole(toolbox);
   let executed = await jsterm.execute("10000+1");
   ok(executed.textContent.includes("10001"),
       "Text for message appeared correct");
 
-  let oncePaused = gTarget.once("thread-paused");
-  EventUtils.sendMouseEvent({ type: "mousedown" }, gResumeButton, gDebugger);
-  let pausedExecution = jsterm.execute("10000+2");
+  await clickElement(dbg, "pause");
+
+  const pausedExecution = jsterm.execute("10000+2");
 
   info("Executed a command with 'break on next' active, waiting for pause");
-  await oncePaused;
+  await waitForPaused(dbg);
 
   executed = await jsterm.execute("10000+3");
   ok(executed.textContent.includes("10003"),
       "Text for message appeared correct");
 
   info("Waiting for a resume");
-  let onceResumed = gTarget.once("thread-resumed");
-  EventUtils.sendMouseEvent({ type: "mousedown" }, gResumeButton, gDebugger);
-  await onceResumed;
+  await clickElement(dbg, "resume");
 
   executed = await pausedExecution;
   ok(executed.textContent.includes("10002"),
       "Text for message appeared correct");
 
   terminateWorkerInTab(tab, WORKER_URL);
   await waitForWorkerClose(workerClient);
   await gDevTools.closeToolbox(TargetFactory.forWorker(workerClient));
rename from devtools/client/debugger/test/mochitest/browser_dbg_worker-console-04.js
rename to devtools/client/shared/test/browser_dbg_worker-console-04.js
--- a/devtools/client/debugger/test/mochitest/browser_dbg_worker-console-04.js
+++ b/devtools/client/shared/test/browser_dbg_worker-console-04.js
@@ -1,26 +1,42 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Check to make sure that a worker can be attached to a toolbox
+// and that the console works.
+
+// Import helpers for the workers
+/* import-globals-from helper_workers.js */
+Services.scriptloader.loadSubScript(
+  "chrome://mochitests/content/browser/devtools/client/shared/test/helper_workers.js",
+  this);
+
 // Check that the date and regexp previewers work in the console of a worker debugger.
 
 "use strict";
 
 // There are shutdown issues for which multiple rejections are left uncaught.
 // See bug 1018184 for resolving these issues.
 const { PromiseTestUtils } = scopedCuImport("resource://testing-common/PromiseTestUtils.jsm");
 PromiseTestUtils.whitelistRejectionsGlobally(/connection just closed/);
 
 const TAB_URL = EXAMPLE_URL + "doc_WorkerTargetActor.attachThread-tab.html";
 const WORKER_URL = "code_WorkerTargetActor.attachThread-worker.js";
 
 add_task(async function testPausedByConsole() {
-  let {client, tab, workerClient, toolbox} =
+  const {client, tab, workerClient, toolbox} =
     await initWorkerDebugger(TAB_URL, WORKER_URL);
 
   info("Check Date objects can be used in the console");
-  let jsterm = await getSplitConsole(toolbox);
+  const jsterm = await getSplitConsole(toolbox);
   let executed = await jsterm.execute("new Date(0)");
   ok(executed.textContent.includes("1970-01-01T00:00:00.000Z"),
       "Text for message appeared correct");
 
   info("Check RegExp objects can be used in the console");
   executed = await jsterm.execute("new RegExp('.*')");
   ok(executed.textContent.includes("/.*/"),
       "Text for message appeared correct");
copy from devtools/client/debugger/test/mochitest/code_WorkerTargetActor.attach-worker1.js
copy to devtools/client/shared/test/code_WorkerTargetActor.attach-worker1.js
--- a/devtools/client/debugger/test/mochitest/code_WorkerTargetActor.attach-worker1.js
+++ b/devtools/client/shared/test/code_WorkerTargetActor.attach-worker1.js
@@ -1,5 +1,5 @@
 "use strict";
 
-self.onmessage = function () {};
+self.onmessage = function() {};
 
 postMessage("load");
copy from devtools/client/debugger/test/mochitest/code_WorkerTargetActor.attach-worker2.js
copy to devtools/client/shared/test/code_WorkerTargetActor.attach-worker2.js
--- a/devtools/client/debugger/test/mochitest/code_WorkerTargetActor.attach-worker2.js
+++ b/devtools/client/shared/test/code_WorkerTargetActor.attach-worker2.js
@@ -1,5 +1,5 @@
 "use strict";
 
-self.onmessage = function () {};
+self.onmessage = function() {};
 
 postMessage("load");
copy from devtools/client/debugger/test/mochitest/code_WorkerTargetActor.attachThread-worker.js
copy to devtools/client/shared/test/code_WorkerTargetActor.attachThread-worker.js
--- a/devtools/client/debugger/test/mochitest/code_WorkerTargetActor.attachThread-worker.js
+++ b/devtools/client/shared/test/code_WorkerTargetActor.attachThread-worker.js
@@ -1,16 +1,18 @@
 "use strict";
 
 function f() {
-  var a = 1;
-  var b = 2;
-  var c = 3;
+  const a = 1;
+  const b = 2;
+  const c = 3;
+
+  return [a, b, c];
 }
 
-self.onmessage = function (event) {
+self.onmessage = function(event) {
   if (event.data == "ping") {
     f();
     postMessage("pong");
   }
 };
 
 postMessage("load");
copy from devtools/client/debugger/test/mochitest/code_frame-script.js
copy to devtools/client/shared/test/code_frame-script.js
--- a/devtools/client/debugger/test/mochitest/code_frame-script.js
+++ b/devtools/client/shared/test/code_frame-script.js
@@ -1,102 +1,105 @@
+/* eslint-disable */
 "use strict";
 
 const { loadSubScript } = Cc["@mozilla.org/moz/jssubscript-loader;1"].
                           getService(Ci.mozIJSSubScriptLoader);
 
 // Set up a dummy environment so that EventUtils works. We need to be careful to
 // pass a window object into each EventUtils method we call rather than having
 // it rely on the |window| global.
-let EventUtils = {};
+const EventUtils = {};
 EventUtils.window = content;
 EventUtils.parent = EventUtils.window;
 EventUtils._EU_Ci = Ci;
 EventUtils._EU_Cc = Cc;
 EventUtils.navigator = content.navigator;
 EventUtils.KeyboardEvent = content.KeyboardEvent;
 loadSubScript("chrome://mochikit/content/tests/SimpleTest/EventUtils.js", EventUtils);
 
 dump("Frame script loaded.\n");
 
 var workers = {};
 
-this.call = function (name, args) {
+this.call = function(name, args) {
   dump("Calling function with name " + name + ".\n");
 
   dump("args " + JSON.stringify(args) + "\n");
-  return XPCNativeWrapper.unwrap(content)[name].apply(undefined, Cu.cloneInto(args, content));
+  return XPCNativeWrapper
+    .unwrap(content)[name]
+    .apply(undefined, Cu.cloneInto(args, content));
 };
 
-this._eval = function (string) {
+this._eval = function(string) {
   dump("Evalling string.\n");
 
   return content.eval(string);
 };
 
-this.generateMouseClick = function (path) {
+this.generateMouseClick = function(path) {
   dump("Generating mouse click.\n");
 
-  let target = eval(path);
+  const target = eval(path);
   EventUtils.synthesizeMouseAtCenter(target, {},
                                      target.ownerDocument.defaultView);
 };
 
-this.createWorker = function (url) {
+this.createWorker = function(url) {
   dump("Creating worker with url '" + url + "'.\n");
 
-  return new Promise(function (resolve, reject) {
-    let worker = new content.Worker(url);
-    worker.addEventListener("message", function () {
+  return new Promise(function(resolve, reject) {
+    const worker = new content.Worker(url);
+    worker.addEventListener("message", function() {
       workers[url] = worker;
       resolve();
     }, {once: true});
   });
 };
 
-this.terminateWorker = function (url) {
+this.terminateWorker = function(url) {
   dump("Terminating worker with url '" + url + "'.\n");
 
   workers[url].terminate();
   delete workers[url];
 };
 
-this.postMessageToWorker = function (url, message) {
+this.postMessageToWorker = function(url, message) {
   dump("Posting message to worker with url '" + url + "'.\n");
 
-  return new Promise(function (resolve) {
-    let worker = workers[url];
+  return new Promise(function(resolve) {
+    const worker = workers[url];
     worker.postMessage(message);
-    worker.addEventListener("message", function () {
+    worker.addEventListener("message", function() {
       resolve();
     }, {once: true});
   });
 };
 
-addMessageListener("jsonrpc", function ({ data: { method, params, id } }) {
+addMessageListener("jsonrpc", function({ data: { method, params, id } }) {
   method = this[method];
-  Promise.resolve().then(function () {
+  Promise.resolve().then(function() {
     return method.apply(undefined, params);
-  }).then(function (result) {
+  }).then(function(result) {
     sendAsyncMessage("jsonrpc", {
       result: result,
       error: null,
       id: id
     });
-  }, function (error) {
+  }, function(error) {
     sendAsyncMessage("jsonrpc", {
       result: null,
       error: error.message.toString(),
       id: id
     });
   });
 });
 
-addMessageListener("test:postMessageToWorker", function (message) {
+addMessageListener("test:postMessageToWorker", function(message) {
   dump("Posting message '" + message.data.message + "' to worker with url '" +
        message.data.url + "'.\n");
 
   let worker = workers[message.data.url];
   worker.postMessage(message.data.message);
-  worker.addEventListener("message", function () {
+  worker.addEventListener("message", function() {
     sendAsyncMessage("test:postMessageToWorker");
   }, {once: true});
 });
copy from devtools/client/debugger/test/mochitest/doc_WorkerTargetActor.attach-tab1.html
copy to devtools/client/shared/test/doc_WorkerTargetActor.attach-tab1.html
copy from devtools/client/debugger/test/mochitest/doc_WorkerTargetActor.attach-tab2.html
copy to devtools/client/shared/test/doc_WorkerTargetActor.attach-tab2.html
copy from devtools/client/debugger/test/mochitest/doc_WorkerTargetActor.attachThread-tab.html
copy to devtools/client/shared/test/doc_WorkerTargetActor.attachThread-tab.html
copy from devtools/client/debugger/test/mochitest/doc_empty-tab-01.html
copy to devtools/client/shared/test/doc_empty-tab-01.html
copy from devtools/client/debugger/test/mochitest/doc_empty-tab-02.html
copy to devtools/client/shared/test/doc_empty-tab-02.html
copy from devtools/client/debugger/test/mochitest/doc_script-switching-01.html
copy to devtools/client/shared/test/doc_script-switching-01.html
copy from devtools/client/debugger/test/mochitest/doc_script-switching-02.html
copy to devtools/client/shared/test/doc_script-switching-02.html
--- a/devtools/client/shared/test/head.js
+++ b/devtools/client/shared/test/head.js
@@ -11,16 +11,18 @@
 Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/devtools/client/shared/test/shared-head.js", this);
 
 const {DOMHelpers} = ChromeUtils.import("resource://devtools/client/shared/DOMHelpers.jsm", {});
 const {Hosts} = require("devtools/client/framework/toolbox-hosts");
 
 const TEST_URI_ROOT = "http://example.com/browser/devtools/client/shared/test/";
 const OPTIONS_VIEW_URL = TEST_URI_ROOT + "doc_options-view.xul";
 
+const EXAMPLE_URL = "chrome://mochitests/content/browser/devtools/client/shared/test/";
+
 function catchFail(func) {
   return function() {
     try {
       return func.apply(null, arguments);
     } catch (ex) {
       ok(false, ex);
       console.error(ex);
       finish();
new file mode 100644
--- /dev/null
+++ b/devtools/client/shared/test/helper_workers.js
@@ -0,0 +1,227 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+/* eslint no-unused-vars: [2, {"vars": "local", "args": "none"}] */
+
+"use strict";
+
+/* import-globals-from ../../debugger/new/test/mochitest/helpers.js */
+/* import-globals-from ../../debugger/new/test/mochitest/helpers/context.js */
+Services.scriptloader.loadSubScript(
+  "chrome://mochitests/content/browser/devtools/client/debugger/new/test/mochitest/helpers.js",
+  this);
+
+var { DebuggerServer } = require("devtools/server/main");
+var { DebuggerClient } = require("devtools/shared/client/debugger-client");
+
+var { Toolbox } = require("devtools/client/framework/toolbox");
+
+const FRAME_SCRIPT_URL = getRootDirectory(gTestPath) + "code_frame-script.js";
+
+var nextId = 0;
+
+function getDeferredPromise() {
+  // Override promise with deprecated-sync-thenables
+  const promise = require("devtools/shared/deprecated-sync-thenables");
+  return promise;
+}
+
+function jsonrpc(tab, method, params) {
+  return new Promise(function(resolve, reject) {
+    const currentId = nextId++;
+    const messageManager = tab.linkedBrowser.messageManager;
+    messageManager.sendAsyncMessage("jsonrpc", {
+      method: method,
+      params: params,
+      id: currentId
+    });
+    messageManager.addMessageListener("jsonrpc", function listener(res) {
+      const { data: { result, error, id } } = res;
+      if (id !== currentId) {
+        return;
+      }
+
+      messageManager.removeMessageListener("jsonrpc", listener);
+      if (error != null) {
+        reject(error);
+      }
+
+      resolve(result);
+    });
+  });
+}
+
+function createWorkerInTab(tab, url) {
+  info("Creating worker with url '" + url + "' in tab.");
+
+  return jsonrpc(tab, "createWorker", [url]);
+}
+
+function terminateWorkerInTab(tab, url) {
+  info("Terminating worker with url '" + url + "' in tab.");
+
+  return jsonrpc(tab, "terminateWorker", [url]);
+}
+
+function postMessageToWorkerInTab(tab, url, message) {
+  info("Posting message to worker with url '" + url + "' in tab.");
+
+  return jsonrpc(tab, "postMessageToWorker", [url, message]);
+}
+
+function connect(client) {
+  info("Connecting client.");
+  return client.connect();
+}
+
+function close(client) {
+  info("Waiting for client to close.\n");
+  return client.close();
+}
+
+function listTabs(client) {
+  info("Listing tabs.");
+  return client.listTabs();
+}
+
+function findTab(tabs, url) {
+  info("Finding tab with url '" + url + "'.");
+  for (const tab of tabs) {
+    if (tab.url === url) {
+      return tab;
+    }
+  }
+  return null;
+}
+
+function attachTarget(client, tab) {
+  info("Attaching to tab with url '" + tab.url + "'.");
+  return client.attachTarget(tab.actor);
+}
+
+function listWorkers(tabClient) {
+  info("Listing workers.");
+  return tabClient.listWorkers();
+}
+
+function findWorker(workers, url) {
+  info("Finding worker with url '" + url + "'.");
+  for (const worker of workers) {
+    if (worker.url === url) {
+      return worker;
+    }
+  }
+  return null;
+}
+
+function attachWorker(tabClient, worker) {
+  info("Attaching to worker with url '" + worker.url + "'.");
+  return tabClient.attachWorker(worker.actor);
+}
+
+function attachThread(workerClient, options) {
+  info("Attaching to thread.");
+  return workerClient.attachThread(options);
+}
+
+function waitForWorkerClose(workerClient) {
+  info("Waiting for worker to close.");
+  return new Promise(function(resolve) {
+    workerClient.addOneTimeListener("close", function() {
+      info("Worker did close.");
+      resolve();
+    });
+  });
+}
+
+// Return a promise with a reference to jsterm, opening the split
+// console if necessary.  This cleans up the split console pref so
+// it won't pollute other tests.
+function getSplitConsole(toolbox, win) {
+  if (!win) {
+    win = toolbox.win;
+  }
+
+  if (!toolbox.splitConsole) {
+    EventUtils.synthesizeKey("VK_ESCAPE", {}, win);
+  }
+
+  return new Promise(resolve => {
+    toolbox.getPanelWhenReady("webconsole").then(() => {
+      ok(toolbox.splitConsole, "Split console is shown.");
+      const jsterm = toolbox.getPanel("webconsole").hud.jsterm;
+      resolve(jsterm);
+    });
+  });
+}
+
+async function initWorkerDebugger(TAB_URL, WORKER_URL) {
+  DebuggerServer.init();
+  DebuggerServer.registerAllActors();
+
+  const client = new DebuggerClient(DebuggerServer.connectPipe());
+  await connect(client);
+
+  const tab = await addTab(TAB_URL);
+  const { tabs } = await listTabs(client);
+  const [, tabClient] = await attachTarget(client, findTab(tabs, TAB_URL));
+
+  await createWorkerInTab(tab, WORKER_URL);
+
+  const { workers } = await listWorkers(tabClient);
+  const [, workerClient] = await attachWorker(tabClient,
+                                             findWorker(workers, WORKER_URL));
+
+  const toolbox = await gDevTools.showToolbox(TargetFactory.forWorker(workerClient),
+                                            "jsdebugger",
+                                            Toolbox.HostType.WINDOW);
+
+  const debuggerPanel = toolbox.getCurrentPanel();
+
+  const gDebugger = debuggerPanel.panelWin;
+
+  const context = createDebuggerContext(toolbox);
+
+  return { ...context, client, tab, tabClient, workerClient, toolbox, gDebugger};
+}
+
+// Override addTab/removeTab as defined by shared-head, since these have
+// an extra window parameter and add a frame script
+this.addTab = function addTab(url, win) {
+  info("Adding tab: " + url);
+
+  const deferred = getDeferredPromise().defer();
+  const targetWindow = win || window;
+  const targetBrowser = targetWindow.gBrowser;
+
+  targetWindow.focus();
+  const tab = targetBrowser.selectedTab = BrowserTestUtils.addTab(targetBrowser, url);
+  const linkedBrowser = tab.linkedBrowser;
+
+  info("Loading frame script with url " + FRAME_SCRIPT_URL + ".");
+  linkedBrowser.messageManager.loadFrameScript(FRAME_SCRIPT_URL, false);
+
+  BrowserTestUtils.browserLoaded(linkedBrowser)
+    .then(function() {
+      info("Tab added and finished loading: " + url);
+      deferred.resolve(tab);
+    });
+
+  return deferred.promise;
+};
+
+this.removeTab = function removeTab(tab, win) {
+  info("Removing tab.");
+
+  const deferred = getDeferredPromise().defer();
+  const targetWindow = win || window;
+  const targetBrowser = targetWindow.gBrowser;
+  const tabContainer = targetBrowser.tabContainer;
+
+  tabContainer.addEventListener("TabClose", function() {
+    info("Tab removed and finished closing.");
+    deferred.resolve();
+  }, {once: true});
+
+  targetBrowser.removeTab(tab);
+  return deferred.promise;
+};
copy from devtools/client/debugger/test/mochitest/testactors.js
copy to devtools/client/shared/test/testactors.js
--- a/devtools/client/debugger/test/mochitest/testactors.js
+++ b/devtools/client/shared/test/testactors.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-function TestActor1(aConnection, aTab) {
-  this.conn = aConnection;
-  this.tab = aTab;
+function TestActor1(connection, tab) {
+  this.conn = connection;
+  this.tab = tab;
 }
 
 TestActor1.prototype = {
   actorPrefix: "testOne",
 
   grip: function TA1_grip() {
     return { actor: this.actorID,
              test: "TestActor1" };
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -39,16 +39,17 @@
 #include "mozilla/MozPromise.h"
 #include "mozilla/NullPrincipal.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/Types.h"
 #include "mozilla/PeerIdentity.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/FeaturePolicyUtils.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/MediaStreamBinding.h"
 #include "mozilla/dom/MediaStreamTrackBinding.h"
 #include "mozilla/dom/GetUserMediaRequestBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/MediaDevices.h"
 #include "mozilla/Base64.h"
 #include "mozilla/ipc/BackgroundChild.h"
@@ -2680,16 +2681,21 @@ MediaManager::GetUserMedia(nsPIDOMWindow
   }
 
   nsCOMPtr<nsIPrincipal> principal =
     nsGlobalWindowInner::Cast(aWindow)->GetPrincipal();
   if (NS_WARN_IF(!principal)) {
     return NS_ERROR_FAILURE;
   }
 
+  nsIDocument* doc = aWindow->GetExtantDoc();
+  if (NS_WARN_IF(!doc)) {
+    return NS_ERROR_FAILURE;
+  }
+
   // This principal needs to be sent to different threads and so via IPC.
   // For this reason it's better to convert it to PrincipalInfo right now.
   ipc::PrincipalInfo principalInfo;
   rv = PrincipalToPrincipalInfo(principal, &principalInfo);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
@@ -2870,35 +2876,48 @@ MediaManager::GetUserMedia(nsPIDOMWindow
   if (!privileged) {
     // Check if this site has had persistent permissions denied.
     nsCOMPtr<nsIPermissionManager> permManager =
       do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
     uint32_t audioPerm = nsIPermissionManager::UNKNOWN_ACTION;
     if (IsOn(c.mAudio)) {
-      if (audioType == MediaSourceEnum::Microphone &&
-          Preferences::GetBool("media.getusermedia.microphone.deny", false)) {
-        audioPerm = nsIPermissionManager::DENY_ACTION;
+      if (audioType == MediaSourceEnum::Microphone) {
+        if (Preferences::GetBool("media.getusermedia.microphone.deny", false) ||
+            !dom::FeaturePolicyUtils::IsFeatureAllowed(doc,
+                                                       NS_LITERAL_STRING("microphone"))) {
+          audioPerm = nsIPermissionManager::DENY_ACTION;
+        } else {
+          rv = permManager->TestExactPermissionFromPrincipal(
+            principal, "microphone", &audioPerm);
+          NS_ENSURE_SUCCESS(rv, rv);
+        }
       } else {
         rv = permManager->TestExactPermissionFromPrincipal(
-          principal, "microphone", &audioPerm);
+          principal, "screen", &audioPerm);
         NS_ENSURE_SUCCESS(rv, rv);
       }
     }
 
     uint32_t videoPerm = nsIPermissionManager::UNKNOWN_ACTION;
     if (IsOn(c.mVideo)) {
-      if (videoType == MediaSourceEnum::Camera &&
-          Preferences::GetBool("media.getusermedia.camera.deny", false)) {
-        videoPerm = nsIPermissionManager::DENY_ACTION;
+      if (videoType == MediaSourceEnum::Camera) {
+        if (Preferences::GetBool("media.getusermedia.camera.deny", false) ||
+            !dom::FeaturePolicyUtils::IsFeatureAllowed(doc,
+                                                       NS_LITERAL_STRING("camera"))) {
+          videoPerm = nsIPermissionManager::DENY_ACTION;
+        } else {
+          rv = permManager->TestExactPermissionFromPrincipal(
+            principal, "camera", &videoPerm);
+          NS_ENSURE_SUCCESS(rv, rv);
+        }
       } else {
         rv = permManager->TestExactPermissionFromPrincipal(
-          principal, videoType == MediaSourceEnum::Camera ? "camera" : "screen",
-          &videoPerm);
+          principal, "screen", &videoPerm);
         NS_ENSURE_SUCCESS(rv, rv);
       }
     }
 
     if ((!IsOn(c.mAudio) && !IsOn(c.mVideo)) ||
         (IsOn(c.mAudio) && audioPerm == nsIPermissionManager::DENY_ACTION) ||
         (IsOn(c.mVideo) && videoPerm == nsIPermissionManager::DENY_ACTION)) {
       RefPtr<MediaStreamError> error =
@@ -4123,35 +4142,55 @@ MediaManager::IsActivelyCapturingOrHasAP
   }
 
   // Or are persistent permissions (audio or video) granted?
 
   auto* window = nsGlobalWindowInner::GetInnerWindowWithId(aWindowId);
   if (NS_WARN_IF(!window) || NS_WARN_IF(!window->GetPrincipal())) {
     return false;
   }
+
+  nsIDocument* doc = window->GetExtantDoc();
+  if (NS_WARN_IF(!doc)) {
+    return false;
+  }
+
+  nsIPrincipal* principal = window->GetPrincipal();
+  if (NS_WARN_IF(!principal)) {
+    return false;
+  }
+
   // Check if this site has persistent permissions.
   nsresult rv;
   nsCOMPtr<nsIPermissionManager> mgr =
     do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return false; // no permission manager no permissions!
   }
 
   uint32_t audio = nsIPermissionManager::UNKNOWN_ACTION;
   uint32_t video = nsIPermissionManager::UNKNOWN_ACTION;
   {
-    auto* principal = window->GetPrincipal();
-    rv = mgr->TestExactPermissionFromPrincipal(principal, "microphone", &audio);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return false;
+    if (!dom::FeaturePolicyUtils::IsFeatureAllowed(doc, NS_LITERAL_STRING("microphone"))) {
+      audio = nsIPermissionManager::DENY_ACTION;
+    } else {
+      rv = mgr->TestExactPermissionFromPrincipal(principal, "microphone", &audio);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return false;
+      }
     }
-    rv = mgr->TestExactPermissionFromPrincipal(principal, "camera", &video);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return false;
+
+    if (!dom::FeaturePolicyUtils::IsFeatureAllowed(doc,
+                                                   NS_LITERAL_STRING("camera"))) {
+      video = nsIPermissionManager::DENY_ACTION;
+    } else {
+      rv = mgr->TestExactPermissionFromPrincipal(principal, "camera", &video);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return false;
+      }
     }
   }
   return audio == nsIPermissionManager::ALLOW_ACTION ||
          video == nsIPermissionManager::ALLOW_ACTION;
 }
 
 SourceListener::SourceListener()
   : mStopped(false)
--- a/dom/security/featurepolicy/FeaturePolicyUtils.cpp
+++ b/dom/security/featurepolicy/FeaturePolicyUtils.cpp
@@ -21,22 +21,20 @@ struct FeatureMap {
 };
 
 /*
  * IMPORTANT: Do not change this list without review from a DOM peer _AND_ a
  * DOM Security peer!
  */
 static FeatureMap sSupportedFeatures[] = {
   { "autoplay", FeatureMap::eAll },
-  // TODO: not supported yet!!!
   { "camera", FeatureMap::eAll  },
   { "encrypted-media", FeatureMap::eAll  },
   { "fullscreen", FeatureMap::eAll  },
   { "geolocation", FeatureMap::eAll  },
-  // TODO: not supported yet!!!
   { "microphone", FeatureMap::eAll  },
   { "midi", FeatureMap::eAll  },
   { "payment", FeatureMap::eAll  },
   // TODO: not supported yet!!!
   { "speaker", FeatureMap::eAll  },
   { "vr", FeatureMap::eAll  },
 };
 
--- a/gfx/vr/VRManager.cpp
+++ b/gfx/vr/VRManager.cpp
@@ -25,17 +25,17 @@
 #include "gfxVROculus.h"
 #endif
 #if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
 #include "gfxVROpenVR.h"
 #endif
 
 #include "gfxVRPuppet.h"
 #include "ipc/VRLayerParent.h"
-#if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
+#if !defined(MOZ_WIDGET_ANDROID)
 #include "service/VRService.h"
 #endif
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
 using namespace mozilla::gl;
 
@@ -94,17 +94,17 @@ VRManager::VRManager()
    *
    * OpenvR comes second, as it is the native interface for HTC Vive
    * which is the most common HMD at this time.
    *
    * OSVR will be used if Oculus SDK and OpenVR don't detect any HMDS,
    * to support everyone else.
    */
 
-#if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
+#if !defined(MOZ_WIDGET_ANDROID)
   // The VR Service accesses all hardware from a separate process
   // and replaces the other VRSystemManager when enabled.
   if (!gfxPrefs::VRProcessEnabled()) {
     mVRService = VRService::Create();
   } else if (gfxPrefs::VRProcessEnabled() && XRE_IsGPUProcess()) {
     gfx::GPUParent* gpu = GPUParent::GetSingleton();
     MOZ_ASSERT(gpu);
     Unused << gpu->SendCreateVRProcess();
@@ -162,34 +162,34 @@ void
 VRManager::Destroy()
 {
   StopTasks();
   mVRDisplays.Clear();
   mVRControllers.Clear();
   for (uint32_t i = 0; i < mManagers.Length(); ++i) {
     mManagers[i]->Destroy();
   }
-#if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
+#if !defined(MOZ_WIDGET_ANDROID)
   if (mVRService) {
     mVRService->Stop();
     mVRService = nullptr;
   }
 #endif
   mInitialized = false;
 }
 
 void
 VRManager::Shutdown()
 {
   mVRDisplays.Clear();
   mVRControllers.Clear();
   for (uint32_t i = 0; i < mManagers.Length(); ++i) {
     mManagers[i]->Shutdown();
   }
-#if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
+#if !defined(MOZ_WIDGET_ANDROID)
   if (mVRService) {
     mVRService->Stop();
   }
   if (gfxPrefs::VRProcessEnabled() &&
       VRGPUChild::IsCreated()) {
     RefPtr<Runnable> task = NS_NewRunnableFunction(
       "VRGPUChild::SendStopVRService",
       [] () -> void {
@@ -541,17 +541,17 @@ void
 VRManager::RefreshVRDisplays(bool aMustDispatch)
 {
   /**
   * If we aren't viewing WebVR content, don't enumerate
   * new hardware, as it will cause some devices to power on
   * or interrupt other VR activities.
   */
   if (mVRDisplaysRequested || aMustDispatch) {
-#if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
+#if !defined(MOZ_WIDGET_ANDROID)
     // Tell VR process to start VR service.
     if (gfxPrefs::VRProcessEnabled() && !mVRServiceStarted) {
       RefPtr<Runnable> task = NS_NewRunnableFunction(
         "VRGPUChild::SendStartVRService",
         [] () -> void {
           VRGPUChild* vrGPUChild = VRGPUChild::Get();
           vrGPUChild->SendStartVRService();
       });
--- a/gfx/vr/VRManager.h
+++ b/gfx/vr/VRManager.h
@@ -19,17 +19,17 @@ namespace mozilla {
 namespace layers {
 class TextureHost;
 }
 namespace gfx {
 
 class VRLayerParent;
 class VRManagerParent;
 class VRDisplayHost;
-#if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
+#if !defined(MOZ_WIDGET_ANDROID)
 class VRService;
 #endif
 class VRSystemManagerPuppet;
 class VRSystemManagerExternal;
 
 class VRManager
 {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::gfx::VRManager)
@@ -103,17 +103,17 @@ private:
 
   TimeStamp mLastControllerEnumerationTime;
   TimeStamp mLastDisplayEnumerationTime;
   TimeStamp mLastActiveTime;
   TimeStamp mLastTickTime;
   double mAccumulator100ms;
   RefPtr<VRSystemManagerPuppet> mPuppetManager;
   RefPtr<VRSystemManagerExternal> mExternalManager;
-#if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
+#if !defined(MOZ_WIDGET_ANDROID)
   RefPtr<VRService> mVRService;
 #endif
   bool mVRDisplaysRequested;
   bool mVRControllersRequested;
   bool mVRServiceStarted;
   uint32_t mTaskInterval;
   RefPtr<nsITimer> mTaskTimer;
 };
--- a/gfx/vr/ipc/VRGPUParent.cpp
+++ b/gfx/vr/ipc/VRGPUParent.cpp
@@ -21,17 +21,17 @@ VRGPUParent::VRGPUParent(ProcessId aChil
   MOZ_ASSERT(NS_IsMainThread());
 
   SetOtherProcessId(aChildProcessId);
 }
 
 void
 VRGPUParent::ActorDestroy(ActorDestroyReason aWhy)
 {
-#if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
+#if !defined(MOZ_WIDGET_ANDROID)
   if (mVRService) {
     mVRService->Stop();
     mVRService = nullptr;
   }
 #endif
 
   MessageLoop::current()->PostTask(
   NewRunnableMethod("gfx::VRGPUParent::DeferredDestroy",
@@ -67,30 +67,30 @@ VRGPUParent::Bind(Endpoint<PVRGPUParent>
   }
 
   mSelfRef = this;
 }
 
 mozilla::ipc::IPCResult
 VRGPUParent::RecvStartVRService()
 {
-#if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
+#if !defined(MOZ_WIDGET_ANDROID)
   mVRService = VRService::Create();
   MOZ_ASSERT(mVRService);
 
   mVRService->Start();
 #endif
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 VRGPUParent::RecvStopVRService()
 {
-#if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
+#if !defined(MOZ_WIDGET_ANDROID)
   if (mVRService) {
     mVRService->Stop();
     mVRService = nullptr;
   }
 #endif
 
   return IPC_OK();
 }
--- a/gfx/vr/ipc/VRGPUParent.h
+++ b/gfx/vr/ipc/VRGPUParent.h
@@ -27,17 +27,17 @@ protected:
   void Bind(Endpoint<PVRGPUParent>&& aEndpoint);
   virtual mozilla::ipc::IPCResult RecvStartVRService() override;
   virtual mozilla::ipc::IPCResult RecvStopVRService() override;
 
 private:
   void DeferredDestroy();
 
   RefPtr<VRGPUParent> mSelfRef;
-#if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
+#if !defined(MOZ_WIDGET_ANDROID)
   RefPtr<VRService> mVRService;
 #endif
 };
 
 } // namespace gfx
 } // namespace mozilla
 
-#endif // GFX_VR_CONTENT_PARENT_H
\ No newline at end of file
+#endif // GFX_VR_CONTENT_PARENT_H
--- a/gfx/vr/moz.build
+++ b/gfx/vr/moz.build
@@ -60,29 +60,32 @@ SOURCES += [
     'VRDisplayHost.cpp',
     'VRDisplayLocal.cpp',
 ]
 
 # Build OpenVR on Windows, Linux, and macOS desktop targets
 if CONFIG['OS_TARGET'] in ('WINNT', 'Linux', 'Darwin'):
     DIRS += [
         'openvr',
-        'service',
     ]
     SOURCES += [
         'gfxVROpenVR.cpp',
     ]
 
 if CONFIG['OS_TARGET'] == 'WINNT':
     SOURCES += [
         'gfxVROculus.cpp',
     ]
 
 if CONFIG['OS_TARGET'] == 'Android':
     LOCAL_INCLUDES += ['/widget/android']
+else:
+    DIRS += [
+        'service',
+    ]
 
 IPDL_SOURCES = [
     'ipc/PVR.ipdl',
     'ipc/PVRGPU.ipdl',
     'ipc/PVRLayer.ipdl',
     'ipc/PVRManager.ipdl',
 ]
 
--- a/gfx/vr/service/VRService.cpp
+++ b/gfx/vr/service/VRService.cpp
@@ -6,16 +6,18 @@
 
 #include "VRService.h"
 #include "gfxPrefs.h"
 #include "base/thread.h"                // for Thread
 #include <cstring>                      // for memcmp
 
 #if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
 #include "OpenVRSession.h"
+#endif
+#if !defined(MOZ_WIDGET_ANDROID)
 #include "OSVRSession.h"
 #endif
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace std;
 
 namespace {
@@ -215,16 +217,18 @@ VRService::ServiceInitialize()
 #if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
   // Try OpenVR
   if (!session) {
     session = MakeUnique<OpenVRSession>();
     if (!session->Initialize(mSystemState)) {
       session = nullptr;
     }
   }
+#endif
+#if !defined(MOZ_WIDGET_ANDROID)
   // Try OSVR
   if (!session) {
     session = MakeUnique<OSVRSession>();
     if (!session->Initialize(mSystemState)) {
       session = nullptr;
     }
   }
 #endif
--- a/gfx/vr/service/VRService.h
+++ b/gfx/vr/service/VRService.h
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef GFX_VR_SERVICE_VRSERVICE_H
 #define GFX_VR_SERVICE_VRSERVICE_H
 
 #include "mozilla/Atomics.h"
+#include "base/process.h"               // for ProcessHandle
 
 #include "moz_external_vr.h"
 
 namespace base {
 class Thread;
 } // namespace base
 namespace mozilla {
 namespace gfx {
--- a/gfx/vr/service/moz.build
+++ b/gfx/vr/service/moz.build
@@ -3,25 +3,25 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # Build OSVR on all platforms except Android
 if CONFIG['OS_TARGET'] != 'Android':
     UNIFIED_SOURCES += [
         'OSVRSession.cpp',
+        'VRService.cpp',
+        'VRSession.cpp',
     ]
+    include('/ipc/chromium/chromium-config.mozbuild')
 
 # Build OpenVR on Windows, Linux, and macOS desktop targets
 if CONFIG['OS_TARGET'] in ('WINNT', 'Linux', 'Darwin'):
     UNIFIED_SOURCES += [
         'OpenVRSession.cpp',
-        'VRService.cpp',
-        'VRSession.cpp',
     ]
     LOCAL_INCLUDES += [
         '/dom/base',
         '/gfx/layers/d3d11',
         '/gfx/thebes',
     ]
-    include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
--- a/gfx/webrender/res/brush.glsl
+++ b/gfx/webrender/res/brush.glsl
@@ -61,17 +61,17 @@ void main(void) {
 
         // TODO(gw): transform bounds may be referenced by
         //           the fragment shader when running in
         //           the alpha pass, even on non-transformed
         //           items. For now, just ensure it has no
         //           effect. We can tidy this up as we move
         //           more items to be brush shaders.
 #ifdef WR_FEATURE_ALPHA_PASS
-        init_transform_vs(vec4(vec2(-1000000.0), vec2(1000000.0)));
+        init_transform_vs(vec4(vec2(-1.0e16), vec2(1.0e16)));
 #endif
     } else {
         bvec4 edge_mask = notEqual(edge_flags & ivec4(1, 2, 4, 8), ivec4(0));
 
         vi = write_transform_vertex(
             local_segment_rect,
             ph.local_rect,
             ph.local_clip_rect,
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-69fddc3faf1a379a560106f12687d08cbbe304dd
+1396114d80fb19df2295a40b0b14abc8f24afa03
--- a/js/src/jit-test/tests/gc/bug-1155455.js
+++ b/js/src/jit-test/tests/gc/bug-1155455.js
@@ -1,9 +1,9 @@
-// |jit-test| error: TypeError; skip-if !('gczeal' in this)
+// |jit-test| error: TypeError; skip-if: !('gczeal' in this)
 var g = newGlobal();
 gczeal(10, 2)
 var dbg = Debugger(g);
 dbg.onDebuggerStatement = function (frame1) {
     function hit(frame2) {
       return hit[0] = "mutated";
     }
     var s = frame1.script;
--- a/layout/reftests/box-shadow/reftest.list
+++ b/layout/reftests/box-shadow/reftest.list
@@ -28,17 +28,17 @@ fuzzy(0-2,0-440) fails-if(webrender&&gtk
 == boxshadow-color-rounding-middle.html boxshadow-color-rounding-middle-ref.html
 fuzzy(0-3,0-500) fuzzy-if(d2d,0-2,0-1080) == boxshadow-border-radius-int.html boxshadow-border-radius-int-ref.html
 == boxshadow-inset-neg-spread.html about:blank
 == boxshadow-inset-neg-spread2.html boxshadow-inset-neg-spread2-ref.html
 fuzzy(0-26,0-3610) fuzzy-if(d2d,0-26,0-5910) == boxshadow-rotated.html boxshadow-rotated-ref.html # Bug 1211264
 == boxshadow-inset-large-border-radius.html boxshadow-inset-large-border-radius-ref.html
 
 # fuzzy due to blur going inside, but as long as it's essentially black instead of a light gray its ok.
-fuzzy(0-13,0-9445) fuzzy-if(d2d,0-13,0-10926) fails-if(webrender) == boxshadow-inset-large-offset.html boxshadow-inset-large-offset-ref.html
+fuzzy(0-13,0-9445) fuzzy-if(d2d,0-13,0-10926) fuzzy-if(webrender,14-15,11263-13267) == boxshadow-inset-large-offset.html boxshadow-inset-large-offset-ref.html
 
 == overflow-not-scrollable-1.html overflow-not-scrollable-1-ref.html
 == overflow-not-scrollable-1.html overflow-not-scrollable-1-ref2.html
 == overflow-not-scrollable-2.html overflow-not-scrollable-2-ref.html
 fuzzy-if(webrender,0-1,0-655) == 611574-1.html 611574-1-ref.html
 fuzzy-if(webrender,0-4,0-144) == 611574-2.html 611574-2-ref.html
 fuzzy-if(winWidget,0-5,0-30) fuzzy-if(skiaContent,0-16,0-10) fuzzy-if(webrender,34-34,82-82) == fieldset.html fieldset-ref.html # minor anti-aliasing problem on Windows
 fuzzy-if(winWidget,0-5,0-30) fuzzy-if(skiaContent,0-16,0-10) fails-if(webrender) == fieldset-inset.html fieldset-inset-ref.html # minor anti-aliasing problem on Windows
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -300,17 +300,17 @@ fuzzy-if(Android,0-3,0-50) fuzzy-if(skia
 == 280708-1a.html 280708-1-ref.html
 == 280708-1b.html 280708-1-ref.html
 == 281241-1.html 281241-1-ref.html
 == 281241-2.xhtml 281241-1-ref.html
 == 283686-1.html about:blank
 == 283686-2.html 283686-2-ref.html
 == 283686-3.html about:blank
 == 289384-1.xhtml 289384-ref.xhtml
-fails-if(webrender) random-if(d2d) fuzzy-if(Android,0-8,0-1439) HTTP == 289480.html#top 289480-ref.html # basically-verbatim acid2 test, HTTP for a 404 page -- bug 578114 for the d2d failures
+fails-if(webrender&&!winWidget) random-if(d2d) fuzzy-if(Android,0-8,0-1439) HTTP == 289480.html#top 289480-ref.html # basically-verbatim acid2 test, HTTP for a 404 page -- bug 578114 for the d2d failures
 == 290129-1.html 290129-1-ref.html
 == 291078-1.html 291078-1-ref.html
 == 291078-2.html 291078-2-ref.html
 == 291262-1.html 291262-1-ref.html
 == 294306-1.html 294306-1a-ref.html
 != 294306-1.html 294306-1b-ref.html
 == 296361-1.html 296361-ref.html
 == 296904-1.html 296904-1-ref.html
@@ -1159,23 +1159,23 @@ fuzzy-if(skiaContent,0-1,0-3) == 442542-
 == 444928-2.html 444928-2-ref.html
 != 444928-3.html 444928-3-notref.html
 random == 445004-1.html 445004-1-ref.html # bug 472268
 == 445142-1a.html 445142-1-ref.html
 == 445142-1b.html 445142-1-ref.html
 == 445142-1c.html 445142-1-ref.html
 == 445142-2a.html 445142-2-ref.html
 == 445142-2b.html 445142-2-ref.html
-fails-if(usesRepeatResampling) fails-if(webrender) == 446100-1a.html about:blank
-fails-if(Android) fails-if(usesRepeatResampling) fails-if(webrender) == 446100-1b.html about:blank
-fails-if(Android) fails-if(usesRepeatResampling) fails-if(webrender) == 446100-1c.html about:blank
-fails-if(usesRepeatResampling) fails-if(webrender) == 446100-1d.html about:blank
-fails-if(usesRepeatResampling) fails-if(webrender) == 446100-1e.html about:blank
+fails-if(usesRepeatResampling) fails-if(webrender&&!winWidget) == 446100-1a.html about:blank
+fails-if(Android) fails-if(usesRepeatResampling) fails-if(webrender&&!winWidget) == 446100-1b.html about:blank
+fails-if(Android) fails-if(usesRepeatResampling) fails-if(webrender&&!winWidget) == 446100-1c.html about:blank
+fails-if(usesRepeatResampling) fails-if(webrender&&!winWidget) == 446100-1d.html about:blank
+fails-if(usesRepeatResampling) fails-if(webrender&&!winWidget) == 446100-1e.html about:blank
 == 446100-1f.html about:blank
-fails-if(usesRepeatResampling) fails-if(Android) fails-if(webrender) == 446100-1g.html about:blank
+fails-if(usesRepeatResampling) fails-if(Android) fails-if(webrender&&!winWidget) == 446100-1g.html about:blank
 == 446100-1h.html about:blank
 == 447749-1.html 447749-1-ref.html
 fuzzy(0-127,0-2) == 448193.html 448193-ref.html
 != 449149-1a.html about:blank
 != 449149-1b.html about:blank
 # Test again with original XBL bindings
 test-pref(dom.ua_widget.enabled,false) != 449149-1a.html about:blank
 test-pref(dom.ua_widget.enabled,false) != 449149-1b.html about:blank
@@ -1994,18 +1994,18 @@ fuzzy(0-8,0-1900) fails-if(webrender) ==
 # should be same.  |fuzzy()| here allows the difference in border, but not
 # background color.
 fuzzy(0-255,0-1000) skip-if(!cocoaWidget) == 1294102-1.html 1294102-1-ref.html
 random-if(Android) fuzzy-if(skiaContent,0-15,0-50) == 1295466-1.xhtml 1295466-1-ref.xhtml #bug 982547
 fuzzy-if(Android,0-27,0-874) fuzzy-if(!Android,0-14,0-43) == 1313772.xhtml 1313772-ref.xhtml # Bug 1128229, Bug 1389319
 fuzzy(0-2,0-320000) == 1315113-1.html 1315113-1-ref.html
 fuzzy(0-2,0-20000) == 1315113-2.html 1315113-2-ref.html
 == 1315632-1.html 1315632-1-ref.html
-fuzzy(0-2,0-40000) fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu),0-13,0-40000) fails-if(webrender) == 1316719-1a.html 1316719-1-ref.html
-fuzzy(0-13,0-40000) fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu),0-13,0-40000) fails-if(webrender) == 1316719-1b.html 1316719-1-ref.html
+fuzzy(0-2,0-40000) fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu),0-13,0-40000) == 1316719-1a.html 1316719-1-ref.html
+fuzzy(0-13,0-40000) fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu),0-13,0-40000) == 1316719-1b.html 1316719-1-ref.html
 fuzzy(0-13,0-40000) fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu),0-13,0-40000) == 1316719-1c.html 1316719-1-ref.html
 skip-if(Android) != 1318769-1.html 1318769-1-ref.html
 == 1322512-1.html 1322512-1-ref.html
 skip-if(isDebugBuild&&winWidget) == 1330051.svg 1330051-ref.svg
 == 1348481-1.html 1348481-ref.html
 == 1348481-2.html 1348481-ref.html
 == 1352464-1.html 1352464-1-ref.html
 == 1358375-1.html 1358375-ref.html
--- a/layout/reftests/invalidation/reftest.list
+++ b/layout/reftests/invalidation/reftest.list
@@ -39,18 +39,18 @@ pref(layout.animated-image-layers.enable
 == filter-userspace-offset.svg?offsetContainer=foreignObject&mask=boundingBox filter-userspace-offset.svg
 == filter-userspace-offset.svg?offsetContainer=rect&mask=userSpace-at100 filter-userspace-offset.svg
 == filter-userspace-offset.svg?offsetContainer=use&mask=userSpace-atZero filter-userspace-offset.svg
 == filter-userspace-offset.svg?offsetContainer=innerSVG&mask=userSpace-atZero filter-userspace-offset.svg
 == filter-userspace-offset.svg?offsetContainer=foreignObject&mask=userSpace-at100 filter-userspace-offset.svg
 == filter-userspace-offset.svg?offsetContainer=rect&filter=matrix-fillPaint-boundingBox filter-userspace-offset.svg
 == filter-userspace-offset.svg?offsetContainer=rect&filter=matrix-fillPaint-userSpace-at100 filter-userspace-offset.svg
 
-fails-if(webrender) fails-if(!Android) != scroll-inactive-layers.html about:blank   # bug 1494110 for the fails-if(!Android) (Android is a false pass, no doubt)
-fails-if(webrender) fails-if(!Android) != scroll-inactive-layers-2.html about:blank # bug 1494110 for the fails-if(!Android) (Android is a false pass, no doubt)
+fails-if(!Android) != scroll-inactive-layers.html about:blank   # bug 1494110 for the fails-if(!Android) (Android is a false pass, no doubt)
+fails-if(!Android) != scroll-inactive-layers-2.html about:blank # bug 1494110 for the fails-if(!Android) (Android is a false pass, no doubt)
 != inactive-layertree-visible-region-1.html about:blank
 != inactive-layertree-visible-region-2.html about:blank
 != transform-floating-point-invalidation.html about:blank
 != transform-floating-point-invalidation.html?reverse about:blank
 != nudge-to-integer-invalidation.html about:blank
 != nudge-to-integer-invalidation.html?reverse about:blank
 != clipped-animated-transform-1.html about:blank
 != paintedlayer-recycling-1.html about:blank
--- a/layout/reftests/svg/filters/css-filters/reftest.list
+++ b/layout/reftests/svg/filters/css-filters/reftest.list
@@ -20,18 +20,18 @@ fuzzy-if(webrender,5-7,19040-22652) == b
 == containing-block-1.html containing-block-1-ref.html
 == contrast.html contrast-ref.html
 == contrast-extreme.html contrast-extreme-ref.html
 == contrast-one.html contrast-one-ref.html
 == contrast-percent.html contrast-percent-ref.html
 == contrast-reduce.html contrast-reduce-ref.html
 == contrast-zero.html contrast-zero-ref.html
 fuzzy-if(webrender,9-9,2625-3002) == drop-shadow.html drop-shadow-ref.html
-fuzzy-if(webrender,9-9,2625-3002) fails-if(webrender&&winWidget) == drop-shadow-default-color.html drop-shadow-default-color-ref.html
-fuzzy-if(webrender,9-9,2625-3002) fails-if(webrender&&winWidget) == drop-shadow-negative-offset.html drop-shadow-negative-offset-ref.html
+fuzzy-if(webrender,9-9,2625-3002) == drop-shadow-default-color.html drop-shadow-default-color-ref.html
+fuzzy-if(webrender,9-9,2625-3002) == drop-shadow-negative-offset.html drop-shadow-negative-offset-ref.html
 == filter-on-huge-bbox.html pass.svg
 == filter-on-outer-svg.html pass.svg
 fuzzy-if(webrender,0-1,0-10000) fuzzy-if(d2d,0-1,0-10000) == grayscale.html grayscale-ref.html
 fuzzy-if(webrender,0-1,0-10000) fuzzy-if(d2d,0-1,0-10000) == grayscale-one.html grayscale-one-ref.html
 fuzzy-if(webrender,0-1,0-10000) fuzzy-if(d2d,0-1,0-10000) == grayscale-over-one.html grayscale-over-one-ref.html
 fuzzy-if(webrender,0-1,0-10000) fuzzy-if(d2d,0-1,0-10000) == grayscale-percent.html grayscale-percent-ref.html
 fuzzy-if(webrender,0-1,0-10000) == grayscale-zero.html grayscale-zero-ref.html
 == hue-rotate.html hue-rotate-ref.html
--- a/layout/xul/test/browser.ini
+++ b/layout/xul/test/browser.ini
@@ -1,9 +1,9 @@
 [DEFAULT]
 
 [browser_bug685470.js]
 [browser_bug703210.js]
-skip-if = os == 'linux' && debug || (verify && (os == 'linux')) # Bug 1382428
+skip-if = os == 'linux' || (verify && (os == 'linux')) # Bug 1382428
 [browser_bug706743.js]
 skip-if = (os == 'linux') || e10s # Bug 1157576
 [browser_bug1163304.js]
 skip-if = os != 'linux' && os != 'win' // Due to testing menubar behavior with keyboard
--- a/taskcluster/ci/cron-bouncer-check/kind.yml
+++ b/taskcluster/ci/cron-bouncer-check/kind.yml
@@ -46,16 +46,17 @@ jobs:
                     jamun:
                         - releases/dev_bouncer_firefox_esr.py
                     default:
                         - releases/dev_bouncer_firefox_beta.py
             product-field:
                 by-project:
                     mozilla-beta: LATEST_FIREFOX_RELEASED_DEVEL_VERSION
                     mozilla-release: LATEST_FIREFOX_VERSION
+                    mozilla-esr60: FIREFOX_ESR
                     default: LATEST_FIREFOX_DEVEL_VERSION
             products-url: https://product-details.mozilla.org/1.0/firefox_versions.json
         treeherder:
             platform: firefox-release/opt
 
     devedition:
         shipping-product: devedition
         index:
--- a/taskcluster/taskgraph/transforms/partials_signing.py
+++ b/taskcluster/taskgraph/transforms/partials_signing.py
@@ -35,17 +35,17 @@ def generate_upstream_artifacts(job, rel
         "taskType": 'partials',
         "paths": [
             "{}/{}".format(artifact_prefix, path)
             for path, version in artifacts
             # TODO Use mozilla-version to avoid comparing strings. Otherwise Firefox 100 will be
             # considered smaller than Firefox 56
             if version is None or version >= '56'
         ],
-        "formats": ["autograph_mar384"],
+        "formats": ["autograph_hash_only_mar384"],
     }]
 
     old_mar_upstream_artifacts = {
         "taskId": {"task-reference": '<partials>'},
         "taskType": 'partials',
         "paths": [
             "{}/{}".format(artifact_prefix, path)
             for path, version in artifacts
@@ -98,17 +98,17 @@ def make_task_description(config, jobs):
             dep_job, config.params['release_history'], balrog_platform, locale)
 
         build_platform = dep_job.attributes.get('build_platform')
         is_nightly = dep_job.attributes.get('nightly')
         signing_cert_scope = get_signing_cert_scope_per_platform(
             build_platform, is_nightly, config
         )
 
-        scopes = [signing_cert_scope, 'project:releng:signing:format:autograph_mar384']
+        scopes = [signing_cert_scope, 'project:releng:signing:format:autograph_hash_only_mar384']
         if any("mar" in upstream_details["formats"] for upstream_details in upstream_artifacts):
             scopes.append('project:releng:signing:format:mar')
 
         task = {
             'label': label,
             'description': "{} Partials".format(
                 dep_job.task["metadata"]["description"]),
             'worker-type': get_worker_type_for_scope(config, signing_cert_scope),
--- a/taskcluster/taskgraph/transforms/repackage_signing.py
+++ b/taskcluster/taskgraph/transforms/repackage_signing.py
@@ -31,17 +31,17 @@ repackage_signing_description_schema = S
     Required('depname', default='repackage'): basestring,
     Optional('label'): basestring,
     Optional('treeherder'): task_description_schema['treeherder'],
     Optional('shipping-product'): task_description_schema['shipping-product'],
     Optional('shipping-phase'): task_description_schema['shipping-phase'],
 })
 
 SIGNING_FORMATS = {
-    'target.complete.mar': ["autograph_mar384"],
+    'target.complete.mar': ["autograph_hash_only_mar384"],
     'target.bz2.complete.mar': ["mar"],
     "target.installer.exe": ["sha2signcode"],
     "target.stub-installer.exe": ["sha2signcodestub"],
 }
 
 
 @transforms.add
 def validate(config, jobs):
--- a/testing/geckodriver/README.md
+++ b/testing/geckodriver/README.md
@@ -35,16 +35,17 @@ Documentation
   * [Types](https://developer.mozilla.org/en-US/docs/Web/WebDriver/Types)
 
 * [Cross browser testing](https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing)
 
 * [Selenium](https://seleniumhq.github.io/docs/) (work in progress)
   * [C# API](https://seleniumhq.github.io/selenium/docs/api/dotnet/)
   * [JavaScript API](https://seleniumhq.github.io/selenium/docs/api/javascript/)
   * [Java API](https://seleniumhq.github.io/selenium/docs/api/java/)
+  * [Perl API](https://metacpan.org/pod/Selenium::Remote::Driver)
   * [Python API](https://seleniumhq.github.io/selenium/docs/api/py/)
   * [Ruby API](https://seleniumhq.github.io/selenium/docs/api/rb/)
 
 * [geckodriver usage](https://firefox-source-docs.mozilla.org/testing/geckodriver/geckodriver/Usage.html)
   * [Supported platforms](https://firefox-source-docs.mozilla.org/testing/geckodriver/geckodriver/Support.html)
   * [Firefox capabilities](https://firefox-source-docs.mozilla.org/testing/geckodriver/geckodriver/Capabilities.html)
   * [Capabilities example](https://firefox-source-docs.mozilla.org/testing/geckodriver/geckodriver/Capabilities.html#capabilities-example)
   * [Enabling trace logs](https://firefox-source-docs.mozilla.org/testing/geckodriver/geckodriver/TraceLogs.html)
--- a/testing/geckodriver/doc/Testing.md
+++ b/testing/geckodriver/doc/Testing.md
@@ -23,22 +23,23 @@ make sure you have built Firefox:
 
 	% ./mach build
 	% ./mach wpt testing/web-platform/tests/webdriver
 
 As these are functional integration tests and pop up Firefox windows
 sporadically, a helpful tip is to surpress the window whilst you
 are running them by using Firefox’ [headless mode]:
 
-	% MOZ_HEADLESS=1 ./mach wpt testing/web-platform/tests/webdriver
+	% ./mach wpt --headless testing/web-platform/tests/webdriver
 
-In addition to the `MOZ_HEADLESS` output variable there is also
-`MOZ_HEADLESS_WIDTH` and `MOZ_HEADLESS_HEIGHT` to control the
+The `--headless` flag is equivalent to setting the `MOZ_HEADLESS`
+output variable.  In addition to `MOZ_HEADLESS` there is also
+`MOZ_HEADLESS_WIDTH` and `MOZ_HEADLESS_HEIGHT` for controlling the
 dimensions of the no-op virtual display.  This is similar to using
-xvfb(1) which you may know from the X windowing system, but has
+Xvfb(1) which you may know from the X windowing system, but has
 the additional benefit of also working on macOS and Windows.
 
 As you get in to development of geckodriver and Marionette you will
 increasingly grow to understand our love for [trace-level logs].
 They provide us with the input—the HTTP requests—from the client
 (in WPT’s case from the tests’ use of a custom WebDriver client),
 the translation geckodriver makes to the [Marionette protocol],
 the log output from Marionette, its responses back to geckodriver,
--- a/testing/marionette/doc/Testing.md
+++ b/testing/marionette/doc/Testing.md
@@ -60,22 +60,22 @@ you can redirect all Gecko output to std
     % ./mach marionette test --gecko-log - TEST
 
 Our functional integration tests pop up Firefox windows sporadically,
 and a helpful tip is to suppress the window can be to use Firefox’
 [headless mode]:
 
     % ./mach marionette test -z TEST
 
-`-z` is an alias for `--headless` and equivalent to setting the
-`MOZ_HEADLESS` output variable.  In addition to `MOZ_HEADLESS` there
-is also `MOZ_HEADLESS_WIDTH` and `MOZ_HEADLESS_HEIGHT` for controlling
-the dimensions of the no-op virtual display.  This is similar to
-using xvfb(1) which you may know from the X windowing system, but
-has the additional benefit of also working on macOS and Windows.
+`-z` is an alias for the `--headless` flag and equivalent to setting
+the `MOZ_HEADLESS` output variable.  In addition to `MOZ_HEADLESS`
+there is also `MOZ_HEADLESS_WIDTH` and `MOZ_HEADLESS_HEIGHT` for
+controlling the dimensions of the no-op virtual display.  This is
+similar to using Xvfb(1) which you may know from the X windowing system,
+but has the additional benefit of also working on macOS and Windows.
 
 
 ### Android
 
 Prerequisites:
 
 *   You have [built Fennec](https://developer.mozilla.org/en-US/docs/Mozilla/Developer_guide/Build_Instructions/Simple_Firefox_for_Android_build) with
     `ac_add_options --enable-marionette` in your mozconfig.
--- a/testing/mozharness/scripts/release/bouncer_check.py
+++ b/testing/mozharness/scripts/release/bouncer_check.py
@@ -96,26 +96,45 @@ class BouncerCheck(BaseScript, Virtualen
 
         if self.config['product_field'] not in firefox_versions:
             self.fatal('Unknown Firefox label: {}'.format(self.config['product_field']))
         self.config["version"] = firefox_versions[self.config["product_field"]]
         self.log("Set Firefox version {}".format(self.config["version"]))
 
     def check_url(self, session, url):
         from redo import retry
+        try:
+            from urllib.parse import urlparse
+        except ImportError:
+            # Python 2
+            from urlparse import urlparse
+
+        mozilla_locations = [
+            'download-installer.cdn.mozilla.net',
+            'download.cdn.mozilla.net',
+            'download.mozilla.org',
+            'archive.mozilla.org',
+        ]
 
         def do_check_url():
             self.log("Checking {}".format(url))
             r = session.head(url, verify=True, timeout=10, allow_redirects=True)
             try:
                 r.raise_for_status()
             except Exception:
                 self.warning("FAIL: {}, status: {}".format(url, r.status_code))
                 raise
 
+            final_url = urlparse(r.url)
+            if final_url.scheme != 'https':
+                self.warning('FAIL: URL scheme is not https: {}'.format(r.url))
+
+            if final_url.netloc not in mozilla_locations:
+                self.warning('FAIL: host not in allowed locations: {}'.format(r.url))
+
         retry(do_check_url, sleeptime=3, max_sleeptime=10, attempts=3)
 
     def get_urls(self):
         for product in self.config["products"].values():
             if not product["check_uptake"]:
                 continue
             product_name = product["product-name"] % {"version": self.config["version"]}
             for path in product["paths"].values():
--- a/testing/web-platform/meta/mediacapture-streams/MediaStream-default-feature-policy.https.html.ini
+++ b/testing/web-platform/meta/mediacapture-streams/MediaStream-default-feature-policy.https.html.ini
@@ -1,39 +1,21 @@
 [MediaStream-default-feature-policy.https.sub.html]
   [Default "microphone" feature policy ["self"\] disallows cross-origin iframes.]
     expected: FAIL
 
-  [Feature policy "microphone" can be enabled in cross-origin iframes using "allow" attribute.]
-    expected: FAIL
-
   [Default "camera" feature policy ["self"\] disallows cross-origin iframes.]
     expected: FAIL
 
-  [Feature policy "camera" can be enabled in cross-origin iframes using "allow" attribute.]
-    expected: FAIL
-
   [Default "camera; microphone" feature policy ["self"\] disallows cross-origin iframes.]
     expected: FAIL
 
-  [Feature policy "camera; microphone" can be enabled in cross-origin iframes using "allow" attribute.]
-    expected: FAIL
-
 
 [MediaStream-default-feature-policy.https.html]
   [Default "microphone" feature policy ["self"\] disallows cross-origin iframes.]
     expected: FAIL
 
-  [Feature policy "microphone" can be enabled in cross-origin iframes using "allow" attribute.]
-    expected: FAIL
-
   [Default "camera" feature policy ["self"\] disallows cross-origin iframes.]
     expected: FAIL
 
-  [Feature policy "camera" can be enabled in cross-origin iframes using "allow" attribute.]
-    expected: FAIL
-
   [Default "camera; microphone" feature policy ["self"\] disallows cross-origin iframes.]
     expected: FAIL
 
-  [Feature policy "camera; microphone" can be enabled in cross-origin iframes using "allow" attribute.]
-    expected: FAIL
-
--- a/testing/web-platform/meta/mediacapture-streams/__dir__.ini
+++ b/testing/web-platform/meta/mediacapture-streams/__dir__.ini
@@ -1,2 +1,2 @@
-prefs: [media.navigator.permission.disabled:true, media.navigator.streams.fake:true]
+prefs: [media.navigator.permission.disabled:true, media.navigator.streams.fake:true, dom.security.featurePolicy.enabled:true]
 lsan-allowed: [Alloc, NewEmptyScopeData, XPCWrappedNative::GetNewOrUsed, js_new, mozilla::BasePrincipal::CreateCodebasePrincipal, mozilla::dom::ChromeUtils::GenerateQI, nsStringBuffer::Alloc]
--- a/toolkit/content/tests/chrome/chrome.ini
+++ b/toolkit/content/tests/chrome/chrome.ini
@@ -135,16 +135,17 @@ support-files = window_navigate_persist.
 [test_menuitem_commands.xul]
 [test_menulist.xul]
 [test_menulist_keynav.xul]
 [test_menulist_null_value.xul]
 [test_menulist_paging.xul]
 [test_menulist_position.xul]
 [test_mousescroll.xul]
 [test_notificationbox.xul]
+skip-if = (os == 'linux' && debug) || (os == 'win') # Bug 1429649
 [test_panel.xul]
 [test_panel_anchoradjust.xul]
 [test_panelfrommenu.xul]
 [test_popup_anchor.xul]
 [test_popup_anchoratrect.xul]
 skip-if = os == 'linux' # 1167694
 [test_popup_attribute.xul]
 skip-if = os == 'linux' && asan # Bug 1131634