Merge mozilla-central to inbound a=merge on a CLOSED TREE
authorCoroiu Cristina <ccoroiu@mozilla.com>
Fri, 13 Apr 2018 19:09:10 +0300
changeset 413251 e2dc38fde43a9296fdb6d5bc2ab2e91a8298ee55
parent 413250 9bacb7ba98d4e17e9de5849f3efd3a3c94aa2eb9 (current diff)
parent 413183 2243b83d2c5ca7a13b592e0fed4639657a2882f1 (diff)
child 413252 bc424899c8f89c3765cdb7b9325f83d2541e0aea
push id33840
push userapavel@mozilla.com
push dateFri, 13 Apr 2018 21:56:54 +0000
treeherdermozilla-central@6547c27303bc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone61.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 mozilla-central to inbound a=merge on a CLOSED TREE
testing/marionette/test_action.js
testing/marionette/test_assert.js
testing/marionette/test_browser.js
testing/marionette/test_cookie.js
testing/marionette/test_dom.js
testing/marionette/test_element.js
testing/marionette/test_error.js
testing/marionette/test_evaluate.js
testing/marionette/test_format.js
testing/marionette/test_message.js
testing/marionette/test_navigate.js
testing/marionette/test_session.js
testing/marionette/test_sync.js
testing/marionette/unit.ini
toolkit/crashreporter/test/moz.build
toolkit/crashreporter/test/nsTestCrasher.cpp
--- a/.eslintignore
+++ b/.eslintignore
@@ -111,44 +111,41 @@ devtools/client/shared/components/test/m
 devtools/client/shared/shim/test/test_*.html
 devtools/client/shared/test/browser_toolbar_webconsole_errors_count.html
 devtools/client/storage/test/*.html
 !devtools/client/storage/test/storage-cookies.html
 !devtools/client/storage/test/storage-overflow.html
 !devtools/client/storage/test/storage-search.html
 !devtools/client/storage/test/storage-unsecured-iframe.html
 !devtools/client/storage/test/storage-unsecured-iframe-usercontextid.html
-devtools/client/webaudioeditor/**
-devtools/client/webconsole/old/net/**
-!devtools/client/webconsole/test/mochitest/**
-devtools/client/webconsole/old/test/**
-devtools/client/webconsole/old/webconsole.js
-devtools/client/webide/**
-!devtools/client/webide/components/webideCli.js
 devtools/server/tests/browser/storage-*.html
 !devtools/server/tests/browser/storage-unsecured-iframe.html
 devtools/server/tests/browser/stylesheets-nested-iframes.html
-devtools/server/tests/unit/xpcshell_debugging_script.js
 devtools/client/shared/webpack/shims/test/test_clipboard.html
 devtools/shared/qrcode/tests/mochitest/test_decode.html
 devtools/shared/tests/mochitest/*.html
 devtools/shared/webconsole/test/test_*.html
 
 # Soon to be removed
 devtools/client/commandline/**
 # Soon to be removed, the new/ directory is explicitly excluded below due to
 # also being an imported repository.
 devtools/client/debugger/**
+# Soon to be removed
+devtools/client/webconsole/old/net/**
+devtools/client/webconsole/old/test/**
+devtools/client/webconsole/old/webconsole.js
 
 # Ignore devtools imported repositories
 devtools/client/debugger/new/**
 devtools/client/shared/components/reps/**
 
 # Ignore devtools preferences files
 devtools/client/preferences/**
+devtools/client/webide/preferences/**
 devtools/shared/preferences/**
 devtools/startup/preferences/devtools-startup.js
 
 # Ignore devtools third-party libs
 devtools/shared/jsbeautify/*
 devtools/shared/acorn/*
 devtools/shared/gcli/source/*
 devtools/shared/node-properties/*
@@ -175,16 +172,19 @@ devtools/client/framework/test/code_*
 devtools/client/inspector/markup/test/events_bundle.js
 devtools/client/netmonitor/test/xhr_bundle.js
 devtools/client/webconsole/test/mochitest/code_bundle_nosource.js
 devtools/client/webconsole/test/mochitest/code_bundle_invalidmap.js
 devtools/server/tests/unit/babel_and_browserify_script_with_source_map.js
 devtools/server/tests/unit/setBreakpoint*
 devtools/server/tests/unit/sourcemapped.js
 
+# devtools specific format test file
+devtools/server/tests/unit/xpcshell_debugging_script.js
+
 # dom/ exclusions
 dom/abort/**
 dom/animation/**
 dom/archivereader/**
 dom/asmjscache/**
 dom/audiochannel/**
 dom/base/**
 dom/battery/**
@@ -338,17 +338,16 @@ security/nss/**
 # Uses `#filter substitution`
 services/sync/modules/constants.js
 services/sync/services-sync.js
 
 # Servo is imported.
 servo/**
 
 # Remote protocol exclusions
-testing/marionette/test_*.js
 testing/marionette/atom.js
 testing/marionette/legacyaction.js
 testing/marionette/client
 testing/marionette/doc
 testing/marionette/harness
 
 # other testing/ exclusions
 testing/mochitest/**
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -494,18 +494,17 @@ const gSessionHistoryObserver = {
     gURLBar.editor.transactionManager.clear();
   }
 };
 
 const gStoragePressureObserver = {
   _lastNotificationTime: -1,
 
   observe(subject, topic, data) {
-    if (topic != "QuotaManager::StoragePressure" ||
-        !Services.prefs.getBoolPref("browser.storageManager.enabled")) {
+    if (topic != "QuotaManager::StoragePressure") {
       return;
     }
 
     const NOTIFICATION_VALUE = "storage-pressure-notification";
     let notificationBox = document.getElementById("high-priority-global-notificationbox");
     if (notificationBox.getNotificationWithValue(NOTIFICATION_VALUE)) {
       // Do not display the 2nd notification when there is already one
       return;
--- a/browser/base/content/test/general/browser_storagePressure_notification.js
+++ b/browser/base/content/test/general/browser_storagePressure_notification.js
@@ -18,17 +18,16 @@ function openAboutPrefPromise() {
     TestUtils.topicObserved("privacy-pane-loaded", () => true)
   ];
   return Promise.all(promises);
 }
 
 // Test only displaying notification once within the given interval
 add_task(async function() {
   const TEST_NOTIFICATION_INTERVAL_MS = 2000;
-  await SpecialPowers.pushPrefEnv({set: [["browser.storageManager.enabled", true]]});
   await SpecialPowers.pushPrefEnv({set: [["browser.storageManager.pressureNotification.minIntervalMS", TEST_NOTIFICATION_INTERVAL_MS]]});
   // Commenting this to see if we really need it
   // await SpecialPowers.pushPrefEnv({set: [["privacy.reduceTimerPrecision", false]]});
 
   await notifyStoragePressure();
   let notificationbox = document.getElementById("high-priority-global-notificationbox");
   let notification = notificationbox.getNotificationWithValue("storage-pressure-notification");
   ok(notification instanceof XULElement, "Should display storage pressure notification");
@@ -42,17 +41,16 @@ add_task(async function() {
   await notifyStoragePressure();
   notification = notificationbox.getNotificationWithValue("storage-pressure-notification");
   ok(notification instanceof XULElement, "Should display storage pressure notification after the given interval");
   notification.close();
 });
 
 // Test guiding user to the about:preferences when usage exceeds the given threshold
 add_task(async function() {
-  await SpecialPowers.pushPrefEnv({ set: [["browser.storageManager.enabled", true]] });
   await SpecialPowers.pushPrefEnv({ set: [["browser.storageManager.pressureNotification.minIntervalMS", 0]] });
   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "https://example.com");
 
   const BYTES_IN_GIGABYTE = 1073741824;
   const USAGE_THRESHOLD_BYTES = BYTES_IN_GIGABYTE *
     Services.prefs.getIntPref("browser.storageManager.pressureNotification.usageThresholdGB");
   await notifyStoragePressure(USAGE_THRESHOLD_BYTES);
   let notificationbox = document.getElementById("high-priority-global-notificationbox");
@@ -69,17 +67,16 @@ add_task(async function() {
   is_element_visible(siteDataGroup, "Should open to the siteDataGroup section in about:preferences");
   BrowserTestUtils.removeTab(aboutPrefTab);
   BrowserTestUtils.removeTab(tab);
 });
 
 // Test not displaying the 2nd notification if one is already being displayed
 add_task(async function() {
   const TEST_NOTIFICATION_INTERVAL_MS = 0;
-  await SpecialPowers.pushPrefEnv({set: [["browser.storageManager.enabled", true]]});
   await SpecialPowers.pushPrefEnv({set: [["browser.storageManager.pressureNotification.minIntervalMS", TEST_NOTIFICATION_INTERVAL_MS]]});
 
   await notifyStoragePressure();
   await notifyStoragePressure();
   let notificationbox = document.getElementById("high-priority-global-notificationbox");
   let allNotifications = notificationbox.allNotifications;
   let pressureNotificationCount = 0;
   allNotifications.forEach(notification => {
--- a/browser/components/customizableui/CustomizableWidgets.jsm
+++ b/browser/components/customizableui/CustomizableWidgets.jsm
@@ -739,17 +739,19 @@ if (Services.prefs.getBoolPref("identity
       item.setAttribute("class", "subviewbutton");
       item.setAttribute("targetURI", tabInfo.url);
       item.setAttribute("label", tabInfo.title != "" ? tabInfo.title : tabInfo.url);
       item.setAttribute("image", tabInfo.icon);
       item.setAttribute("tooltiptext", tooltipText);
       // We need to use "click" instead of "command" here so openUILink
       // respects different buttons (eg, to open in a new tab).
       item.addEventListener("click", e => {
-        doc.defaultView.openWebLinkIn(tabInfo.url, e);
+        doc.defaultView.openUILink(tabInfo.url, e, {
+          triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({})
+        });
         if (doc.defaultView.whereToOpenLink(e) != "current") {
           e.preventDefault();
           e.stopPropagation();
         } else {
           CustomizableUI.hidePanelForNode(item);
         }
         BrowserUITelemetry.countSyncedTabEvent("open", "toolbarbutton-subview");
       });
--- a/browser/components/customizableui/test/browser_synced_tabs_menu.js
+++ b/browser/components/customizableui/test/browser_synced_tabs_menu.js
@@ -14,16 +14,18 @@ ChromeUtils.defineModuleGetter(this, "UI
 
 // These are available on the widget implementation, but it seems impossible
 // to grab that impl at runtime.
 const DECKINDEX_TABS = 0;
 const DECKINDEX_TABSDISABLED = 1;
 const DECKINDEX_FETCHING = 2;
 const DECKINDEX_NOCLIENTS = 3;
 
+const SAMPLE_TAB_URL = "https://example.com/";
+
 var initialLocation = gBrowser.currentURI.spec;
 var newTab = null;
 
 // A helper to notify there are new tabs. Returns a promise that is resolved
 // once the UI has been updated.
 function updateTabsPanel() {
   let promiseTabsUpdated = promiseObserverNotified("synced-tabs-menu:test:tabs-updated");
   Services.obs.notifyObservers(null, SyncedTabs.TOPIC_TABS_CHANGED);
@@ -330,24 +332,25 @@ add_task(async function() {
         name: "My Desktop",
         lastModified: 1492201200,
         tabs: function() {
           let allTabsDesktop = [];
           // We choose 77 tabs, because TABS_PER_PAGE is 25, which means
           // on the second to last page we should have 22 items shown
           // (because we have to show at least NEXT_PAGE_MIN_TABS=5 tabs on the last page)
           for (let i = 1; i <= 77; i++) {
-            allTabsDesktop.push({ title: "Tab #" + i });
+            allTabsDesktop.push({ title: "Tab #" + i, url: SAMPLE_TAB_URL });
           }
           return allTabsDesktop;
         }(),
       }
     ]);
   };
 
+
   gSync.updateAllUI({ status: UIState.STATUS_SIGNED_IN, lastSync: new Date(),
                       email: "foo@bar.com" });
 
   await document.getElementById("nav-bar").overflowable.show();
   let tabsUpdatedPromise = promiseObserverNotified("synced-tabs-menu:test:tabs-updated");
   let syncPanel = document.getElementById("PanelUI-remotetabs");
   let viewShownPromise = BrowserTestUtils.waitForEvent(syncPanel, "ViewShown");
   let syncButton = document.getElementById("sync-button");
@@ -365,29 +368,38 @@ add_task(async function() {
     let tabList = document.getElementById("PanelUI-remotetabs-tabslist");
     let node = tabList.firstChild;
     is(node.getAttribute("itemtype"), "client", "node is a client entry");
     is(node.textContent, "My Desktop", "correct client");
     for (let i = 0; i < tabsShownCount; i++) {
       node = node.nextSibling;
       is(node.getAttribute("itemtype"), "tab", "node is a tab");
       is(node.getAttribute("label"), "Tab #" + (i + 1), "the tab is the correct one");
+      is(node.getAttribute("targetURI"), SAMPLE_TAB_URL, "url is the correct one");
     }
     let showMoreButton;
     if (showMoreLabel) {
       node = showMoreButton = node.nextSibling;
       is(node.getAttribute("itemtype"), "showmorebutton", "node is a show more button");
       is(node.getAttribute("label"), showMoreLabel);
     }
     node = node.nextSibling;
     is(node, null, "no more entries");
 
     return showMoreButton;
   }
 
+  async function checkCanOpenURL() {
+    let tabList = document.getElementById("PanelUI-remotetabs-tabslist");
+    let node = tabList.firstChild.nextSibling;
+    let promiseTabOpened = BrowserTestUtils.waitForLocationChange(gBrowser, SAMPLE_TAB_URL);
+    node.click();
+    await promiseTabOpened;
+  }
+
   let showMoreButton;
   function clickShowMoreButton() {
     let promise = promiseObserverNotified("synced-tabs-menu:test:tabs-updated");
     showMoreButton.click();
     return promise;
   }
 
   showMoreButton = checkTabsPage(25, "Show More");
@@ -395,11 +407,11 @@ add_task(async function() {
 
   showMoreButton = checkTabsPage(50, "Show More");
   await clickShowMoreButton();
 
   showMoreButton = checkTabsPage(72, "Show All");
   await clickShowMoreButton();
 
   checkTabsPage(77, null);
-
-  await hideOverflow();
+  /* calling this will close the overflow menu */
+  await checkCanOpenURL();
 });
--- a/browser/components/extensions/ExtensionPopups.jsm
+++ b/browser/components/extensions/ExtensionPopups.jsm
@@ -363,17 +363,17 @@ class BasePopup {
     // of the extension content. Passing `null` should be treated the same as no argument,
     // which is why we can't use default parameters here.
     if (!background) {
       background = "#fff";
     }
     this.panel.style.setProperty("--arrowpanel-background", background);
     if (background == "#fff") {
       // Set a usable default color that work with the default background-color.
-      this.panel.style.setProperty("--arrowpanel-border-color", "hsla(210,4%,10%,.05)");
+      this.panel.style.setProperty("--arrowpanel-border-color", "hsla(210,4%,10%,.15)");
     }
     this.background = background;
   }
 }
 
 /**
  * A map of active popups for a given browser window.
  *
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -2762,19 +2762,17 @@ const ContentPermissionIntegration = {
     switch (type) {
       case "geolocation": {
         return new PermissionUI.GeolocationPermissionPrompt(request);
       }
       case "desktop-notification": {
         return new PermissionUI.DesktopNotificationPermissionPrompt(request);
       }
       case "persistent-storage": {
-        if (Services.prefs.getBoolPref("browser.storageManager.enabled")) {
-          return new PermissionUI.PersistentStoragePermissionPrompt(request);
-        }
+        return new PermissionUI.PersistentStoragePermissionPrompt(request);
       }
       case "midi": {
         return new PermissionUI.MIDIPermissionPrompt(request);
       }
     }
     return undefined;
   },
 };
--- a/browser/components/preferences/in-content/privacy.js
+++ b/browser/components/preferences/in-content/privacy.js
@@ -332,35 +332,31 @@ var gPrivacyPane = {
       document.l10n.setAttributes(checkbox, "permissions-notification-pause");
       if (AlertsServiceDND.manualDoNotDisturb) {
         let notificationsDoNotDisturb =
           document.getElementById("notificationsDoNotDisturb");
         notificationsDoNotDisturb.setAttribute("checked", true);
       }
     }
 
-    if (Services.prefs.getBoolPref("browser.storageManager.enabled")) {
-      Services.obs.addObserver(this, "sitedatamanager:sites-updated");
-      Services.obs.addObserver(this, "sitedatamanager:updating-sites");
-      let unload = () => {
-        window.removeEventListener("unload", unload);
-        Services.obs.removeObserver(this, "sitedatamanager:sites-updated");
-        Services.obs.removeObserver(this, "sitedatamanager:updating-sites");
-      };
-      window.addEventListener("unload", unload);
-      SiteDataManager.updateSites();
-      setEventListener("clearSiteDataButton", "command",
-        gPrivacyPane.clearSiteData);
-      setEventListener("siteDataSettings", "command",
-        gPrivacyPane.showSiteDataSettings);
-      let url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "storage-permissions";
-      document.getElementById("siteDataLearnMoreLink").setAttribute("href", url);
-      let siteDataGroup = document.getElementById("siteDataGroup");
-      siteDataGroup.removeAttribute("data-hidden-from-search");
-    }
+    Services.obs.addObserver(this, "sitedatamanager:sites-updated");
+    Services.obs.addObserver(this, "sitedatamanager:updating-sites");
+    let unload = () => {
+      window.removeEventListener("unload", unload);
+      Services.obs.removeObserver(this, "sitedatamanager:sites-updated");
+      Services.obs.removeObserver(this, "sitedatamanager:updating-sites");
+    };
+    window.addEventListener("unload", unload);
+    SiteDataManager.updateSites();
+    setEventListener("clearSiteDataButton", "command",
+      gPrivacyPane.clearSiteData);
+    setEventListener("siteDataSettings", "command",
+      gPrivacyPane.showSiteDataSettings);
+    let url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "storage-permissions";
+    document.getElementById("siteDataLearnMoreLink").setAttribute("href", url);
 
     let notificationInfoURL =
       Services.urlFormatter.formatURLPref("app.support.baseURL") + "push";
     document.getElementById("notificationPermissionsLearnMore").setAttribute("href",
       notificationInfoURL);
     let drmInfoURL =
       Services.urlFormatter.formatURLPref("app.support.baseURL") + "drm-content";
     document.getElementById("playDRMContentLink").setAttribute("href", drmInfoURL);
--- a/browser/components/preferences/in-content/privacy.xul
+++ b/browser/components/preferences/in-content/privacy.xul
@@ -155,17 +155,17 @@
               class="accessory-button"
               icon="clear"
               data-l10n-id="history-clear-button"/>
     </vbox>
   </hbox>
 </groupbox>
 
 <!-- Site Data -->
-<groupbox id="siteDataGroup" hidden="true" data-category="panePrivacy" data-hidden-from-search="true">
+<groupbox id="siteDataGroup" data-category="panePrivacy">
   <caption><label data-l10n-id="sitedata-header"/></caption>
 
   <hbox data-subcategory="sitedata" align="baseline">
     <vbox flex="1">
       <description class="description-with-side-element" flex="1">
         <html:span id="totalSiteDataSize" class="tail-with-learn-more"></html:span>
         <label id="siteDataLearnMoreLink"
           class="learnMore text-link" data-l10n-id="sitedata-learn-more"/>
--- a/browser/components/preferences/in-content/tests/browser_bug731866.js
+++ b/browser/components/preferences/in-content/tests/browser_bug731866.js
@@ -1,15 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 ChromeUtils.import("resource://gre/modules/PlacesUtils.jsm");
 ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
 
-const storageManagerDisabled = !SpecialPowers.getBoolPref("browser.storageManager.enabled");
 const browserContainersGroupDisabled = !SpecialPowers.getBoolPref("privacy.userContext.ui.enabled");
 
 function test() {
   waitForExplicitFinish();
   open_preferences(runTest);
 }
 
 var gElements;
@@ -17,24 +16,17 @@ var gElements;
 function checkElements(expectedPane) {
   for (let element of gElements) {
     // keyset elements fail is_element_visible checks because they are never visible.
     // special-case the drmGroup item because its visibility depends on pref + OS version
     if (element.nodeName == "keyset" ||
         element.id === "drmGroup") {
       continue;
     }
-    // The siteDataGroup in the Storage Management project is currently only pref-on on Nightly for testing purpose.
-    // During the test and the transition period, we have to check the pref to see if the siteDataGroup
-    // should be hidden always. This would be a bit bothersome, same as the offlineGroup as below.
-    // However, this checking is necessary to make sure we don't leak the siteDataGroup into beta/release build
-    if (element.id == "siteDataGroup" && storageManagerDisabled) {
-      is_element_hidden(element, "Disabled siteDataGroup should be hidden");
-      continue;
-    }
+
     // The browserContainersGroup is still only pref-on on Nightly
     if (element.id == "browserContainersGroup" && browserContainersGroupDisabled) {
       is_element_hidden(element, "Disabled browserContainersGroup should be hidden");
       continue;
     }
 
     let attributeValue = element.getAttribute("data-category");
     let suffix = " (id=" + element.id + ")";
--- a/browser/components/preferences/in-content/tests/browser_search_subdialogs_within_preferences_3.js
+++ b/browser/components/preferences/in-content/tests/browser_search_subdialogs_within_preferences_3.js
@@ -1,17 +1,16 @@
 /*
 * This file contains tests for the Preferences search bar.
 */
 
 // Enabling Searching functionatily. Will display search bar form this testcase forward.
 add_task(async function() {
   await SpecialPowers.pushPrefEnv({"set": [
-    ["browser.preferences.search", true],
-    ["browser.storageManager.enabled", true]
+    ["browser.preferences.search", true]
   ]});
 });
 
 /**
  * Test for searching for the "Allowed Sites - Add-ons Installation" subdialog.
  */
 add_task(async function() {
   await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
--- a/browser/components/preferences/in-content/tests/browser_search_subdialogs_within_preferences_7.js
+++ b/browser/components/preferences/in-content/tests/browser_search_subdialogs_within_preferences_7.js
@@ -2,18 +2,17 @@
 * This file contains tests for the Preferences search bar.
 */
 
 requestLongerTimeout(2);
 
 // Enabling Searching functionatily. Will display search bar form this testcase forward.
 add_task(async function() {
   await SpecialPowers.pushPrefEnv({"set": [
-    ["browser.preferences.search", true],
-    ["browser.storageManager.enabled", true]
+    ["browser.preferences.search", true]
   ]});
 });
 
 /**
  * Test for searching for the "Device Manager" subdialog.
  */
 add_task(async function() {
   await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
--- a/browser/components/preferences/in-content/tests/browser_search_within_preferences_1.js
+++ b/browser/components/preferences/in-content/tests/browser_search_within_preferences_1.js
@@ -208,59 +208,16 @@ add_task(async function exiting_search_r
 
   // Checks if back to normal
   is_element_visible(generalPane, "Should be in generalPane");
 
   BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
 
 /**
- * Test for "Site Data" case, verifying elements with data-hidden-from-search = true
- * are hidden in search result.
- */
-add_task(async function verify_hidden_from_search_elements_dont_show_up() {
-  await SpecialPowers.pushPrefEnv({ "set": [["browser.storageManager.enabled", false]] });
-  await openPreferencesViaOpenPreferencesAPI("privacy", { leaveOpen: true });
-  let generalPane = gBrowser.contentDocument.getElementById("generalCategory");
-
-  is_element_hidden(generalPane, "Should not be in general");
-
-  // Performs search
-  let searchInput = gBrowser.contentDocument.getElementById("searchInput");
-
-  is(searchInput, gBrowser.contentDocument.activeElement.closest("#searchInput"),
-    "Search input should be focused when visiting preferences");
-
-  let query = "site data";
-  let searchCompletedPromise = BrowserTestUtils.waitForEvent(
-    gBrowser.contentWindow, "PreferencesSearchCompleted", evt => evt.detail == query);
-  EventUtils.sendString(query);
-  await searchCompletedPromise;
-
-  let mainPrefTag = gBrowser.contentDocument.getElementById("mainPrefPane");
-
-  let child = mainPrefTag.querySelector("#siteDataGroup");
-  is_element_hidden(child, "Should be hidden in search results");
-
-  // Takes search off
-  searchCompletedPromise = BrowserTestUtils.waitForEvent(
-    gBrowser.contentWindow, "PreferencesSearchCompleted", evt => evt.detail == "");
-  let count = query.length;
-  while (count--) {
-    EventUtils.sendKey("BACK_SPACE");
-  }
-  await searchCompletedPromise;
-
-  // Checks if back to normal
-  is_element_visible(generalPane, "Should be in generalPane");
-
-  BrowserTestUtils.removeTab(gBrowser.selectedTab);
-});
-
-/**
  * Test for if we go to another tab after searching
  */
 add_task(async function changing_tabs_after_searching() {
   await openPreferencesViaOpenPreferencesAPI("paneGeneral", { leaveOpen: true });
   let searchInput = gBrowser.contentDocument.getElementById("searchInput");
 
   is(searchInput, gBrowser.contentDocument.activeElement.closest("#searchInput"),
     "Search input should be focused when visiting preferences");
--- a/browser/components/preferences/in-content/tests/browser_search_within_preferences_command.js
+++ b/browser/components/preferences/in-content/tests/browser_search_within_preferences_command.js
@@ -1,15 +1,14 @@
 "use strict";
 
 /**
  * Test for "command" event on search input (when user clicks the x button)
  */
 add_task(async function() {
-  await SpecialPowers.pushPrefEnv({"set": [["browser.storageManager.enabled", false]]});
   await openPreferencesViaOpenPreferencesAPI("privacy", {leaveOpen: true});
   let generalPane = gBrowser.contentDocument.getElementById("generalCategory");
 
   is_element_hidden(generalPane, "Should not be in general");
 
   // Performs search
   let searchInput = gBrowser.contentDocument.getElementById("searchInput");
   is(searchInput, gBrowser.contentDocument.activeElement.closest("#searchInput"),
--- a/browser/modules/SitePermissions.jsm
+++ b/browser/modules/SitePermissions.jsm
@@ -679,22 +679,16 @@ var gPermissionObject = {
     exactHostMatch: true
   },
 
   "midi-sysex": {
     exactHostMatch: true
   }
 };
 
-// Delete this entry while being pre-off
-// or the persistent-storage permission would appear in Page info's Permission section
-if (!Services.prefs.getBoolPref("browser.storageManager.enabled")) {
-  delete gPermissionObject["persistent-storage"];
-}
-
 if (!Services.prefs.getBoolPref("dom.webmidi.enabled")) {
   // ESLint gets angry about array versus dot notation here, but some permission
   // names use hyphens. Disabling rule for line to keep things consistent.
   // eslint-disable-next-line dot-notation
   delete gPermissionObject["midi"];
   delete gPermissionObject["midi-sysex"];
 }
 
--- a/browser/modules/test/unit/test_SitePermissions.js
+++ b/browser/modules/test/unit/test_SitePermissions.js
@@ -1,29 +1,23 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 "use strict";
 
 ChromeUtils.import("resource:///modules/SitePermissions.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
-const STORAGE_MANAGER_ENABLED = Services.prefs.getBoolPref("browser.storageManager.enabled");
 const RESIST_FINGERPRINTING_ENABLED = Services.prefs.getBoolPref("privacy.resistFingerprinting");
 const MIDI_ENABLED = Services.prefs.getBoolPref("dom.webmidi.enabled");
 
 add_task(async function testPermissionsListing() {
   let expectedPermissions = ["camera", "cookie", "desktop-notification", "focus-tab-by-prompt",
-     "geo", "image", "install", "microphone", "plugin:flash", "popup", "screen", "shortcuts"];
-  if (STORAGE_MANAGER_ENABLED) {
-    // The persistent-storage permission is still only pref-on on Nightly
-    // so we add it only when it's pref-on.
-    // Should remove this checking and add it as default after it is fully pref-on.
-    expectedPermissions.push("persistent-storage");
-  }
+     "geo", "image", "install", "microphone", "plugin:flash", "popup", "screen", "shortcuts",
+     "persistent-storage"];
   if (RESIST_FINGERPRINTING_ENABLED) {
     // Canvas permission should be hidden unless privacy.resistFingerprinting
     // is true.
     expectedPermissions.push("canvas");
   }
   if (MIDI_ENABLED) {
     // Should remove this checking and add it as default after it is fully pref'd-on.
     expectedPermissions.push("midi");
@@ -108,23 +102,17 @@ add_task(async function testGetAvailable
                      SitePermissions.BLOCK ]);
 });
 
 add_task(async function testExactHostMatch() {
   let uri = Services.io.newURI("https://example.com");
   let subUri = Services.io.newURI("https://test1.example.com");
 
   let exactHostMatched = ["desktop-notification", "focus-tab-by-prompt", "camera",
-                          "microphone", "screen", "geo"];
-  if (STORAGE_MANAGER_ENABLED) {
-    // The persistent-storage permission is still only pref-on on Nightly
-    // so we add it only when it's pref-on.
-    // Should remove this checking and add it as default after it is fully pref-on.
-    exactHostMatched.push("persistent-storage");
-  }
+                          "microphone", "screen", "geo", "persistent-storage"];
   if (RESIST_FINGERPRINTING_ENABLED) {
     // Canvas permission should be hidden unless privacy.resistFingerprinting
     // is true.
     exactHostMatched.push("canvas");
   }
   if (MIDI_ENABLED) {
     // WebMIDI is only pref'd on in nightly.
     // Should remove this checking and add it as default after it is fully pref-on.
--- a/browser/tools/mozscreenshots/mozscreenshots/extension/configurations/PermissionPrompts.jsm
+++ b/browser/tools/mozscreenshots/mozscreenshots/extension/configurations/PermissionPrompts.jsm
@@ -11,17 +11,16 @@ ChromeUtils.import("resource://gre/modul
 ChromeUtils.import("resource://testing-common/ContentTask.jsm");
 ChromeUtils.import("resource://testing-common/BrowserTestUtils.jsm");
 
 const URL = "https://test1.example.com/browser/browser/tools/mozscreenshots/mozscreenshots/extension/mozscreenshots/browser/chrome/mozscreenshots/lib/permissionPrompts.html";
 let lastTab = null;
 
 var PermissionPrompts = {
   init(libDir) {
-    Services.prefs.setBoolPref("browser.storageManager.enabled", true);
     Services.prefs.setBoolPref("media.navigator.permission.fake", true);
     Services.prefs.setBoolPref("extensions.install.requireBuiltInCerts", false);
     Services.prefs.setBoolPref("signon.rememberSignons", true);
   },
 
   configurations: {
     shareDevices: {
       selectors: ["#notification-popup"],
--- a/devtools/.eslintrc.js
+++ b/devtools/.eslintrc.js
@@ -42,35 +42,38 @@ module.exports = {
       "camelcase": "off",
     }
   }, {
     "files": [
       "client/framework/**",
       "client/scratchpad/**",
       "client/shared/*.jsm",
       "client/shared/widgets/*.jsm",
+      "client/webide/**",
     ],
     "rules": {
       "consistent-return": "off",
     }
   }, {
     "files": [
       "client/framework/**",
       "client/scratchpad/**",
       "client/shared/AppCacheUtils.jsm",
+      "client/webide/**",
     ],
     "rules": {
       "max-nested-callbacks": "off",
     }
   }, {
     "files": [
       "client/framework/**",
       "client/scratchpad/**",
       "client/shared/*.jsm",
       "client/shared/widgets/*.jsm",
+      "client/webide/**",
     ],
     "rules": {
       "max-len": "off",
     }
   }, {
     "files": [
       "client/scratchpad/test/browser_scratchpad_inspect.js",
       "client/scratchpad/test/browser_scratchpad_inspect_primitives.js",
@@ -79,16 +82,17 @@ module.exports = {
       "no-labels": "off",
     }
   }, {
     "files": [
       "client/framework/**",
       "client/scratchpad/**",
       "client/shared/*.jsm",
       "client/shared/widgets/*.jsm",
+      "client/webide/**",
     ],
     "rules": {
       "mozilla/no-aArgs": "off",
     }
   }, {
     "files": [
       "client/framework/test/**",
       "client/scratchpad/**",
@@ -97,46 +101,53 @@ module.exports = {
       "mozilla/var-only-at-top-level": "off",
     }
   }, {
     "files": [
       "client/framework/**",
       "client/scratchpad/**",
       "client/shared/AppCacheUtils.jsm",
       "client/shared/widgets/*.jsm",
+      "client/webide/**",
     ],
     "rules": {
       "no-shadow": "off",
     }
   }, {
     "files": [
       "client/framework/**",
       "client/scratchpad/**",
+      "client/webide/**",
     ],
     "rules": {
       "strict": "off",
     }
   }, {
     "files": [
       // Note: Bug 1403938 may be removing canvasdebugger, check before
       // doing more work on enabling these rules.
       "client/canvasdebugger/**",
       // Note: Bug 1342237 may be removing shadereditor, check before
       // doing more work on enabling these rules.
       "client/shadereditor/**",
+      // Note: Bug 1403944 may be removing webaudioeditor, check before
+      // doing more work on enabling these rules.
+      "client/webaudioeditor/**",
     ],
     "rules": {
       "consistent-return": "off",
       "max-len": "off",
       "mozilla/no-aArgs": "off",
       "mozilla/var-only-at-top-level": "off",
+      "no-redeclare": "off",
       "no-return-assign": "off",
       "no-shadow": "off",
       "no-undef": "off",
       "no-unused-vars": "off",
+      "no-useless-call": "off",
       "strict": "off",
     }
   }, {
     // For all head*.js files, turn off no-unused-vars at a global level
     "files": [
       "**/head*.js",
     ],
     "rules": {
--- a/devtools/client/aboutdebugging/test/head.js
+++ b/devtools/client/aboutdebugging/test/head.js
@@ -10,21 +10,16 @@
 // Load the shared-head file first.
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/shared/test/shared-head.js",
   this);
 
 const { AddonManager } = ChromeUtils.import("resource://gre/modules/AddonManager.jsm", {});
 const { Management } = ChromeUtils.import("resource://gre/modules/Extension.jsm", {});
 
-flags.testing = true;
-registerCleanupFunction(() => {
-  flags.testing = false;
-});
-
 async function openAboutDebugging(page, win) {
   info("opening about:debugging");
   let url = "about:debugging";
   if (page) {
     url += "#" + page;
   }
 
   let tab = await addTab(url, { window: win });
--- a/devtools/client/commandline/test/head.js
+++ b/devtools/client/commandline/test/head.js
@@ -14,21 +14,16 @@ var { require } = ChromeUtils.import("re
 var flags = require("devtools/shared/flags");
 var { Task } = require("devtools/shared/task");
 
 // Import the GCLI test helper
 var testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
 Services.scriptloader.loadSubScript(testDir + "/helpers.js", this);
 Services.scriptloader.loadSubScript(testDir + "/mockCommands.js", this, "UTF-8");
 
-flags.testing = true;
-SimpleTest.registerCleanupFunction(() => {
-  flags.testing = false;
-});
-
 function whenDelayedStartupFinished(aWindow, aCallback) {
   Services.obs.addObserver(function observer(aSubject, aTopic) {
     if (aWindow == aSubject) {
       Services.obs.removeObserver(observer, aTopic);
       executeSoon(aCallback);
     }
   }, "browser-delayed-startup-finished");
 }
--- a/devtools/client/framework/test/browser_ignore_toolbox_network_requests.js
+++ b/devtools/client/framework/test/browser_ignore_toolbox_network_requests.js
@@ -7,28 +7,26 @@
 
 // Test that network requests originating from the toolbox don't get recorded in
 // the network panel.
 
 add_task(async function() {
   // TODO: This test tries to verify the normal behavior of the netmonitor and
   // therefore needs to avoid the explicit check for tests. Bug 1167188 will
   // allow us to remove this workaround.
-  let isTesting = flags.testing;
-  flags.testing = false;
+  await pushPref("devtools.testing", false);
 
   let tab = await addTab(URL_ROOT + "doc_viewsource.html");
   let target = TargetFactory.forTab(tab);
   let toolbox = await gDevTools.showToolbox(target, "styleeditor");
   let panel = toolbox.getPanel("styleeditor");
 
   is(panel.UI.editors.length, 1, "correct number of editors opened");
 
   let monitor = await toolbox.selectTool("netmonitor");
   let { store, windowRequire } = monitor.panelWin;
 
   is(store.getState().requests.requests.size, 0, "No network requests appear in the network panel");
 
   await gDevTools.closeToolbox(target);
   tab = target = toolbox = panel = null;
   gBrowser.removeCurrentTab();
-  flags.testing = isTesting;
 });
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -18,16 +18,17 @@ const HTML_NS = "http://www.w3.org/1999/
 var {Ci, Cc} = require("chrome");
 var promise = require("promise");
 var defer = require("devtools/shared/defer");
 var Services = require("Services");
 var ChromeUtils = require("ChromeUtils");
 var {gDevTools} = require("devtools/client/framework/devtools");
 var EventEmitter = require("devtools/shared/event-emitter");
 var Telemetry = require("devtools/client/shared/telemetry");
+const { getUnicodeUrl } = require("devtools/client/shared/unicode-url");
 var { attachThread, detachThread } = require("./attach-thread");
 var Menu = require("devtools/client/framework/menu");
 var MenuItem = require("devtools/client/framework/menu-item");
 var { DOMHelpers } = require("resource://devtools/client/shared/DOMHelpers.jsm");
 const { KeyCodes } = require("devtools/client/shared/keycodes");
 var Startup = Cc["@mozilla.org/devtools/startup-clh;1"].getService(Ci.nsISupports)
   .wrappedJSObject;
 
@@ -2052,21 +2053,23 @@ Toolbox.prototype = {
 
   /**
    * Refresh the host's title.
    */
   _refreshHostTitle: function() {
     let title;
     if (this.target.name && this.target.name != this.target.url) {
       const url = this.target.isWebExtension ?
-                  this.target.getExtensionPathName(this.target.url) : this.target.url;
+                    this.target.getExtensionPathName(this.target.url) :
+                    getUnicodeUrl(this.target.url);
       title = L10N.getFormatStr("toolbox.titleTemplate2", this.target.name,
                                                           url);
     } else {
-      title = L10N.getFormatStr("toolbox.titleTemplate1", this.target.url);
+      title = L10N.getFormatStr("toolbox.titleTemplate1",
+                                getUnicodeUrl(this.target.url));
     }
     this.postMessage({
       name: "set-host-title",
       title
     });
   },
 
   // Returns an instance of the preference actor
@@ -2143,21 +2146,22 @@ Toolbox.prototype = {
         await this.target.actorHasMethod("domwalker", "getNodeActorFromWindowID");
     }
 
     // Generate list of menu items from the list of frames.
     this.frameMap.forEach(frame => {
       // A frame is checked if it's the selected one.
       let checked = frame.id == this.selectedFrameId;
 
-      let label = frame.url;
-
+      let label;
       if (this.target.isWebExtension) {
         // Show a shorter url for extensions page.
         label = this.target.getExtensionPathName(frame.url);
+      } else {
+        label = getUnicodeUrl(frame.url);
       }
 
       // Create menu item.
       menu.append(new MenuItem({
         label,
         type: "radio",
         checked,
         click: () => {
--- a/devtools/client/inspector/markup/test/head.js
+++ b/devtools/client/inspector/markup/test/head.js
@@ -14,22 +14,16 @@ Services.scriptloader.loadSubScript(
 var {getInplaceEditorForSpan: inplaceEditor} = require("devtools/client/shared/inplace-editor");
 var clipboard = require("devtools/shared/platform/clipboard");
 var {ActorRegistryFront} = require("devtools/shared/fronts/actor-registry");
 
 // If a test times out we want to see the complete log and not just the last few
 // lines.
 SimpleTest.requestCompleteLog();
 
-// Set the testing flag on DevToolsUtils and reset it when the test ends
-flags.testing = true;
-registerCleanupFunction(() => {
-  flags.testing = false;
-});
-
 // Toggle this pref on to see all DevTools event communication. This is hugely
 // useful for fixing race conditions.
 // Services.prefs.setBoolPref("devtools.dump.emit", true);
 
 // Clear preferences that may be set during the course of tests.
 registerCleanupFunction(() => {
   Services.prefs.clearUserPref("devtools.dump.emit");
   Services.prefs.clearUserPref("devtools.inspector.htmlPanelOpen");
--- a/devtools/client/inspector/test/head.js
+++ b/devtools/client/inspector/test/head.js
@@ -27,21 +27,16 @@ Services.scriptloader.loadSubScript(
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/inspector/test/shared-head.js",
   this);
 
 const {LocalizationHelper} = require("devtools/shared/l10n");
 const INSPECTOR_L10N =
       new LocalizationHelper("devtools/client/locales/inspector.properties");
 
-flags.testing = true;
-registerCleanupFunction(() => {
-  flags.testing = false;
-});
-
 registerCleanupFunction(() => {
   Services.prefs.clearUserPref("devtools.inspector.activeSidebar");
 });
 
 registerCleanupFunction(function() {
   // Move the mouse outside inspector. If the test happened fake a mouse event
   // somewhere over inspector the pointer is considered to be there when the
   // next test begins. This might cause unexpected events to be emitted when
--- a/devtools/client/memory/test/chrome/head.js
+++ b/devtools/client/memory/test/chrome/head.js
@@ -18,18 +18,16 @@ SimpleTest.registerCleanupFunction(funct
     ok(false, "Should have had the expected number of DevToolsUtils.assert() failures." +
       "Expected " + EXPECTED_DTU_ASSERT_FAILURE_COUNT +
       ", got " + DevToolsUtils.assertionFailureCount);
   }
 });
 
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
 var { immutableUpdate } = DevToolsUtils;
-var flags = require("devtools/shared/flags");
-flags.testing = true;
 
 var constants = require("devtools/client/memory/constants");
 var {
   censusDisplays,
   diffingState,
   labelDisplays,
   dominatorTreeState,
   snapshotState,
--- a/devtools/client/memory/test/unit/head.js
+++ b/devtools/client/memory/test/unit/head.js
@@ -5,20 +5,23 @@
 
 // via xpcshell.ini
 /* import-globals-from ../../../shared/test/shared-redux-head.js */
 
 var { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
 
 var Services = require("Services");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
-var flags = require("devtools/shared/flags");
-flags.testing = true;
-flags.wantLogging = true;
-flags.wantVerbose = false;
+
+Services.prefs.setBoolPref("devtools.testing", true);
+Services.prefs.setBoolPref("devtools.debugger.log", true);
+registerCleanupFunction(() => {
+  Services.prefs.clearUserPref("devtools.testing");
+  Services.prefs.clearUserPref("devtools.debugger.log");
+});
 
 var { OS } = require("resource://gre/modules/osfile.jsm");
 var { FileUtils } = require("resource://gre/modules/FileUtils.jsm");
 var { TargetFactory } = require("devtools/client/framework/target");
 var promise = require("promise");
 var defer = require("devtools/shared/defer");
 var { expectState } = require("devtools/server/actors/common");
 var HeapSnapshotFileUtils = require("devtools/shared/heapsnapshot/HeapSnapshotFileUtils");
--- a/devtools/client/netmonitor/src/utils/firefox/open-request-in-tab.js
+++ b/devtools/client/netmonitor/src/utils/firefox/open-request-in-tab.js
@@ -1,13 +1,22 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 /* eslint-disable mozilla/reject-some-requires */
 
+// This file is a chrome-API-dependent version of the module
+// devtools/client/netmonitor/src/utils/open-request-in-tab.js, so that it can
+// take advantage of utilizing chrome APIs. But because of this, it isn't
+// intended to be used in Chrome-API-free applications, such as the Launchpad.
+//
+// Please keep in mind that if the feature in this file has changed, don't
+// forget to also change that accordingly in
+// devtools/client/netmonitor/src/utils/open-request-in-tab.js.
+
 "use strict";
 
 let { Cc, Ci } = require("chrome");
 const Services = require("Services");
 const { gDevTools } = require("devtools/client/framework/devtools");
 
 /**
  * Opens given request in a new tab.
--- a/devtools/client/netmonitor/src/utils/open-request-in-tab.js
+++ b/devtools/client/netmonitor/src/utils/open-request-in-tab.js
@@ -1,12 +1,22 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+// This file is a chrome-API-free version of the module
+// devtools/client/netmonitor/src/utils/firefox/open-request-in-tab.js, so that
+// it can be used in Chrome-API-free applications, such as the Launchpad. But
+// because of this, it cannot take advantage of utilizing chrome APIs and should
+// implement the similar functionalities on its own.
+//
+// Please keep in mind that if the feature in this file has changed, don't
+// forget to also change that accordingly in
+// devtools/client/netmonitor/src/utils/firefox/open-request-in-tab.js.
+
 "use strict";
 
 const Services = require("Services");
 const { gDevTools } = require("devtools/client/framework/devtools");
 
 /**
  * Opens given request in a new tab.
  */
--- a/devtools/client/netmonitor/src/utils/request-utils.js
+++ b/devtools/client/netmonitor/src/utils/request-utils.js
@@ -1,16 +1,19 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* eslint-disable mozilla/reject-some-requires */
 
 "use strict";
 
+const { getUnicodeUrl, getUnicodeUrlPath, getUnicodeHostname } =
+  require("devtools/client/shared/unicode-url");
+
 const {
   UPDATE_PROPS,
 } = require("devtools/client/netmonitor/src/constants");
 
 const CONTENT_MIME_TYPE_ABBREVIATIONS = {
   "ecmascript": "js",
   "javascript": "js",
   "x-javascript": "js"
@@ -116,32 +119,16 @@ function formDataURI(mimeType, encoding,
  * @param {array} headers - array of headers info { name, value }
  * @return {string} list of headers in text format
  */
 function writeHeaderText(headers) {
   return headers.map(({name, value}) => name + ": " + value).join("\n");
 }
 
 /**
- * Convert a string into unicode if string is valid.
- * If there is a malformed URI sequence, it returns input string.
- *
- * @param {string} url - a string
- * @return {string} unicode string
- */
-function decodeUnicodeUrl(string) {
-  try {
-    return decodeURIComponent(string);
-  } catch (err) {
-    // Ignore error and return input string directly.
-  }
-  return string;
-}
-
-/**
  * Decode base64 string.
  *
  * @param {string} url - a string
  * @return {string} decoded string
  */
 function decodeUnicodeBase64(string) {
   try {
     return decodeURIComponent(atob(string));
@@ -170,17 +157,17 @@ function getAbbreviatedMimeType(mimeType
  * For example helper returns "basename" from http://domain.com/path/basename
  * If basename portion is empty, it returns the url pathname.
  *
  * @param {string} url - url string
  * @return {string} unicode basename of a url
  */
 function getUrlBaseName(url) {
   const pathname = (new URL(url)).pathname;
-  return decodeUnicodeUrl(
+  return getUnicodeUrlPath(
     pathname.replace(/\S*\//, "") || pathname || "/");
 }
 
 /**
  * Helpers for getting the query portion of a url.
  *
  * @param {string} url - url string
  * @return {string} unicode query of a url
@@ -191,37 +178,37 @@ function getUrlQuery(url) {
 
 /**
  * Helpers for getting unicode name and query portions of a url.
  *
  * @param {string} url - url string
  * @return {string} unicode basename and query portions of a url
  */
 function getUrlBaseNameWithQuery(url) {
-  return getUrlBaseName(url) + decodeUnicodeUrl((new URL(url)).search);
+  return getUrlBaseName(url) + getUnicodeUrlPath((new URL(url)).search);
 }
 
 /**
- * Helpers for getting unicode hostname portion of an URL.
+ * Helpers for getting hostname portion of an URL.
  *
  * @param {string} url - url string
  * @return {string} unicode hostname of a url
  */
 function getUrlHostName(url) {
-  return decodeUnicodeUrl((new URL(url)).hostname);
+  return new URL(url).hostname;
 }
 
 /**
- * Helpers for getting unicode host portion of an URL.
+ * Helpers for getting host portion of an URL.
  *
  * @param {string} url - url string
  * @return {string} unicode host of a url
  */
 function getUrlHost(url) {
-  return decodeUnicodeUrl((new URL(url)).host);
+  return new URL(url).host;
 }
 
 /**
  * Helpers for getting the shceme portion of a url.
  * For example helper returns "http" from http://domain.com/path/basename
  *
  * @param {string} url - url string
  * @return {string} string scheme of a url
@@ -232,19 +219,32 @@ function getUrlScheme(url) {
 
 /**
  * Extract several details fields from a URL at once.
  */
 function getUrlDetails(url) {
   let baseNameWithQuery = getUrlBaseNameWithQuery(url);
   let host = getUrlHost(url);
   let hostname = getUrlHostName(url);
-  let unicodeUrl = decodeUnicodeUrl(url);
+  let unicodeUrl = getUnicodeUrl(url);
   let scheme = getUrlScheme(url);
 
+  // If the hostname contains unreadable ASCII characters, we need to do the
+  // following two steps:
+  // 1. Converting the unreadable hostname to a readable Unicode domain name.
+  //    For example, converting xn--g6w.xn--8pv into a Unicode domain name.
+  // 2. Replacing the unreadable hostname portion in the `host` with the
+  //    readable hostname.
+  //    For example, replacing xn--g6w.xn--8pv:8000 with [Unicode domain]:8000
+  // After finishing the two steps, we get a readable `host`.
+  const unicodeHostname = getUnicodeHostname(hostname);
+  if (unicodeHostname !== hostname) {
+    host = host.replace(hostname, unicodeHostname);
+  }
+
   // Mark local hosts specially, where "local" is  as defined in the W3C
   // spec for secure contexts.
   // http://www.w3.org/TR/powerful-features/
   //
   //  * If the name falls under 'localhost'
   //  * If the name is an IPv4 address within 127.0.0.0/8
   //  * If the name is an IPv6 address within ::1/128
   //
@@ -272,18 +272,18 @@ function getUrlDetails(url) {
 function parseQueryString(query) {
   if (!query) {
     return null;
   }
 
   return query.replace(/^[?&]/, "").split("&").map(e => {
     let param = e.split("=");
     return {
-      name: param[0] ? decodeUnicodeUrl(param[0]) : "",
-      value: param[1] ? decodeUnicodeUrl(param[1]) : "",
+      name: param[0] ? getUnicodeUrlPath(param[0]) : "",
+      value: param[1] ? getUnicodeUrlPath(param[1]) : "",
     };
   });
 }
 
 /**
  * Parse a string of formdata sections into its components
  *
  * @param {string} sections - sections of formdata joined by &
@@ -292,18 +292,18 @@ function parseQueryString(query) {
 function parseFormData(sections) {
   if (!sections) {
     return null;
   }
 
   return sections.replace(/^&/, "").split("&").map(e => {
     let param = e.split("=");
     return {
-      name: param[0] ? decodeUnicodeUrl(param[0]) : "",
-      value: param[1] ? decodeUnicodeUrl(param[1]) : "",
+      name: param[0] ? getUnicodeUrlPath(param[0]) : "",
+      value: param[1] ? getUnicodeUrlPath(param[1]) : "",
     };
   });
 }
 
 /**
  * Reduces an IP address into a number for easier sorting
  *
  * @param {string} ip - IP address to reduce
@@ -486,17 +486,16 @@ function processNetworkUpdates(request =
 
 module.exports = {
   decodeUnicodeBase64,
   getFormDataSections,
   fetchHeaders,
   fetchNetworkUpdatePacket,
   formDataURI,
   writeHeaderText,
-  decodeUnicodeUrl,
   getAbbreviatedMimeType,
   getEndTime,
   getFormattedProtocol,
   getResponseHeader,
   getResponseTime,
   getStartTime,
   getUrlBaseName,
   getUrlBaseNameWithQuery,
--- a/devtools/client/netmonitor/test/head.js
+++ b/devtools/client/netmonitor/test/head.js
@@ -13,18 +13,18 @@
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/shared/test/shared-head.js",
   this);
 
 const {
   getFormattedIPAndPort,
   getFormattedTime,
 } = require("devtools/client/netmonitor/src/utils/format-utils");
+const { getUnicodeUrl } = require("devtools/client/shared/unicode-url");
 const {
-  decodeUnicodeUrl,
   getFormattedProtocol,
   getUrlBaseName,
   getUrlHost,
   getUrlQuery,
   getUrlScheme,
 } = require("devtools/client/netmonitor/src/utils/request-utils");
 const { EVENTS } = require("devtools/client/netmonitor/src/constants");
 
@@ -405,17 +405,17 @@ function verifyRequestItemTarget(documen
 
   info("Visible index of item: " + visibleIndex);
 
   let { fuzzyUrl, status, statusText, cause, type, fullMimeType,
         transferred, size, time, displayedStatus } = data;
 
   let target = document.querySelectorAll(".request-list-item")[visibleIndex];
   // Bug 1414981 - Request URL should not show #hash
-  let unicodeUrl = decodeUnicodeUrl(url).split("#")[0];
+  let unicodeUrl = getUnicodeUrl(url.split("#")[0]);
   let name = getUrlBaseName(url);
   let query = getUrlQuery(url);
   let host = getUrlHost(url);
   let scheme = getUrlScheme(url);
   let {
     remoteAddress,
     remotePort,
     totalTime,
--- a/devtools/client/netmonitor/webpack.config.js
+++ b/devtools/client/netmonitor/webpack.config.js
@@ -83,16 +83,17 @@ let webpackConfig = {
       "devtools/client/shared/vendor/jszip": "jszip",
 
       "devtools/client/sourceeditor/editor": "devtools-source-editor/src/source-editor",
 
       "devtools/shared/event-emitter": "devtools-modules/src/utils/event-emitter",
       "devtools/shared/fronts/timeline": path.join(__dirname, "../../client/shared/webpack/shims/fronts-timeline-shim"),
       "devtools/shared/platform/clipboard": path.join(__dirname, "../../client/shared/webpack/shims/platform-clipboard-stub"),
       "devtools/client/netmonitor/src/utils/firefox/open-request-in-tab": path.join(__dirname, "src/utils/open-request-in-tab"),
+      "devtools/client/shared/unicode-url": path.join(__dirname, "../../client/shared/webpack/shims/unicode-url-stub"),
 
       // Locales need to be explicitly mapped to the en-US subfolder
       "devtools/client/locales": path.join(__dirname, "../../client/locales/en-US"),
       "devtools/shared/locales": path.join(__dirname, "../../shared/locales/en-US"),
       "devtools/startup/locales": path.join(__dirname, "../../shared/locales/en-US"),
       "toolkit/locales": path.join(__dirname, "../../../toolkit/locales/en-US"),
 
       // Unless a path explicitly needs to be rewritten or shimmed, all devtools paths can
--- a/devtools/client/performance-new/test/chrome/head.js
+++ b/devtools/client/performance-new/test/chrome/head.js
@@ -10,19 +10,17 @@ const { BrowserLoader } = ChromeUtils.im
 var { require } = BrowserLoader({
   baseURI: "resource://devtools/client/performance-new/",
   window
 });
 
 const EventEmitter = require("devtools/shared/event-emitter");
 const { perfDescription } = require("devtools/shared/specs/perf");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
-const flags = require("devtools/shared/flags");
 
-flags.testing = true;
 let EXPECTED_DTU_ASSERT_FAILURE_COUNT = 0;
 SimpleTest.registerCleanupFunction(function() {
   if (DevToolsUtils.assertionFailureCount !== EXPECTED_DTU_ASSERT_FAILURE_COUNT) {
     ok(false, "Should have had the expected number of DevToolsUtils.assert() failures." +
       "Expected " + EXPECTED_DTU_ASSERT_FAILURE_COUNT +
       ", got " + DevToolsUtils.assertionFailureCount);
   }
 });
--- a/devtools/client/performance/components/test/head.js
+++ b/devtools/client/performance/components/test/head.js
@@ -9,17 +9,16 @@
 let { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
 let { Assert } = require("resource://testing-common/Assert.jsm");
 let { BrowserLoader } = ChromeUtils.import("resource://devtools/client/shared/browser-loader.js", {});
 let defer = require("devtools/shared/defer");
 let flags = require("devtools/shared/flags");
 let { TargetFactory } = require("devtools/client/framework/target");
 let { Toolbox } = require("devtools/client/framework/toolbox");
 
-flags.testing = true;
 let { require: browserRequire } = BrowserLoader({
   baseURI: "resource://devtools/client/performance/",
   window
 });
 
 let $ = (selector, scope = document) => scope.querySelector(selector);
 let $$ = (selector, scope = document) => scope.querySelectorAll(selector);
 
--- a/devtools/client/performance/test/head.js
+++ b/devtools/client/performance/test/head.js
@@ -47,36 +47,32 @@ const rightMousedown = (node, win = wind
 
 // Shortcut for firing a key event, like "VK_UP", "VK_DOWN", etc.
 const key = (id, win = window) => {
   EventUtils.synthesizeKey(id, {}, win);
 };
 
 // Don't pollute global scope.
 (() => {
-  const flags = require("devtools/shared/flags");
   const PrefUtils = require("devtools/client/performance/test/helpers/prefs");
 
-  flags.testing = true;
-
   // Make sure all the prefs are reverted to their defaults once tests finish.
   let stopObservingPrefs = PrefUtils.whenUnknownPrefChanged("devtools.performance",
     pref => {
       ok(false, `Unknown pref changed: ${pref}. Please add it to test/helpers/prefs.js ` +
         "to make sure it's reverted to its default value when the tests finishes, " +
         "and avoid interfering with future tests.\n");
     });
 
   // By default, enable memory flame graphs for tests for now.
   // TODO: remove when we have flame charts via bug 1148663.
   Services.prefs.setBoolPref(PrefUtils.UI_ENABLE_MEMORY_FLAME_CHART, true);
 
   registerCleanupFunction(() => {
     info("finish() was called, cleaning up...");
-    flags.testing = false;
 
     PrefUtils.rollbackPrefsToDefault();
     stopObservingPrefs();
 
     // Manually stop the profiler module at the end of all tests, to hopefully
     // avoid at least some leaks on OSX. Theoretically the module should never
     // be active at this point. We shouldn't have to do this, but rather
     // find and fix the leak in the module itself. Bug 1257439.
--- a/devtools/client/responsive.html/test/browser/head.js
+++ b/devtools/client/responsive.html/test/browser/head.js
@@ -43,24 +43,22 @@ const { addDevice, removeDevice, removeL
 SimpleTest.requestCompleteLog();
 SimpleTest.waitForExplicitFinish();
 
 // Toggling the RDM UI involves several docShell swap operations, which are somewhat slow
 // on debug builds. Usually we are just barely over the limit, so a blanket factor of 2
 // should be enough.
 requestLongerTimeout(2);
 
-flags.testing = true;
 Services.prefs.setCharPref("devtools.devices.url", TEST_URI_ROOT + "devices.json");
 // The appearance of this notification causes intermittent behavior in some tests that
 // send mouse events, since it causes the content to shift when it appears.
 Services.prefs.setBoolPref("devtools.responsive.reloadNotification.enabled", false);
 
 registerCleanupFunction(async () => {
-  flags.testing = false;
   Services.prefs.clearUserPref("devtools.devices.url");
   Services.prefs.clearUserPref("devtools.responsive.reloadNotification.enabled");
   Services.prefs.clearUserPref("devtools.responsive.html.displayedDeviceList");
   Services.prefs.clearUserPref("devtools.responsive.reloadConditions.touchSimulation");
   Services.prefs.clearUserPref("devtools.responsive.reloadConditions.userAgent");
   await asyncStorage.removeItem("devtools.devices.url_cache");
   await removeLocalDevices();
 });
--- a/devtools/client/responsive.html/test/unit/head.js
+++ b/devtools/client/responsive.html/test/unit/head.js
@@ -3,17 +3,17 @@
 
 "use strict";
 
 /* eslint no-unused-vars: [2, {"vars": "local"}] */
 
 const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
 
 const promise = require("promise");
+const Services = require("Services");
 const Store = require("devtools/client/responsive.html/store");
 
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 
-const flags = require("devtools/shared/flags");
-flags.testing = true;
+Services.prefs.setBoolPref("devtools.testing", true);
 registerCleanupFunction(() => {
-  flags.testing = false;
+  Services.prefs.clearUserPref("devtools.testing");
 });
--- a/devtools/client/scratchpad/test/head.js
+++ b/devtools/client/scratchpad/test/head.js
@@ -6,27 +6,21 @@
 
 const {NetUtil} = ChromeUtils.import("resource://gre/modules/NetUtil.jsm", {});
 const {FileUtils} = ChromeUtils.import("resource://gre/modules/FileUtils.jsm", {});
 const {ScratchpadManager} = ChromeUtils.import("resource://devtools/client/scratchpad/scratchpad-manager.jsm", {});
 const {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
 const {gDevTools} = require("devtools/client/framework/devtools");
 const Services = require("Services");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
-const flags = require("devtools/shared/flags");
 const promise = require("promise");
 const defer = require("devtools/shared/defer");
 
 var gScratchpadWindow; // Reference to the Scratchpad chrome window object
 
-flags.testing = true;
-registerCleanupFunction(() => {
-  flags.testing = false;
-});
-
 /**
  * Open a Scratchpad window.
  *
  * @param function aReadyCallback
  *        Optional. The function you want invoked when the Scratchpad instance
  *        is ready.
  * @param object aOptions
  *        Optional. Options for opening the scratchpad:
--- a/devtools/client/shared/components/Frame.js
+++ b/devtools/client/shared/components/Frame.js
@@ -2,16 +2,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { Component } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+const { getUnicodeUrl, getUnicodeUrlPath, getUnicodeHostname } =
+  require("devtools/client/shared/unicode-url");
 const { getSourceNames, parseURL, isScratchpadScheme, getSourceMappedFile } =
   require("devtools/client/shared/source-utils");
 const { LocalizationHelper } = require("devtools/shared/l10n");
 
 const l10n = new LocalizationHelper("devtools/client/locales/components.properties");
 const webl10n = new LocalizationHelper("devtools/client/locales/webconsole.properties");
 
 class Frame extends Component {
@@ -128,28 +130,32 @@ class Frame extends Component {
     // What's needed is only the last part after " -> ".
     let source = frame.source
       ? String(frame.source).split(" -> ").pop()
       : "";
     let line = frame.line != void 0 ? Number(frame.line) : null;
     let column = frame.column != void 0 ? Number(frame.column) : null;
 
     const { short, long, host } = getSourceNames(source);
+    const unicodeShort = getUnicodeUrlPath(short);
+    const unicodeLong  = getUnicodeUrl(long);
+    const unicodeHost  = host ? getUnicodeHostname(host) : "";
+
     // Reparse the URL to determine if we should link this; `getSourceNames`
     // has already cached this indirectly. We don't want to attempt to
     // link to "self-hosted" and "(unknown)". However, we do want to link
     // to Scratchpad URIs.
     // Source mapped sources might not necessary linkable, but they
     // are still valid in the debugger.
     const isLinkable = !!(isScratchpadScheme(source) || parseURL(source))
       || isSourceMapped;
     const elements = [];
     const sourceElements = [];
     let sourceEl;
-    let tooltip = long;
+    let tooltip = unicodeLong;
 
     // Exclude all falsy values, including `0`, as line numbers start with 1.
     if (line) {
       tooltip += `:${line}`;
       // Intentionally exclude 0
       if (column) {
         tooltip += `:${column}`;
       }
@@ -172,17 +178,17 @@ class Frame extends Component {
             key: "function-display-name",
             className: "frame-link-function-display-name",
           }, functionDisplayName),
           " "
         );
       }
     }
 
-    let displaySource = showFullSourceUrl ? long : short;
+    let displaySource = showFullSourceUrl ? unicodeLong : unicodeShort;
     if (isSourceMapped) {
       displaySource = getSourceMappedFile(displaySource);
     } else if (showEmptyPathAsHost && (displaySource === "" || displaySource === "/")) {
       displaySource = host;
     }
 
     sourceElements.push(dom.span({
       key: "filename",
@@ -233,21 +239,21 @@ class Frame extends Component {
     } else {
       sourceEl = dom.span({
         key: "source",
         className: "frame-link-source",
       }, sourceInnerEl);
     }
     elements.push(sourceEl);
 
-    if (showHost && host) {
+    if (showHost && unicodeHost) {
       elements.push(" ");
       elements.push(dom.span({
         key: "host",
         className: "frame-link-host",
-      }, host));
+      }, unicodeHost));
     }
 
     return dom.span(attributes, ...elements);
   }
 }
 
 module.exports = Frame;
--- a/devtools/client/shared/components/test/mochitest/head.js
+++ b/devtools/client/shared/components/test/mochitest/head.js
@@ -10,21 +10,19 @@ var { Assert } = require("resource://tes
 var { gDevTools } = require("devtools/client/framework/devtools");
 var { BrowserLoader } = ChromeUtils.import("resource://devtools/client/shared/browser-loader.js", {});
 var promise = require("promise");
 var defer = require("devtools/shared/defer");
 var Services = require("Services");
 var { DebuggerServer } = require("devtools/server/main");
 var { DebuggerClient } = require("devtools/shared/client/debugger-client");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
-var flags = require("devtools/shared/flags");
 var { TargetFactory } = require("devtools/client/framework/target");
 var { Toolbox } = require("devtools/client/framework/toolbox");
 
-flags.testing = true;
 var { require: browserRequire } = BrowserLoader({
   baseURI: "resource://devtools/client/shared/",
   window
 });
 
 const React = browserRequire("devtools/client/shared/vendor/react");
 const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
 const dom = browserRequire("devtools/client/shared/vendor/react-dom-factories");
--- a/devtools/client/shared/moz.build
+++ b/devtools/client/shared/moz.build
@@ -48,16 +48,17 @@ DevToolsModules(
     'scroll.js',
     'source-utils.js',
     'SplitView.jsm',
     'stylesheet-utils.js',
     'suggestion-picker.js',
     'telemetry.js',
     'theme.js',
     'undo.js',
+    'unicode-url.js',
     'view-source.js',
     'webgl-utils.js',
     'zoom-keys.js',
 )
 
 with Files('**'):
     BUG_COMPONENT = ('Firefox', 'Developer Tools')
 
--- a/devtools/client/shared/redux/middleware/test/head.js
+++ b/devtools/client/shared/redux/middleware/test/head.js
@@ -2,19 +2,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* exported waitUntilState */
 
 "use strict";
 
 const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
-const flags = require("devtools/shared/flags");
-
-flags.testing = true;
 
 function waitUntilState(store, predicate) {
   return new Promise(resolve => {
     let unsubscribe = store.subscribe(check);
     function check() {
       if (predicate(store.getState())) {
         unsubscribe();
         resolve();
--- a/devtools/client/shared/test/browser_telemetry_button_responsive.js
+++ b/devtools/client/shared/test/browser_telemetry_button_responsive.js
@@ -12,23 +12,23 @@ const TOOL_DELAY = 200;
 
 const asyncStorage = require("devtools/shared/async-storage");
 
 // Toggling the RDM UI involves several docShell swap operations, which are somewhat slow
 // on debug builds. Usually we are just barely over the limit, so a blanket factor of 2
 // should be enough.
 requestLongerTimeout(2);
 
-flags.testing = true;
+Services.prefs.setBoolPref("devtools.testing", true);
 Services.prefs.clearUserPref("devtools.responsive.html.displayedDeviceList");
 Services.prefs.setCharPref("devtools.devices.url",
   "http://example.com/browser/devtools/client/responsive.html/test/browser/devices.json");
 
 registerCleanupFunction(() => {
-  flags.testing = false;
+  Services.prefs.clearUserPref("devtools.testing");
   Services.prefs.clearUserPref("devtools.devices.url");
   Services.prefs.clearUserPref("devtools.responsive.html.displayedDeviceList");
   asyncStorage.removeItem("devtools.devices.url_cache");
   asyncStorage.removeItem("devtools.devices.local");
 });
 
 loader.lazyRequireGetter(this, "ResponsiveUIManager", "devtools/client/responsive.html/manager", true);
 
--- a/devtools/client/shared/test/shared-head.js
+++ b/devtools/client/shared/test/shared-head.js
@@ -20,17 +20,16 @@ function scopedCuImport(path) {
 }
 
 const {ScratchpadManager} = scopedCuImport("resource://devtools/client/scratchpad/scratchpad-manager.jsm");
 const {loader, require} = scopedCuImport("resource://devtools/shared/Loader.jsm");
 
 const {gDevTools} = require("devtools/client/framework/devtools");
 const {TargetFactory} = require("devtools/client/framework/target");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
-const flags = require("devtools/shared/flags");
 let promise = require("promise");
 let defer = require("devtools/shared/defer");
 const Services = require("Services");
 const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
 
 const TEST_DIR = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
 const CHROME_URL_ROOT = TEST_DIR + "/";
 const URL_ROOT = CHROME_URL_ROOT.replace("chrome://mochitests/content/",
@@ -104,19 +103,17 @@ function loadFrameScriptUtils(browser = 
   info("Loading the helper frame script " + frameURL);
   mm.loadFrameScript(frameURL, false);
   SimpleTest.registerCleanupFunction(() => {
     mm = null;
   });
   return mm;
 }
 
-flags.testing = true;
 registerCleanupFunction(() => {
-  flags.testing = false;
   Services.prefs.clearUserPref("devtools.dump.emit");
   Services.prefs.clearUserPref("devtools.toolbox.host");
   Services.prefs.clearUserPref("devtools.toolbox.previousHost");
   Services.prefs.clearUserPref("devtools.toolbox.splitconsoleEnabled");
   Services.prefs.clearUserPref("devtools.toolbox.splitconsoleHeight");
 });
 
 registerCleanupFunction(async function cleanup() {
new file mode 100644
--- /dev/null
+++ b/devtools/client/shared/test/unit/test_unicode-url.js
@@ -0,0 +1,240 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * Tests utility functions contained in `unicode-url.js`
+ */
+
+const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
+const { getUnicodeUrl, getUnicodeUrlPath, getUnicodeHostname } =
+  require("devtools/client/shared/unicode-url");
+
+// List of URLs used to test Unicode URL conversion
+const TEST_URLS = [
+  // Type:     Readable ASCII URLs
+  // Expected: All of Unicode versions should equal to the raw.
+  {
+    raw: "https://example.org",
+    expectedUnicode: "https://example.org",
+  },
+  {
+    raw: "http://example.org",
+    expectedUnicode: "http://example.org",
+  },
+  {
+    raw: "ftp://example.org",
+    expectedUnicode: "ftp://example.org",
+  },
+  {
+    raw: "https://example.org.",
+    expectedUnicode: "https://example.org.",
+  },
+  {
+    raw: "https://example.org/",
+    expectedUnicode: "https://example.org/",
+  },
+  {
+    raw: "https://example.org/test",
+    expectedUnicode: "https://example.org/test",
+  },
+  {
+    raw: "https://example.org/test.html",
+    expectedUnicode: "https://example.org/test.html",
+  },
+  {
+    raw: "https://example.org/test.html?one=1&two=2",
+    expectedUnicode: "https://example.org/test.html?one=1&two=2",
+  },
+  {
+    raw: "https://example.org/test.html#here",
+    expectedUnicode: "https://example.org/test.html#here",
+  },
+  {
+    raw: "https://example.org/test.html?one=1&two=2#here",
+    expectedUnicode: "https://example.org/test.html?one=1&two=2#here",
+  },
+  // Type:     Unreadable URLs with either Punycode domain names or URI-encoded
+  //           paths
+  // Expected: Unreadable domain names and URI-encoded paths should be converted
+  //           to readable Unicode.
+  {
+    raw: "https://xn--g6w.xn--8pv/test.html",
+    // Do not type Unicode characters directly, because this test file isn't
+    // specified with a known encoding.
+    expectedUnicode: "https://\u6e2c.\u672c/test.html",
+  },
+  {
+    raw: "https://example.org/%E6%B8%AC%E8%A9%A6.html",
+    // Do not type Unicode characters directly, because this test file isn't
+    // specified with a known encoding.
+    expectedUnicode: "https://example.org/\u6e2c\u8a66.html",
+  },
+  {
+    raw: "https://example.org/test.html?One=%E4%B8%80",
+    // Do not type Unicode characters directly, because this test file isn't
+    // specified with a known encoding.
+    expectedUnicode: "https://example.org/test.html?One=\u4e00",
+  },
+  {
+    raw: "https://example.org/test.html?%E4%B8%80=1",
+    // Do not type Unicode characters directly, because this test file isn't
+    // specified with a known encoding.
+    expectedUnicode: "https://example.org/test.html?\u4e00=1",
+  },
+  {
+    raw: "https://xn--g6w.xn--8pv/%E6%B8%AC%E8%A9%A6.html" +
+         "?%E4%B8%80=%E4%B8%80" +
+         "#%E6%AD%A4",
+    // Do not type Unicode characters directly, because this test file isn't
+    // specified with a known encoding.
+    expectedUnicode: "https://\u6e2c.\u672c/\u6e2c\u8a66.html" +
+                     "?\u4e00=\u4e00" +
+                     "#\u6b64",
+  },
+  // Type:     data: URIs
+  // Expected: All should not be converted.
+  {
+    raw: "data:text/plain;charset=UTF-8;Hello%20world",
+    expectedUnicode: "data:text/plain;charset=UTF-8;Hello%20world",
+  },
+  {
+    raw: "data:text/plain;charset=UTF-8;%E6%B8%AC%20%E8%A9%A6",
+    expectedUnicode: "data:text/plain;charset=UTF-8;%E6%B8%AC%20%E8%A9%A6",
+  },
+  {
+    raw: "data:image/png;base64,iVBORw0KGgoAAA" +
+         "ANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4" +
+         "//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU" +
+         "5ErkJggg==",
+    expectedUnicode: "data:image/png;base64,iVBORw0KGgoAAA" +
+                     "ANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4" +
+                     "//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU" +
+                     "5ErkJggg==",
+  },
+  // Type:     Malformed URLs
+  // Expected: All should not be converted.
+  {
+    raw: "://example.org/test",
+    expectedUnicode: "://example.org/test",
+  },
+  {
+    raw: "://xn--g6w.xn--8pv/%E6%B8%AC%E8%A9%A6.html" +
+         "?%E4%B8%80=%E4%B8%80",
+    expectedUnicode: "://xn--g6w.xn--8pv/%E6%B8%AC%E8%A9%A6.html" +
+                     "?%E4%B8%80=%E4%B8%80",
+  },
+  {
+    // %E8%A9 isn't a valid UTF-8 code, so this URL is malformed.
+    raw: "https://xn--g6w.xn--8pv/%E6%B8%AC%E8%A9",
+    expectedUnicode: "https://xn--g6w.xn--8pv/%E6%B8%AC%E8%A9",
+  },
+];
+
+// List of hostanmes used to test Unicode hostname conversion
+const TEST_HOSTNAMES = [
+  // Type:     Readable ASCII hostnames
+  // Expected: All of Unicode versions should equal to the raw.
+  {
+    raw: "example",
+    expectedUnicode: "example",
+  },
+  {
+    raw: "example.org",
+    expectedUnicode: "example.org",
+  },
+  // Type:     Unreadable Punycode hostnames
+  // Expected: Punycode should be converted to readable Unicode.
+  {
+    raw: "xn--g6w",
+    // Do not type Unicode characters directly, because this test file isn't
+    // specified with a known encoding.
+    expectedUnicode: "\u6e2c",
+  },
+  {
+    raw: "xn--g6w.xn--8pv",
+    // Do not type Unicode characters directly, because this test file isn't
+    // specified with a known encoding.
+    expectedUnicode: "\u6e2c.\u672c",
+  },
+];
+
+// List of URL paths used to test Unicode URL path conversion
+const TEST_URL_PATHS = [
+  // Type:     Readable ASCII URL paths
+  // Expected: All of Unicode versions should equal to the raw.
+  {
+    raw: "test",
+    expectedUnicode: "test",
+  },
+  {
+    raw: "/",
+    expectedUnicode: "/",
+  },
+  {
+    raw: "/test",
+    expectedUnicode: "/test",
+  },
+  {
+    raw: "/test.html?one=1&two=2#here",
+    expectedUnicode: "/test.html?one=1&two=2#here",
+  },
+  // Type:     Unreadable URI-encoded URL paths
+  // Expected: URL paths should be converted to readable Unicode.
+  {
+    raw: "/%E6%B8%AC%E8%A9%A6",
+    // Do not type Unicode characters directly, because this test file isn't
+    // specified with a known encoding.
+    expectedUnicode: "/\u6e2c\u8a66",
+  },
+  {
+    raw: "/%E6%B8%AC%E8%A9%A6.html",
+    // Do not type Unicode characters directly, because this test file isn't
+    // specified with a known encoding.
+    expectedUnicode: "/\u6e2c\u8a66.html",
+  },
+  {
+    raw: "/%E6%B8%AC%E8%A9%A6.html" +
+         "?%E4%B8%80=%E4%B8%80&%E4%BA%8C=%E4%BA%8C" +
+         "#%E6%AD%A4",
+    // Do not type Unicode characters directly, because this test file isn't
+    // specified with a known encoding.
+    expectedUnicode: "/\u6e2c\u8a66.html" +
+                     "?\u4e00=\u4e00&\u4e8c=\u4e8c" +
+                     "#\u6b64",
+  },
+  // Type:     Malformed URL paths
+  // Expected: All should not be converted.
+  {
+    // %E8%A9 isn't a valid UTF-8 code, so this URL is malformed.
+    raw: "/%E6%B8%AC%E8%A9",
+    expectedUnicode: "/%E6%B8%AC%E8%A9",
+  },
+];
+
+function run_test() {
+  // Test URLs
+  for (let url of TEST_URLS) {
+    let result = getUnicodeUrl(url.raw);
+    equal(result, url.expectedUnicode,
+          "Test getUnicodeUrl: " + url.raw +
+            " should be unicodized to " + url.expectedUnicode);
+  }
+
+  // Test hostnames
+  for (let hostname of TEST_HOSTNAMES) {
+    let result = getUnicodeHostname(hostname.raw);
+    equal(result, hostname.expectedUnicode,
+          "Test getUnicodeHostname: " + hostname.raw +
+            " should be unicodized to " + hostname.expectedUnicode);
+  }
+
+  // Test URL paths
+  for (let urlPath of TEST_URL_PATHS) {
+    let result = getUnicodeUrlPath(urlPath.raw);
+    equal(result, urlPath.expectedUnicode,
+          "Test getUnicodeUrlPath: " + urlPath.raw +
+            " should be unicodized to " + urlPath.expectedUnicode);
+  }
+}
--- a/devtools/client/shared/test/unit/xpcshell.ini
+++ b/devtools/client/shared/test/unit/xpcshell.ini
@@ -20,10 +20,11 @@ support-files =
 [test_escapeCSSComment.js]
 [test_parseDeclarations.js]
 [test_parsePseudoClassesAndAttributes.js]
 [test_parseSingleValue.js]
 [test_rewriteDeclarations.js]
 [test_source-utils.js]
 [test_suggestion-picker.js]
 [test_undoStack.js]
+[test_unicode-url.js]
 [test_VariablesView_filtering-without-controller.js]
 [test_VariablesView_getString_promise.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/shared/unicode-url.js
@@ -0,0 +1,115 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+// This file is a chrome-API-dependent version of the module
+// devtools/client/shared/webpack/shims/unicode-url-stub.js, so that it can
+// take advantage of utilizing chrome APIs. But because of this, it isn't
+// intended to be used in Chrome-API-free applications, such as the Launchpad.
+//
+// Please keep in mind that if the feature in this file has changed, don't
+// forget to also change that accordingly in
+// devtools/client/shared/webpack/shims/unicode-url-stub.js.
+
+const { Cc, Ci } = require("chrome");
+const idnService =
+        Cc["@mozilla.org/network/idn-service;1"].getService(Ci.nsIIDNService);
+
+/**
+ * Gets a readble Unicode hostname from a hostname.
+ *
+ * If the `hostname` is a readable ASCII hostname, such as example.org, then
+ * this function will simply return the original `hostname`.
+ *
+ * If the `hostname` is a Punycode hostname representing a Unicode domain name,
+ * such as xn--g6w.xn--8pv, then this function will return the readable Unicode
+ * domain name by decoding the Punycode hostname.
+ *
+ * @param {string}  hostname
+ *                  the hostname from which the Unicode hostname will be
+ *                  parsed, such as example.org, xn--g6w.xn--8pv.
+ * @return {string} The Unicode hostname. It may be the same as the `hostname`
+ *                  passed to this function if the `hostname` itself is
+ *                  a readable ASCII hostname or a Unicode hostname.
+ */
+function getUnicodeHostname(hostname) {
+  return idnService.convertToDisplayIDN(hostname, {});
+}
+
+/**
+ * Gets a readble Unicode URL pathname from a URL pathname.
+ *
+ * If the `urlPath` is a readable ASCII URL pathname, such as /a/b/c.js, then
+ * this function will simply return the original `urlPath`.
+ *
+ * If the `urlPath` is a URI-encoded pathname, such as %E8%A9%A6/%E6%B8%AC.js,
+ * then this function will return the readable Unicode pathname.
+ *
+ * If the `urlPath` is a malformed URL pathname, then this function will simply
+ * return the original `urlPath`.
+ *
+ * @param {string}  urlPath
+ *                  the URL path from which the Unicode URL path will be parsed,
+ *                  such as /a/b/c.js, %E8%A9%A6/%E6%B8%AC.js.
+ * @return {string} The Unicode URL Path. It may be the same as the `urlPath`
+ *                  passed to this function if the `urlPath` itself is a readable
+ *                  ASCII url or a Unicode url.
+ */
+function getUnicodeUrlPath(urlPath) {
+  try {
+    return decodeURIComponent(urlPath);
+  } catch (err) {
+    dump("Warning: getUnicodeUrlPath failed to get a Unicode URL from" +
+            `${urlPath}, reason: ${err}`);
+  }
+  return urlPath;
+}
+
+/**
+ * Gets a readable Unicode URL from a URL.
+ *
+ * If the `url` is a readable ASCII URL, such as http://example.org/a/b/c.js,
+ * then this function will simply return the original `url`.
+ *
+ * If the `url` includes either an unreadable Punycode domain name or an
+ * unreadable URI-encoded pathname, such as
+ * http://xn--g6w.xn--8pv/%E8%A9%A6/%E6%B8%AC.js, then this function will return
+ * the readable URL by decoding all its unreadable URL components to Unicode
+ * characters.
+ *
+ * If the `url` is a malformed URL, then this function will return the original
+ * `url`.
+ *
+ * If the `url` is a data: URI, then this function will return the original
+ * `url`.
+ *
+ * @param {string}  url
+ *                  the full URL, or a data: URI. from which the readable URL
+ *                  will be parsed, such as, http://example.org/a/b/c.js,
+ *                  http://xn--g6w.xn--8pv/%E8%A9%A6/%E6%B8%AC.js
+ * @return {string} The readable URL. It may be the same as the `url` passed to
+ *                  this function if the `url` itself is readable.
+ */
+function getUnicodeUrl(url) {
+  try {
+    const { protocol, hostname } = new URL(url);
+    if (protocol === "data:") {
+      // Never convert a data: URI.
+      return url;
+    }
+    const readableHostname = getUnicodeHostname(hostname);
+    url = decodeURIComponent(url);
+    return url.replace(hostname, readableHostname);
+  } catch (err) {
+    dump("Warning: getUnicodeUrl failed to get a Unicode URL from" +
+            `${url}, reason: ${err}`);
+  }
+  return url;
+}
+
+module.exports = {
+  getUnicodeHostname,
+  getUnicodeUrlPath,
+  getUnicodeUrl,
+};
new file mode 100644
--- /dev/null
+++ b/devtools/client/shared/webpack/shims/unicode-url-stub.js
@@ -0,0 +1,32 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// TODO This file aims to implement a Chrome-API-free replacement for
+// devtools/client/shared/unicode-url.js, so that it can be used in the
+// Launchpad.
+//
+// Currently this is just a dummpy mock of
+// devtools/client/shared/unicode-url.js, no actual functionaly involved.
+// Eventually we'll want to implement it. Once implemented, we should keep the
+// feature the same as devtools/client/shared/unicode-url.js.
+
+"use strict";
+
+function getUnicodeHostname(hostname) {
+  return hostname;
+}
+
+function getUnicodeUrlPath(urlPath) {
+  return urlPath;
+}
+
+function getUnicodeUrl(url) {
+  return url;
+}
+
+module.exports = {
+  getUnicodeHostname,
+  getUnicodeUrlPath,
+  getUnicodeUrl,
+};
--- a/devtools/client/sourceeditor/test/head.js
+++ b/devtools/client/sourceeditor/test/head.js
@@ -11,21 +11,16 @@
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/shared/test/shared-head.js",
   this);
 
 const { NetUtil } = require("resource://gre/modules/NetUtil.jsm");
 const Editor = require("devtools/client/sourceeditor/editor");
 const {getClientCssProperties} = require("devtools/shared/fronts/css-properties");
 
-flags.testing = true;
-SimpleTest.registerCleanupFunction(() => {
-  flags.testing = false;
-});
-
 function promiseWaitForFocus() {
   return new Promise(resolve =>
     waitForFocus(resolve));
 }
 
 function setup(cb, additionalOpts = {}) {
   cb = cb || function() {};
   return new Promise(resolve => {
--- a/devtools/client/storage/ui.js
+++ b/devtools/client/storage/ui.js
@@ -5,16 +5,17 @@
 
 "use strict";
 
 const EventEmitter = require("devtools/shared/event-emitter");
 const {LocalizationHelper, ELLIPSIS} = require("devtools/shared/l10n");
 const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
 const JSOL = require("devtools/client/shared/vendor/jsol");
 const {KeyCodes} = require("devtools/client/shared/keycodes");
+const { getUnicodeHostname } = require("devtools/client/shared/unicode-url");
 
 // GUID to be used as a separator in compound keys. This must match the same
 // constant in devtools/server/actors/storage.js,
 // devtools/client/storage/test/head.js and
 // devtools/server/tests/browser/head.js
 const SEPARATOR_GUID = "{9d414cc5-8319-0a04-0586-c0a6ae01670a}";
 
 loader.lazyRequireGetter(this, "TreeWidget",
@@ -450,17 +451,18 @@ class StorageUI {
   /**
    * Handle added items received by onEdit
    *
    * @param {object} See onEdit docs
    */
   async handleAddedItems(added) {
     for (let type in added) {
       for (let host in added[type]) {
-        this.tree.add([type, {id: host, type: "url"}]);
+        const label = this.getReadableLabelFromHostname(host);
+        this.tree.add([type, {id: host, label: label, type: "url"}]);
         for (let name of added[type][host]) {
           try {
             name = JSON.parse(name);
             if (name.length == 3) {
               name.splice(2, 1);
             }
             this.tree.add([type, host, ...name]);
             if (!this.tree.selectedItem) {
@@ -667,17 +669,18 @@ class StorageUI {
         console.error("Unable to localize tree label type:" + type);
       }
       this.tree.add([{id: type, label: typeLabel, type: "store"}]);
       if (!storageTypes[type].hosts) {
         continue;
       }
       this.storageTypes[type] = storageTypes[type];
       for (let host in storageTypes[type].hosts) {
-        this.tree.add([type, {id: host, type: "url"}]);
+        const label = this.getReadableLabelFromHostname(host);
+        this.tree.add([type, {id: host, label: label, type: "url"}]);
         for (let name of storageTypes[type].hosts[host]) {
           try {
             let names = JSON.parse(name);
             this.tree.add([type, host, ...names]);
             if (!this.tree.selectedItem) {
               this.tree.selectedItem = [type, host, names[0], names[1]];
             }
           } catch (ex) {
@@ -768,23 +771,52 @@ class StorageUI {
         this.parseItemValue(key, item[key]);
       }
     }
 
     this.emit("sidebar-updated");
   }
 
   /**
+   * Gets a readable label from the hostname. If the hostname is a Punycode
+   * domain(I.e. an ASCII domain name representing a Unicode domain name), then
+   * this function decodes it to the readable Unicode domain name, and label
+   * the Unicode domain name toggether with the original domian name, and then
+   * return the label; if the hostname isn't a Punycode domain(I.e. it isn't
+   * encoded and is readable on its own), then this function simply returns the
+   * original hostname.
+   *
+   * @param {string} host
+   *        The string representing a host, e.g, example.com, example.com:8000
+   */
+  getReadableLabelFromHostname(host) {
+    try {
+      const { hostname } = new URL(host);
+      const unicodeHostname = getUnicodeHostname(hostname);
+      if (hostname !== unicodeHostname) {
+        // If the hostname is a Punycode domain representing a Unicode domain,
+        // we decode it to the Unicode domain name, and then label the Unicode
+        // domain name together with the original domain name.
+        return host.replace(hostname, unicodeHostname) + " [ " + host + " ]";
+      }
+    } catch (_) {
+      // Skip decoding for a host which doesn't include a domain name, simply
+      // consider them to be readable.
+    }
+    return host;
+  }
+
+  /**
    * Tries to parse a string value into either a json or a key-value separated
    * object and populates the sidebar with the parsed value. The value can also
    * be a key separated array.
    *
    * @param {string} name
    *        The key corresponding to the `value` string in the object
-   * @param {string} value
+   * @param {string} originalValue
    *        The string to be parsed into an object
    */
   parseItemValue(name, originalValue) {
     // Find if value is URLEncoded ie
     let decodedValue = "";
     try {
       decodedValue = decodeURIComponent(originalValue);
     } catch (e) {
@@ -876,18 +908,16 @@ class StorageUI {
     }
     return null;
   }
 
   /**
    * Select handler for the storage tree. Fetches details of the selected item
    * from the storage details and populates the storage tree.
    *
-   * @param {string} event
-   *        The name of the event fired
    * @param {array} item
    *        An array of ids which represent the location of the selected item in
    *        the storage tree
    */
   async onHostSelect(item) {
     this.table.clear();
     this.hideSidebar();
     this.searchBox.value = "";
--- a/devtools/client/webaudioeditor/controller.js
+++ b/devtools/client/webaudioeditor/controller.js
@@ -78,44 +78,44 @@ var WebAudioEditorController = {
     // early request to ensure the CallWatcherActor is running and watching for new window
     // globals.
     gFront.setup({ reload: false });
   },
 
   /**
    * Remove events emitted by the current tab target.
    */
-  destroy: function () {
+  destroy: function() {
     gTarget.off("will-navigate", this._onTabWillNavigate);
     gFront.off("start-context", this._onStartContext);
     gFront.off("create-node", this._onCreateNode);
     gFront.off("connect-node", this._onConnectNode);
     gFront.off("connect-param", this._onConnectParam);
     gFront.off("disconnect-node", this._onDisconnectNode);
     gFront.off("change-param", this._onChangeParam);
     gFront.off("destroy-node", this._onDestroyNode);
     this._prefObserver.off("devtools.theme", this._onThemeChange);
     this._prefObserver.destroy();
   },
 
   /**
    * Called when page is reloaded to show the reload notice and waiting
    * for an audio context notice.
    */
-  reset: function () {
+  reset: function() {
     $("#content").hidden = true;
     ContextView.resetUI();
     InspectorView.resetUI();
     PropertiesView.resetUI();
   },
 
   // Since node events (create, disconnect, connect) are all async,
   // we have to make sure to wait that the node has finished creating
   // before performing an operation on it.
-  getNode: async function (nodeActor) {
+  getNode: async function(nodeActor) {
     let id = nodeActor.actorID;
     let node = gAudioNodes.get(id);
 
     if (!node) {
       let { resolve, promise } = defer();
       gAudioNodes.on("add", function createNodeListener(createdNode) {
         if (createdNode.id === id) {
           gAudioNodes.off("add", createNodeListener);
@@ -127,17 +127,17 @@ var WebAudioEditorController = {
     return node;
   },
 
   /**
    * Fired when the devtools theme changes (light, dark, etc.)
    * so that the graph can update marker styling, as that
    * cannot currently be done with CSS.
    */
-  _onThemeChange: function () {
+  _onThemeChange: function() {
     let newValue = Services.prefs.getCharPref("devtools.theme");
     window.emit(EVENTS.THEME_CHANGE, newValue);
   },
 
   /**
    * Called for each location change in the debugged tab.
    */
   _onTabWillNavigate: function({isFrameSwitching}) {
@@ -162,36 +162,36 @@ var WebAudioEditorController = {
 
     window.emit(EVENTS.UI_RESET);
   },
 
   /**
    * Called after the first audio node is created in an audio context,
    * signaling that the audio context is being used.
    */
-  _onStartContext: function () {
+  _onStartContext: function() {
     $("#reload-notice").hidden = true;
     $("#waiting-notice").hidden = true;
     $("#content").hidden = false;
     window.emit(EVENTS.START_CONTEXT);
   },
 
   /**
    * Called when a new node is created. Creates an `AudioNodeView` instance
    * for tracking throughout the editor.
    */
-  _onCreateNode: function (nodeActor) {
+  _onCreateNode: function(nodeActor) {
     gAudioNodes.add(nodeActor);
   },
 
   /**
    * Called on `destroy-node` when an AudioNode is GC'd. Removes
    * from the AudioNode array and fires an event indicating the removal.
    */
-  _onDestroyNode: function (nodeActor) {
+  _onDestroyNode: function(nodeActor) {
     gAudioNodes.remove(gAudioNodes.get(nodeActor.actorID));
   },
 
   /**
    * Called when a node is connected to another node.
    */
   async _onConnectNode({ source: sourceActor, dest: destActor }) {
     let source = await WebAudioEditorController.getNode(sourceActor);
--- a/devtools/client/webaudioeditor/includes.js
+++ b/devtools/client/webaudioeditor/includes.js
@@ -79,18 +79,22 @@ var gToolbox, gTarget, gFront;
 /**
  * Convenient way of emitting events from the panel window.
  */
 EventEmitter.decorate(this);
 
 /**
  * DOM query helper.
  */
-function $(selector, target = document) { return target.querySelector(selector); }
-function $$(selector, target = document) { return target.querySelectorAll(selector); }
+function $(selector, target = document) {
+  return target.querySelector(selector);
+}
+function $$(selector, target = document) {
+  return target.querySelectorAll(selector);
+}
 
 /**
  * Takes an iterable collection, and a hash. Return the first
  * object in the collection that matches the values in the hash.
  * From Backbone.Collection#findWhere
  * http://backbonejs.org/#Collection-findWhere
  */
 function findWhere(collection, attrs) {
--- a/devtools/client/webaudioeditor/models.js
+++ b/devtools/client/webaudioeditor/models.js
@@ -136,17 +136,16 @@ class AudioNodeModel extends EventEmitte
     }
   }
 
   toString() {
     return "[object AudioNodeModel]";
   }
 }
 
-
 /**
  * Constructor for a Collection of `AudioNodeModel` models.
  *
  * Events:
  * - `add`: node
  * - `remove`: node
  * - `connect`: node, destinationNode, parameter
  * - `disconnect`: node
@@ -176,16 +175,17 @@ class AudioNodesCollection extends Event
    * instance.
    *
    * Emits "add" event on instance when completed.
    *
    * @param Object obj
    * @return AudioNodeModel
    */
   add(obj) {
+    // eslint-disable-next-line new-cap
     let node = new this.model(obj);
     node.collection = this;
 
     this.models.add(node);
 
     node.on("*", this._onModelEvent);
     EventEmitter.emit(this, "add", node);
     return node;
--- a/devtools/client/webaudioeditor/panel.js
+++ b/devtools/client/webaudioeditor/panel.js
@@ -15,17 +15,17 @@ function WebAudioEditorPanel(iframeWindo
   this._destroyer = null;
 
   EventEmitter.decorate(this);
 }
 
 exports.WebAudioEditorPanel = WebAudioEditorPanel;
 
 WebAudioEditorPanel.prototype = {
-  open: function () {
+  open: function() {
     let targetPromise;
 
     // Local debugging needs to make the target remote.
     if (!this.target.isRemote) {
       targetPromise = this.target.makeRemote();
     } else {
       targetPromise = Promise.resolve(this.target);
     }
@@ -50,17 +50,17 @@ WebAudioEditorPanel.prototype = {
   },
 
   // DevToolPanel API
 
   get target() {
     return this._toolbox.target;
   },
 
-  destroy: function () {
+  destroy: function() {
     // Make sure this panel is not already destroyed.
     if (this._destroyer) {
       return this._destroyer;
     }
 
     return this._destroyer = this.panelWin.shutdownWebAudioEditor().then(() => {
       // Destroy front to ensure packet handler is removed from client
       this.panelWin.gFront.destroy();
--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-add-automation-event.js
+++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-add-automation-event.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Test AudioNode#addAutomationEvent();
  */
 
-add_task(async function () {
+add_task(async function() {
   let { target, front } = await initBackend(SIMPLE_CONTEXT_URL);
   let [_, [destNode, oscNode, gainNode]] = await Promise.all([
     front.setup({ reload: true }),
     get3(front, "create-node")
   ]);
   let count = 0;
   let counter = () => count++;
   front.on("automation-event", counter);
--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-bypass.js
+++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-bypass.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Test AudioNode#bypass(), AudioNode#isBypassed()
  */
 
-add_task(async function () {
+add_task(async function() {
   let { target, front } = await initBackend(SIMPLE_CONTEXT_URL);
   let [_, [destNode, oscNode, gainNode]] = await Promise.all([
     front.setup({ reload: true }),
     get3(front, "create-node")
   ]);
 
   is((await gainNode.isBypassed()), false, "Nodes start off unbypassed.");
 
--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-bypassable.js
+++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-bypassable.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Test AudioNode#bypassable
  */
 
-add_task(async function () {
+add_task(async function() {
   let { target, front } = await initBackend(SIMPLE_NODES_URL);
   let [_, nodes] = await Promise.all([
     front.setup({ reload: true }),
     getN(front, "create-node", 14)
   ]);
 
   let actualBypassability = nodes.map(node => node.bypassable);
   let expectedBypassability = [
--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-connectnode-disconnect.js
+++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-connectnode-disconnect.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that AudioNodeActor#connectNode() and AudioNodeActor#disconnect() work.
  * Uses the editor front as the actors do not retain connect state.
  */
 
-add_task(async function () {
+add_task(async function() {
   let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, gAudioNodes } = panelWin;
 
   let events = Promise.all([
     get3(gFront, "create-node"),
     waitForGraphRendered(panelWin, 3, 2)
   ]);
--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-connectparam.js
+++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-connectparam.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that AudioNodeActor#connectParam() work.
  * Uses the editor front as the actors do not retain connect state.
  */
 
-add_task(async function () {
+add_task(async function() {
   let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, gAudioNodes } = panelWin;
 
   let events = Promise.all([
     get3(gFront, "create-node"),
     waitForGraphRendered(panelWin, 3, 2)
   ]);
--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-get-automation-data-01.js
+++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-get-automation-data-01.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Test AudioNode#addAutomationEvent() checking automation values, also using
  * a curve as the last event to check duration spread.
  */
 
-add_task(async function () {
+add_task(async function() {
   let { target, front } = await initBackend(SIMPLE_CONTEXT_URL);
   let [_, [destNode, oscNode, gainNode]] = await Promise.all([
     front.setup({ reload: true }),
     get3(front, "create-node")
   ]);
 
   let t0 = 0, t1 = 0.1, t2 = 0.2, t3 = 0.3, t4 = 0.4, t5 = 0.6, t6 = 0.7, t7 = 1;
   let curve = [-1, 0, 1];
--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-get-automation-data-02.js
+++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-get-automation-data-02.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Test AudioNode#addAutomationEvent() when automation series ends with
  * `setTargetAtTime`, which approaches its target to infinity.
  */
 
-add_task(async function () {
+add_task(async function() {
   let { target, front } = await initBackend(SIMPLE_CONTEXT_URL);
   let [_, [destNode, oscNode, gainNode]] = await Promise.all([
     front.setup({ reload: true }),
     get3(front, "create-node")
   ]);
 
   await oscNode.addAutomationEvent("frequency", "setValueAtTime", [300, 0.1]);
   await oscNode.addAutomationEvent("frequency", "linearRampToValueAtTime", [500, 0.4]);
--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-get-automation-data-03.js
+++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-get-automation-data-03.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Test that `cancelScheduledEvents` clears out events on and after
  * its argument.
  */
 
-add_task(async function () {
+add_task(async function() {
   let { target, front } = await initBackend(SIMPLE_CONTEXT_URL);
   let [_, [destNode, oscNode, gainNode]] = await Promise.all([
     front.setup({ reload: true }),
     get3(front, "create-node")
   ]);
 
   await oscNode.addAutomationEvent("frequency", "setValueAtTime", [300, 0]);
   await oscNode.addAutomationEvent("frequency", "linearRampToValueAtTime", [500, 0.9]);
--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-get-param-flags.js
+++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-get-param-flags.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Test AudioNode#getParamFlags()
  */
 
-add_task(async function () {
+add_task(async function() {
   let { target, front } = await initBackend(SIMPLE_NODES_URL);
   let [_, nodes] = await Promise.all([
     front.setup({ reload: true }),
     getN(front, "create-node", 15)
   ]);
 
   let allNodeParams = await Promise.all(nodes.map(node => node.getParams()));
   let nodeTypes = [
@@ -28,20 +28,18 @@ add_task(async function () {
     let params = allNodeParams[i];
 
     for (let {param, value, flags} of params) {
       let testFlags = await nodes[i].getParamFlags(param);
       ok(typeof testFlags === "object", type + " has flags from #getParamFlags(" + param + ")");
 
       if (param === "buffer") {
         is(flags.Buffer, true, "`buffer` params have Buffer flag");
-      }
-      else if (param === "bufferSize" || param === "frequencyBinCount") {
+      } else if (param === "bufferSize" || param === "frequencyBinCount") {
         is(flags.readonly, true, param + " is readonly");
-      }
-      else if (param === "curve") {
-        is(flags["Float32Array"], true, "`curve` param has Float32Array flag");
+      } else if (param === "curve") {
+        is(flags.Float32Array, true, "`curve` param has Float32Array flag");
       }
     }
   }
 
   await removeTab(target.tab);
 });
--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-get-params-01.js
+++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-get-params-01.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Test AudioNode#getParams()
  */
 
-add_task(async function () {
+add_task(async function() {
   let { target, front } = await initBackend(SIMPLE_NODES_URL);
   let [_, nodes] = await Promise.all([
     front.setup({ reload: true }),
     getN(front, "create-node", 15)
   ]);
 
   await loadFrameScriptUtils();
 
@@ -30,20 +30,18 @@ add_task(async function () {
 
     params.forEach(({param, value, flags}) => {
       ok(param in defaults[i], "expected parameter for " + type);
 
       ok(typeof flags === "object", type + " has a flags object");
 
       if (param === "buffer") {
         is(flags.Buffer, true, "`buffer` params have Buffer flag");
-      }
-      else if (param === "bufferSize" || param === "frequencyBinCount") {
+      } else if (param === "bufferSize" || param === "frequencyBinCount") {
         is(flags.readonly, true, param + " is readonly");
-      }
-      else if (param === "curve") {
-        is(flags["Float32Array"], true, "`curve` param has Float32Array flag");
+      } else if (param === "curve") {
+        is(flags.Float32Array, true, "`curve` param has Float32Array flag");
       }
     });
   });
 
   await removeTab(target.tab);
 });
--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-get-params-02.js
+++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-get-params-02.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that default properties are returned with the correct type
  * from the AudioNode actors.
  */
 
-add_task(async function () {
+add_task(async function() {
   let { target, front } = await initBackend(SIMPLE_NODES_URL);
   let [_, nodes] = await Promise.all([
     front.setup({ reload: true }),
     getN(front, "create-node", 15)
   ]);
 
   await loadFrameScriptUtils();
 
@@ -34,18 +34,17 @@ add_task(async function () {
   await removeTab(target.tab);
 });
 
 function compare(actual, expected, type) {
   actual.forEach(({ value, param }) => {
     value = getGripValue(value);
     if (typeof expected[param] === "function") {
       ok(expected[param](value), type + " has a passing value for " + param);
-    }
-    else {
+    } else {
       is(value, expected[param], type + " has correct default value and type for " + param);
     }
   });
 
   info(Object.keys(expected).join(",") + " - " + JSON.stringify(expected));
 
   is(actual.length, Object.keys(expected).length,
     type + " has correct amount of properties.");
--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-get-set-param.js
+++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-get-set-param.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Test AudioNode#getParam() / AudioNode#setParam()
  */
 
-add_task(async function () {
+add_task(async function() {
   let { target, front } = await initBackend(SIMPLE_CONTEXT_URL);
   let [_, [destNode, oscNode, gainNode]] = await Promise.all([
     front.setup({ reload: true }),
     get3(front, "create-node")
   ]);
 
   let freq = await oscNode.getParam("frequency");
   info(typeof freq);
--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-source.js
+++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-source.js
@@ -1,27 +1,28 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Test AudioNode#source
  */
 
-add_task(async function () {
+add_task(async function() {
   let { target, front } = await initBackend(SIMPLE_NODES_URL);
   let [_, nodes] = await Promise.all([
     front.setup({ reload: true }),
     getN(front, "create-node", 14)
   ]);
 
   let actualTypes = nodes.map(node => node.type);
   let isSourceResult = nodes.map(node => node.source);
 
   actualTypes.forEach((type, i) => {
     let shouldBeSource = type === "AudioBufferSourceNode" || type === "OscillatorNode";
-    if (shouldBeSource)
+    if (shouldBeSource) {
       is(isSourceResult[i], true, type + "'s `source` is `true`");
-    else
+    } else {
       is(isSourceResult[i], false, type + "'s `source` is `false`");
+    }
   });
 
   await removeTab(target.tab);
 });
--- a/devtools/client/webaudioeditor/test/browser_audionode-actor-type.js
+++ b/devtools/client/webaudioeditor/test/browser_audionode-actor-type.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Test AudioNode#type
  */
 
-add_task(async function () {
+add_task(async function() {
   let { target, front } = await initBackend(SIMPLE_NODES_URL);
   let [_, nodes] = await Promise.all([
     front.setup({ reload: true }),
     getN(front, "create-node", 14)
   ]);
 
   let actualTypes = nodes.map(node => node.type);
   let expectedTypes = [
--- a/devtools/client/webaudioeditor/test/browser_callwatcher-01.js
+++ b/devtools/client/webaudioeditor/test/browser_callwatcher-01.js
@@ -5,17 +5,17 @@
  * Bug 1130901
  * Tests to ensure that calling call/apply on methods wrapped
  * via CallWatcher do not throw a security permissions error:
  * "Error: Permission denied to access property 'call'"
  */
 
 const BUG_1130901_URL = EXAMPLE_URL + "doc_bug_1130901.html";
 
-add_task(async function () {
+add_task(async function() {
   let { target, panel } = await initWebAudioEditor(BUG_1130901_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, gAudioNodes } = panelWin;
 
   let rendered = waitForGraphRendered(panelWin, 3, 0);
   reload(target);
   await rendered;
 
--- a/devtools/client/webaudioeditor/test/browser_callwatcher-02.js
+++ b/devtools/client/webaudioeditor/test/browser_callwatcher-02.js
@@ -4,17 +4,17 @@
 /**
  * Bug 1112378
  * Tests to ensure that errors called on wrapped functions via call-watcher
  * correctly looks like the error comes from the content, not from within the devtools.
  */
 
 const BUG_1112378_URL = EXAMPLE_URL + "doc_bug_1112378.html";
 
-add_task(async function () {
+add_task(async function() {
   let { target, panel } = await initWebAudioEditor(BUG_1112378_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, gAudioNodes } = panelWin;
 
   loadFrameScriptUtils();
 
   let rendered = waitForGraphRendered(panelWin, 2, 0);
   reload(target);
--- a/devtools/client/webaudioeditor/test/browser_wa_automation-view-01.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_automation-view-01.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that automation view shows the correct view depending on if events
  * or params exist.
  */
 
-add_task(async function () {
+add_task(async function() {
   let { target, panel } = await initWebAudioEditor(AUTOMATION_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS } = panelWin;
 
   let started = once(gFront, "start-context");
 
   let events = Promise.all([
     get3(gFront, "create-node"),
--- a/devtools/client/webaudioeditor/test/browser_wa_automation-view-02.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_automation-view-02.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that automation view selects the first parameter by default and
  * switching between AudioParam rerenders the graph.
  */
 
-add_task(async function () {
+add_task(async function() {
   let { target, panel } = await initWebAudioEditor(AUTOMATION_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, AutomationView } = panelWin;
 
   let started = once(gFront, "start-context");
 
   let events = Promise.all([
     get3(gFront, "create-node"),
--- a/devtools/client/webaudioeditor/test/browser_wa_controller-01.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_controller-01.js
@@ -4,17 +4,17 @@
 /**
  * Bug 1125817
  * Tests to ensure that disconnecting a node immediately
  * after creating it does not fail.
  */
 
 const BUG_1125817_URL = EXAMPLE_URL + "doc_bug_1125817.html";
 
-add_task(async function () {
+add_task(async function() {
   let { target, panel } = await initWebAudioEditor(BUG_1125817_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, gAudioNodes } = panelWin;
 
   let events = Promise.all([
     once(gAudioNodes, "add", 2),
     once(gAudioNodes, "disconnect"),
     waitForGraphRendered(panelWin, 2, 0)
--- a/devtools/client/webaudioeditor/test/browser_wa_destroy-node-01.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_destroy-node-01.js
@@ -4,17 +4,17 @@
 /**
  * Tests that the destruction node event is fired and that the nodes are no
  * longer stored internally in the tool, that the graph is updated properly, and
  * that selecting a soon-to-be dead node clears the inspector.
  *
  * All done in one test since this test takes a few seconds to clear GC.
  */
 
-add_task(async function () {
+add_task(async function() {
   let { target, panel } = await initWebAudioEditor(DESTROY_NODES_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, gAudioNodes } = panelWin;
 
   let started = once(gFront, "start-context");
 
   let events = Promise.all([
     getNSpread(gAudioNodes, "add", 13),
--- a/devtools/client/webaudioeditor/test/browser_wa_first-run.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_first-run.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that the reloading/onContentLoaded hooks work.
  */
 
-add_task(async function () {
+add_task(async function() {
   let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { gFront, $ } = panel.panelWin;
 
   is($("#reload-notice").hidden, false,
     "The 'reload this page' notice should initially be visible.");
   is($("#waiting-notice").hidden, true,
     "The 'waiting for an audio context' notice should initially be hidden.");
   is($("#content").hidden, true,
--- a/devtools/client/webaudioeditor/test/browser_wa_graph-click.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_graph-click.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that the clicking on a node in the GraphView opens and sets
  * the correct node in the InspectorView
  */
 
-add_task(async function () {
+add_task(async function() {
   let { target, panel } = await initWebAudioEditor(COMPLEX_CONTEXT_URL);
   let panelWin = panel.panelWin;
   let { gFront, $, $$, InspectorView } = panelWin;
 
   let started = once(gFront, "start-context");
 
   let events = Promise.all([
     getN(gFront, "create-node", 8),
--- a/devtools/client/webaudioeditor/test/browser_wa_graph-markers.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_graph-markers.js
@@ -2,17 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that the SVG marker styling is updated when devtools theme changes.
  */
 
 const { setTheme } = require("devtools/client/shared/theme");
 
-add_task(async function () {
+add_task(async function() {
   let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, MARKER_STYLING } = panelWin;
 
   let currentTheme = Services.prefs.getCharPref("devtools.theme");
 
   ok(MARKER_STYLING.light, "Marker styling exists for light theme.");
   ok(MARKER_STYLING.dark, "Marker styling exists for dark theme.");
--- a/devtools/client/webaudioeditor/test/browser_wa_graph-render-01.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_graph-render-01.js
@@ -2,17 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that SVG nodes and edges were created for the Graph View.
  */
 
 var connectCount = 0;
 
-add_task(async function () {
+add_task(async function() {
   let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, gAudioNodes } = panelWin;
 
   let started = once(gFront, "start-context");
 
   gAudioNodes.on("connect", onConnectNode);
 
--- a/devtools/client/webaudioeditor/test/browser_wa_graph-render-02.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_graph-render-02.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests more edge rendering for complex graphs.
  */
 
-add_task(async function () {
+add_task(async function() {
   let { target, panel } = await initWebAudioEditor(COMPLEX_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$ } = panelWin;
 
   let started = once(gFront, "start-context");
 
   let events = Promise.all([
     getN(gFront, "create-node", 8),
@@ -18,17 +18,16 @@ add_task(async function () {
   ]);
   reload(target);
   let [actors] = await events;
   let nodeIDs = actors.map(actor => actor.actorID);
 
   let types = ["AudioDestinationNode", "OscillatorNode", "GainNode", "ScriptProcessorNode",
                "OscillatorNode", "GainNode", "AudioBufferSourceNode", "BiquadFilterNode"];
 
-
   types.forEach((type, i) => {
     ok(findGraphNode(panelWin, nodeIDs[i]).classList.contains("type-" + type), "found " + type + " with class");
   });
 
   let edges = [
     [1, 2, "osc1 -> gain1"],
     [1, 3, "osc1 -> proc"],
     [2, 0, "gain1 -> dest"],
--- a/devtools/client/webaudioeditor/test/browser_wa_graph-render-03.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_graph-render-03.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests to ensure that selected nodes stay selected on graph redraw.
  */
 
-add_task(async function () {
+add_task(async function() {
   let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS } = panelWin;
 
   let events = Promise.all([
     getN(gFront, "create-node", 3),
     waitForGraphRendered(panelWin, 3, 2)
   ]);
--- a/devtools/client/webaudioeditor/test/browser_wa_graph-render-04.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_graph-render-04.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests audio param connection rendering.
  */
 
-add_task(async function () {
+add_task(async function() {
   let { target, panel } = await initWebAudioEditor(CONNECT_MULTI_PARAM_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS } = panelWin;
 
   let started = once(gFront, "start-context");
 
   let events = Promise.all([
     getN(gFront, "create-node", 5),
--- a/devtools/client/webaudioeditor/test/browser_wa_graph-render-05.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_graph-render-05.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests to ensure that param connections trigger graph redraws
  */
 
-add_task(async function () {
+add_task(async function() {
   let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS } = panelWin;
 
   let events = Promise.all([
     getN(gFront, "create-node", 3),
     waitForGraphRendered(panelWin, 3, 2, 0)
   ]);
--- a/devtools/client/webaudioeditor/test/browser_wa_graph-render-06.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_graph-render-06.js
@@ -2,17 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests to ensure that param connections trigger graph redraws
  */
 
 const BUG_1141261_URL = EXAMPLE_URL + "doc_bug_1141261.html";
 
-add_task(async function () {
+add_task(async function() {
   let { target, panel } = await initWebAudioEditor(BUG_1141261_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS } = panelWin;
 
   let events = Promise.all([
     getN(gFront, "create-node", 3),
     waitForGraphRendered(panelWin, 3, 1, 0)
   ]);
--- a/devtools/client/webaudioeditor/test/browser_wa_graph-selected.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_graph-selected.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that SVG nodes and edges were created for the Graph View.
  */
 
-add_task(async function () {
+add_task(async function() {
   let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS } = panelWin;
 
   let started = once(gFront, "start-context");
 
   let events = Promise.all([
     get3(gFront, "create-node"),
--- a/devtools/client/webaudioeditor/test/browser_wa_graph-zoom.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_graph-zoom.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that the graph's scale and position is reset on a page reload.
  */
 
-add_task(async function () {
+add_task(async function() {
   let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, ContextView } = panelWin;
 
   let started = once(gFront, "start-context");
 
   await Promise.all([
     waitForGraphRendered(panelWin, 3, 2),
--- a/devtools/client/webaudioeditor/test/browser_wa_inspector-bypass-01.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_inspector-bypass-01.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that nodes are correctly bypassed when bypassing.
  */
 
-add_task(async function () {
+add_task(async function() {
   let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, gAudioNodes } = panelWin;
 
   let events = Promise.all([
     get3(gFront, "create-node"),
     waitForGraphRendered(panelWin, 3, 2)
   ]);
--- a/devtools/client/webaudioeditor/test/browser_wa_inspector-toggle.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_inspector-toggle.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that the inspector toggle button shows and hides
  * the inspector panel as intended.
  */
 
-add_task(async function () {
+add_task(async function() {
   let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, InspectorView } = panelWin;
   let gVars = InspectorView._propsView;
 
   let started = once(gFront, "start-context");
 
   let events = Promise.all([
--- a/devtools/client/webaudioeditor/test/browser_wa_inspector-width.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_inspector-width.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Test that the WebAudioInspector's Width is saved as
  * a preference
  */
 
-add_task(async function () {
+add_task(async function() {
   let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, InspectorView } = panelWin;
   let gVars = InspectorView._propsView;
 
   let started = once(gFront, "start-context");
 
   let events = Promise.all([
--- a/devtools/client/webaudioeditor/test/browser_wa_inspector.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_inspector.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that inspector view opens on graph node click, and
  * loads the correct node inside the inspector.
  */
 
-add_task(async function () {
+add_task(async function() {
   let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, InspectorView } = panelWin;
   let gVars = InspectorView._propsView;
 
   let started = once(gFront, "start-context");
 
   let events = Promise.all([
--- a/devtools/client/webaudioeditor/test/browser_wa_navigate.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_navigate.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests naviating from a page to another will repopulate
  * the audio graph if both pages have an AudioContext.
  */
 
-add_task(async function () {
+add_task(async function() {
   let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $ } = panelWin;
 
   let events = Promise.all([
     get3(gFront, "create-node"),
     waitForGraphRendered(panelWin, 3, 2)
   ]);
--- a/devtools/client/webaudioeditor/test/browser_wa_properties-view-edit-01.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_properties-view-edit-01.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that properties are updated when modifying the VariablesView.
  */
 
-add_task(async function () {
+add_task(async function() {
   let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, PropertiesView } = panelWin;
   let gVars = PropertiesView._propsView;
 
   let started = once(gFront, "start-context");
 
   let events = Promise.all([
@@ -32,34 +32,34 @@ add_task(async function () {
 
   checkVariableView(gVars, 0, {
     "type": "sine",
     "frequency": 440,
     "detune": 0
   }, "default loaded string");
 
   click(panelWin, findGraphNode(panelWin, nodeIds[2]));
-  await waitForInspectorRender(panelWin, EVENTS),
+  await waitForInspectorRender(panelWin, EVENTS);
   checkVariableView(gVars, 0, {
     "gain": 0
   }, "default loaded number");
 
   click(panelWin, findGraphNode(panelWin, nodeIds[1]));
-  await waitForInspectorRender(panelWin, EVENTS),
+  await waitForInspectorRender(panelWin, EVENTS);
   await setAndCheck(0, "type", "square", "square", "sets string as string");
 
   click(panelWin, findGraphNode(panelWin, nodeIds[2]));
-  await waitForInspectorRender(panelWin, EVENTS),
+  await waitForInspectorRender(panelWin, EVENTS);
   await setAndCheck(0, "gain", "0.005", 0.005, "sets number as number");
   await setAndCheck(0, "gain", "0.1", 0.1, "sets float as float");
   await setAndCheck(0, "gain", ".2", 0.2, "sets float without leading zero as float");
 
   await teardown(target);
 });
 
 function setAndCheckVariable(panelWin, gVars) {
-  return async function (varNum, prop, value, expected, desc) {
+  return async function(varNum, prop, value, expected, desc) {
     await modifyVariableView(panelWin, gVars, varNum, prop, value);
     var props = {};
     props[prop] = expected;
     checkVariableView(gVars, varNum, props, desc);
   };
 }
--- a/devtools/client/webaudioeditor/test/browser_wa_properties-view-edit-02.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_properties-view-edit-02.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that properties are not updated when modifying the VariablesView.
  */
 
-add_task(async function () {
+add_task(async function() {
   let { target, panel } = await initWebAudioEditor(COMPLEX_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, PropertiesView } = panelWin;
   let gVars = PropertiesView._propsView;
 
   let started = once(gFront, "start-context");
 
   let events = Promise.all([
--- a/devtools/client/webaudioeditor/test/browser_wa_properties-view-media-nodes.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_properties-view-media-nodes.js
@@ -10,32 +10,33 @@ var MEDIA_PERMISSION = "media.navigator.
 
 function waitForDeviceClosed() {
   info("Checking that getUserMedia streams are no longer in use.");
 
   let temp = {};
   ChromeUtils.import("resource:///modules/webrtcUI.jsm", temp);
   let webrtcUI = temp.webrtcUI;
 
-  if (!webrtcUI.showGlobalIndicator)
+  if (!webrtcUI.showGlobalIndicator) {
     return Promise.resolve();
+  }
 
   return new Promise((resolve, reject) => {
     const message = "webrtc:UpdateGlobalIndicators";
     Services.ppmm.addMessageListener(message, function listener(aMessage) {
       info("Received " + message + " message");
       if (!aMessage.data.showGlobalIndicator) {
         Services.ppmm.removeMessageListener(message, listener);
         resolve();
       }
     });
   });
 }
 
-add_task(async function () {
+add_task(async function() {
   let { target, panel } = await initWebAudioEditor(MEDIA_NODES_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, PropertiesView } = panelWin;
   let gVars = PropertiesView._propsView;
 
   // Auto enable getUserMedia
   let mediaPermissionPref = Services.prefs.getBoolPref(MEDIA_PERMISSION);
   Services.prefs.setBoolPref(MEDIA_PERMISSION, true);
--- a/devtools/client/webaudioeditor/test/browser_wa_properties-view-params-objects.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_properties-view-params-objects.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that params view correctly displays non-primitive properties
  * like AudioBuffer and Float32Array in properties of AudioNodes.
  */
 
-add_task(async function () {
+add_task(async function() {
   let { target, panel } = await initWebAudioEditor(BUFFER_AND_ARRAY_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, PropertiesView } = panelWin;
   let gVars = PropertiesView._propsView;
 
   let started = once(gFront, "start-context");
 
   let events = Promise.all([
--- a/devtools/client/webaudioeditor/test/browser_wa_properties-view-params.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_properties-view-params.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that params view correctly displays all properties for nodes
  * correctly, with default values and correct types.
  */
 
-add_task(async function () {
+add_task(async function() {
   let { target, panel } = await initWebAudioEditor(SIMPLE_NODES_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, PropertiesView } = panelWin;
   let gVars = PropertiesView._propsView;
 
   let started = once(gFront, "start-context");
 
   await loadFrameScriptUtils();
--- a/devtools/client/webaudioeditor/test/browser_wa_properties-view.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_properties-view.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that params view shows params when they exist, and are hidden otherwise.
  */
 
-add_task(async function () {
+add_task(async function() {
   let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, $$, EVENTS, PropertiesView } = panelWin;
   let gVars = PropertiesView._propsView;
 
   let started = once(gFront, "start-context");
 
   let events = Promise.all([
--- a/devtools/client/webaudioeditor/test/browser_wa_reset-01.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_reset-01.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that reloading a tab will properly listen for the `start-context`
  * event and reshow the tools after reloading.
  */
 
-add_task(async function () {
+add_task(async function() {
   let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { gFront, $ } = panel.panelWin;
 
   is($("#reload-notice").hidden, false,
     "The 'reload this page' notice should initially be visible.");
   is($("#waiting-notice").hidden, true,
     "The 'waiting for an audio context' notice should initially be hidden.");
   is($("#content").hidden, true,
--- a/devtools/client/webaudioeditor/test/browser_wa_reset-02.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_reset-02.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests reloading a tab with the tools open properly cleans up
  * the graph.
  */
 
-add_task(async function () {
+add_task(async function() {
   let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $ } = panelWin;
 
   let events = Promise.all([
     get3(gFront, "create-node"),
     waitForGraphRendered(panelWin, 3, 2)
   ]);
--- a/devtools/client/webaudioeditor/test/browser_wa_reset-03.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_reset-03.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests reloading a tab with the tools open properly cleans up
  * the inspector and selected node.
  */
 
-add_task(async function () {
+add_task(async function() {
   let { target, panel } = await initWebAudioEditor(SIMPLE_CONTEXT_URL);
   let { panelWin } = panel;
   let { gFront, $, InspectorView } = panelWin;
 
   let events = Promise.all([
     get3(gFront, "create-node"),
     waitForGraphRendered(panelWin, 3, 2)
   ]);
--- a/devtools/client/webaudioeditor/test/browser_wa_reset-04.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_reset-04.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that switching to an iframe works fine.
  */
 
-add_task(async function () {
+add_task(async function() {
   Services.prefs.setBoolPref("devtools.command-button-frames.enabled", true);
 
   let { target, panel, toolbox } = await initWebAudioEditor(IFRAME_CONTEXT_URL);
   let { gFront, $ } = panel.panelWin;
 
   is($("#reload-notice").hidden, false,
     "The 'reload this page' notice should initially be visible.");
   is($("#waiting-notice").hidden, true,
--- a/devtools/client/webaudioeditor/test/browser_webaudio-actor-automation-event.js
+++ b/devtools/client/webaudioeditor/test/browser_webaudio-actor-automation-event.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Test that the WebAudioActor receives and emits the `automation-event` events
  * with correct arguments from the content.
  */
 
-add_task(async function () {
+add_task(async function() {
   let { target, front } = await initBackend(AUTOMATION_URL);
   let events = [];
 
   let expected = [
     ["setValueAtTime", 0.2, 0],
     ["linearRampToValueAtTime", 1, 0.3],
     ["exponentialRampToValueAtTime", 0.75, 0.6],
     ["setValueCurveAtTime", [-1, 0, 1], 0.7, 0.3],
--- a/devtools/client/webaudioeditor/test/browser_webaudio-actor-connect-param.js
+++ b/devtools/client/webaudioeditor/test/browser_webaudio-actor-connect-param.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Test the `connect-param` event on the web audio actor.
  */
 
-add_task(async function () {
+add_task(async function() {
   let { target, front } = await initBackend(CONNECT_PARAM_URL);
   let [, , [destNode, carrierNode, modNode, gainNode], , connectParam] = await Promise.all([
     front.setup({ reload: true }),
     once(front, "start-context"),
     getN(front, "create-node", 4),
     get2(front, "connect-node"),
     once(front, "connect-param")
   ]);
--- a/devtools/client/webaudioeditor/test/browser_webaudio-actor-destroy-node.js
+++ b/devtools/client/webaudioeditor/test/browser_webaudio-actor-destroy-node.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Test `destroy-node` event on WebAudioActor.
  */
 
-add_task(async function () {
+add_task(async function() {
   let { target, front } = await initBackend(DESTROY_NODES_URL);
 
   let [, , created] = await Promise.all([
     front.setup({ reload: true }),
     once(front, "start-context"),
     // Should create dest, gain, and oscillator node and 10
     // disposable buffer nodes
     getN(front, "create-node", 13)
@@ -29,13 +29,14 @@ add_task(async function () {
       "`destroy-node` called only on AudioNodes in current document.");
   });
 
   await removeTab(target.tab);
 });
 
 function actorIsInList(list, actor) {
   for (let i = 0; i < list.length; i++) {
-    if (list[i].actorID === actor.actorID)
+    if (list[i].actorID === actor.actorID) {
       return list[i];
+    }
   }
   return null;
 }
--- a/devtools/client/webaudioeditor/test/browser_webaudio-actor-simple.js
+++ b/devtools/client/webaudioeditor/test/browser_webaudio-actor-simple.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Test basic communication of Web Audio actor
  */
 
-add_task(async function () {
+add_task(async function() {
   let { target, front } = await initBackend(SIMPLE_CONTEXT_URL);
   let [_, __, [destNode, oscNode, gainNode], [connect1, connect2]] = await Promise.all([
     front.setup({ reload: true }),
     once(front, "start-context"),
     get3(front, "create-node"),
     get2(front, "connect-node")
   ]);
 
--- a/devtools/client/webaudioeditor/test/doc_buffer-and-array.html
+++ b/devtools/client/webaudioeditor/test/doc_buffer-and-array.html
@@ -37,17 +37,17 @@
         for (let i = 0; i < n; ++i) {
           let x = (i - n2) / n2;
           let y = Math.atan(5 * x) / (0.5 * Math.PI);
         }
 
         return curve;
       }
 
-      function getBuffer (url, callback) {
+      function getBuffer(url, callback) {
         let xhr = new XMLHttpRequest();
         xhr.open("GET", url, true);
         xhr.responseType = "arraybuffer";
         xhr.onload = callback;
         xhr.send();
         return xhr;
       }
     </script>
--- a/devtools/client/webaudioeditor/test/doc_bug_1112378.html
+++ b/devtools/client/webaudioeditor/test/doc_bug_1112378.html
@@ -11,47 +11,46 @@
   <body>
 
     <script type="text/javascript">
       "use strict";
 
       let ctx = new AudioContext();
       let osc = ctx.createOscillator();
 
-      function throwError () {
+      function throwError() {
         try {
           osc.connect({});
         } catch (e) {
           return {
             lineNumber: e.lineNumber,
             fileName: e.fileName,
             columnNumber: e.columnNumber,
             message: e.message,
             instanceof: e instanceof TypeError,
             stringified: e.toString(),
             name: e.name
-          }
+          };
         }
       }
 
-      function throwDOMException () {
+      function throwDOMException() {
         try {
           osc.frequency.setValueAtTime(0, -1);
         } catch (e) {
           return {
             lineNumber: e.lineNumber,
             columnNumber: e.columnNumber,
             filename: e.filename,
             message: e.message,
             code: e.code,
             result: e.result,
             instanceof: e instanceof DOMException,
             stringified: e.toString(),
             name: e.name
-          }
+          };
         }
       }
 
-
     </script>
   </body>
 
 </html>
--- a/devtools/client/webaudioeditor/test/doc_destroy-nodes.html
+++ b/devtools/client/webaudioeditor/test/doc_destroy-nodes.html
@@ -11,26 +11,26 @@
   <body>
 
     <script type="text/javascript">
       "use strict";
       // Keep the nodes we want to GC alive until we are ready for them to
       // be collected. We will zero this reference by force from the devtools
       // side.
       var keepAlive = [];
-      (function () {
-      let ctx = new AudioContext();
-      let osc = ctx.createOscillator();
-      let gain = ctx.createGain();
+      (function() {
+        let ctx = new AudioContext();
+        let osc = ctx.createOscillator();
+        let gain = ctx.createGain();
 
-      for (let i = 0; i < 10; i++) {
-        keepAlive.push(ctx.createBufferSource());
-      }
+        for (let i = 0; i < 10; i++) {
+          keepAlive.push(ctx.createBufferSource());
+        }
 
-      osc.connect(gain);
-      gain.connect(ctx.destination);
-      gain.gain.value = 0;
-      osc.start();
+        osc.connect(gain);
+        gain.connect(ctx.destination);
+        gain.gain.value = 0;
+        osc.start();
       })();
     </script>
   </body>
 
 </html>
--- a/devtools/client/webaudioeditor/test/head.js
+++ b/devtools/client/webaudioeditor/test/head.js
@@ -57,17 +57,17 @@ function navigate(aTarget, aUrl, aWaitFo
  * This requires calling removeTab before the test ends.
  */
 function initBackend(aUrl) {
   info("Initializing a web audio editor front.");
 
   DebuggerServer.init();
   DebuggerServer.registerAllActors();
 
-  return (async function () {
+  return (async function() {
     let tab = await addTab(aUrl);
     let target = TargetFactory.forTab(tab);
 
     await target.makeRemote();
 
     let front = new WebAudioFront(target.client, target.form);
     return { target, front };
   })();
@@ -76,17 +76,17 @@ function initBackend(aUrl) {
 /**
  * Adds a new tab, and open the toolbox for that tab, selecting the audio editor
  * panel.
  * This requires calling teardown before the test ends.
  */
 function initWebAudioEditor(aUrl) {
   info("Initializing a web audio editor pane.");
 
-  return (async function () {
+  return (async function() {
     let tab = await addTab(aUrl);
     let target = TargetFactory.forTab(tab);
 
     await target.makeRemote();
 
     Services.prefs.setBoolPref("devtools.webaudioeditor.enabled", true);
     let toolbox = await gDevTools.showToolbox(target, "webaudioeditor");
     let panel = toolbox.getCurrentPanel();
@@ -113,49 +113,63 @@ function teardown(aTarget) {
 //
 // Takes a `front` object that is an event emitter, the number of
 // programs that should be listened to and waited on, and an optional
 // `onAdd` function that calls with the entire actors array on program link
 function getN(front, eventName, count, spread) {
   let actors = [];
   info(`Waiting for ${count} ${eventName} events`);
 
-  return new Promise((resolve, reject) => {
+  return new Promise((resolve) => {
     front.on(eventName, function onEvent(...args) {
       let actor = args[0];
       if (actors.length !== count) {
         actors.push(spread ? args : actor);
       }
       info(`Got ${actors.length} / ${count} ${eventName} events`);
       if (actors.length === count) {
         front.off(eventName, onEvent);
         resolve(actors);
       }
     });
   });
 }
 
-function get(front, eventName) { return getN(front, eventName, 1); }
-function get2(front, eventName) { return getN(front, eventName, 2); }
-function get3(front, eventName) { return getN(front, eventName, 3); }
-function getSpread(front, eventName) { return getN(front, eventName, 1, true); }
-function get2Spread(front, eventName) { return getN(front, eventName, 2, true); }
-function get3Spread(front, eventName) { return getN(front, eventName, 3, true); }
-function getNSpread(front, eventName, count) { return getN(front, eventName, count, true); }
+function get(front, eventName) {
+  return getN(front, eventName, 1);
+}
+function get2(front, eventName) {
+  return getN(front, eventName, 2);
+}
+function get3(front, eventName) {
+  return getN(front, eventName, 3);
+}
+function getSpread(front, eventName) {
+  return getN(front, eventName, 1, true);
+}
+function get2Spread(front, eventName) {
+  return getN(front, eventName, 2, true);
+}
+function get3Spread(front, eventName) {
+  return getN(front, eventName, 3, true);
+}
+function getNSpread(front, eventName, count) {
+  return getN(front, eventName, count, true);
+}
 
 /**
  * Waits for the UI_GRAPH_RENDERED event to fire, but only
  * resolves when the graph was rendered with the correct count of
  * nodes and edges.
  */
 function waitForGraphRendered(front, nodeCount, edgeCount, paramEdgeCount) {
   let eventName = front.EVENTS.UI_GRAPH_RENDERED;
   info(`Wait for graph rendered with ${nodeCount} nodes, ${edgeCount} edges`);
 
-  return new Promise((resolve, reject) => {
+  return new Promise((resolve) => {
     front.on(eventName, function onGraphRendered(nodes, edges, pEdges) {
       let paramEdgesDone = paramEdgeCount != null ? paramEdgeCount === pEdges : true;
       info(`Got graph rendered with ${nodes} / ${nodeCount} nodes, ` +
            `${edges} / ${edgeCount} edges`);
       if (nodes === nodeCount && edges === edgeCount && paramEdgesDone) {
         front.off(eventName, onGraphRendered);
         resolve();
       }
@@ -183,44 +197,42 @@ function checkVariableView(view, index, 
       "Correct property name for " + variable);
     let value = aVar.target.querySelector(".value").getAttribute("value");
 
     // Cast value with JSON.parse if possible;
     // will fail when displaying Object types like "ArrayBuffer"
     // and "Float32Array", but will match the original value.
     try {
       value = JSON.parse(value);
-    }
-    catch (e) {}
+    } catch (e) {}
     if (typeof hash[variable] === "function") {
       ok(hash[variable](value),
         "Passing property value of " + value + " for " + variable + " " + description);
-    }
-    else {
+    } else {
       is(value, hash[variable],
         "Correct property value of " + hash[variable] + " for " + variable + " " + description);
     }
   });
 }
 
 function modifyVariableView(win, view, index, prop, value) {
   let scope = view.getScopeAtIndex(index);
   let aVar = scope.get(prop);
   scope.expand();
 
   return new Promise((resolve, reject) => {
     const onParamSetSuccess = () => {
       win.off(win.EVENTS.UI_SET_PARAM_ERROR, onParamSetError);
       resolve();
-    }
+    };
 
     const onParamSetError = () => {
       win.off(win.EVENTS.UI_SET_PARAM, onParamSetSuccess);
       reject();
-    }
+    };
     win.once(win.EVENTS.UI_SET_PARAM, onParamSetSuccess);
     win.once(win.EVENTS.UI_SET_PARAM_ERROR, onParamSetError);
 
     // Focus and select the variable to begin editing
     win.focus();
     aVar.focus();
     EventUtils.sendKey("RETURN", win);
 
@@ -320,17 +332,17 @@ function countGraphObjects(win) {
     edges: win.document.querySelectorAll(".edgePaths > .edgePath").length
   };
 }
 
 /**
 * Forces cycle collection and GC, used in AudioNode destruction tests.
 */
 function forceNodeCollection() {
-  ContentTask.spawn(gBrowser.selectedBrowser, {}, async function () {
+  ContentTask.spawn(gBrowser.selectedBrowser, {}, async function() {
     // Kill the reference keeping stuff alive.
     content.wrappedJSObject.keepAlive = null;
 
     // Collect the now-deceased nodes.
     Cu.forceGC();
     Cu.forceCC();
     Cu.forceGC();
     Cu.forceCC();
@@ -379,17 +391,19 @@ function waitForInspectorRender(panelWin
 
 /**
  * Takes an AudioNode type and returns it's properties (from audionode.json)
  * as keys and their default values as keys
  */
 function nodeDefaultValues(nodeName) {
   let fn = NODE_CONSTRUCTORS[nodeName];
 
-  if (typeof fn === "undefined") return {};
+  if (typeof fn === "undefined") {
+    return {};
+  }
 
   let init = nodeName === "AudioDestinationNode" ? "destination" : `create${fn}()`;
 
   let definition = JSON.stringify(audioNodes[nodeName].properties);
 
   let evalNode = evalInDebuggee(`
     let ins = (new AudioContext()).${init};
     let props = ${definition};
--- a/devtools/client/webaudioeditor/views/automation.js
+++ b/devtools/client/webaudioeditor/views/automation.js
@@ -7,43 +7,43 @@
  * Functions handling the audio node inspector UI.
  */
 
 var AutomationView = {
 
   /**
    * Initialization function called when the tool starts up.
    */
-  initialize: function () {
+  initialize: function() {
     this._buttons = $("#automation-param-toolbar-buttons");
     this.graph = new LineGraphWidget($("#automation-graph"), { avg: false });
     this.graph.selectionEnabled = false;
 
     this._onButtonClick = this._onButtonClick.bind(this);
     this._onNodeSet = this._onNodeSet.bind(this);
     this._onResize = this._onResize.bind(this);
 
     this._buttons.addEventListener("click", this._onButtonClick);
     window.on(EVENTS.UI_INSPECTOR_RESIZE, this._onResize);
     window.on(EVENTS.UI_INSPECTOR_NODE_SET, this._onNodeSet);
   },
 
   /**
    * Destruction function called when the tool cleans up.
    */
-  destroy: function () {
+  destroy: function() {
     this._buttons.removeEventListener("click", this._onButtonClick);
     window.off(EVENTS.UI_INSPECTOR_RESIZE, this._onResize);
     window.off(EVENTS.UI_INSPECTOR_NODE_SET, this._onNodeSet);
   },
 
   /**
    * Empties out the props view.
    */
-  resetUI: function () {
+  resetUI: function() {
     this._currentNode = null;
   },
 
   /**
    * On a new node selection, create the Automation panel for
    * that specific node.
    */
   async build() {
@@ -78,17 +78,17 @@ var AutomationView = {
     await this.graph.setDataWhenReady(values);
     window.emit(EVENTS.UI_AUTOMATION_TAB_RENDERED, node.id);
   },
 
   /**
    * Create the buttons for each AudioParam, that when clicked,
    * render the graph for that AudioParam.
    */
-  _createParamButtons: function (params) {
+  _createParamButtons: function(params) {
     this._buttons.innerHTML = "";
     params.forEach((param, i) => {
       let button = document.createElement("toolbarbutton");
       button.setAttribute("class", "devtools-toolbarbutton automation-param-button");
       button.setAttribute("data-param", param.param);
       // Set label to the parameter name, should not be L10N'd
       button.setAttribute("label", param.param);
 
@@ -100,29 +100,29 @@ var AutomationView = {
       this._buttons.appendChild(button);
     });
   },
 
   /**
    * Internally sets the current audio node and rebuilds appropriate
    * views.
    */
-  _setAudioNode: function (node) {
+  _setAudioNode: function(node) {
     this._currentNode = node;
     if (this._currentNode) {
       this.build();
     }
   },
 
   /**
    * Toggles the subviews to display messages whether or not
    * the audio node has no AudioParams, no automation events, or
    * shows the graph.
    */
-  _setState: function (state) {
+  _setState: function(state) {
     let contentView = $("#automation-content");
     let emptyView = $("#automation-empty");
 
     let graphView = $("#automation-graph-container");
     let noEventsView = $("#automation-no-events");
 
     contentView.hidden = state === "no-params";
     emptyView.hidden = state !== "no-params";
@@ -130,30 +130,30 @@ var AutomationView = {
     graphView.hidden = state !== "show";
     noEventsView.hidden = state !== "no-events";
   },
 
   /**
    * Event handlers
    */
 
-  _onButtonClick: function (e) {
+  _onButtonClick: function(e) {
     Array.forEach($$(".automation-param-button"), $btn => $btn.removeAttribute("selected"));
     let paramName = e.target.getAttribute("data-param");
     e.target.setAttribute("selected", true);
     this._selectedParamName = paramName;
     this.render();
   },
 
   /**
    * Called when the inspector is resized.
    */
-  _onResize: function () {
+  _onResize: function() {
     this.graph.refresh();
   },
 
   /**
    * Called when the inspector view determines a node is selected.
    */
-  _onNodeSet: function (id) {
+  _onNodeSet: function(id) {
     this._setAudioNode(id != null ? gAudioNodes.get(id) : null);
   }
 };
--- a/devtools/client/webaudioeditor/views/context.js
+++ b/devtools/client/webaudioeditor/views/context.js
@@ -38,151 +38,151 @@ const GRAPH_REDRAW_EVENTS = ["add", "con
 
 /**
  * Functions handling the graph UI.
  */
 var ContextView = {
   /**
    * Initialization function, called when the tool is started.
    */
-  initialize: function () {
+  initialize: function() {
     this._onGraphClick = this._onGraphClick.bind(this);
     this._onThemeChange = this._onThemeChange.bind(this);
     this._onStartContext = this._onStartContext.bind(this);
     this._onEvent = this._onEvent.bind(this);
 
     this.draw = debounce(this.draw.bind(this), GRAPH_DEBOUNCE_TIMER);
     $("#graph-target").addEventListener("click", this._onGraphClick);
 
     window.on(EVENTS.THEME_CHANGE, this._onThemeChange);
     window.on(EVENTS.START_CONTEXT, this._onStartContext);
     gAudioNodes.on("*", this._onEvent);
   },
 
   /**
    * Destruction function, called when the tool is closed.
    */
-  destroy: function () {
+  destroy: function() {
     // If the graph was rendered at all, then the handler
     // for zooming in will be set. We must remove it to prevent leaks.
     if (this._zoomBinding) {
       this._zoomBinding.on("zoom", null);
     }
     $("#graph-target").removeEventListener("click", this._onGraphClick);
 
     window.off(EVENTS.THEME_CHANGE, this._onThemeChange);
     window.off(EVENTS.START_CONTEXT, this._onStartContext);
     gAudioNodes.off("*", this._onEvent);
   },
 
   /**
    * Called when a page is reloaded and waiting for a "start-context" event
    * and clears out old content
    */
-  resetUI: function () {
+  resetUI: function() {
     this.clearGraph();
     this.resetGraphTransform();
   },
 
   /**
    * Clears out the rendered graph, called when resetting the SVG elements to draw again,
    * or when resetting the entire UI tool
    */
-  clearGraph: function () {
+  clearGraph: function() {
     $("#graph-target").innerHTML = "";
   },
 
   /**
    * Moves the graph back to its original scale and translation.
    */
-  resetGraphTransform: function () {
+  resetGraphTransform: function() {
     // Only reset if the graph was ever drawn.
     if (this._zoomBinding) {
       let { translate, scale } = GRAPH_DEFAULTS;
       // Must set the `zoomBinding` so the next `zoom` event is in sync with
       // where the graph is visually (set by the `transform` attribute).
       this._zoomBinding.scale(scale);
       this._zoomBinding.translate(translate);
       d3.select("#graph-target")
         .attr("transform", "translate(" + translate + ") scale(" + scale + ")");
     }
   },
 
-  getCurrentScale: function () {
+  getCurrentScale: function() {
     return this._zoomBinding ? this._zoomBinding.scale() : null;
   },
 
-  getCurrentTranslation: function () {
+  getCurrentTranslation: function() {
     return this._zoomBinding ? this._zoomBinding.translate() : null;
   },
 
   /**
    * Makes the corresponding graph node appear "focused", removing
    * focused styles from all other nodes. If no `actorID` specified,
    * make all nodes appear unselected.
    */
-  focusNode: function (actorID) {
+  focusNode: function(actorID) {
     // Remove class "selected" from all nodes
     Array.forEach($$(".nodes > g"), $node => $node.classList.remove("selected"));
     // Add to "selected"
     if (actorID) {
       this._getNodeByID(actorID).classList.add("selected");
     }
   },
 
   /**
    * Takes an actorID and returns the corresponding DOM SVG element in the graph
    */
-  _getNodeByID: function (actorID) {
+  _getNodeByID: function(actorID) {
     return $(".nodes > g[data-id='" + actorID + "']");
   },
 
   /**
    * Sets the appropriate class on an SVG node when its bypass
    * status is toggled.
    */
-  _bypassNode: function (node, enabled) {
+  _bypassNode: function(node, enabled) {
     let el = this._getNodeByID(node.id);
     el.classList[enabled ? "add" : "remove"]("bypassed");
   },
 
   /**
    * This method renders the nodes currently available in `gAudioNodes` and is
    * throttled to be called at most every `GRAPH_DEBOUNCE_TIMER` milliseconds.
    * It's called whenever the audio context routing changes, after being debounced.
    */
-  draw: function () {
+  draw: function() {
     // Clear out previous SVG information
     this.clearGraph();
 
     let graph = new dagreD3.Digraph();
     let renderer = new dagreD3.Renderer();
     gAudioNodes.populateGraph(graph);
 
     // Post-render manipulation of the nodes
     let oldDrawNodes = renderer.drawNodes();
-    renderer.drawNodes(function (graph, root) {
+    renderer.drawNodes(function(graph, root) {
       let svgNodes = oldDrawNodes(graph, root);
-      svgNodes.each(function (n) {
+      svgNodes.each(function(n) {
         let node = graph.node(n);
         let classString = "audionode type-" + node.type + (node.bypassed ? " bypassed" : "");
         this.setAttribute("class", classString);
         this.setAttribute("data-id", node.id);
         this.setAttribute("data-type", node.type);
       });
       return svgNodes;
     });
 
     // Post-render manipulation of edges
     let oldDrawEdgePaths = renderer.drawEdgePaths();
     let defaultClasses = "edgePath enter";
 
-    renderer.drawEdgePaths(function (graph, root) {
+    renderer.drawEdgePaths(function(graph, root) {
       let svgEdges = oldDrawEdgePaths(graph, root);
-      svgEdges.each(function (e) {
+      svgEdges.each(function(e) {
         let edge = graph.edge(e);
 
         // We have to manually specify the default classes on the edges
         // as to not overwrite them
         let edgeClass = defaultClasses + (edge.param ? (" param-connection " + edge.param) : "");
 
         this.setAttribute("data-source", edge.source);
         this.setAttribute("data-target", edge.target);
@@ -237,17 +237,17 @@ var ContextView = {
     });
 
     let layout = dagreD3.layout().rankDir("LR");
     renderer.layout(layout).run(graph, d3.select("#graph-target"));
 
     // Handle the sliding and zooming of the graph,
     // store as `this._zoomBinding` so we can unbind during destruction
     if (!this._zoomBinding) {
-      this._zoomBinding = d3.behavior.zoom().on("zoom", function () {
+      this._zoomBinding = d3.behavior.zoom().on("zoom", function() {
         var ev = d3.event;
         d3.select("#graph-target")
           .attr("transform", "translate(" + ev.translate + ") scale(" + ev.scale + ")");
       });
       d3.select("svg").call(this._zoomBinding);
 
       // Set initial translation and scale -- this puts D3's awareness of
       // the graph in sync with what the user sees originally.
@@ -258,57 +258,58 @@ var ContextView = {
   /**
    * Event handlers
    */
 
   /**
    * Called once "start-context" is fired, indicating that there is an audio
    * context being created to view so render the graph.
    */
-  _onStartContext: function () {
+  _onStartContext: function() {
     this.draw();
   },
 
   /**
    * Called when `gAudioNodes` fires an event -- most events (listed
    * in GRAPH_REDRAW_EVENTS) qualify as a redraw event.
    */
-  _onEvent: function (eventName, ...args) {
+  _onEvent: function(eventName, ...args) {
     // If bypassing, just toggle the class on the SVG node
     // rather than rerendering everything
     if (eventName === "bypass") {
       this._bypassNode.apply(this, args);
     }
     if (~GRAPH_REDRAW_EVENTS.indexOf(eventName)) {
       this.draw();
     }
   },
 
   /**
    * Fired when the devtools theme changes.
    */
-  _onThemeChange: function (theme) {
+  _onThemeChange: function(theme) {
     let markerColor = MARKER_STYLING[theme];
     let marker = $("#arrowhead");
     if (marker) {
       marker.setAttribute("style", "fill: " + markerColor);
     }
   },
 
   /**
    * Fired when a click occurs in the graph.
    *
    * @param Event e
    *        Click event.
    */
-  _onGraphClick: function (e) {
+  _onGraphClick: function(e) {
     let node = findGraphNodeParent(e.target);
     // If node not found (clicking outside of an audio node in the graph),
     // then ignore this event
-    if (!node)
+    if (!node) {
       return;
+    }
 
     let id = node.getAttribute("data-id");
 
     this.focusNode(id);
     window.emit(EVENTS.UI_SELECT_NODE, id);
   }
 };
--- a/devtools/client/webaudioeditor/views/inspector.js
+++ b/devtools/client/webaudioeditor/views/inspector.js
@@ -23,17 +23,17 @@ var InspectorView = {
   _expandString: EXPAND_INSPECTOR_STRING,
   _toggleEvent: EVENTS.UI_INSPECTOR_TOGGLED,
   _animated: true,
   _delayed: true,
 
   /**
    * Initialization function called when the tool starts up.
    */
-  initialize: function () {
+  initialize: function() {
     // Set up view controller
     this.el = $("#web-audio-inspector");
     this.splitter = $("#inspector-splitter");
     this.el.setAttribute("width", Services.prefs.getIntPref("devtools.webaudioeditor.inspectorWidth"));
     this.button = $("#inspector-pane-toggle");
     mixin(this, ToggleMixin);
     this.bindToggle();
 
@@ -51,17 +51,17 @@ var InspectorView = {
     }
     window.on(EVENTS.UI_SELECT_NODE, this._onNodeSelect);
     gAudioNodes.on("remove", this._onDestroyNode);
   },
 
   /**
    * Destruction function called when the tool cleans up.
    */
-  destroy: function () {
+  destroy: function() {
     this.unbindToggle();
     this.splitter.removeEventListener("mouseup", this._onResize);
 
     $("#audio-node-toolbar toolbarbutton").removeEventListener("command", this._onCommandClick);
     for (let $el of $$("#audio-node-toolbar toolbarbutton")) {
       $el.removeEventListener("command", this._onCommandClick);
     }
     window.off(EVENTS.UI_SELECT_NODE, this._onNodeSelect);
@@ -80,45 +80,44 @@ var InspectorView = {
     this._currentNode = node || null;
 
     // If no node selected, set the inspector back to "no AudioNode selected"
     // view.
     if (!node) {
       $("#web-audio-editor-details-pane-empty").removeAttribute("hidden");
       $("#web-audio-editor-tabs").setAttribute("hidden", "true");
       window.emit(EVENTS.UI_INSPECTOR_NODE_SET, null);
-    }
-    // Otherwise load up the tabs view and hide the empty placeholder
-    else {
+    } else {
+      // Otherwise load up the tabs view and hide the empty placeholder
       $("#web-audio-editor-details-pane-empty").setAttribute("hidden", "true");
       $("#web-audio-editor-tabs").removeAttribute("hidden");
       this._buildToolbar();
       window.emit(EVENTS.UI_INSPECTOR_NODE_SET, this._currentNode.id);
     }
   },
 
   /**
    * Returns the current AudioNodeView.
    */
-  getCurrentAudioNode: function () {
+  getCurrentAudioNode: function() {
     return this._currentNode;
   },
 
   /**
    * Empties out the props view.
    */
-  resetUI: function () {
+  resetUI: function() {
     // Set current node to empty to load empty view
     this.setCurrentAudioNode();
 
     // Reset AudioNode inspector and hide
     this.hideImmediately();
   },
 
-  _buildToolbar: function () {
+  _buildToolbar: function() {
     let node = this.getCurrentAudioNode();
 
     let bypassable = node.bypassable;
     let bypassed = node.isBypassed();
     let button = $("#audio-node-toolbar .bypass");
 
     if (!bypassable) {
       button.setAttribute("disabled", true);
@@ -136,42 +135,42 @@ var InspectorView = {
   /**
    * Event handlers
    */
 
   /**
    * Called on EVENTS.UI_SELECT_NODE, and takes an actorID `id`
    * and calls `setCurrentAudioNode` to scaffold the inspector view.
    */
-  _onNodeSelect: function (id) {
+  _onNodeSelect: function(id) {
     this.setCurrentAudioNode(gAudioNodes.get(id));
 
     // Ensure inspector is visible when selecting a new node
     this.show();
   },
 
-  _onResize: function () {
+  _onResize: function() {
     if (this.el.getAttribute("width") < MIN_INSPECTOR_WIDTH) {
       this.el.setAttribute("width", MIN_INSPECTOR_WIDTH);
     }
     Services.prefs.setIntPref("devtools.webaudioeditor.inspectorWidth", this.el.getAttribute("width"));
     window.emit(EVENTS.UI_INSPECTOR_RESIZE);
   },
 
   /**
    * Called when `DESTROY_NODE` is fired to remove the node from props view if
    * it's currently selected.
    */
-  _onDestroyNode: function (node) {
+  _onDestroyNode: function(node) {
     if (this._currentNode && this._currentNode.id === node.id) {
       this.setCurrentAudioNode(null);
     }
   },
 
-  _onCommandClick: function (e) {
+  _onCommandClick: function(e) {
     let node = this.getCurrentAudioNode();
     let button = e.target;
     let command = button.getAttribute("data-command");
     let checked = button.getAttribute("checked");
 
     if (button.getAttribute("disabled")) {
       return;
     }
--- a/devtools/client/webaudioeditor/views/properties.js
+++ b/devtools/client/webaudioeditor/views/properties.js
@@ -18,46 +18,46 @@ const GENERIC_VARIABLES_VIEW_SETTINGS = 
  * Functions handling the audio node inspector UI.
  */
 
 var PropertiesView = {
 
   /**
    * Initialization function called when the tool starts up.
    */
-  initialize: function () {
+  initialize: function() {
     this._onEval = this._onEval.bind(this);
     this._onNodeSet = this._onNodeSet.bind(this);
 
     window.on(EVENTS.UI_INSPECTOR_NODE_SET, this._onNodeSet);
     this._propsView = new VariablesView($("#properties-content"), GENERIC_VARIABLES_VIEW_SETTINGS);
     this._propsView.eval = this._onEval;
   },
 
   /**
    * Destruction function called when the tool cleans up.
    */
-  destroy: function () {
+  destroy: function() {
     window.off(EVENTS.UI_INSPECTOR_NODE_SET, this._onNodeSet);
     this._propsView = null;
   },
 
   /**
    * Empties out the props view.
    */
-  resetUI: function () {
+  resetUI: function() {
     this._propsView.empty();
     this._currentNode = null;
   },
 
   /**
    * Internally sets the current audio node and rebuilds appropriate
    * views.
    */
-  _setAudioNode: function (node) {
+  _setAudioNode: function(node) {
     this._currentNode = node;
     if (this._currentNode) {
       this._buildPropertiesView();
     }
   },
 
   /**
    * Reconstructs the `Properties` tab in the inspector
@@ -90,41 +90,41 @@ var PropertiesView = {
 
     window.emit(EVENTS.UI_PROPERTIES_TAB_RENDERED, node.id);
   },
 
   /**
    * Toggles the display of the "empty" properties view when
    * node has no properties to display.
    */
-  _togglePropertiesView: function (show) {
+  _togglePropertiesView: function(show) {
     let propsView = $("#properties-content");
     let emptyView = $("#properties-empty");
     (show ? propsView : emptyView).removeAttribute("hidden");
     (show ? emptyView : propsView).setAttribute("hidden", "true");
   },
 
   /**
    * Returns the scope for AudioParams in the
    * VariablesView.
    *
    * @return Scope
    */
-  _getAudioPropertiesScope: function () {
+  _getAudioPropertiesScope: function() {
     return this._propsView.getScopeAtIndex(0);
   },
 
   /**
    * Event handlers
    */
 
   /**
    * Called when the inspector view determines a node is selected.
    */
-  _onNodeSet: function (id) {
+  _onNodeSet: function(id) {
     this._setAudioNode(gAudioNodes.get(id));
   },
 
   /**
    * Executed when an audio prop is changed in the UI.
    */
   async _onEval(variable, value) {
     let ownerScope = variable.ownerView;
@@ -139,18 +139,17 @@ var PropertiesView = {
       try {
         let number = parseFloat(value);
         if (!isNaN(number)) {
           value = number;
         } else {
           value = JSON.parse(value);
         }
         error = await node.actor.setParam(propName, value);
-      }
-      catch (e) {
+      } catch (e) {
         error = e;
       }
     }
 
     // TODO figure out how to handle and display set prop errors
     // and enable `test/brorwser_wa_properties-view-edit.js`
     // Bug 994258
     if (!error) {
--- a/devtools/client/webaudioeditor/views/utils.js
+++ b/devtools/client/webaudioeditor/views/utils.js
@@ -6,24 +6,25 @@
 /**
  * Takes an element in an SVG graph and iterates over
  * ancestors until it finds the graph node container. If not found,
  * returns null.
  */
 
 function findGraphNodeParent(el) {
   // Some targets may not contain `classList` property
-  if (!el.classList)
+  if (!el.classList) {
     return null;
+  }
 
   while (!el.classList.contains("nodes")) {
-    if (el.classList.contains("audionode"))
+    if (el.classList.contains("audionode")) {
       return el;
-    else
-      el = el.parentNode;
+    }
+    el = el.parentNode;
   }
   return null;
 }
 
 /**
  * Object for use with `mix` into a view.
  * Must have the following properties defined on the view:
  * - `el`
@@ -34,70 +35,69 @@ function findGraphNodeParent(el) {
  *
  * Optional properties on the view can be defined to specify default
  * visibility options.
  * - `_animated`
  * - `_delayed`
  */
 var ToggleMixin = {
 
-  bindToggle: function () {
+  bindToggle: function() {
     this._onToggle = this._onToggle.bind(this);
     this.button.addEventListener("mousedown", this._onToggle);
   },
 
-  unbindToggle: function () {
+  unbindToggle: function() {
     this.button.removeEventListener("mousedown", this._onToggle);
   },
 
-  show: function () {
+  show: function() {
     this._viewController({ visible: true });
   },
 
-  hide: function () {
+  hide: function() {
     this._viewController({ visible: false });
   },
 
-  hideImmediately: function () {
+  hideImmediately: function() {
     this._viewController({ visible: false, delayed: false, animated: false });
   },
 
   /**
    * Returns a boolean indicating whether or not the view.
    * is currently being shown.
    */
-  isVisible: function () {
+  isVisible: function() {
     return !this.el.classList.contains("pane-collapsed");
   },
 
   /**
    * Toggles the visibility of the view.
    *
    * @param object visible
    *        - visible: boolean indicating whether the panel should be shown or not
    *        - animated: boolean indiciating whether the pane should be animated
    *        - delayed: boolean indicating whether the pane's opening should wait
    *                   a few cycles or not
    */
-  _viewController: function ({ visible, animated, delayed }) {
+  _viewController: function({ visible, animated, delayed }) {
     let flags = {
       visible: visible,
       animated: animated != null ? animated : !!this._animated,
       delayed: delayed != null ? delayed : !!this._delayed,
       callback: () => window.emit(this._toggleEvent, visible)
     };
 
     ViewHelpers.togglePane(flags, this.el);
 
     if (flags.visible) {
       this.button.classList.remove("pane-collapsed");
       this.button.setAttribute("tooltiptext", this._collapseString);
-    }
-    else {
+    } else {
       this.button.classList.add("pane-collapsed");
       this.button.setAttribute("tooltiptext", this._expandString);
     }
   },
 
-  _onToggle: function () {
+  _onToggle: function() {
     this._viewController({ visible: !this.isVisible() });
   }
 };
--- a/devtools/client/webconsole/old/test/head.js
+++ b/devtools/client/webconsole/old/test/head.js
@@ -38,18 +38,16 @@ const SEVERITY_LOG = 3;
 const GROUP_INDENT = 12;
 
 var WCUL10n = require("devtools/client/webconsole/webconsole-l10n");
 
 const DOCS_GA_PARAMS = "?utm_source=mozilla" +
                        "&utm_medium=firefox-console-errors" +
                        "&utm_campaign=default";
 
-flags.testing = true;
-
 Services.prefs.setBoolPref("devtools.browserconsole.new-frontend-enabled", false);
 registerCleanupFunction(async function () {
   Services.prefs.clearUserPref("devtools.browserconsole.new-frontend-enabled");
 });
 
 function loadTab(url, preferredRemoteType) {
   return addTab(url, { preferredRemoteType }).then( tab => {
     return { tab, browser: tab.linkedBrowser };
@@ -320,18 +318,16 @@ var finishTest = Task.async(function* ()
 
 // Always use the 'old' frontend for tests that rely on it
 Services.prefs.setBoolPref("devtools.webconsole.new-frontend-enabled", false);
 registerCleanupFunction(function* () {
   Services.prefs.clearUserPref("devtools.webconsole.new-frontend-enabled");
 });
 
 registerCleanupFunction(function* () {
-  flags.testing = false;
-
   // Remove stored console commands in between tests
   yield asyncStorage.removeItem("webConsoleHistory");
 
   dumpConsoles();
 
   let browserConsole = HUDService.getBrowserConsole();
   if (browserConsole) {
     if (browserConsole.jsterm) {
--- a/devtools/client/webconsole/reducers/messages.js
+++ b/devtools/client/webconsole/reducers/messages.js
@@ -13,16 +13,17 @@ const {
 const constants = require("devtools/client/webconsole/constants");
 const {
   DEFAULT_FILTERS,
   FILTERS,
   MESSAGE_TYPE,
   MESSAGE_SOURCE,
 } = constants;
 const { getGripPreviewItems } = require("devtools/client/shared/components/reps/reps");
+const { getUnicodeUrlPath } = require("devtools/client/shared/unicode-url");
 const { getSourceNames } = require("devtools/client/shared/source-utils");
 
 const {
   UPDATE_REQUEST,
 } = require("devtools/client/netmonitor/src/constants");
 
 const {
   processNetworkUpdates,
@@ -777,20 +778,23 @@ function isTextInFrame(text, frame) {
 
   const {
     functionName,
     line,
     column,
     source
   } = frame;
   const { short } = getSourceNames(source);
+  const unicodeShort = getUnicodeUrlPath(short);
 
-  return `${functionName ? functionName + " " : ""}${short}:${line}:${column}`
+  const includes =
+    `${functionName ? functionName + " " : ""}${unicodeShort}:${line}:${column}`
     .toLocaleLowerCase()
     .includes(text.toLocaleLowerCase());
+  return includes;
 }
 
 /**
 * Returns true if given text is included in provided parameters.
 */
 function isTextInParameters(text, parameters) {
   if (!parameters) {
     return false;
--- a/devtools/client/webconsole/test/mochitest/browser.ini
+++ b/devtools/client/webconsole/test/mochitest/browser.ini
@@ -15,19 +15,16 @@ support-files =
   test_bug_770099_violation.html
   test_bug_770099_violation.html^headers^
   test_console_csp_ignore_reflected_xss_message.html
   test_console_csp_ignore_reflected_xss_message.html^headers^
   test_hpkp-invalid-headers.sjs
   test_hsts-invalid-headers.sjs
   test-autocomplete-in-stackframe.html
   test-batching.html
-  test-bug_923281_console_log_filter.html
-  test-bug_923281_test1.js
-  test-bug_923281_test2.js
   test-console-trace-duplicates.html
   test-bug-585956-console-trace.html
   test-bug-599725-response-headers.sjs
   test-bug-601177-log-levels.html
   test-bug-601177-log-levels.js
   test-bug-630733-response-redirect-headers.sjs
   test-bug-632275-getters.html
   test-bug-646025-console-file-location.html
@@ -273,16 +270,17 @@ skip-if = (e10s && debug) || (e10s && os
 [browser_webconsole_duplicate_errors.js]
 [browser_webconsole_errors_after_page_reload.js]
 [browser_webconsole_eval_in_debugger_stackframe.js]
 [browser_webconsole_eval_in_debugger_stackframe2.js]
 [browser_webconsole_execution_scope.js]
 [browser_webconsole_external_script_errors.js]
 [browser_webconsole_file_uri.js]
 skip-if = true #	Bug 1404382
+[browser_webconsole_filter_by_input.js]
 [browser_webconsole_filter_scroll.js]
 [browser_webconsole_filters.js]
 [browser_webconsole_filters_persist.js]
 [browser_webconsole_highlighter_console_helper.js]
 [browser_webconsole_history_arrow_keys.js]
 [browser_webconsole_hpkp_invalid-headers.js]
 [browser_webconsole_hsts_invalid-headers.js]
 [browser_webconsole_iframe_wrong_hud.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_filter_by_input.js
@@ -0,0 +1,242 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the text filter box works to filter based on location.
+
+"use strict";
+
+// In this test, we are trying to test the filtering functionality of the filter
+// input. We test filtering not only for the contents of logs themseleves but
+// also for the filenames.
+//
+// We simulate an HTML file which executes two Javascript files, one with an
+// ASCII filename outputs some ASCII logs and the other one with a Unicode
+// filename outputs some Unicode logs.
+
+const SEASON = {
+  english: "season",
+  chinese: "\u5b63",
+};
+const SEASONS = [
+  {
+    english: "spring",
+    chinese: "\u6625",
+    escapedChinese: "\\u6625",
+  },
+  {
+    english: "summer",
+    chinese: "\u590f",
+    escapedChinese: "\\u590f",
+  },
+  {
+    english: "autumn",
+    chinese: "\u79cb",
+    escapedChinese: "\\u79cb",
+  },
+  {
+    english: "winter",
+    chinese: "\u51ac",
+    escapedChinese: "\\u51ac",
+  },
+];
+
+// filenames
+const HTML_FILENAME = `test.html`;
+const JS_ASCII_FILENAME = `${SEASON.english}.js`;
+const JS_UNICODE_FILENAME = `${SEASON.chinese}.js`;
+const ENCODED_JS_UNICODE_FILENAME = encodeURIComponent(JS_UNICODE_FILENAME);
+
+// file contents
+const HTML_CONSOLE_OUTPUT = `Test filtering ${SEASON.english} names.`;
+const HTML_CONTENT = `<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Test filtering logs by filling keywords in the filter input.</title>
+<script>
+console.log("${HTML_CONSOLE_OUTPUT}");
+</script>
+<script src="/${JS_ASCII_FILENAME}"></script>
+<script src="/${ENCODED_JS_UNICODE_FILENAME}"></script>`;
+
+add_task(async function() {
+  const testUrl = createServerAndGetTestUrl();
+  const hud = await openNewTabAndConsole(testUrl);
+
+  // Let's wait for the last logged message of each file to be displayed in the
+  // output, in order to make sure all the logged messages have been displayed.
+  const lastSeason = SEASONS[SEASONS.length - 1];
+  await waitFor(() =>
+    findMessage(hud, lastSeason.english) &&
+    findMessage(hud, lastSeason.chinese)
+  );
+
+  // One external Javascript file outputs every season name in English, and the
+  // other Javascript file outputs every season name in Chinese.
+  // The HTML file outputs one line on its own.
+  // So the total number of all the logs is the doubled number of seasons plus
+  // one.
+  let visibleLogs = getVisibleLogs(hud);
+  is(visibleLogs.length, SEASONS.length * 2 + 1,
+       "the total number of all the logs before starting filtering");
+  checkLogContent(visibleLogs[0], HTML_CONSOLE_OUTPUT, HTML_FILENAME);
+  for (let i = 0; i < SEASONS.length; i++) {
+    checkLogContent(visibleLogs[i + 1], SEASONS[i].english, JS_ASCII_FILENAME);
+  }
+  for (let i = 0; i < SEASONS.length; i++) {
+    checkLogContent(visibleLogs[i + 1 + SEASONS.length], SEASONS[i].chinese,
+                    JS_UNICODE_FILENAME);
+  }
+
+  // All the logs outputted by the ASCII Javascript file are visible, the others
+  // are hidden.
+  setFilterInput(hud, JS_ASCII_FILENAME);
+  visibleLogs = getVisibleLogs(hud);
+  is(visibleLogs.length, SEASONS.length,
+       `the number of all the logs containing ${JS_ASCII_FILENAME}`);
+  for (let i = 0; i < SEASONS.length; i++) {
+    checkLogContent(visibleLogs[i], SEASONS[i].english, JS_ASCII_FILENAME);
+  }
+
+  // Every season name in English is outputted once.
+  for (let curSeason of SEASONS) {
+    setFilterInput(hud, curSeason.english);
+    visibleLogs = getVisibleLogs(hud);
+    is(visibleLogs.length, 1,
+         `the number of all the logs containing ${curSeason.english}`);
+    checkLogContent(visibleLogs[0], curSeason.english, JS_ASCII_FILENAME);
+  }
+
+  // All the logs outputted by the Unicode Javascript file are visible, the
+  // others are hidden.
+  setFilterInput(hud, JS_UNICODE_FILENAME);
+  visibleLogs = getVisibleLogs(hud);
+  is(visibleLogs.length, SEASONS.length,
+       `the number of all the logs containing ${JS_UNICODE_FILENAME}`);
+  for (let i = 0; i < SEASONS.length; i++) {
+    checkLogContent(visibleLogs[i], SEASONS[i].chinese, JS_UNICODE_FILENAME);
+  }
+
+  // Every season name in Chinese is outputted once.
+  for (let curSeason of SEASONS) {
+    setFilterInput(hud, curSeason.chinese);
+    visibleLogs = getVisibleLogs(hud);
+    is(visibleLogs.length, 1,
+         `the number of all the logs containing ${curSeason.chinese}`);
+    checkLogContent(visibleLogs[0], curSeason.chinese, JS_UNICODE_FILENAME);
+  }
+
+  // The filename of the ASCII Javascript file contains the English word season,
+  // so all the logs outputted by the file are visible, besides, the HTML
+  // outputs one line containing the English word season, so it is also visible.
+  // The other logs are hidden. So the number of all the visible logs is the
+  // season number plus one.
+  setFilterInput(hud, SEASON.english);
+  visibleLogs = getVisibleLogs(hud);
+  is(visibleLogs.length, SEASONS.length + 1,
+       `the number of all the logs containing ${SEASON.english}`);
+  checkLogContent(visibleLogs[0], HTML_CONSOLE_OUTPUT, HTML_FILENAME);
+  for (let i = 0; i < SEASONS.length; i++) {
+    checkLogContent(visibleLogs[i + 1], SEASONS[i].english, JS_ASCII_FILENAME);
+  }
+
+  // The filename of the Unicode Javascript file contains the Chinese word
+  // season, so all the logs outputted by the file are visible. The other logs
+  // are hidden. So the number of all the visible logs is the season number.
+  setFilterInput(hud, SEASON.chinese);
+  visibleLogs = getVisibleLogs(hud);
+  is(visibleLogs.length, SEASONS.length,
+       `the number of all the logs containing ${SEASON.chinese}`);
+  for (let i = 0; i < SEASONS.length; i++) {
+    checkLogContent(visibleLogs[i], SEASONS[i].chinese, JS_UNICODE_FILENAME);
+  }
+
+  // After clearing the text in the filter input box, all the logs are visible
+  // again.
+  clearFilterInput(hud);
+  visibleLogs = getVisibleLogs(hud);
+  is(visibleLogs.length, SEASONS.length * 2 + 1,
+       "the total number of all the logs after clearing filtering");
+  checkLogContent(visibleLogs[0], HTML_CONSOLE_OUTPUT, HTML_FILENAME);
+  for (let i = 0; i < SEASONS.length; i++) {
+    checkLogContent(visibleLogs[i + 1], SEASONS[i].english, JS_ASCII_FILENAME);
+  }
+  for (let i = 0; i < SEASONS.length; i++) {
+    checkLogContent(visibleLogs[i + 1 + SEASONS.length], SEASONS[i].chinese,
+                    JS_UNICODE_FILENAME);
+  }
+});
+
+// Create an HTTP server to simulate a response for the a URL request and return
+// the URL.
+function createServerAndGetTestUrl() {
+  const httpServer = createTestHTTPServer();
+
+  httpServer.registerContentType("html", "text/html");
+  httpServer.registerContentType("js", "application/javascript");
+
+  httpServer.registerPathHandler("/" + HTML_FILENAME,
+    function(request, response) {
+      response.setStatusLine(request.httpVersion, 200, "OK");
+      response.write(HTML_CONTENT);
+    }
+  );
+  httpServer.registerPathHandler("/" + JS_ASCII_FILENAME,
+    function(request, response) {
+      response.setStatusLine(request.httpVersion, 200, "OK");
+      let content = "";
+      for (let curSeason of SEASONS) {
+        content += `console.log("${curSeason.english}");`;
+      }
+      response.write(content);
+    }
+  );
+  httpServer.registerPathHandler("/" + ENCODED_JS_UNICODE_FILENAME,
+    function(request, response) {
+      response.setStatusLine(request.httpVersion, 200, "OK");
+      let content = "";
+      for (let curSeason of SEASONS) {
+        content += `console.log("${curSeason.escapedChinese}");`;
+      }
+      response.write(content);
+    }
+  );
+  const port = httpServer.identity.primaryPort;
+  return `http://localhost:${port}/${HTML_FILENAME}`;
+}
+
+function setFilterInput(hud, value) {
+  hud.ui.filterBox.focus();
+  hud.ui.filterBox.select();
+  EventUtils.sendString(value);
+}
+
+function clearFilterInput(hud) {
+  hud.ui.filterBox.focus();
+  hud.ui.filterBox.select();
+  EventUtils.synthesizeKey("KEY_Delete");
+}
+
+function getVisibleLogs(hud) {
+  let outputNode = hud.outputNode;
+  return outputNode.querySelectorAll(".message");
+}
+
+/**
+ * Check if the content of a log message is what we expect.
+ *
+ * @param Object node
+ *        The node for the log message.
+ * @param String expectedMessageBody
+ *        The string we expect to match the message body in the log message.
+ * @param String expectedFilename
+ *        The string we expect to match the filename in the log message.
+ */
+function checkLogContent(node, expectedMessageBody, expectedFilename) {
+  const messageBody = node.querySelector(".message-body").textContent;
+  const location = node.querySelector(".message-location").textContent;
+  // The location detail contains the line number and the column number, let's
+  // strip them to get the filename.
+  const filename = location.split(":")[0];
+
+  is(messageBody, expectedMessageBody, "the expected message body");
+  is(filename, expectedFilename, "the expected filename");
+}
--- a/devtools/client/webconsole/webpack.config.js
+++ b/devtools/client/webconsole/webpack.config.js
@@ -87,16 +87,17 @@ webpackConfig.resolve = {
     "devtools/client/shared/vendor/reselect": "reselect",
 
     "resource://gre/modules/AppConstants.jsm": path.join(__dirname, "../../client/shared/webpack/shims/app-constants-stub"),
 
     "devtools/client/framework/devtools": path.join(__dirname, "../../client/shared/webpack/shims/framework-devtools-shim"),
     "devtools/client/framework/menu": "devtools-modules/src/menu",
     "devtools/client/sourceeditor/editor": "devtools-source-editor/src/source-editor",
 
+    "devtools/client/shared/unicode-url": path.join(__dirname, "../../client/shared/webpack/shims/unicode-url-stub"),
     "devtools/client/shared/zoom-keys": "devtools-modules/src/zoom-keys",
 
     "devtools/shared/fronts/timeline": path.join(__dirname, "../../client/shared/webpack/shims/fronts-timeline-shim"),
     "devtools/shared/old-event-emitter": "devtools-modules/src/utils/event-emitter",
     "devtools/shared/event-emitter": "devtools-modules/src/utils/event-emitter",
     "devtools/shared/client/debugger-client": path.join(__dirname, "test/fixtures/DebuggerClient"),
     "devtools/shared/platform/clipboard": path.join(__dirname, "../../client/shared/webpack/shims/platform-clipboard-stub"),
     "devtools/shared/platform/stack": path.join(__dirname, "../../client/shared/webpack/shims/platform-stack-stub"),
--- a/devtools/client/webide/content/addons.js
+++ b/devtools/client/webide/content/addons.js
@@ -3,41 +3,40 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
 const Services = require("Services");
 const {gDevTools} = require("devtools/client/framework/devtools");
 const {GetAvailableAddons, ForgetAddonsList} = require("devtools/client/webide/modules/addons");
 const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties");
 
-window.addEventListener("load", function () {
-  document.querySelector("#aboutaddons").onclick = function () {
+window.addEventListener("load", function() {
+  document.querySelector("#aboutaddons").onclick = function() {
     let browserWin = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
     if (browserWin && browserWin.BrowserOpenAddonsMgr) {
       browserWin.BrowserOpenAddonsMgr("addons://list/extension");
     }
   };
   document.querySelector("#close").onclick = CloseUI;
   BuildUI(GetAvailableAddons());
 }, {capture: true, once: true});
 
-window.addEventListener("unload", function () {
+window.addEventListener("unload", function() {
   ForgetAddonsList();
 }, {capture: true, once: true});
 
 function CloseUI() {
   window.parent.UI.openProject();
 }
 
 function BuildUI(addons) {
   BuildItem(addons.adb, "adb");
 }
 
 function BuildItem(addon, type) {
-
   function onAddonUpdate(arg) {
     progress.removeAttribute("value");
     li.setAttribute("status", addon.status);
     status.textContent = Strings.GetStringFromName("addons_status_" + addon.status);
   }
 
   function onAddonFailure(arg) {
     window.parent.UI.reportError("error_operationFail", arg);
@@ -50,17 +49,17 @@ function BuildItem(addon, type) {
       progress.value = arg;
     }
   }
 
   addon.on("update", onAddonUpdate);
   addon.on("failure", onAddonFailure);
   addon.on("progress", onAddonProgress);
 
-  window.addEventListener("unload", function () {
+  window.addEventListener("unload", function() {
     addon.off("update", onAddonUpdate);
     addon.off("failure", onAddonFailure);
     addon.off("progress", onAddonProgress);
   }, {once: true});
 
   let li = document.createElement("li");
   li.setAttribute("status", addon.status);
 
--- a/devtools/client/webide/content/details.js
+++ b/devtools/client/webide/content/details.js
@@ -1,23 +1,22 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
-const Services = require("Services");
 const {AppManager} = require("devtools/client/webide/modules/app-manager");
 
-window.addEventListener("load", function () {
+window.addEventListener("load", function() {
   document.addEventListener("visibilitychange", updateUI, true);
   AppManager.on("app-manager-update", onAppManagerUpdate);
   updateUI();
 }, {capture: true, once: true});
 
-window.addEventListener("unload", function () {
+window.addEventListener("unload", function() {
   AppManager.off("app-manager-update", onAppManagerUpdate);
 }, {capture: true, once: true});
 
 function onAppManagerUpdate(what, details) {
   if (what == "project" ||
       what == "project-validated") {
     updateUI();
   }
@@ -35,17 +34,16 @@ function resetUI() {
   document.querySelector("h1").textContent = "";
   document.querySelector("#description").textContent = "";
   document.querySelector("#type").textContent = "";
   document.querySelector("#manifestURL").textContent = "";
   document.querySelector("#location").textContent = "";
 
   document.querySelector("#errorslist").innerHTML = "";
   document.querySelector("#warningslist").innerHTML = "";
-
 }
 
 function updateUI() {
   resetUI();
 
   let project = AppManager.selectedProject;
   if (!project) {
     return;
@@ -113,11 +111,13 @@ function updateUI() {
       li.textContent = w;
       warningsNode.appendChild(li);
     }
   }
 
   AppManager.update("details");
 }
 
+// Used in details.xhtml.
+/* exported removeProject */
 function removeProject() {
   AppManager.removeSelectedProject();
 }
--- a/devtools/client/webide/content/devicepreferences.js
+++ b/devtools/client/webide/content/devicepreferences.js
@@ -4,29 +4,29 @@
 
 const {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
 const {AppManager} = require("devtools/client/webide/modules/app-manager");
 const {Connection} = require("devtools/shared/client/connection-manager");
 const ConfigView = require("devtools/client/webide/modules/config-view");
 
 var configView = new ConfigView(window);
 
-window.addEventListener("load", function () {
+window.addEventListener("load", function() {
   AppManager.on("app-manager-update", OnAppManagerUpdate);
   document.getElementById("close").onclick = CloseUI;
   document.getElementById("device-fields").onchange = UpdateField;
   document.getElementById("device-fields").onclick = CheckReset;
   document.getElementById("search-bar").onkeyup = document.getElementById("search-bar").onclick = SearchField;
   document.getElementById("custom-value").onclick = UpdateNewField;
   document.getElementById("custom-value-type").onchange = ClearNewFields;
   document.getElementById("add-custom-field").onkeyup = CheckNewFieldSubmit;
   BuildUI();
 }, {capture: true, once: true});
 
-window.addEventListener("unload", function () {
+window.addEventListener("unload", function() {
   AppManager.off("app-manager-update", OnAppManagerUpdate);
 }, {once: true});
 
 function CloseUI() {
   window.parent.UI.openProject();
 }
 
 function OnAppManagerUpdate(what) {
@@ -54,17 +54,19 @@ function CheckReset(event) {
 function UpdateField(event) {
   configView.updateField(event);
 }
 
 function SearchField(event) {
   configView.search(event);
 }
 
-var getAllPrefs; // Used by tests
+// Used by tests
+/* exported getAllPrefs */
+var getAllPrefs;
 function BuildUI() {
   configView.resetTable();
 
   if (AppManager.connection &&
       AppManager.connection.status == Connection.Status.CONNECTED &&
       AppManager.preferenceFront) {
     configView.front = AppManager.preferenceFront;
     configView.kind = "Pref";
--- a/devtools/client/webide/content/newapp.js
+++ b/devtools/client/webide/content/newapp.js
@@ -1,30 +1,29 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
-const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
 const Services = require("Services");
 const {FileUtils} = require("resource://gre/modules/FileUtils.jsm");
 const {AppProjects} = require("devtools/client/webide/modules/app-projects");
 const {AppManager} = require("devtools/client/webide/modules/app-manager");
 const {getJSON} = require("devtools/client/shared/getjson");
 
 ChromeUtils.defineModuleGetter(this, "ZipUtils", "resource://gre/modules/ZipUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "Downloads", "resource://gre/modules/Downloads.jsm");
 
 const TEMPLATES_URL = "devtools.webide.templatesURL";
 
 var gTemplateList = null;
 
-window.addEventListener("load", function () {
+window.addEventListener("load", function() {
   let projectNameNode = document.querySelector("#project-name");
   projectNameNode.addEventListener("input", canValidate, true);
   getTemplatesJSON();
 }, {capture: true, once: true});
 
 function getTemplatesJSON() {
   getJSON(TEMPLATES_URL).then(list => {
     if (!Array.isArray(list)) {
@@ -62,18 +61,17 @@ function getTemplatesJSON() {
       doOK();
     }
   }, (e) => {
     failAndBail("Can't download app templates: " + e);
   });
 }
 
 function failAndBail(msg) {
-  let promptService = Cc["@mozilla.org/embedcomp/prompt-service;1"].getService(Ci.nsIPromptService);
-  promptService.alert(window, "error", msg);
+  Services.prompt.alert(window, "error", msg);
   window.close();
 }
 
 function canValidate() {
   let projectNameNode = document.querySelector("#project-name");
   let dialogNode = document.querySelector("dialog");
   if (projectNameNode.value.length > 0) {
     dialogNode.removeAttribute("buttondisabledaccept");
@@ -149,17 +147,19 @@ function doOK() {
       target.remove(false);
       AppProjects.addPackaged(folder).then((project) => {
         window.arguments[0].location = project.location;
         AppManager.validateAndUpdateProject(project).then(() => {
           if (project.manifest) {
             project.manifest.name = projectName;
             AppManager.writeManifest(project).then(() => {
               AppManager.validateAndUpdateProject(project).then(
-                () => {window.close();}, bail);
+                () => {
+                  window.close();
+                }, bail);
             }, bail);
           } else {
             bail("Manifest not found");
           }
         }, bail);
       }, bail);
     }, bail);
   }, bail);
--- a/devtools/client/webide/content/prefs.js
+++ b/devtools/client/webide/content/prefs.js
@@ -1,36 +1,35 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm", {});
 
-window.addEventListener("load", function () {
+window.addEventListener("load", function() {
   // Listen to preference changes
   let inputs = document.querySelectorAll("[data-pref]");
   for (let i of inputs) {
     let pref = i.dataset.pref;
     Services.prefs.addObserver(pref, FillForm);
     i.addEventListener("change", SaveForm);
   }
 
   // Buttons
   document.querySelector("#close").onclick = CloseUI;
   document.querySelector("#restore").onclick = RestoreDefaults;
   document.querySelector("#manageComponents").onclick = ShowAddons;
 
   // Initialize the controls
   FillForm();
-
 }, {capture: true, once: true});
 
-window.addEventListener("unload", function () {
+window.addEventListener("unload", function() {
   let inputs = document.querySelectorAll("[data-pref]");
   for (let i of inputs) {
     let pref = i.dataset.pref;
     i.removeEventListener("change", SaveForm);
     Services.prefs.removeObserver(pref, FillForm);
   }
 }, {capture: true, once: true});
 
--- a/devtools/client/webide/content/project-listing.js
+++ b/devtools/client/webide/content/project-listing.js
@@ -4,26 +4,26 @@
 
 /* eslint-env browser */
 
 const {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
 const ProjectList = require("devtools/client/webide/modules/project-list");
 
 var projectList = new ProjectList(window, window.parent);
 
-window.addEventListener("load", function () {
+window.addEventListener("load", function() {
   document.getElementById("new-app").onclick = CreateNewApp;
   document.getElementById("hosted-app").onclick = ImportHostedApp;
   document.getElementById("packaged-app").onclick = ImportPackagedApp;
   document.getElementById("refresh-tabs").onclick = RefreshTabs;
   projectList.update();
   projectList.updateCommands();
 }, {capture: true, once: true});
 
-window.addEventListener("unload", function () {
+window.addEventListener("unload", function() {
   projectList.destroy();
 }, {once: true});
 
 function RefreshTabs() {
   projectList.refreshTabs();
 }
 
 function CreateNewApp() {
--- a/devtools/client/webide/content/project-panel.js
+++ b/devtools/client/webide/content/project-panel.js
@@ -1,11 +1,12 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+/* exported ProjectPanel */
 var ProjectPanel = {
   // TODO: Expand function to save toggle state.
-  toggleSidebar: function () {
+  toggleSidebar: function() {
     document.querySelector("#project-listing-panel").setAttribute("sidebar-displayed", true);
     document.querySelector("#project-listing-splitter").setAttribute("sidebar-displayed", true);
   }
 };
--- a/devtools/client/webide/content/runtime-listing.js
+++ b/devtools/client/webide/content/runtime-listing.js
@@ -2,30 +2,30 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
 const RuntimeList = require("devtools/client/webide/modules/runtime-list");
 
 var runtimeList = new RuntimeList(window, window.parent);
 
-window.addEventListener("load", function () {
+window.addEventListener("load", function() {
   document.getElementById("runtime-screenshot").onclick = TakeScreenshot;
   document.getElementById("runtime-details").onclick = ShowRuntimeDetails;
   document.getElementById("runtime-disconnect").onclick = DisconnectRuntime;
   document.getElementById("runtime-preferences").onclick = ShowDevicePreferences;
   document.getElementById("runtime-settings").onclick = ShowSettings;
   document.getElementById("runtime-panel-noadbhelper").onclick = ShowAddons;
   document.getElementById("runtime-panel-nousbdevice").onclick = ShowTroubleShooting;
   document.getElementById("refresh-devices").onclick = RefreshScanners;
   runtimeList.update();
   runtimeList.updateCommands();
 }, {capture: true, once: true});
 
-window.addEventListener("unload", function () {
+window.addEventListener("unload", function() {
   runtimeList.destroy();
 }, {once: true});
 
 function TakeScreenshot() {
   runtimeList.takeScreenshot();
 }
 
 function ShowRuntimeDetails() {
--- a/devtools/client/webide/content/runtime-panel.js
+++ b/devtools/client/webide/content/runtime-panel.js
@@ -1,11 +1,12 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+/* exported RuntimePanel */
 var RuntimePanel = {
   // TODO: Expand function to save toggle state.
-  toggleSidebar: function () {
+  toggleSidebar: function() {
     document.querySelector("#runtime-listing-panel").setAttribute("sidebar-displayed", true);
     document.querySelector("#runtime-listing-splitter").setAttribute("sidebar-displayed", true);
   }
 };
--- a/devtools/client/webide/content/runtimedetails.js
+++ b/devtools/client/webide/content/runtimedetails.js
@@ -6,29 +6,29 @@ const {require} = ChromeUtils.import("re
 const Services = require("Services");
 const {AppManager} = require("devtools/client/webide/modules/app-manager");
 const {Connection} = require("devtools/shared/client/connection-manager");
 const {RuntimeTypes} = require("devtools/client/webide/modules/runtimes");
 const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties");
 
 const UNRESTRICTED_HELP_URL = "https://developer.mozilla.org/docs/Tools/WebIDE/Running_and_debugging_apps#Unrestricted_app_debugging_%28including_certified_apps_main_process_etc.%29";
 
-window.addEventListener("load", function () {
+window.addEventListener("load", function() {
   document.querySelector("#close").onclick = CloseUI;
   document.querySelector("#devtools-check button").onclick = EnableCertApps;
   document.querySelector("#adb-check button").onclick = RootADB;
-  document.querySelector("#unrestricted-privileges").onclick = function () {
+  document.querySelector("#unrestricted-privileges").onclick = function() {
     window.parent.UI.openInBrowser(UNRESTRICTED_HELP_URL);
   };
   AppManager.on("app-manager-update", OnAppManagerUpdate);
   BuildUI();
   CheckLockState();
 }, {capture: true, once: true});
 
-window.addEventListener("unload", function () {
+window.addEventListener("unload", function() {
   AppManager.off("app-manager-update", OnAppManagerUpdate);
 }, {once: true});
 
 function CloseUI() {
   window.parent.UI.openProject();
 }
 
 function OnAppManagerUpdate(what) {
@@ -47,17 +47,19 @@ function generateFields(json) {
     tr.appendChild(td);
     td = document.createElement("td");
     td.textContent = json[name];
     tr.appendChild(td);
     table.appendChild(tr);
   }
 }
 
-var getDescriptionPromise; // Used by tests
+// Used by tests
+/* exported getDescriptionPromise */
+var getDescriptionPromise;
 function BuildUI() {
   let table = document.querySelector("table");
   table.innerHTML = "";
   if (AppManager.connection &&
       AppManager.connection.status == Connection.Status.CONNECTED &&
       AppManager.deviceFront) {
     getDescriptionPromise = AppManager.deviceFront.getDescription()
                             .then(json => generateFields(json));
@@ -65,17 +67,16 @@ function BuildUI() {
     CloseUI();
   }
 }
 
 function CheckLockState() {
   let adbCheckResult = document.querySelector("#adb-check > .yesno");
   let devtoolsCheckResult = document.querySelector("#devtools-check > .yesno");
   let flipCertPerfButton = document.querySelector("#devtools-check button");
-  let adbRootButton = document.querySelector("#adb-check button");
   let flipCertPerfAction = document.querySelector("#devtools-check > .action");
   let adbRootAction = document.querySelector("#adb-check > .action");
 
   let sYes = Strings.GetStringFromName("runtimedetails_checkyes");
   let sNo = Strings.GetStringFromName("runtimedetails_checkno");
   let sUnknown = Strings.GetStringFromName("runtimedetails_checkunknown");
   let sNotUSB = Strings.GetStringFromName("runtimedetails_notUSBDevice");
 
@@ -83,17 +84,16 @@ function CheckLockState() {
   flipCertPerfAction.setAttribute("hidden", "true");
   adbRootAction.setAttribute("hidden", "true");
 
   adbCheckResult.textContent = sUnknown;
   devtoolsCheckResult.textContent = sUnknown;
 
   if (AppManager.connection &&
       AppManager.connection.status == Connection.Status.CONNECTED) {
-
     // ADB check
     if (AppManager.selectedRuntime.type === RuntimeTypes.USB) {
       let device = AppManager.selectedRuntime.device;
       if (device && device.summonRoot) {
         device.isRoot().then(isRoot => {
           if (isRoot) {
             adbCheckResult.textContent = sYes;
             flipCertPerfButton.removeAttribute("disabled");
@@ -120,19 +120,17 @@ function CheckLockState() {
           devtoolsCheckResult.textContent = sYes;
         }
       }, console.error);
     } catch (e) {
       // Exception. pref actor is only accessible if forbird-certified-apps is false
       devtoolsCheckResult.textContent = sNo;
       flipCertPerfAction.removeAttribute("hidden");
     }
-
   }
-
 }
 
 function EnableCertApps() {
   let device = AppManager.selectedRuntime.device;
   // TODO: Remove `network.disable.ipc.security` once bug 1125916 is fixed.
   device.shell(
     "stop b2g && " +
     "cd /data/b2g/mozilla/*.default/ && " +
--- a/devtools/client/webide/content/webide.js
+++ b/devtools/client/webide/content/webide.js
@@ -1,32 +1,34 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+// These files are loaded via webide.xul
+/* import-globals-from project-panel.js */
+/* import-globals-from runtime-panel.js */
+
 const {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
 const {gDevTools} = require("devtools/client/framework/devtools");
 const {gDevToolsBrowser} = require("devtools/client/framework/devtools-browser");
 const {Toolbox} = require("devtools/client/framework/toolbox");
 const Services = require("Services");
 const {AppProjects} = require("devtools/client/webide/modules/app-projects");
 const {Connection} = require("devtools/shared/client/connection-manager");
 const {AppManager} = require("devtools/client/webide/modules/app-manager");
 const EventEmitter = require("devtools/shared/event-emitter");
 const promise = require("promise");
 const {GetAvailableAddons} = require("devtools/client/webide/modules/addons");
 const {getJSON} = require("devtools/client/shared/getjson");
-const utils = require("devtools/client/webide/modules/utils");
 const Telemetry = require("devtools/client/shared/telemetry");
 const {RuntimeScanners} = require("devtools/client/webide/modules/runtimes");
 const {showDoorhanger} = require("devtools/client/shared/doorhanger");
 
 const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties");
 
-const HTML = "http://www.w3.org/1999/xhtml";
 const HELP_URL = "https://developer.mozilla.org/docs/Tools/WebIDE/Troubleshooting";
 
 const MAX_ZOOM = 1.4;
 const MIN_ZOOM = 0.6;
 
 [["AppManager", AppManager],
  ["AppProjects", AppProjects],
  ["Connection", Connection]].forEach(([key, value]) => {
@@ -36,26 +38,26 @@ const MIN_ZOOM = 0.6;
      writable: false
    });
  });
 
 // Download remote resources early
 getJSON("devtools.webide.templatesURL");
 getJSON("devtools.devices.url");
 
-window.addEventListener("load", function () {
+window.addEventListener("load", function() {
   UI.init();
 }, {once: true});
 
-window.addEventListener("unload", function () {
+window.addEventListener("unload", function() {
   UI.destroy();
 }, {once: true});
 
 var UI = {
-  init: function () {
+  init: function() {
     this._telemetry = new Telemetry();
     this._telemetry.toolOpened("webide");
 
     AppManager.init();
 
     this.appManagerUpdate = this.appManagerUpdate.bind(this);
     AppManager.on("app-manager-update", this.appManagerUpdate);
 
@@ -90,26 +92,26 @@ var UI = {
                                .getInterface(Ci.nsIWebNavigation)
                                .QueryInterface(Ci.nsIDocShell)
                                .contentViewer;
     this.contentViewer.fullZoom = Services.prefs.getCharPref("devtools.webide.zoom");
 
     gDevToolsBrowser.isWebIDEInitialized.resolve();
   },
 
-  destroy: function () {
+  destroy: function() {
     window.removeEventListener("focus", this.onfocus, true);
     AppManager.off("app-manager-update", this.appManagerUpdate);
     AppManager.destroy();
     this.updateConnectionTelemetry();
     this._telemetry.toolClosed("webide");
     this._telemetry.destroy();
   },
 
-  onfocus: function () {
+  onfocus: function() {
     // Because we can't track the activity in the folder project,
     // we need to validate the project regularly. Let's assume that
     // if a modification happened, it happened when the window was
     // not focused.
     if (AppManager.selectedProject &&
         AppManager.selectedProject.type != "mainProcess" &&
         AppManager.selectedProject.type != "runtimeApp" &&
         AppManager.selectedProject.type != "tab") {
@@ -117,30 +119,30 @@ var UI = {
     }
 
     // Hook to display promotional Developer Edition doorhanger. Only displayed once.
     // Hooked into the `onfocus` event because sometimes does not work
     // when run at the end of `init`. ¯\(°_o)/¯
     showDoorhanger({ window, type: "deveditionpromo", anchor: document.querySelector("#deck") });
   },
 
-  appManagerUpdate: function (what, details) {
+  appManagerUpdate: function(what, details) {
     // Got a message from app-manager.js
     // See AppManager.update() for descriptions of what these events mean.
     switch (what) {
       case "runtime-list":
         this.autoConnectRuntime();
         break;
       case "connection":
         this.updateRuntimeButton();
         this.updateCommands();
         this.updateConnectionTelemetry();
         break;
       case "project":
-        this._updatePromise = (async function () {
+        this._updatePromise = (async function() {
           UI.updateTitle();
           await UI.destroyToolbox();
           UI.updateCommands();
           UI.openProject();
           await UI.autoStartProject();
           UI.autoOpenToolbox();
           UI.saveLastSelectedProject();
           UI.updateRemoveProjectButton();
@@ -173,83 +175,83 @@ var UI = {
         break;
       case "runtime-targets":
         this.autoSelectProject();
         break;
     }
     this._updatePromise = promise.resolve();
   },
 
-  openInBrowser: function (url) {
+  openInBrowser: function(url) {
     // Open a URL in a Firefox window
     let mainWindow = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
     if (mainWindow) {
       mainWindow.openWebLinkIn(url, "tab");
-      mainWindow.focus()
+      mainWindow.focus();
     } else {
       window.open(url);
     }
   },
 
-  updateTitle: function () {
+  updateTitle: function() {
     let project = AppManager.selectedProject;
     if (project) {
       window.document.title = Strings.formatStringFromName("title_app", [project.name], 1);
     } else {
       window.document.title = Strings.GetStringFromName("title_noApp");
     }
   },
 
   /** ******** BUSY UI **********/
 
   _busyTimeout: null,
   _busyOperationDescription: null,
   _busyPromise: null,
 
-  busy: function () {
+  busy: function() {
     let win = document.querySelector("window");
     win.classList.add("busy");
     win.classList.add("busy-undetermined");
     this.updateCommands();
     this.update("busy");
   },
 
-  unbusy: function () {
+  unbusy: function() {
     let win = document.querySelector("window");
     win.classList.remove("busy");
     win.classList.remove("busy-determined");
     win.classList.remove("busy-undetermined");
     this.updateCommands();
     this.update("unbusy");
     this._busyPromise = null;
   },
 
-  setupBusyTimeout: function () {
+  setupBusyTimeout: function() {
     this.cancelBusyTimeout();
     this._busyTimeout = setTimeout(() => {
       this.unbusy();
       UI.reportError("error_operationTimeout", this._busyOperationDescription);
     }, Services.prefs.getIntPref("devtools.webide.busyTimeout"));
   },
 
-  cancelBusyTimeout: function () {
+  cancelBusyTimeout: function() {
     clearTimeout(this._busyTimeout);
   },
 
-  busyWithProgressUntil: function (promise, operationDescription) {
+  busyWithProgressUntil: function(promise, operationDescription) {
     let busy = this.busyUntil(promise, operationDescription);
     let win = document.querySelector("window");
     let progress = document.querySelector("#action-busy-determined");
     progress.mode = "undetermined";
     win.classList.add("busy-determined");
     win.classList.remove("busy-undetermined");
     return busy;
   },
 
-  busyUntil: function (promise, operationDescription) {
+  busyUntil: function(promise, operationDescription) {
     // Freeze the UI until the promise is resolved. A timeout will unfreeze the
     // UI, just in case the promise never gets resolved.
     this._busyPromise = promise;
     this._busyOperationDescription = operationDescription;
     this.setupBusyTimeout();
     this.busy();
     promise.then(() => {
       this.cancelBusyTimeout();
@@ -271,62 +273,62 @@ var UI = {
           console.error(e);
         }
       }
       this.unbusy();
     });
     return promise;
   },
 
-  reportError: function (l10nProperty, ...l10nArgs) {
+  reportError: function(l10nProperty, ...l10nArgs) {
     let text;
 
     if (l10nArgs.length > 0) {
       text = Strings.formatStringFromName(l10nProperty, l10nArgs, l10nArgs.length);
     } else {
       text = Strings.GetStringFromName(l10nProperty);
     }
 
     console.error(text);
 
     let buttons = [{
       label: Strings.GetStringFromName("notification_showTroubleShooting_label"),
       accessKey: Strings.GetStringFromName("notification_showTroubleShooting_accesskey"),
-      callback: function () {
+      callback: function() {
         Cmds.showTroubleShooting();
       }
     }];
 
     let nbox = document.querySelector("#notificationbox");
     nbox.removeAllNotifications(true);
     nbox.appendNotification(text, "webide:errornotification", null,
                             nbox.PRIORITY_WARNING_LOW, buttons);
   },
 
-  dismissErrorNotification: function () {
+  dismissErrorNotification: function() {
     let nbox = document.querySelector("#notificationbox");
     nbox.removeAllNotifications(true);
   },
 
   /** ******** COMMANDS **********/
 
   /**
    * This module emits various events when state changes occur.
    *
    * The events this module may emit include:
    *   busy:
    *     The window is currently busy and certain UI functions may be disabled.
    *   unbusy:
    *     The window is not busy and certain UI functions may be re-enabled.
    */
-  update: function (what, details) {
+  update: function(what, details) {
     this.emit("webide-update", what, details);
   },
 
-  updateCommands: function () {
+  updateCommands: function() {
     // Action commands
     let playCmd = document.querySelector("#cmd_play");
     let stopCmd = document.querySelector("#cmd_stop");
     let debugCmd = document.querySelector("#cmd_toggleToolbox");
     let playButton = document.querySelector("#action-button-play");
     let projectPanelCmd = document.querySelector("#cmd_showProjectPanel");
 
     if (document.querySelector("window").classList.contains("busy")) {
@@ -357,23 +359,21 @@ var UI = {
       if (AppManager.selectedProject.type == "runtimeApp") {
         playCmd.removeAttribute("disabled");
       } else if (AppManager.selectedProject.type == "tab") {
         playCmd.removeAttribute("disabled");
         stopCmd.setAttribute("disabled", "true");
       } else if (AppManager.selectedProject.type == "mainProcess") {
         playCmd.setAttribute("disabled", "true");
         stopCmd.setAttribute("disabled", "true");
-      } else {
-        if (AppManager.selectedProject.errorsCount == 0 &&
+      } else if (AppManager.selectedProject.errorsCount == 0 &&
             AppManager.runtimeCanHandleApps()) {
-          playCmd.removeAttribute("disabled");
-        } else {
-          playCmd.setAttribute("disabled", "true");
-        }
+        playCmd.removeAttribute("disabled");
+      } else {
+        playCmd.setAttribute("disabled", "true");
       }
     }
 
     // Runtime commands
     let screenshotCmd = document.querySelector("#cmd_takeScreenshot");
     let detailsCmd = document.querySelector("#cmd_showRuntimeDetails");
     let disconnectCmd = document.querySelector("#cmd_disconnectRuntime");
     let devicePrefsCmd = document.querySelector("#cmd_showDevicePrefs");
@@ -404,17 +404,17 @@ var UI = {
     } else {
       runtimePanelButton.removeAttribute("active");
       runtimePanelButton.setAttribute("hidden", "true");
     }
 
     projectPanelCmd.removeAttribute("disabled");
   },
 
-  updateRemoveProjectButton: function () {
+  updateRemoveProjectButton: function() {
     // Remove command
     let removeCmdNode = document.querySelector("#cmd_removeProject");
     if (AppManager.selectedProject) {
       removeCmdNode.removeAttribute("disabled");
     } else {
       removeCmdNode.setAttribute("disabled", "true");
     }
   },
@@ -424,25 +424,25 @@ var UI = {
   get lastConnectedRuntime() {
     return Services.prefs.getCharPref("devtools.webide.lastConnectedRuntime");
   },
 
   set lastConnectedRuntime(runtime) {
     Services.prefs.setCharPref("devtools.webide.lastConnectedRuntime", runtime);
   },
 
-  autoConnectRuntime: function () {
+  autoConnectRuntime: function() {
     // Automatically reconnect to the previously selected runtime,
     // if available and has an ID and feature is enabled
     if (AppManager.selectedRuntime ||
         !Services.prefs.getBoolPref("devtools.webide.autoConnectRuntime") ||
         !this.lastConnectedRuntime) {
       return;
     }
-    let [_, type, id] = this.lastConnectedRuntime.match(/^(\w+):(.+)$/);
+    let [ , type, id] = this.lastConnectedRuntime.match(/^(\w+):(.+)$/);
 
     type = type.toLowerCase();
 
     // Local connection is mapped to AppManager.runtimeList.other array
     if (type == "local") {
       type = "other";
     }
 
@@ -456,17 +456,17 @@ var UI = {
           // Only want one auto-connect attempt, so clear last runtime value
           this.lastConnectedRuntime = "";
           this.connectToRuntime(runtime);
         }
       }
     }
   },
 
-  connectToRuntime: function (runtime) {
+  connectToRuntime: function(runtime) {
     let name = runtime.name;
     let promise = AppManager.connectToRuntime(runtime);
     promise.then(() => this.initConnectionTelemetry())
            .catch(() => {
              // Empty rejection handler to silence uncaught rejection warnings
              // |busyUntil| will listen for rejections.
              // Bug 1121100 may find a better way to silence these.
            });
@@ -474,27 +474,27 @@ var UI = {
     // Stop busy timeout for runtimes that take unknown or long amounts of time
     // to connect.
     if (runtime.prolongedConnection) {
       this.cancelBusyTimeout();
     }
     return promise;
   },
 
-  updateRuntimeButton: function () {
+  updateRuntimeButton: function() {
     let labelNode = document.querySelector("#runtime-panel-button > .panel-button-label");
     if (!AppManager.selectedRuntime) {
       labelNode.setAttribute("value", Strings.GetStringFromName("runtimeButton_label"));
     } else {
       let name = AppManager.selectedRuntime.name;
       labelNode.setAttribute("value", name);
     }
   },
 
-  saveLastConnectedRuntime: function () {
+  saveLastConnectedRuntime: function() {
     if (AppManager.selectedRuntime &&
         AppManager.selectedRuntime.id !== undefined) {
       this.lastConnectedRuntime = AppManager.selectedRuntime.type + ":" +
                                   AppManager.selectedRuntime.id;
     } else {
       this.lastConnectedRuntime = "";
     }
   },
@@ -503,53 +503,53 @@ var UI = {
 
   _actionsToLog: new Set(),
 
   /**
    * For each new connection, track whether play and debug were ever used.  Only
    * one value is collected for each button, even if they are used multiple
    * times during a connection.
    */
-  initConnectionTelemetry: function () {
+  initConnectionTelemetry: function() {
     this._actionsToLog.add("play");
     this._actionsToLog.add("debug");
   },
 
   /**
    * Action occurred.  Log that it happened, and remove it from the loggable
    * set.
    */
-  onAction: function (action) {
+  onAction: function(action) {
     if (!this._actionsToLog.has(action)) {
       return;
     }
     this.logActionState(action, true);
     this._actionsToLog.delete(action);
   },
 
   /**
    * Connection status changed or we are shutting down.  Record any loggable
    * actions as having not occurred.
    */
-  updateConnectionTelemetry: function () {
+  updateConnectionTelemetry: function() {
     for (let action of this._actionsToLog.values()) {
       this.logActionState(action, false);
     }
     this._actionsToLog.clear();
   },
 
-  logActionState: function (action, state) {
+  logActionState: function(action, state) {
     let histogramId = "DEVTOOLS_WEBIDE_CONNECTION_" +
                       action.toUpperCase() + "_USED";
     this._telemetry.log(histogramId, state);
   },
 
   /** ******** PROJECTS **********/
 
-  openProject: function () {
+  openProject: function() {
     let project = AppManager.selectedProject;
 
     if (!project) {
       this.resetDeck();
       return;
     }
 
     this.selectDeckPanel("details");
@@ -606,17 +606,17 @@ var UI = {
 
     // Select project
     AppManager.selectedProject = project;
 
     this._telemetry.actionOccurred("webideImportProject");
   },
 
   // Remember the last selected project on the runtime
-  saveLastSelectedProject: function () {
+  saveLastSelectedProject: function() {
     let shouldRestore = Services.prefs.getBoolPref("devtools.webide.restoreLastProject");
     if (!shouldRestore) {
       return;
     }
 
     // Ignore unselection of project on runtime disconnection
     if (!AppManager.connected) {
       return;
@@ -639,33 +639,33 @@ var UI = {
     if (type) {
       Services.prefs.setCharPref("devtools.webide.lastSelectedProject",
                                  type + ":" + project);
     } else {
       Services.prefs.clearUserPref("devtools.webide.lastSelectedProject");
     }
   },
 
-  autoSelectProject: function () {
+  autoSelectProject: function() {
     if (AppManager.selectedProject) {
       return;
     }
     let shouldRestore = Services.prefs.getBoolPref("devtools.webide.restoreLastProject");
     if (!shouldRestore) {
       return;
     }
     let pref = Services.prefs.getCharPref("devtools.webide.lastSelectedProject");
     if (!pref) {
       return;
     }
     let m = pref.match(/^(\w+):(.*)$/);
     if (!m) {
       return;
     }
-    let [_, type, project] = m;
+    let [ , type, project] = m;
 
     if (type == "local") {
       let lastProject = AppProjects.get(project);
       if (lastProject) {
         AppManager.selectedProject = lastProject;
       }
     }
 
@@ -690,44 +690,44 @@ var UI = {
           name: app.manifest.name
         };
       }
     }
   },
 
   /** ******** DECK **********/
 
-  setupDeck: function () {
+  setupDeck: function() {
     let iframes = document.querySelectorAll("#deck > iframe");
     for (let iframe of iframes) {
       iframe.tooltip = "aHTMLTooltip";
     }
   },
 
-  resetFocus: function () {
+  resetFocus: function() {
     document.commandDispatcher.focusedElement = document.documentElement;
   },
 
-  selectDeckPanel: function (id) {
+  selectDeckPanel: function(id) {
     let deck = document.querySelector("#deck");
     if (deck.selectedPanel && deck.selectedPanel.id === "deck-panel-" + id) {
       // This panel is already displayed.
       return;
     }
     this.resetFocus();
     let panel = deck.querySelector("#deck-panel-" + id);
     let lazysrc = panel.getAttribute("lazysrc");
     if (lazysrc) {
       panel.removeAttribute("lazysrc");
       panel.setAttribute("src", lazysrc);
     }
     deck.selectedPanel = panel;
   },
 
-  resetDeck: function () {
+  resetDeck: function() {
     this.resetFocus();
     let deck = document.querySelector("#deck");
     deck.selectedPanel = null;
   },
 
   async checkRuntimeVersion() {
     if (AppManager.connected) {
       let { client } = AppManager.connection;
@@ -751,17 +751,17 @@ var UI = {
    *   * Toggle toolbox wrench in WebIDE
    *   * Disconnect the current runtime gracefully
    *   * Yank cord out of device
    *   * Close or crash the app/tab
    * We can't know for sure which one was used here, so reset the
    * |toolboxPromise| since someone must be destroying it to reach here,
    * and call our own close method.
    */
-  _onToolboxClosed: function (promise, iframe) {
+  _onToolboxClosed: function(promise, iframe) {
     // Only save toolbox size, disable wrench button, workaround focus issue...
     // if we are closing the last toolbox:
     //  - toolboxPromise is nullified by destroyToolbox and is still null here
     //    if no other toolbox has been opened in between,
     //  - having two distinct promise means we are receiving closed event
     //    for a previous, non-current, toolbox.
     if (!this.toolboxPromise || this.toolboxPromise === promise) {
       this.toolboxPromise = null;
@@ -772,27 +772,27 @@ var UI = {
       splitter.setAttribute("hidden", "true");
       document.querySelector("#action-button-debug").removeAttribute("active");
     }
     // We have to destroy the iframe, otherwise, the keybindings of webide don't work
     // properly anymore.
     iframe.remove();
   },
 
-  destroyToolbox: function () {
+  destroyToolbox: function() {
     // Only have a live toolbox if |this.toolboxPromise| exists
     if (this.toolboxPromise) {
       let toolboxPromise = this.toolboxPromise;
       this.toolboxPromise = null;
       return toolboxPromise.then(toolbox => toolbox.destroy());
     }
     return promise.resolve();
   },
 
-  createToolbox: function () {
+  createToolbox: function() {
     // If |this.toolboxPromise| exists, there is already a live toolbox
     if (this.toolboxPromise) {
       return this.toolboxPromise;
     }
 
     let iframe = document.createElement("iframe");
     iframe.id = "toolbox";
 
@@ -810,70 +810,70 @@ var UI = {
       // toolbox.destroy's promise resolves.
       toolbox.once("destroyed", this._onToolboxClosed.bind(this, promise, iframe));
       return toolbox;
     }, console.error);
 
     return this.busyUntil(this.toolboxPromise, "opening toolbox");
   },
 
-  _showToolbox: function (target, iframe) {
+  _showToolbox: function(target, iframe) {
     let splitter = document.querySelector(".devtools-horizontal-splitter");
     splitter.removeAttribute("hidden");
 
     document.querySelector("notificationbox").insertBefore(iframe, splitter.nextSibling);
     let host = Toolbox.HostType.CUSTOM;
     let options = { customIframe: iframe, zoom: false, uid: iframe.uid };
 
     document.querySelector("#action-button-debug").setAttribute("active", "true");
 
     return gDevTools.showToolbox(target, null, host, options);
   },
 };
 
 EventEmitter.decorate(UI);
 
 var Cmds = {
-  quit: function () {
+  quit: function() {
     window.close();
   },
 
-  showProjectPanel: function () {
+  showProjectPanel: function() {
     ProjectPanel.toggleSidebar();
     return promise.resolve();
   },
 
-  showRuntimePanel: function () {
+  showRuntimePanel: function() {
     RuntimeScanners.scan();
     RuntimePanel.toggleSidebar();
   },
 
-  disconnectRuntime: function () {
-    let disconnecting = (async function () {
+  disconnectRuntime: function() {
+    let disconnecting = (async function() {
       await UI.destroyToolbox();
       await AppManager.disconnectRuntime();
     })();
     return UI.busyUntil(disconnecting, "disconnecting from runtime");
   },
 
-  takeScreenshot: function () {
+  takeScreenshot: function() {
     let url = AppManager.deviceFront.screenshotToDataURL();
     return UI.busyUntil(url.then(longstr => {
       return longstr.string().then(dataURL => {
         longstr.release().catch(console.error);
         UI.openInBrowser(dataURL);
       });
     }), "taking screenshot");
   },
 
-  showRuntimeDetails: function () {
+  showRuntimeDetails: function() {
     UI.selectDeckPanel("runtimedetails");
   },
 
-  showDevicePrefs: function () {
+  showDevicePrefs: function() {
     UI.selectDeckPanel("devicepreferences");
   },
 
   async play() {
     let busy;
     switch (AppManager.selectedProject.type) {
       case "packaged":
         busy = UI.busyWithProgressUntil(AppManager.installAndRunProject(),
@@ -892,57 +892,56 @@ var Cmds = {
     }
     if (!busy) {
       return promise.reject();
     }
     UI.onAction("play");
     return busy;
   },
 
-  stop: function () {
+  stop: function() {
     return UI.busyUntil(AppManager.stopRunningApp(), "stopping app");
   },
 
-  toggleToolbox: function () {
+  toggleToolbox: function() {
     UI.onAction("debug");
     if (UI.toolboxPromise) {
       UI.destroyToolbox();
       return promise.resolve();
-    } else {
-      return UI.createToolbox();
     }
+    return UI.createToolbox();
   },
 
-  removeProject: function () {
+  removeProject: function() {
     AppManager.removeSelectedProject();
   },
 
-  showTroubleShooting: function () {
+  showTroubleShooting: function() {
     UI.openInBrowser(HELP_URL);
   },
 
-  showAddons: function () {
+  showAddons: function() {
     UI.selectDeckPanel("addons");
   },
 
-  showPrefs: function () {
+  showPrefs: function() {
     UI.selectDeckPanel("prefs");
   },
 
-  zoomIn: function () {
+  zoomIn: function() {
     if (UI.contentViewer.fullZoom < MAX_ZOOM) {
       UI.contentViewer.fullZoom += 0.1;
       Services.prefs.setCharPref("devtools.webide.zoom", UI.contentViewer.fullZoom);
     }
   },
 
-  zoomOut: function () {
+  zoomOut: function() {
     if (UI.contentViewer.fullZoom > MIN_ZOOM) {
       UI.contentViewer.fullZoom -= 0.1;
       Services.prefs.setCharPref("devtools.webide.zoom", UI.contentViewer.fullZoom);
     }
   },
 
-  resetZoom: function () {
+  resetZoom: function() {
     UI.contentViewer.fullZoom = 1;
     Services.prefs.setCharPref("devtools.webide.zoom", 1);
   }
 };
--- a/devtools/client/webide/content/wifi-auth.js
+++ b/devtools/client/webide/content/wifi-auth.js
@@ -1,20 +1,19 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { require } =
   ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
-const Services = require("Services");
 const QR = require("devtools/shared/qrcode/index");
 
-window.addEventListener("load", function () {
+window.addEventListener("load", function() {
   document.getElementById("close").onclick = () => window.close();
   document.getElementById("no-scanner").onclick = showToken;
   document.getElementById("yes-scanner").onclick = hideToken;
   buildUI();
 }, {once: true});
 
 function buildUI() {
   let { oob } = window.arguments[0];
--- a/devtools/client/webide/modules/addons.js
+++ b/devtools/client/webide/modules/addons.js
@@ -1,17 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {AddonManager} = require("resource://gre/modules/AddonManager.jsm");
 const Services = require("Services");
-const {getJSON} = require("devtools/client/shared/getjson");
 const EventEmitter = require("devtools/shared/event-emitter");
 
 var ADB_LINK = Services.prefs.getCharPref("devtools.webide.adbAddonURL");
 var ADB_ADDON_ID = Services.prefs.getCharPref("devtools.webide.adbAddonID");
 
 var platform = Services.appShell.hiddenDOMWindow.navigator.platform;
 var OS = "";
 if (platform.includes("Win")) {
@@ -32,53 +31,53 @@ addonsListener.onDisabled =
 addonsListener.onInstalled =
 addonsListener.onUninstalled = (updatedAddon) => {
   let addons = GetAvailableAddons();
   addons.adb.updateInstallStatus();
 };
 AddonManager.addAddonListener(addonsListener);
 
 var AvailableAddons = null;
-var GetAvailableAddons = exports.GetAvailableAddons = function () {
+var GetAvailableAddons = exports.GetAvailableAddons = function() {
   if (!AvailableAddons) {
     AvailableAddons = {
       adb: new ADBAddon()
     };
   }
   return AvailableAddons;
 };
 
-exports.ForgetAddonsList = function () {
+exports.ForgetAddonsList = function() {
   AvailableAddons = null;
 };
 
 function Addon() {}
 Addon.prototype = {
   _status: "unknown",
   set status(value) {
     if (this._status != value) {
       this._status = value;
       this.emit("update");
     }
   },
   get status() {
     return this._status;
   },
 
-  updateInstallStatus: function () {
+  updateInstallStatus: function() {
     AddonManager.getAddonByID(this.addonID, (addon) => {
       if (addon && !addon.userDisabled) {
         this.status = "installed";
       } else {
         this.status = "uninstalled";
       }
     });
   },
 
-  install: function () {
+  install: function() {
     AddonManager.getAddonByID(this.addonID, (addon) => {
       if (addon && !addon.userDisabled) {
         this.status = "installed";
         return;
       }
       this.status = "preparing";
       if (addon && addon.userDisabled) {
         addon.userDisabled = false;
@@ -86,57 +85,57 @@ Addon.prototype = {
         AddonManager.getInstallForURL(this.xpiLink, (install) => {
           install.addListener(this);
           install.install();
         }, "application/x-xpinstall");
       }
     });
   },
 
-  uninstall: function () {
+  uninstall: function() {
     AddonManager.getAddonByID(this.addonID, (addon) => {
       addon.uninstall();
     });
   },
 
-  installFailureHandler: function (install, message) {
+  installFailureHandler: function(install, message) {
     this.status = "uninstalled";
     this.emit("failure", message);
   },
 
-  onDownloadStarted: function () {
+  onDownloadStarted: function() {
     this.status = "downloading";
   },
 
-  onInstallStarted: function () {
+  onInstallStarted: function() {
     this.status = "installing";
   },
 
-  onDownloadProgress: function (install) {
+  onDownloadProgress: function(install) {
     if (install.maxProgress == -1) {
       this.emit("progress", -1);
     } else {
       this.emit("progress", install.progress / install.maxProgress);
     }
   },
 
-  onInstallEnded: function ({addon}) {
+  onInstallEnded: function({addon}) {
     addon.userDisabled = false;
   },
 
-  onDownloadCancelled: function (install) {
+  onDownloadCancelled: function(install) {
     this.installFailureHandler(install, "Download cancelled");
   },
-  onDownloadFailed: function (install) {
+  onDownloadFailed: function(install) {
     this.installFailureHandler(install, "Download failed");
   },
-  onInstallCancelled: function (install) {
+  onInstallCancelled: function(install) {
     this.installFailureHandler(install, "Install cancelled");
   },
-  onInstallFailed: function (install) {
+  onInstallFailed: function(install) {
     this.installFailureHandler(install, "Install failed");
   },
 };
 
 function ADBAddon() {
   EventEmitter.decorate(this);
   // This addon uses the string "linux" for "linux32"
   let fixedOS = OS == "linux32" ? "linux" : OS;
--- a/devtools/client/webide/modules/app-manager.js
+++ b/devtools/client/webide/modules/app-manager.js
@@ -1,14 +1,12 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-const {Cu} = require("chrome");
-
 const {TargetFactory} = require("devtools/client/framework/target");
 const Services = require("Services");
 const {FileUtils} = require("resource://gre/modules/FileUtils.jsm");
 const EventEmitter = require("devtools/shared/event-emitter");
 const {OS} = require("resource://gre/modules/osfile.jsm");
 const {AppProjects} = require("devtools/client/webide/modules/app-projects");
 const TabStore = require("devtools/client/webide/modules/tab-store");
 const {AppValidator} = require("devtools/client/webide/modules/app-validator");
@@ -23,17 +21,17 @@ const Strings = Services.strings.createB
 
 var AppManager = exports.AppManager = {
 
   DEFAULT_PROJECT_ICON: "chrome://webide/skin/default-app-icon.png",
   DEFAULT_PROJECT_NAME: "--",
 
   _initialized: false,
 
-  init: function () {
+  init: function() {
     if (this._initialized) {
       return;
     }
     this._initialized = true;
 
     let port = Services.prefs.getIntPref("devtools.debugger.remote-port");
     this.connection = ConnectionManager.createConnection("localhost", port);
     this.onConnectionChanged = this.onConnectionChanged.bind(this);
@@ -51,17 +49,17 @@ var AppManager = exports.AppManager = {
     this._rebuildRuntimeList = this._rebuildRuntimeList.bind(this);
     RuntimeScanners.on("runtime-list-updated", this._rebuildRuntimeList);
     RuntimeScanners.enable();
     this._rebuildRuntimeList();
 
     this._telemetry = new Telemetry();
   },
 
-  destroy: function () {
+  destroy: function() {
     if (!this._initialized) {
       return;
     }
     this._initialized = false;
 
     this.selectedProject = null;
     this.selectedRuntime = null;
     RuntimeScanners.off("runtime-list-updated", this._rebuildRuntimeList);
@@ -117,37 +115,37 @@ var AppManager = exports.AppManager = {
    *     Detailed runtime telemetry has been recorded.  Used by tests.
    *   runtime-targets:
    *     The list of remote runtime targets available from the currently
    *     connected runtime (such as tabs or apps) has changed, or any of the
    *     user-visible details (like names) for the non-selected runtime targets
    *     has changed.  This event includes |type| in the details, to distinguish
    *     "apps" and "tabs".
    */
-  update: function (what, details) {
+  update: function(what, details) {
     // Anything we want to forward to the UI
     this.emit("app-manager-update", what, details);
   },
 
-  reportError: function (l10nProperty, ...l10nArgs) {
+  reportError: function(l10nProperty, ...l10nArgs) {
     let win = Services.wm.getMostRecentWindow("devtools:webide");
     if (win) {
       win.UI.reportError(l10nProperty, ...l10nArgs);
     } else {
       let text;
       if (l10nArgs.length > 0) {
         text = Strings.formatStringFromName(l10nProperty, l10nArgs, l10nArgs.length);
       } else {
         text = Strings.GetStringFromName(l10nProperty);
       }
       console.error(text);
     }
   },
 
-  onConnectionChanged: function () {
+  onConnectionChanged: function() {
     console.log("Connection status changed: " + this.connection.status);
 
     if (this.connection.status == Connection.Status.DISCONNECTED) {
       this.selectedRuntime = null;
     }
 
     if (!this.connected) {
       this._listTabsResponse = null;
@@ -165,51 +163,50 @@ var AppManager = exports.AppManager = {
   get connected() {
     return this.connection &&
            this.connection.status == Connection.Status.CONNECTED;
   },
 
   get apps() {
     if (this._appsFront) {
       return this._appsFront.apps;
-    } else {
-      return new Map();
     }
+    return new Map();
   },
 
-  isProjectRunning: function () {
+  isProjectRunning: function() {
     if (this.selectedProject.type == "mainProcess" ||
         this.selectedProject.type == "tab") {
       return true;
     }
 
     let app = this._getProjectFront(this.selectedProject);
     return app && app.running;
   },
 
-  checkIfProjectIsRunning: function () {
+  checkIfProjectIsRunning: function() {
     if (this.selectedProject) {
       if (this.isProjectRunning()) {
         this.update("project-started");
       } else {
         this.update("project-stopped");
       }
     }
   },
 
-  listTabs: function () {
+  listTabs: function() {
     return this.tabStore.listTabs();
   },
 
-  onTabList: function () {
+  onTabList: function() {
     this.update("runtime-targets", { type: "tabs" });
   },
 
   // TODO: Merge this into TabProject as part of project-agnostic work
-  onTabNavigate: function () {
+  onTabNavigate: function() {
     this.update("runtime-targets", { type: "tabs" });
     if (this.selectedProject.type !== "tab") {
       return;
     }
     let tab = this.selectedProject.app = this.tabStore.selectedTab;
     let uri = NetUtil.newURI(tab.url);
     // Wanted to use nsIFaviconService here, but it only works for visited
     // tabs, so that's no help for any remote tabs.  Maybe some favicon wizard
@@ -221,65 +218,64 @@ var AppManager = exports.AppManager = {
       tab.name = uri.host + ": " + tab.name;
     }
     this.selectedProject.location = tab.url;
     this.selectedProject.name = tab.name;
     this.selectedProject.icon = tab.favicon;
     this.update("project-validated");
   },
 
-  onTabClosed: function () {
+  onTabClosed: function() {
     if (this.selectedProject.type !== "tab") {
       return;
     }
     this.selectedProject = null;
   },
 
-  reloadTab: function () {
+  reloadTab: function() {
     if (this.selectedProject && this.selectedProject.type != "tab") {
       return Promise.reject("tried to reload non-tab project");
     }
     return this.getTarget().then(target => {
       target.activeTab.reload();
     }, console.error);
   },
 
-  getTarget: function () {
+  getTarget: function() {
     if (this.selectedProject.type == "mainProcess") {
       // Fx >=39 exposes a ChromeActor to debug the main process
       if (this.connection.client.mainRoot.traits.allowChromeProcess) {
         return this.connection.client.getProcess()
                    .then(aResponse => {
                      return TargetFactory.forRemoteTab({
                        form: aResponse.form,
                        client: this.connection.client,
                        chrome: true
                      });
                    });
-      } else {
+      }
         // Fx <39 exposes tab actors on the root actor
-        return TargetFactory.forRemoteTab({
+      return TargetFactory.forRemoteTab({
           form: this._listTabsResponse,
           client: this.connection.client,
           chrome: true,
           isTabActor: false
-        });
-      }
+      });
     }
 
     if (this.selectedProject.type == "tab") {
       return this.tabStore.getTargetForTab();
     }
 
     let app = this._getProjectFront(this.selectedProject);
     if (!app) {
       return Promise.reject("Can't find app front for selected project");
     }
 
-    return (async function () {
+    return (async function() {
       // Once we asked the app to launch, the app isn't necessary completely loaded.
       // launch request only ask the app to launch and immediatly returns.
       // We have to keep trying to get app tab actors required to create its target.
 
       for (let i = 0; i < 10; i++) {
         try {
           return await app.getTarget();
         } catch (e) {}
@@ -288,34 +284,34 @@ var AppManager = exports.AppManager = {
         });
       }
 
       AppManager.reportError("error_cantConnectToApp", app.manifest.manifestURL);
       throw new Error("can't connect to app");
     })();
   },
 
-  getProjectManifestURL: function (project) {
+  getProjectManifestURL: function(project) {
     let manifest = null;
     if (project.type == "runtimeApp") {
       manifest = project.app.manifestURL;
     }
 
     if (project.type == "hosted") {
       manifest = project.location;
     }
 
     if (project.type == "packaged" && project.packagedAppOrigin) {
       manifest = "app://" + project.packagedAppOrigin + "/manifest.webapp";
     }
 
     return manifest;
   },
 
-  _getProjectFront: function (project) {
+  _getProjectFront: function(project) {
     let manifest = this.getProjectManifestURL(project);
     if (manifest && this._appsFront) {
       return this._appsFront.apps.get(manifest);
     }
     return null;
   },
 
   _selectedProject: null,
@@ -341,17 +337,19 @@ var AppManager = exports.AppManager = {
       } else if (type === "mainProcess") {
         return;
       } else {
         throw new Error("Unsupported project type: " + type);
       }
     }
 
     let cancelled = false;
-    this.update("before-project", { cancel: () => { cancelled = true; } });
+    this.update("before-project", { cancel: () => {
+      cancelled = true;
+    } });
     if (cancelled) {
       return;
     }
 
     this._selectedProject = project;
 
     // Clear out tab store's selected state, if any
     this.tabStore.selectedTab = null;
@@ -396,18 +394,17 @@ var AppManager = exports.AppManager = {
     }
     this.update("runtime");
   },
 
   get selectedRuntime() {
     return this._selectedRuntime;
   },
 
-  connectToRuntime: function (runtime) {
-
+  connectToRuntime: function(runtime) {
     if (this.connected && this.selectedRuntime === runtime) {
       // Already connected
       return Promise.resolve();
     }
 
     let deferred = new Promise((resolve, reject) => {
       this.disconnectRuntime().then(() => {
         this.selectedRuntime = runtime;
@@ -484,17 +481,17 @@ var AppManager = exports.AppManager = {
                              d.platformversion, true);
     this._telemetry.logKeyed("DEVTOOLS_WEBIDE_CONNECTED_RUNTIME_APP_TYPE",
                              d.apptype, true);
     this._telemetry.logKeyed("DEVTOOLS_WEBIDE_CONNECTED_RUNTIME_VERSION",
                              d.version, true);
     this.update("runtime-telemetry");
   },
 
-  isMainProcessDebuggable: function () {
+  isMainProcessDebuggable: function() {
     // Fx <39 exposes chrome tab actors on RootActor
     // Fx >=39 exposes a dedicated actor via getProcess request
     return this.connection.client &&
            this.connection.client.mainRoot &&
            this.connection.client.mainRoot.traits.allowChromeProcess ||
            (this._listTabsResponse &&
             this._listTabsResponse.consoleActor);
   },
@@ -512,52 +509,51 @@ var AppManager = exports.AppManager = {
 
   get preferenceFront() {
     if (!this._listTabsResponse) {
       return null;
     }
     return getPreferenceFront(this.connection.client, this._listTabsResponse);
   },
 
-  disconnectRuntime: function () {
+  disconnectRuntime: function() {
     if (!this.connected) {
       return Promise.resolve();
     }
 
     return new Promise(resolve => {
       this.connection.once(Connection.Events.DISCONNECTED, () => resolve());
       this.connection.disconnect();
     });
   },
 
-  launchRuntimeApp: function () {
+  launchRuntimeApp: function() {
     if (this.selectedProject && this.selectedProject.type != "runtimeApp") {
       return Promise.reject("attempting to launch a non-runtime app");
     }
     let app = this._getProjectFront(this.selectedProject);
     return app.launch();
   },
 
-  launchOrReloadRuntimeApp: function () {
+  launchOrReloadRuntimeApp: function() {
     if (this.selectedProject && this.selectedProject.type != "runtimeApp") {
       return Promise.reject("attempting to launch / reload a non-runtime app");
     }
     let app = this._getProjectFront(this.selectedProject);
     if (!app.running) {
       return app.launch();
-    } else {
-      return app.reload();
     }
+    return app.reload();
   },
 
-  runtimeCanHandleApps: function () {
+  runtimeCanHandleApps: function() {
     return !!this._appsFront;
   },
 
-  installAndRunProject: function () {
+  installAndRunProject: function() {
     let project = this.selectedProject;
 
     if (!project || (project.type != "packaged" && project.type != "hosted")) {
       console.error("Can't install project. Unknown type of project.");
       return Promise.reject("Can't install");
     }
 
     if (!this._listTabsResponse) {
@@ -565,29 +561,27 @@ var AppManager = exports.AppManager = {
       return Promise.reject("Can't install");
     }
 
     if (!this._appsFront) {
       console.error("Runtime doesn't have a webappsActor");
       return Promise.reject("Can't install");
     }
 
-    return (async function () {
+    return (async function() {
       let self = AppManager;
 
       // Validate project
       await self.validateAndUpdateProject(project);
 
       if (project.errorsCount > 0) {
         self.reportError("error_cantInstallValidationErrors");
         return;
       }
 
-      let installPromise;
-
       if (project.type != "packaged" && project.type != "hosted") {
         return Promise.reject("Don't know how to install project");
       }
 
       let response;
       if (project.type == "packaged") {
         let packageDir = project.location;
         console.log("Installing app from " + packageDir);
@@ -634,30 +628,29 @@ var AppManager = exports.AppManager = {
         await app.launch();
         await deferred;
       } else {
         await app.reload();
       }
     })();
   },
 
-  stopRunningApp: function () {
+  stopRunningApp: function() {
     let app = this._getProjectFront(this.selectedProject);
     return app.close();
   },
 
   /* PROJECT VALIDATION */
 
-  validateAndUpdateProject: function (project) {
+  validateAndUpdateProject: function(project) {
     if (!project) {
       return Promise.reject();
     }
 
-    return (async function () {
-
+    return (async function() {
       let packageDir = project.location;
       let validation = new AppValidator({
         type: project.type,
         // Build process may place the manifest in a non-root directory
         location: packageDir
       });
 
       await validation.validate();
@@ -668,26 +661,24 @@ var AppManager = exports.AppManager = {
         if (manifest.icons) {
           let size = Object.keys(manifest.icons).sort((a, b) => b - a)[0];
           if (size) {
             iconPath = manifest.icons[size];
           }
         }
         if (!iconPath) {
           project.icon = AppManager.DEFAULT_PROJECT_ICON;
-        } else {
-          if (project.type == "hosted") {
-            let manifestURL = Services.io.newURI(project.location);
-            let origin = Services.io.newURI(manifestURL.prePath);
-            project.icon = Services.io.newURI(iconPath, null, origin).spec;
-          } else if (project.type == "packaged") {
-            let projectFolder = FileUtils.File(packageDir);
-            let folderURI = Services.io.newFileURI(projectFolder).spec;
-            project.icon = folderURI + iconPath.replace(/^\/|\\/, "");
-          }
+        } else if (project.type == "hosted") {
+          let manifestURL = Services.io.newURI(project.location);
+          let origin = Services.io.newURI(manifestURL.prePath);
+          project.icon = Services.io.newURI(iconPath, null, origin).spec;
+        } else if (project.type == "packaged") {
+          let projectFolder = FileUtils.File(packageDir);
+          let folderURI = Services.io.newFileURI(projectFolder).spec;
+          project.icon = folderURI + iconPath.replace(/^\/|\\/, "");
         }
         project.manifest = validation.manifest;
 
         if ("name" in project.manifest) {
           project.name = project.manifest.name;
         } else {
           project.name = AppManager.DEFAULT_PROJECT_NAME;
         }
@@ -730,25 +721,25 @@ var AppManager = exports.AppManager = {
       if (AppManager.selectedProject === project) {
         AppManager.update("project-validated");
       }
     })();
   },
 
   /* RUNTIME LIST */
 
-  _clearRuntimeList: function () {
+  _clearRuntimeList: function() {
     this.runtimeList = {
       usb: [],
       wifi: [],
       other: []
     };
   },
 
-  _rebuildRuntimeList: function () {
+  _rebuildRuntimeList: function() {
     let runtimes = RuntimeScanners.listRuntimes();
     this._clearRuntimeList();
 
     // Reorganize runtimes by type
     for (let runtime of runtimes) {
       switch (runtime.type) {
         case RuntimeTypes.USB:
           this.runtimeList.usb.push(runtime);
@@ -762,17 +753,17 @@ var AppManager = exports.AppManager = {
     }
 
     this.update("runtime-details");
     this.update("runtime-list");
   },
 
   /* MANIFEST UTILS */
 
-  writeManifest: function (project) {
+  writeManifest: function(project) {
     if (project.type != "packaged") {
       return Promise.reject("Not a packaged app");
     }
 
     if (!project.manifest) {
       project.manifest = {};
     }
 
--- a/devtools/client/webide/modules/app-projects.js
+++ b/devtools/client/webide/modules/app-projects.js
@@ -1,58 +1,56 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-const {Cc, Ci, Cu, Cr} = require("chrome");
+const {Cc, Ci, Cr} = require("chrome");
 
 const EventEmitter = require("devtools/shared/event-emitter");
 const {generateUUID} = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
 const {FileUtils} = require("resource://gre/modules/FileUtils.jsm");
 
 /**
  * IndexedDB wrapper that just save project objects
  *
  * The only constraint is that project objects have to have
  * a unique `location` object.
  */
 
 const IDB = {
   _db: null,
   databaseName: "AppProjects",
 
-  open: function () {
+  open: function() {
     return new Promise((resolve, reject) => {
       let request = indexedDB.open(IDB.databaseName, 5);
-      request.onerror = function (event) {
+      request.onerror = function(event) {
         reject("Unable to open AppProjects indexedDB: " +
                         this.error.name + " - " + this.error.message);
       };
-      request.onupgradeneeded = function (event) {
+      request.onupgradeneeded = function(event) {
         let db = event.target.result;
         db.createObjectStore("projects", { keyPath: "location" });
       };
 
-      request.onsuccess = function () {
+      request.onsuccess = function() {
         let db = IDB._db = request.result;
         let objectStore = db.transaction("projects").objectStore("projects");
         let projects = [];
         let toRemove = [];
-        objectStore.openCursor().onsuccess = function (event) {
+        objectStore.openCursor().onsuccess = function(event) {
           let cursor = event.target.result;
           if (cursor) {
             if (cursor.value.location) {
-
               // We need to make sure this object has a `.location` property.
               // The UI depends on this property.
               // This should not be needed as we make sure to register valid
               // projects, but in the past (before bug 924568), we might have
               // registered invalid objects.
 
-
               // We also want to make sure the location is valid.
               // If the location doesn't exist, we remove the project.
 
               try {
                 let file = FileUtils.File(cursor.value.location);
                 if (file.exists()) {
                   projects.push(cursor.value);
                 } else {
@@ -75,78 +73,78 @@ const IDB = {
               resolve(projects);
             });
           }
         };
       };
     });
   },
 
-  add: function (project) {
+  add: function(project) {
     return new Promise((resolve, reject) => {
       if (!project.location) {
         // We need to make sure this object has a `.location` property.
         reject("Missing location property on project object.");
       } else {
         let transaction = IDB._db.transaction(["projects"], "readwrite");
         let objectStore = transaction.objectStore("projects");
         let request = objectStore.add(project);
-        request.onerror = function (event) {
+        request.onerror = function(event) {
           reject("Unable to add project to the AppProjects indexedDB: " +
                  this.error.name + " - " + this.error.message);
         };
-        request.onsuccess = function () {
+        request.onsuccess = function() {
           resolve();
         };
       }
     });
   },
 
-  update: function (project) {
+  update: function(project) {
     return new Promise((resolve, reject) => {
-      var transaction = IDB._db.transaction(["projects"], "readwrite");
-      var objectStore = transaction.objectStore("projects");
-      var request = objectStore.put(project);
-      request.onerror = function (event) {
+      let transaction = IDB._db.transaction(["projects"], "readwrite");
+      let objectStore = transaction.objectStore("projects");
+      let request = objectStore.put(project);
+      request.onerror = function(event) {
         reject("Unable to update project to the AppProjects indexedDB: " +
                this.error.name + " - " + this.error.message);
       };
-      request.onsuccess = function () {
+      request.onsuccess = function() {
         resolve();
       };
     });
   },
 
-  remove: function (location) {
+  remove: function(location) {
     return new Promise((resolve, reject) => {
       let request = IDB._db.transaction(["projects"], "readwrite")
                     .objectStore("projects")
                     .delete(location);
-      request.onsuccess = function (event) {
+      request.onsuccess = function(event) {
         resolve();
       };
-      request.onerror = function () {
+      request.onerror = function() {
         reject("Unable to delete project to the AppProjects indexedDB: " +
                this.error.name + " - " + this.error.message);
       };
     });
   }
 };
 
-var loadDeferred = IDB.open().then(function (projects) {
+var loadDeferred = IDB.open().then(function(projects) {
   AppProjects.projects = projects;
   AppProjects.emit("ready", projects);
 });
 
 const AppProjects = {
-  load: function () {
+  load: function() {
     return loadDeferred;
   },
 
-  addPackaged: function (folder) {
+  addPackaged: function(folder) {
     let file = FileUtils.File(folder.path);
     if (!file.exists()) {
       return Promise.reject("path doesn't exist");
     }
     let existingProject = this.get(folder.path);
     if (existingProject) {
       return Promise.reject("Already added");
     }
@@ -163,56 +161,56 @@ const AppProjects = {
       packagedAppOrigin: generateUUID().toString().slice(1, -1)
     };
     return IDB.add(project).then(() => {
       this.projects.push(project);
       return project;
     });
   },
 
-  addHosted: function (manifestURL) {
+  addHosted: function(manifestURL) {
     let existingProject = this.get(manifestURL);
     if (existingProject) {
       return Promise.reject("Already added");
     }
     let project = {
       type: "hosted",
       location: manifestURL
     };
     return IDB.add(project).then(() => {
       this.projects.push(project);
       return project;
     });
   },
 
-  update: function (project) {
+  update: function(project) {
     return IDB.update(project);
   },
 
-  updateLocation: function (project, newLocation) {
+  updateLocation: function(project, newLocation) {
     return IDB.remove(project.location)
               .then(() => {
                 project.location = newLocation;
                 return IDB.add(project);
               });
   },
 
-  remove: function (location) {
+  remove: function(location) {
     return IDB.remove(location).then(() => {
       for (let i = 0; i < this.projects.length; i++) {
         if (this.projects[i].location == location) {
           this.projects.splice(i, 1);
           return;
         }
       }
       throw new Error("Unable to find project in AppProjects store");
     });
   },
 
-  get: function (location) {
+  get: function(location) {
     for (let i = 0; i < this.projects.length; i++) {
       if (this.projects[i].location == location) {
         return this.projects[i];
       }
     }
     return null;
   },
 
--- a/devtools/client/webide/modules/app-validator.js
+++ b/devtools/client/webide/modules/app-validator.js
@@ -1,35 +1,35 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
-var {Ci, Cu} = require("chrome");
+var {Ci} = require("chrome");
 
 const {FileUtils} = require("resource://gre/modules/FileUtils.jsm");
 const Services = require("Services");
 var strings = Services.strings.createBundle("chrome://devtools/locale/app-manager.properties");
 
 function AppValidator({ type, location }) {
   this.type = type;
   this.location = location;
   this.errors = [];
   this.warnings = [];
 }
 
-AppValidator.prototype.error = function (message) {
+AppValidator.prototype.error = function(message) {
   this.errors.push(message);
 };
 
-AppValidator.prototype.warning = function (message) {
+AppValidator.prototype.warning = function(message) {
   this.warnings.push(message);
 };
 
-AppValidator.prototype._getPackagedManifestFile = function () {
+AppValidator.prototype._getPackagedManifestFile = function() {
   let manifestFile = FileUtils.File(this.location);
   if (!manifestFile.exists()) {
     this.error(strings.GetStringFromName("validator.nonExistingFolder"));
     return null;
   }
   if (!manifestFile.isDirectory()) {
     this.error(strings.GetStringFromName("validator.expectProjectFolder"));
     return null;
@@ -47,95 +47,95 @@ AppValidator.prototype._getPackagedManif
   if (!hasAppManifest && !hasJsonManifest) {
     this.error(strings.GetStringFromName("validator.noManifestFile"));
     return null;
   }
 
   return hasAppManifest ? appManifestFile : jsonManifestFile;
 };
 
-AppValidator.prototype._getPackagedManifestURL = function () {
+AppValidator.prototype._getPackagedManifestURL = function() {
   let manifestFile = this._getPackagedManifestFile();
   if (!manifestFile) {
     return null;
   }
   return Services.io.newFileURI(manifestFile).spec;
 };
 
-AppValidator.checkManifest = function (manifestURL) {
+AppValidator.checkManifest = function(manifestURL) {
   return new Promise((resolve, reject) => {
     let error;
 
     let req = new XMLHttpRequest();
     req.overrideMimeType("text/plain");
 
     try {
       req.open("GET", manifestURL, true);
       req.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE | Ci.nsIRequest.INHIBIT_CACHING;
     } catch (e) {
       error = strings.formatStringFromName("validator.invalidManifestURL", [manifestURL], 1);
       return reject(error);
     }
 
-    req.onload = function () {
+    req.onload = function() {
       let manifest = null;
       try {
         manifest = JSON.parse(req.responseText);
       } catch (e) {
         error = strings.formatStringFromName("validator.invalidManifestJSON", [e, manifestURL], 2);
         reject(error);
       }
 
       resolve({manifest, manifestURL});
     };
 
-    req.onerror = function () {
+    req.onerror = function() {
       error = strings.formatStringFromName("validator.noAccessManifestURL", [req.statusText, manifestURL], 2);
       reject(error);
     };
 
     try {
       req.send(null);
     } catch (e) {
       error = strings.formatStringFromName("validator.noAccessManifestURL", [e, manifestURL], 2);
       reject(error);
     }
   });
 };
 
-AppValidator.findManifestAtOrigin = function (manifestURL) {
+AppValidator.findManifestAtOrigin = function(manifestURL) {
   let fixedManifest = Services.io.newURI(manifestURL).prePath + "/manifest.webapp";
   return AppValidator.checkManifest(fixedManifest);
 };
 
-AppValidator.findManifestPath = function (manifestURL) {
+AppValidator.findManifestPath = function(manifestURL) {
   return new Promise((resolve, reject) => {
     if (manifestURL.endsWith("manifest.webapp")) {
       reject();
     } else {
       let fixedManifest = manifestURL + "/manifest.webapp";
       resolve(AppValidator.checkManifest(fixedManifest));
     }
   });
 };
 
-AppValidator.checkAlternateManifest = function (manifestURL) {
-  return (async function () {
+AppValidator.checkAlternateManifest = function(manifestURL) {
+  return (async function() {
     let result;
     try {
       result = await AppValidator.findManifestPath(manifestURL);
     } catch (e) {
       result = await AppValidator.findManifestAtOrigin(manifestURL);
     }
 
     return result;
   })();
 };
 
-AppValidator.prototype._fetchManifest = function (manifestURL) {
+AppValidator.prototype._fetchManifest = function(manifestURL) {
   return new Promise(resolve => {
     this.manifestURL = manifestURL;
 
     AppValidator.checkManifest(manifestURL)
                 .then(({manifest, manifestURL}) => {
                   resolve(manifest);
                 }, error => {
                   AppValidator.checkAlternateManifest(manifestURL)
@@ -145,59 +145,60 @@ AppValidator.prototype._fetchManifest = 
                               }, () => {
                                 this.error(error);
                                 resolve(null);
                               });
                 });
   });
 };
 
-AppValidator.prototype._getManifest = function () {
+AppValidator.prototype._getManifest = function() {
   let manifestURL;
   if (this.type == "packaged") {
     manifestURL = this._getPackagedManifestURL();
-    if (!manifestURL)
+    if (!manifestURL) {
       return Promise.resolve(null);
+    }
   } else if (this.type == "hosted") {
     manifestURL = this.location;
     try {
       Services.io.newURI(manifestURL);
     } catch (e) {
       this.error(strings.formatStringFromName("validator.invalidHostedManifestURL", [manifestURL, e.message], 2));
       return Promise.resolve(null);
     }
   } else {
     this.error(strings.formatStringFromName("validator.invalidProjectType", [this.type], 1));
     return Promise.resolve(null);
   }
   return this._fetchManifest(manifestURL);
 };
 
-AppValidator.prototype.validateManifest = function (manifest) {
+AppValidator.prototype.validateManifest = function(manifest) {
   if (!manifest.name) {
     this.error(strings.GetStringFromName("validator.missNameManifestProperty"));
   }
 
   if (!manifest.icons || Object.keys(manifest.icons).length === 0) {
     this.warning(strings.GetStringFromName("validator.missIconsManifestProperty"));
   } else if (!manifest.icons["128"]) {
     this.warning(strings.GetStringFromName("validator.missIconMarketplace2"));
   }
 };
 
-AppValidator.prototype._getOriginURL = function () {
+AppValidator.prototype._getOriginURL = function() {
   if (this.type == "packaged") {
     let manifestURL = Services.io.newURI(this.manifestURL);
     return Services.io.newURI(".", null, manifestURL).spec;
   } else if (this.type == "hosted") {
     return Services.io.newURI(this.location).prePath;
   }
 };
 
-AppValidator.prototype.validateLaunchPath = function (manifest) {
+AppValidator.prototype.validateLaunchPath = function(manifest) {
   return new Promise(resolve => {
     // The launch_path field has to start with a `/`
     if (manifest.launch_path && manifest.launch_path[0] !== "/") {
       this.error(strings.formatStringFromName("validator.nonAbsoluteLaunchPath", [manifest.launch_path], 1));
       resolve();
     }
     let origin = this._getOriginURL();
     let path;
@@ -219,54 +220,55 @@ AppValidator.prototype.validateLaunchPat
     try {
       req.open("HEAD", indexURL, true);
       req.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE | Ci.nsIRequest.INHIBIT_CACHING;
     } catch (e) {
       this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [indexURL], 1));
       return resolve();
     }
     req.onload = () => {
-      if (req.status >= 400)
+      if (req.status >= 400) {
         this.error(strings.formatStringFromName("validator.accessFailedLaunchPathBadHttpCode", [indexURL, req.status], 2));
+      }
       resolve();
     };
     req.onerror = () => {
       this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [indexURL], 1));
       resolve();
     };
 
     try {
       req.send(null);
     } catch (e) {
       this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [indexURL], 1));
       resolve();
     }
   });
 };
 
-AppValidator.prototype.validateType = function (manifest) {
+AppValidator.prototype.validateType = function(manifest) {
   let appType = manifest.type || "web";
   if (!["web", "privileged", "certified"].includes(appType)) {
     this.error(strings.formatStringFromName("validator.invalidAppType", [appType], 1));
   } else if (this.type == "hosted" &&
              ["certified", "privileged"].includes(appType)) {
     this.error(strings.formatStringFromName("validator.invalidHostedPriviledges", [appType], 1));
   }
 
   // certified app are not fully supported on the simulator
   if (appType === "certified") {
     this.warning(strings.GetStringFromName("validator.noCertifiedSupport"));
   }
 };
 
-AppValidator.prototype.validate = function () {
+AppValidator.prototype.validate = function() {
   this.errors = [];
   this.warnings = [];
-  return this._getManifest().
-    then((manifest) => {
+  return this._getManifest()
+    .then((manifest) => {
       if (manifest) {
         this.manifest = manifest;
 
         // Skip validations for add-ons
         if (manifest.role === "addon" || manifest.manifest_version) {
           return Promise.resolve();
         }
 
--- a/devtools/client/webide/modules/config-view.js
+++ b/devtools/client/webide/modules/config-view.js
@@ -1,29 +1,27 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-const {Cu} = require("chrome");
-
 const EventEmitter = require("devtools/shared/event-emitter");
 const Services = require("Services");
 const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties");
 
 var ConfigView;
 
-module.exports = ConfigView = function (window) {
+module.exports = ConfigView = function(window) {
   EventEmitter.decorate(this);
   this._doc = window.document;
   this._keys = [];
   return this;
 };
 
 ConfigView.prototype = {
-  _renderByType: function (input, name, value, customType) {
+  _renderByType: function(input, name, value, customType) {
     value = customType || typeof value;
 
     switch (value) {
       case "boolean":
         input.setAttribute("data-type", "boolean");
         input.setAttribute("type", "checkbox");
         break;
       case "number":
@@ -57,49 +55,49 @@ ConfigView.prototype = {
   set kind(kind) {
     this._kind = kind;
   },
 
   set includeTypeName(include) {
     this._includeTypeName = include;
   },
 
-  search: function (event) {
+  search: function(event) {
     if (event.target.value.length) {
       let stringMatch = new RegExp(event.target.value, "i");
 
       for (let i = 0; i < this._keys.length; i++) {
         let key = this._keys[i];
         let row = this._doc.getElementById("row-" + key);
         if (key.match(stringMatch)) {
           row.classList.remove("hide");
         } else if (row) {
           row.classList.add("hide");
         }
       }
     } else {
-      var trs = this._doc.getElementById("device-fields").querySelectorAll("tr");
+      let trs = this._doc.getElementById("device-fields").querySelectorAll("tr");
 
       for (let i = 0; i < trs.length; i++) {
         trs[i].classList.remove("hide");
       }
     }
   },
 
-  generateDisplay: function (json) {
+  generateDisplay: function(json) {
     let deviceItems = Object.keys(json);
     deviceItems.sort();
     this.keys = deviceItems;
     for (let i = 0; i < this.keys.length; i++) {
       let key = this.keys[i];
       this.generateField(key, json[key].value, json[key].hasUserValue);
     }
   },
 
-  generateField: function (name, value, hasUserValue, customType, newRow) {
+  generateField: function(name, value, hasUserValue, customType, newRow) {
     let table = this._doc.querySelector("table");
     let sResetDefault = Strings.GetStringFromName("device_reset_default");
 
     if (!this._keys.includes(name)) {
       this._keys.push(name);
     }
 
     let input = this._doc.createElement("input");
@@ -155,48 +153,48 @@ ConfigView.prototype = {
       } else {
         existing.value = value;
       }
     } else {
       table.appendChild(tr);
     }
   },
 
-  resetTable: function () {
+  resetTable: function() {
     let table = this._doc.querySelector("table");
     let trs = table.querySelectorAll("tr:not(#add-custom-field)");
 
-    for (var i = 0; i < trs.length; i++) {
+    for (let i = 0; i < trs.length; i++) {
       table.removeChild(trs[i]);
     }
 
     return table;
   },
 
-  _getCallType: function (type, name) {
+  _getCallType: function(type, name) {
     let frontName = "get";
 
     if (this._includeTypeName) {
       frontName += type;
     }
 
     return this._front[frontName + this._kind](name);
   },
 
-  _setCallType: function (type, name, value) {
+  _setCallType: function(type, name, value) {
     let frontName = "set";
 
     if (this._includeTypeName) {
       frontName += type;
     }
 
     return this._front[frontName + this._kind](name, value);
   },
 
-  _saveByType: function (options) {
+  _saveByType: function(options) {
     let fieldName = options.id;
     let inputType = options.type;
     let value = options.value;
     let input = this._doc.getElementById(fieldName);
 
     switch (inputType) {
       case "boolean":
         this._setCallType("Bool", fieldName, input.checked);
@@ -211,17 +209,17 @@ ConfigView.prototype = {
         this._setCallType("Object", fieldName, value);
         break;
       default:
         this._setCallType("Char", fieldName, value);
         break;
     }
   },
 
-  updateField: function (event) {
+  updateField: function(event) {
     if (event.target) {
       let inputType = event.target.getAttribute("data-type");
       let inputValue = event.target.checked || event.target.value;
 
       if (event.target.nodeName == "input" &&
           event.target.validity.valid &&
           event.target.classList.contains("editable")) {
         let id = event.target.id;
@@ -238,17 +236,17 @@ ConfigView.prototype = {
           type: inputType,
           value: inputValue
         });
         this._doc.getElementById("btn-" + id).classList.remove("hide");
       }
     }
   },
 
-  _resetToDefault: function (name, input, button) {
+  _resetToDefault: function(name, input, button) {
     this._front["clearUser" + this._kind](name);
     let dataType = input.getAttribute("data-type");
     let tr = this._doc.getElementById("row-" + name);
 
     switch (dataType) {
       case "boolean":
         this._defaultField = this._getCallType("Bool", name);
         this._defaultField.then(boolean => {
@@ -282,64 +280,61 @@ ConfigView.prototype = {
           tr.remove();
         });
         break;
     }
 
     button.classList.add("hide");
   },
 
-  checkReset: function (event) {
+  checkReset: function(event) {
     if (event.target.classList.contains("reset")) {
       let btnId = event.target.getAttribute("data-id");
       let input = this._doc.getElementById(btnId);
       this._resetToDefault(btnId, input, event.target);
     }
   },
 
-  updateFieldType: function () {
+  updateFieldType: function() {
     let table = this._doc.querySelector("table");
     let customValueType = table.querySelector("#custom-value-type").value;
     let customTextEl = table.querySelector("#custom-value-text");
-    let customText = customTextEl.value;
 
     if (customValueType.length === 0) {
       return false;
     }
 
     switch (customValueType) {
       case "boolean":
         customTextEl.type = "checkbox";
-        customText = customTextEl.checked;
         break;
       case "number":
-        customText = parseInt(customText, 10) || 0;
         customTextEl.type = "number";
         break;
       default:
         customTextEl.type = "text";
         break;
     }
 
     return customValueType;
   },
 
-  clearNewFields: function () {
+  clearNewFields: function() {
     let table = this._doc.querySelector("table");
     let customTextEl = table.querySelector("#custom-value-text");
     if (customTextEl.checked) {
       customTextEl.checked = false;
     } else {
       customTextEl.value = "";
     }
 
     this.updateFieldType();
   },
 
-  updateNewField: function () {
+  updateNewField: function() {
     let table = this._doc.querySelector("table");
     let customValueType = this.updateFieldType();
 
     if (!customValueType) {
       return;
     }
 
     let customRow = table.querySelector("tr:nth-of-type(2)");
@@ -360,14 +355,14 @@ ConfigView.prototype = {
         type: customValueType,
         value: customText
       });
       customTextNameEl.value = "";
       this.clearNewFields();
     }
   },
 
-  checkNewFieldSubmit: function (event) {
+  checkNewFieldSubmit: function(event) {
     if (event.keyCode === 13) {
       this._doc.getElementById("custom-value").click();
     }
   }
 };
--- a/devtools/client/webide/modules/project-list.js
+++ b/devtools/client/webide/modules/project-list.js
@@ -1,26 +1,24 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-const {Cu} = require("chrome");
-
 const Services = require("Services");
 const {AppProjects} = require("devtools/client/webide/modules/app-projects");
 const {AppManager} = require("devtools/client/webide/modules/app-manager");
 const EventEmitter = require("devtools/shared/event-emitter");
 const utils = require("devtools/client/webide/modules/utils");
 const Telemetry = require("devtools/client/shared/telemetry");
 
 const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties");
 
 var ProjectList;
 
-module.exports = ProjectList = function (win, parentWindow) {
+module.exports = ProjectList = function(win, parentWindow) {
   EventEmitter.decorate(this);
   this._doc = win.document;
   this._UI = parentWindow.UI;
   this._parentWindow = parentWindow;
   this._telemetry = new Telemetry();
   this._panelNodeEl = "div";
 
   this.onWebIDEUpdate = this.onWebIDEUpdate.bind(this);
@@ -31,84 +29,85 @@ module.exports = ProjectList = function 
   AppManager.on("app-manager-update", this.appManagerUpdate);
 };
 
 ProjectList.prototype = {
   get doc() {
     return this._doc;
   },
 
-  appManagerUpdate: function (what, details) {
+  appManagerUpdate: function(what, details) {
     // Got a message from app-manager.js
     // See AppManager.update() for descriptions of what these events mean.
     switch (what) {
       case "project-removed":
       case "runtime-targets":
       case "connection":
         this.update(details);
         break;
       case "project":
         this.updateCommands();
         this.update(details);
         break;
     }
   },
 
-  onWebIDEUpdate: function (what, details) {
+  onWebIDEUpdate: function(what, details) {
     if (what == "busy" || what == "unbusy") {
       this.updateCommands();
     }
   },
 
   /**
    * testOptions: {       chrome mochitest support
    *   folder: nsIFile,   where to store the app
    *   index: Number,     index of the app in the template list
    *   name: String       name of the app
    * }
    */
-  newApp: function (testOptions) {
+  newApp: function(testOptions) {
     let parentWindow = this._parentWindow;
     let self = this;
-    return this._UI.busyUntil((async function () {
+    return this._UI.busyUntil((async function() {
       // Open newapp.xul, which will feed ret.location
       let ret = {location: null, testOptions: testOptions};
       parentWindow.openDialog("chrome://webide/content/newapp.xul", "newapp", "chrome,modal", ret);
-      if (!ret.location)
+      if (!ret.location) {
         return;
+      }
 
       // Retrieve added project
       let project = AppProjects.get(ret.location);
 
       // Select project
       AppManager.selectedProject = project;
 
       self._telemetry.actionOccurred("webideNewProject");
     })(), "creating new app");
   },
 
-  importPackagedApp: function (location) {
+  importPackagedApp: function(location) {
     let parentWindow = this._parentWindow;
     let UI = this._UI;
-    return UI.busyUntil((async function () {
+    return UI.busyUntil((async function() {
       let directory = await utils.getPackagedDirectory(parentWindow, location);
 
       if (!directory) {
         // User cancelled directory selection
         return;
       }
 
       await UI.importAndSelectApp(directory);
     })(), "importing packaged app");
   },
 
-  importHostedApp: function (location) {
+  importHostedApp: function(location) {
     let parentWindow = this._parentWindow;
     let UI = this._UI;
-    return UI.busyUntil((async function () {
+    return UI.busyUntil((async function() {
       let url = utils.getHostedURL(parentWindow, location);
 
       if (!url) {
         return;
       }
 
       await UI.importAndSelectApp(url);
     })(), "importing hosted app");
@@ -116,36 +115,36 @@ ProjectList.prototype = {
 
   /**
    * opts: {
    *   panel: Object,     currenl project panel node
    *   name: String,      name of the project
    *   icon: String       path of the project icon
    * }
    */
-  _renderProjectItem: function (opts) {
+  _renderProjectItem: function(opts) {
     let span = opts.panel.querySelector("span") || this._doc.createElement("span");
     span.textContent = opts.name;
     let icon = opts.panel.querySelector("img") || this._doc.createElement("img");
     icon.className = "project-image";
     icon.setAttribute("src", opts.icon);
     opts.panel.appendChild(icon);
     opts.panel.appendChild(span);
     opts.panel.setAttribute("title", opts.name);
   },
 
-  refreshTabs: function () {
+  refreshTabs: function() {
     if (AppManager.connected) {
       return AppManager.listTabs().then(() => {
         this.updateTabs();
       }).catch(console.error);
     }
   },
 
-  updateTabs: function () {
+  updateTabs: function() {
     let tabsHeaderNode = this._doc.querySelector("#panel-header-tabs");
     let tabsNode = this._doc.querySelector("#project-panel-tabs");
 
     while (tabsNode.hasChildNodes()) {
       tabsNode.firstChild.remove();
     }
 
     if (!AppManager.connected) {
@@ -195,21 +194,21 @@ ProjectList.prototype = {
           name: tab.name
         };
       }, true);
     }
 
     return Promise.resolve();
   },
 
-  updateApps: function () {
+  updateApps: function() {
     let doc = this._doc;
     let runtimeappsHeaderNode = doc.querySelector("#panel-header-runtimeapps");
     let sortedApps = [];
-    for (let [manifestURL, app] of AppManager.apps) {
+    for (let [/* manifestURL */, app] of AppManager.apps) {
       sortedApps.push(app);
     }
     sortedApps = sortedApps.sort((a, b) => {
       return a.manifest.name > b.manifest.name;
     });
     let mainProcess = AppManager.isMainProcessDebuggable();
     if (AppManager.connected && (sortedApps.length > 0 || mainProcess)) {
       runtimeappsHeaderNode.removeAttribute("hidden");
@@ -258,17 +257,17 @@ ProjectList.prototype = {
           name: app.manifest.name
         };
       }, true);
     }
 
     return Promise.resolve();
   },
 
-  updateCommands: function () {
+  updateCommands: function() {
     let doc = this._doc;
     let newAppCmd;
     let packagedAppCmd;
     let hostedAppCmd;
 
     newAppCmd = doc.querySelector("#new-app");
     packagedAppCmd = doc.querySelector("#packaged-app");
     hostedAppCmd = doc.querySelector("#hosted-app");
@@ -290,17 +289,17 @@ ProjectList.prototype = {
   },
 
   /**
    * Trigger an update of the project and remote runtime list.
    * @param options object (optional)
    *        An |options| object containing a type of |apps| or |tabs| will limit
    *        what is updated to only those sections.
    */
-  update: function (options) {
+  update: function(options) {
     if (options && options.type === "apps") {
       return this.updateApps();
     } else if (options && options.type === "tabs") {
       return this.updateTabs();
     }
 
     return new Promise((resolve, reject) => {
       let doc = this._doc;
@@ -354,17 +353,17 @@ ProjectList.prototype = {
       if (AppManager.connected) {
         AppManager.listTabs().then(() => {
           this.updateTabs();
         }).catch(console.error);
       }
     });
   },
 
-  destroy: function () {
+  destroy: function() {
     this._doc = null;
     AppManager.off("app-manager-update", this.appManagerUpdate);
     this._UI.off("webide-update", this.onWebIDEUpdate);
     this._UI = null;
     this._parentWindow = null;
     this._panelNodeEl = null;
   }
 };
--- a/devtools/client/webide/modules/runtime-list.js
+++ b/devtools/client/webide/modules/runtime-list.js
@@ -1,26 +1,22 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const Services = require("Services");
 const {AppManager} = require("devtools/client/webide/modules/app-manager");
 const EventEmitter = require("devtools/shared/event-emitter");
 const {RuntimeScanners, WiFiScanner} = require("devtools/client/webide/modules/runtimes");
 const {Devices} = require("resource://devtools/shared/apps/Devices.jsm");
-const utils = require("devtools/client/webide/modules/utils");
-
-const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties");
 
 var RuntimeList;
 
-module.exports = RuntimeList = function (window, parentWindow) {
+module.exports = RuntimeList = function(window, parentWindow) {
   EventEmitter.decorate(this);
   this._doc = window.document;
   this._UI = parentWindow.UI;
   this._Cmds = parentWindow.Cmds;
   this._parentWindow = parentWindow;
   this._panelNodeEl = "button";
   this._panelBoxEl = "div";
 
@@ -32,65 +28,65 @@ module.exports = RuntimeList = function 
   AppManager.on("app-manager-update", this.appManagerUpdate);
 };
 
 RuntimeList.prototype = {
   get doc() {
     return this._doc;
   },
 
-  appManagerUpdate: function (what, details) {
+  appManagerUpdate: function(what, details) {
     // Got a message from app-manager.js
     // See AppManager.update() for descriptions of what these events mean.
     switch (what) {
       case "runtime-list":
         this.update();
         break;
       case "connection":
       case "runtime-global-actors":
         this.updateCommands();
         break;
     }
   },
 
-  onWebIDEUpdate: function (what, details) {
+  onWebIDEUpdate: function(what, details) {
     if (what == "busy" || what == "unbusy") {
       this.updateCommands();
     }
   },
 
-  takeScreenshot: function () {
+  takeScreenshot: function() {
     this._Cmds.takeScreenshot();
   },
 
-  showRuntimeDetails: function () {
+  showRuntimeDetails: function() {
     this._Cmds.showRuntimeDetails();
   },
 
-  showDevicePreferences: function () {
+  showDevicePreferences: function() {
     this._Cmds.showDevicePrefs();
   },
 
-  showSettings: function () {
+  showSettings: function() {
     this._Cmds.showSettings();
   },
 
-  showTroubleShooting: function () {
+  showTroubleShooting: function() {
     this._Cmds.showTroubleShooting();
   },
 
-  showAddons: function () {
+  showAddons: function() {
     this._Cmds.showAddons();
   },
 
-  refreshScanners: function () {
+  refreshScanners: function() {
     RuntimeScanners.scan();
   },
 
-  updateCommands: function () {
+  updateCommands: function() {
     let doc = this._doc;
 
     // Runtime commands
     let screenshotCmd = doc.querySelector("#runtime-screenshot");
     let detailsCmd = doc.querySelector("#runtime-details");
     let disconnectCmd = doc.querySelector("#runtime-disconnect");
     let devicePrefsCmd = doc.querySelector("#runtime-preferences");
     let settingsCmd = doc.querySelector("#runtime-settings");
@@ -108,17 +104,17 @@ RuntimeList.prototype = {
       detailsCmd.setAttribute("disabled", "true");
       screenshotCmd.setAttribute("disabled", "true");
       disconnectCmd.setAttribute("disabled", "true");
       devicePrefsCmd.setAttribute("disabled", "true");
       settingsCmd.setAttribute("disabled", "true");
     }
   },
 
-  update: function () {
+  update: function() {
     let doc = this._doc;
     let wifiHeaderNode = doc.querySelector("#runtime-header-wifi");
 
     if (WiFiScanner.allowed) {
       wifiHeaderNode.removeAttribute("hidden");
     } else {
       wifiHeaderNode.setAttribute("hidden", "true");
     }
@@ -177,17 +173,17 @@ RuntimeList.prototype = {
           panelItemNode.appendChild(configButton);
         }
 
         parent.appendChild(panelItemNode);
       }
     }
   },
 
-  destroy: function () {
+  destroy: function() {
     this._doc = null;
     AppManager.off("app-manager-update", this.appManagerUpdate);
     this._UI.off("webide-update", this.onWebIDEUpdate);
     this._UI = null;
     this._Cmds = null;
     this._parentWindow = null;
     this._panelNodeEl = null;
   }
--- a/devtools/client/webide/modules/runtimes.js
+++ b/devtools/client/webide/modules/runtimes.js
@@ -2,17 +2,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {Ci} = require("chrome");
 const Services = require("Services");
 const {Devices} = require("resource://devtools/shared/apps/Devices.jsm");
-const {Connection} = require("devtools/shared/client/connection-manager");
 const {DebuggerServer} = require("devtools/server/main");
 const discovery = require("devtools/shared/discovery/discovery");
 const EventEmitter = require("devtools/shared/event-emitter");
 const promise = require("promise");
 loader.lazyRequireGetter(this, "AuthenticationResult",
   "devtools/shared/security/auth", true);
 loader.lazyRequireGetter(this, "DevToolsUtils",
   "devtools/shared/DevToolsUtils");
@@ -209,17 +208,17 @@ var LazyAdbScanner = {
   disable() {
     Devices.emit("adb-stop-polling");
   },
 
   scan() {
     return promise.resolve();
   },
 
-  listRuntimes: function () {
+  listRuntimes: function() {
     return [];
   }
 
 };
 
 EventEmitter.decorate(LazyAdbScanner);
 RuntimeScanners.add(LazyAdbScanner);
 
@@ -258,17 +257,17 @@ var WiFiScanner = {
     this._emitUpdated();
   },
 
   scan() {
     discovery.scan();
     return promise.resolve();
   },
 
-  listRuntimes: function () {
+  listRuntimes: function() {
     return this._runtimes;
   },
 
   ALLOWED_PREF: "devtools.remote.wifi.scan",
 
   get allowed() {
     return Services.prefs.getBoolPref(this.ALLOWED_PREF);
   },
@@ -294,17 +293,19 @@ var WiFiScanner = {
 EventEmitter.decorate(WiFiScanner);
 WiFiScanner.init();
 
 exports.WiFiScanner = WiFiScanner;
 
 var StaticScanner = {
   enable() {},
   disable() {},
-  scan() { return promise.resolve(); },
+  scan() {
+    return promise.resolve();
+  },
   listRuntimes() {
     let runtimes = [gRemoteRuntime];
     if (Services.prefs.getBoolPref("devtools.webide.enableLocalRuntime")) {
       runtimes.push(gLocalRuntime);
     }
     return runtimes;
   }
 };
@@ -327,17 +328,17 @@ var RuntimeTypes = exports.RuntimeTypes 
 function WiFiRuntime(deviceName) {
   this.deviceName = deviceName;
 }
 
 WiFiRuntime.prototype = {
   type: RuntimeTypes.WIFI,
   // Mark runtime as taking a long time to connect
   prolongedConnection: true,
-  connect: function (connection) {
+  connect: function(connection) {
     let service = discovery.getRemoteService("devtools", this.deviceName);
     if (!service) {
       return promise.reject(new Error("Can't find device: " + this.name));
     }
     connection.advertisement = service;
     connection.authenticator.sendOOB = this.sendOOB;
     // Disable the default connection timeout, since QR scanning can take an
     // unknown amount of time.  This prevents spurious errors (even after
@@ -387,17 +388,17 @@ WiFiRuntime.prototype = {
     }
 
     // Listen for the window our prompt opens, so we can close it programatically
     let promptWindow;
     let windowListener = {
       onOpenWindow(xulWindow) {
         let win = xulWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                            .getInterface(Ci.nsIDOMWindow);
-        win.addEventListener("load", function () {
+        win.addEventListener("load", function() {
           if (win.document.documentElement.getAttribute("id") != WINDOW_ID) {
             return;
           }
           // Found the window
           promptWindow = win;
           Services.wm.removeListener(windowListener);
         }, {once: true});
       },
@@ -430,17 +431,17 @@ WiFiRuntime.prototype = {
   }
 };
 
 // For testing use only
 exports._WiFiRuntime = WiFiRuntime;
 
 var gLocalRuntime = {
   type: RuntimeTypes.LOCAL,
-  connect: function (connection) {
+  connect: function(connection) {
     DebuggerServer.init();
     DebuggerServer.registerAllActors();
     DebuggerServer.allowChromeProcess = true;
     connection.host = null; // Force Pipe transport
     connection.port = null;
     connection.connect();
     return promise.resolve();
   },
@@ -452,17 +453,17 @@ var gLocalRuntime = {
   },
 };
 
 // For testing use only
 exports._gLocalRuntime = gLocalRuntime;
 
 var gRemoteRuntime = {
   type: RuntimeTypes.REMOTE,
-  connect: function (connection) {
+  connect: function(connection) {
     let win = Services.wm.getMostRecentWindow("devtools:webide");
     if (!win) {
       return promise.reject(new Error("No WebIDE window found"));
     }
     let ret = {value: connection.host + ":" + connection.port};
     let title = Strings.GetStringFromName("remote_runtime_promptTitle");
     let message = Strings.GetStringFromName("remote_runtime_promptMessage");
     let ok = Services.prompt.prompt(win, title, message, ret, null, {});
--- a/devtools/client/webide/modules/tab-store.js
+++ b/devtools/client/webide/modules/tab-store.js
@@ -1,23 +1,21 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-const { Cu } = require("chrome");
-
 const { TargetFactory } = require("devtools/client/framework/target");
 const EventEmitter = require("devtools/shared/event-emitter");
 const { Connection } = require("devtools/shared/client/connection-manager");
 
 const _knownTabStores = new WeakMap();
 
 var TabStore;
 
-module.exports = TabStore = function (connection) {
+module.exports = TabStore = function(connection) {
   // If we already know about this connection,
   // let's re-use the existing store.
   if (_knownTabStores.has(connection)) {
     return _knownTabStores.get(connection);
   }
 
   _knownTabStores.set(connection, this);
 
@@ -34,36 +32,36 @@ module.exports = TabStore = function (co
   this._onTabListChanged = this._onTabListChanged.bind(this);
   this._onTabNavigated = this._onTabNavigated.bind(this);
   this._onStatusChanged();
   return this;
 };
 
 TabStore.prototype = {
 
-  destroy: function () {
+  destroy: function() {
     if (this._connection) {
       // While this.destroy is bound using .once() above, that event may not
       // have occurred when the TabStore client calls destroy, so we
       // manually remove it here.
       this._connection.off(Connection.Events.DESTROYED, this.destroy);
       this._connection.off(Connection.Events.STATUS_CHANGED, this._onStatusChanged);
       _knownTabStores.delete(this._connection);
       this._connection = null;
     }
   },
 
-  _resetStore: function () {
+  _resetStore: function() {
     this.response = null;
     this.tabs = [];
     this._selectedTab = null;
     this._selectedTabTargetPromise = null;
   },
 
-  _onStatusChanged: function () {
+  _onStatusChanged: function() {
     if (this._connection.status == Connection.Status.CONNECTED) {
       // Watch for changes to remote browser tabs
       this._connection.client.addListener("tabListChanged",
                                           this._onTabListChanged);
       this._connection.client.addListener("tabNavigated",
                                           this._onTabNavigated);
       this.listTabs();
     } else {
@@ -72,31 +70,31 @@ TabStore.prototype = {
                                                this._onTabListChanged);
         this._connection.client.removeListener("tabNavigated",
                                                this._onTabNavigated);
       }
       this._resetStore();
     }
   },
 
-  _onTabListChanged: function () {
+  _onTabListChanged: function() {
     this.listTabs().then(() => this.emit("tab-list"))
                    .catch(console.error);
   },
 
-  _onTabNavigated: function (e, { from, title, url }) {
+  _onTabNavigated: function(e, { from, title, url }) {
     if (!this._selectedTab || from !== this._selectedTab.actor) {
       return;
     }
     this._selectedTab.url = url;
     this._selectedTab.title = title;
     this.emit("navigate");
   },
 
-  listTabs: function () {
+  listTabs: function() {
     if (!this._connection || !this._connection.client) {
       return Promise.reject(new Error("Can't listTabs, not connected."));
     }
 
     return new Promise((resolve, reject) => {
       this._connection.client.listTabs().then(response => {
         if (response.error) {
           this._connection.disconnect();
@@ -130,36 +128,36 @@ TabStore.prototype = {
     this._selectedTab = tab;
     this._selectedTabTargetPromise = null;
     // Attach to the tab to follow navigation events
     if (this._selectedTab) {
       this.getTargetForTab();
     }
   },
 
-  _checkSelectedTab: function () {
+  _checkSelectedTab: function() {
     if (!this._selectedTab) {
       return;
     }
     let alive = this.tabs.some(tab => {
       return tab.actor === this._selectedTab.actor;
     });
     if (!alive) {
       this._selectedTab = null;
       this._selectedTabTargetPromise = null;
       this.emit("closed");
     }
   },
 
-  getTargetForTab: function () {
+  getTargetForTab: function() {
     if (this._selectedTabTargetPromise) {
       return this._selectedTabTargetPromise;
     }
     let store = this;
-    this._selectedTabTargetPromise = (async function () {
+    this._selectedTabTargetPromise = (async function() {
       // If you connect to a tab, then detach from it, the root actor may have
       // de-listed the actors that belong to the tab.  This breaks the toolbox
       // if you try to connect to the same tab again.  To work around this
       // issue, we force a "listTabs" request before connecting to a tab.
       await store.listTabs();
       return TargetFactory.forRemoteTab({
         form: store._selectedTab,
         client: store._connection.client,
--- a/devtools/client/webide/modules/utils.js
+++ b/devtools/client/webide/modules/utils.js
@@ -1,13 +1,13 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-const { Cc, Cu, Ci } = require("chrome");
+const { Cc, Ci } = require("chrome");
 const { FileUtils } = require("resource://gre/modules/FileUtils.jsm");
 const Services = require("Services");
 const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties");
 
 function doesFileExist(location) {
   let file = new FileUtils.File(location);
   return file.exists();
 }
--- a/devtools/client/webide/test/browser_tabs.js
+++ b/devtools/client/webide/test/browser_tabs.js
@@ -1,19 +1,19 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
 const TEST_URI = "http://example.com/browser/devtools/client/webide/test/doc_tabs.html";
 
 function test() {
   waitForExplicitFinish();
-  requestCompleteLog();
+  SimpleTest.requestCompleteLog();
 
-  (async function () {
+  (async function() {
     // Since we test the connections set below, destroy the server in case it
     // was left open.
     DebuggerServer.destroy();
     DebuggerServer.init();
     DebuggerServer.registerAllActors();
 
     let tab = await addTab(TEST_URI);
 
@@ -68,17 +68,17 @@ function connectToLocal(win, docRuntime)
     win.AppManager.connection.once(
       win.Connection.Events.CONNECTED,
       resolve);
     docRuntime.querySelectorAll(".runtime-panel-item-other")[1].click();
   });
 }
 
 function selectTabProject(win, docProject) {
-  return (async function () {
+  return (async function() {
     await waitForUpdate(win, "runtime-targets");
     let tabsNode = docProject.querySelector("#project-panel-tabs");
     let tabNode = tabsNode.querySelectorAll(".panel-item")[1];
     let project = waitForUpdate(win, "project");
     tabNode.click();
     await project;
   })();
 }
--- a/devtools/client/webide/test/device_front_shared.js
+++ b/devtools/client/webide/test/device_front_shared.js
@@ -1,11 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
+/* eslint no-unused-vars: ["error", {"args": "none", "vars": "local"}] */
+
 "use strict";
 
 var customName;
 var customValue;
 var customValueType;
 var customBtn;
 var newField;
 var change;
@@ -104,17 +106,17 @@ function addNewFieldInteger() {
     is(newField.type, "number", "Custom type is a number");
     is(newField.value, "1", "Custom integer value is correct");
   }
   ok(found, "Found new integer field line");
   is(customName.value, "", "Custom integer name reset");
   is(customValue.value, "", "Custom integer value reset");
 }
 
-var editFieldInteger = async function () {
+var editFieldInteger = async function() {
   // Edit existing custom integer preference
   newField.value = 3;
   newField.click();
   is(newField.value, "3", "Custom integer existing value is correct");
 
   // Reset a custom field
   let resetBtn = doc.querySelector("#btn-new-integer-field");
   resetBtn.click();
@@ -125,30 +127,30 @@ var editFieldInteger = async function ()
     let fieldRow = doc.querySelector("#row-new-integer-field");
     if (!fieldRow) {
       found = false;
     }
     ok(!found, "Custom field removed");
   }
 };
 
-var resetExistingField = async function (id) {
+var resetExistingField = async function(id) {
   let existing = doc.getElementById(id);
   existing.click();
   is(existing.checked, true, "Existing boolean value is correct");
   resetBtn = doc.getElementById("btn-" + id);
   resetBtn.click();
 
   await iframe.contentWindow.configView._defaultField;
 
   ok(resetBtn.classList.contains("hide"), true, "Reset button hidden");
   is(existing.checked, true, "Existing field reset");
 };
 
-var resetNewField = async function (id) {
+var resetNewField = async function(id) {
   let custom = doc.getElementById(id);
   custom.click();
   is(custom.value, "test", "New string value is correct");
   resetBtn = doc.getElementById("btn-" + id);
   resetBtn.click();
 
   await iframe.contentWindow.configView._defaultField;
 
@@ -166,17 +168,17 @@ function addNewFieldBoolean() {
   if (newField) {
     found = true;
     is(newField.type, "checkbox", "Custom type is a checkbox");
     is(newField.checked, true, "Custom boolean value is correctly true");
   }
   ok(found, "Found new boolean field line");
 
   // Mouse event trigger
-  var mouseClick = new MouseEvent("click", {
+  let mouseClick = new MouseEvent("click", {
     canBubble: true,
     cancelable: true,
     view: doc.parent,
   });
 
   found = false;
   customValueType.value = "boolean";
   customValueType.dispatchEvent(change);
--- a/devtools/client/webide/test/head.js
+++ b/devtools/client/webide/test/head.js
@@ -5,18 +5,16 @@
 
 const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
 const { FileUtils } = require("resource://gre/modules/FileUtils.jsm");
 const { gDevTools } = require("devtools/client/framework/devtools");
 const Services = require("Services");
 const { AppProjects } = require("devtools/client/webide/modules/app-projects");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 const { DebuggerServer } = require("devtools/server/main");
-const flags = require("devtools/shared/flags");
-flags.testing = true;
 
 var TEST_BASE;
 if (window.location === "chrome://browser/content/browser.xul") {
   TEST_BASE = "chrome://mochitests/content/browser/devtools/client/webide/test/";
 } else {
   TEST_BASE = "chrome://mochitests/content/chrome/devtools/client/webide/test/";
 }
 
@@ -25,60 +23,59 @@ Services.prefs.setBoolPref("devtools.web
 
 Services.prefs.setCharPref("devtools.webide.adbAddonURL", TEST_BASE + "addons/adbhelper-#OS#.xpi");
 Services.prefs.setCharPref("devtools.webide.templatesURL", TEST_BASE + "templates.json");
 Services.prefs.setCharPref("devtools.devices.url", TEST_BASE + "browser_devices.json");
 
 var registerCleanupFunction = registerCleanupFunction ||
                               SimpleTest.registerCleanupFunction;
 registerCleanupFunction(() => {
-  flags.testing = false;
   Services.prefs.clearUserPref("devtools.webide.enabled");
   Services.prefs.clearUserPref("devtools.webide.enableLocalRuntime");
   Services.prefs.clearUserPref("devtools.webide.autoinstallADBHelper");
   Services.prefs.clearUserPref("devtools.webide.busyTimeout");
   Services.prefs.clearUserPref("devtools.webide.lastSelectedProject");
   Services.prefs.clearUserPref("devtools.webide.lastConnectedRuntime");
 });
 
-var openWebIDE = async function (autoInstallAddons) {
+var openWebIDE = async function(autoInstallAddons) {
   info("opening WebIDE");
 
   Services.prefs.setBoolPref("devtools.webide.autoinstallADBHelper", !!autoInstallAddons);
 
-  let ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].getService(Ci.nsIWindowWatcher);
-  let win = ww.openWindow(null, "chrome://webide/content/", "webide", "chrome,centerscreen,resizable", null);
+  let win = Services.ww.openWindow(null, "chrome://webide/content/", "webide",
+                                   "chrome,centerscreen,resizable", null);
 
   await new Promise(resolve => {
-    win.addEventListener("load", function () {
+    win.addEventListener("load", function() {
       SimpleTest.requestCompleteLog();
       SimpleTest.executeSoon(resolve);
     }, {once: true});
   });
 
   info("WebIDE open");
 
   return win;
 };
 
 function closeWebIDE(win) {
   info("Closing WebIDE");
 
   return new Promise(resolve => {
-    win.addEventListener("unload", function () {
+    win.addEventListener("unload", function() {
       info("WebIDE closed");
       SimpleTest.executeSoon(resolve);
     }, {once: true});
 
     win.close();
   });
 }
 
 function removeAllProjects() {
-  return (async function () {
+  return (async function() {
     await AppProjects.load();
     // use a new array so we're not iterating over the same
     // underlying array that's being modified by AppProjects
     let projects = AppProjects.projects.map(p => p.location);
     for (let i = 0; i < projects.length; i++) {
       await AppProjects.remove(projects[i]);
     }
   })();
@@ -122,49 +119,49 @@ function documentIsLoaded(doc) {
         }
       });
     }
   });
 }
 
 function lazyIframeIsLoaded(iframe) {
   return new Promise(resolve => {
-    iframe.addEventListener("load", function () {
+    iframe.addEventListener("load", function() {
       resolve(nextTick());
     }, {capture: true, once: true});
   });
 }
 
 function addTab(aUrl, aWindow) {
   info("Adding tab: " + aUrl);
 
   return new Promise(resolve => {
     let targetWindow = aWindow || window;
     let targetBrowser = targetWindow.gBrowser;
 
     targetWindow.focus();
     let tab = targetBrowser.selectedTab = targetBrowser.addTab(aUrl);
     let linkedBrowser = tab.linkedBrowser;
 
-    BrowserTestUtils.browserLoaded(linkedBrowser).then(function () {
+    BrowserTestUtils.browserLoaded(linkedBrowser).then(function() {
       info("Tab added and finished loading: " + aUrl);
       resolve(tab);
     });
   });
 }
 
 function removeTab(aTab, aWindow) {
   info("Removing tab.");
 
   return new Promise(resolve => {
     let targetWindow = aWindow || window;
     let targetBrowser = targetWindow.gBrowser;
     let tabContainer = targetBrowser.tabContainer;
 
-    tabContainer.addEventListener("TabClose", function (aEvent) {
+    tabContainer.addEventListener("TabClose", function(aEvent) {
       info("Tab removed and finished closing.");
       resolve();
     }, {once: true});
 
     targetBrowser.removeTab(aTab);
   });
 }
 
--- a/devtools/client/webide/test/test_addons.html
+++ b/devtools/client/webide/test/test_addons.html
@@ -10,65 +10,63 @@
     <script type="application/javascript" src="chrome://mochikit/content/chrome-harness.js"></script>
     <script type="application/javascript" src="head.js"></script>
     <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
   </head>
 
   <body>
 
     <script type="application/javascript">
+      // XXX Bug 1072167 - When updating after migration, fix the no-undef issues.
+      /* eslint-disable no-undef */
+
       window.onload = function() {
         SimpleTest.waitForExplicitFinish();
 
-        const {Devices} = ChromeUtils.import("resource://devtools/shared/apps/Devices.jsm");
-
-        let adbAddonsInstalled;
+        const {Devices} = ChromeUtils.import("resource://devtools/shared/apps/Devices.jsm", {});
 
         function uninstallADBFromUI(doc) {
           return new Promise((resolve, reject) => {
             Devices.on("addon-status-updated", function onUpdate() {
               nextTick().then(() => {
                 let li = doc.querySelector('[status="uninstalled"][addon="adb"]');
                 if (li) {
                   Devices.off("addon-status-updated", onUpdate);
                   resolve();
                 } else {
                   reject("Can't find item");
                 }
-              })
+              });
             });
             let li = doc.querySelector('[status="installed"][addon="adb"]');
             li.querySelector(".uninstall-button").click();
           });
         }
 
-        (async function () {
-
+        (async function() {
           ok(!Devices.helperAddonInstalled, "Helper not installed");
 
           let win = await openWebIDE(true);
-          let docRuntime = getRuntimeDocument(win);
 
           ok(Devices.helperAddonInstalled, "Helper has been auto-installed");
 
           await nextTick();
 
           let w = addonDoc.querySelector(".warning");
-          let display = addonDoc.defaultView.getComputedStyle(w).display
+          let display = addonDoc.defaultView.getComputedStyle(w).display;
           is(display, "none", "Warning about missing ADB hidden");
 
           await uninstallADBFromUI(addonDoc, "adb");
 
           items = panelNode.querySelectorAll(".runtime-panel-item-usb");
           is(items.length, 0, "No usb runtime listed");
 
-          display = addonDoc.defaultView.getComputedStyle(w).display
+          display = addonDoc.defaultView.getComputedStyle(w).display;
           is(display, "block", "Warning about missing ADB present");
 
           await closeWebIDE(win);
 
           SimpleTest.finish();
-
         })();
-      }
+      };
     </script>
   </body>
 </html>
--- a/devtools/client/webide/test/test_app_validator.html
+++ b/devtools/client/webide/test/test_app_validator.html
@@ -16,160 +16,158 @@
     <script type="application/javascript">
     ChromeUtils.import("resource://testing-common/httpd.js");
     const {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
 
     const {AppValidator} = require("devtools/client/webide/modules/app-validator");
     const Services = require("Services");
     const nsFile = Components.Constructor("@mozilla.org/file/local;1",
                                            "nsIFile", "initWithPath");
-    const cr = Cc["@mozilla.org/chrome/chrome-registry;1"]
-                 .getService(Ci.nsIChromeRegistry);
     const strings = Services.strings.createBundle("chrome://devtools/locale/app-manager.properties");
-    let httpserver, origin;
+    let httpserver, fakeOrigin;
 
     window.onload = function() {
       SimpleTest.waitForExplicitFinish();
 
       httpserver = new HttpServer();
       httpserver.start(-1);
-      origin = "http://localhost:" + httpserver.identity.primaryPort + "/";
+      fakeOrigin = "http://localhost:" + httpserver.identity.primaryPort + "/";
 
       next();
-    }
+    };
 
-    function createHosted(path, manifestFile="/manifest.webapp") {
+    function createHosted(path, manifestFile = "/manifest.webapp") {
       let dirPath = getTestFilePath("validator/" + path);
       httpserver.registerDirectory("/", nsFile(dirPath));
       return new AppValidator({
         type: "hosted",
-        location: origin + manifestFile
+        location: fakeOrigin + manifestFile
       });
     }
 
     function createPackaged(path) {
       let dirPath = getTestFilePath("validator/" + path);
       return new AppValidator({
         type: "packaged",
         location: dirPath
       });
     }
 
     function next() {
       let test = tests.shift();
       if (test) {
         try {
           test();
-        } catch(e) {
+        } catch (e) {
           console.error("exception", String(e), e, e.stack);
         }
       } else {
         httpserver.stop(function() {
           SimpleTest.finish();
         });
       }
     }
 
     let tests =  [
       // Test a 100% valid example
-      function () {
+      function() {
         let validator = createHosted("valid");
         validator.validate().then(() => {
-            is(validator.errors.length, 0, "valid app got no error");
-            is(validator.warnings.length, 0, "valid app got no warning");
+          is(validator.errors.length, 0, "valid app got no error");
+          is(validator.warnings.length, 0, "valid app got no warning");
 
-            next();
-          });
+          next();
+        });
       },
 
-      function () {
+      function() {
         let validator = createPackaged("valid");
         validator.validate().then(() => {
-            is(validator.errors.length, 0, "valid packaged app got no error");
-            is(validator.warnings.length, 0, "valid packaged app got no warning");
+          is(validator.errors.length, 0, "valid packaged app got no error");
+          is(validator.warnings.length, 0, "valid packaged app got no warning");
 
-            next();
-          });
+          next();
+        });
       },
 
       // Test a launch path that returns a 404
-      function () {
+      function() {
         let validator = createHosted("wrong-launch-path");
         validator.validate().then(() => {
-            is(validator.errors.length, 1, "app with non-existant launch path got an error");
-            is(validator.errors[0], strings.formatStringFromName("validator.accessFailedLaunchPathBadHttpCode", [origin + "wrong-path.html", 404], 2),
+          is(validator.errors.length, 1, "app with non-existant launch path got an error");
+          is(validator.errors[0], strings.formatStringFromName("validator.accessFailedLaunchPathBadHttpCode", [fakeOrigin + "wrong-path.html", 404], 2),
                "with the right error message");
-            is(validator.warnings.length, 0, "but no warning");
-            next();
-          });
+          is(validator.warnings.length, 0, "but no warning");
+          next();
+        });
       },
-      function () {
+      function() {
         let validator = createPackaged("wrong-launch-path");
         validator.validate().then(() => {
-            is(validator.errors.length, 1, "app with wrong path got an error");
-            let file = nsFile(validator.location);
-            file.append("wrong-path.html");
-            let url = Services.io.newFileURI(file);
-            is(validator.errors[0], strings.formatStringFromName("validator.accessFailedLaunchPath", [url.spec], 1),
+          is(validator.errors.length, 1, "app with wrong path got an error");
+          let file = nsFile(validator.location);
+          file.append("wrong-path.html");
+          let url = Services.io.newFileURI(file);
+          is(validator.errors[0], strings.formatStringFromName("validator.accessFailedLaunchPath", [url.spec], 1),
                "with the expected message");
-            is(validator.warnings.length, 0, "but no warning");
+          is(validator.warnings.length, 0, "but no warning");
 
-            next();
-          });
+          next();
+        });
       },
 
       // Test when using a non-absolute path for launch_path
-      function () {
+      function() {
         let validator = createHosted("non-absolute-path");
         validator.validate().then(() => {
-            is(validator.errors.length, 1, "app with non absolute path got an error");
-            is(validator.errors[0], strings.formatStringFromName("validator.nonAbsoluteLaunchPath", ["non-absolute.html"], 1),
+          is(validator.errors.length, 1, "app with non absolute path got an error");
+          is(validator.errors[0], strings.formatStringFromName("validator.nonAbsoluteLaunchPath", ["non-absolute.html"], 1),
                "with expected message");
-            is(validator.warnings.length, 0, "but no warning");
-            next();
-          });
+          is(validator.warnings.length, 0, "but no warning");
+          next();
+        });
       },
-      function () {
+      function() {
         let validator = createPackaged("non-absolute-path");
         validator.validate().then(() => {
-            is(validator.errors.length, 1, "app with non absolute path got an error");
-            is(validator.errors[0], strings.formatStringFromName("validator.nonAbsoluteLaunchPath", ["non-absolute.html"], 1),
+          is(validator.errors.length, 1, "app with non absolute path got an error");
+          is(validator.errors[0], strings.formatStringFromName("validator.nonAbsoluteLaunchPath", ["non-absolute.html"], 1),
                "with expected message");
-            is(validator.warnings.length, 0, "but no warning");
-            next();
-          });
+          is(validator.warnings.length, 0, "but no warning");
+          next();
+        });
       },
 
       // Test multiple failures (missing name [error] and icon [warning])
-      function () {
+      function() {
         let validator = createHosted("no-name-or-icon");
         validator.validate().then(() => {
           checkNoNameOrIcon(validator);
         });
       },
-      function () {
+      function() {
         let validator = createPackaged("no-name-or-icon");
         validator.validate().then(() => {
           checkNoNameOrIcon(validator);
         });
       },
 
       // Test a regular URL instead of a direct link to the manifest
-      function () {
+      function() {
         let validator = createHosted("valid", "/");
         validator.validate().then(() => {
           is(validator.warnings.length, 0, "manifest found got no warning");
           is(validator.errors.length, 0, "manifest found got no error");
 
           next();
         });
       },
 
       // Test finding a manifest at origin's root
-      function () {
+      function() {
         let validator = createHosted("valid", "/unexisting-dir");
         validator.validate().then(() => {
           is(validator.warnings.length, 0, "manifest found at origin root got no warning");
           is(validator.errors.length, 0, "manifest found at origin root got no error");
 
           next();
         });
       },
--- a/devtools/client/webide/test/test_autoconnect_runtime.html
+++ b/devtools/client/webide/test/test_autoconnect_runtime.html
@@ -13,17 +13,17 @@
   </head>
 
   <body>
 
     <script type="application/javascript">
       window.onload = function() {
         SimpleTest.waitForExplicitFinish();
 
-        (async function () {
+        (async function() {
           DebuggerServer.init();
           DebuggerServer.registerAllActors();
 
           let win = await openWebIDE();
           let docRuntime = getRuntimeDocument(win);
 
           let fakeRuntime = {
             type: "USB",
@@ -81,12 +81,12 @@
           await win.Cmds.disconnectRuntime();
 
           await closeWebIDE(win);
 
           DebuggerServer.destroy();
 
           SimpleTest.finish();
         })();
-      }
+      };
     </script>
   </body>
 </html>
--- a/devtools/client/webide/test/test_autoselect_project.html
+++ b/devtools/client/webide/test/test_autoselect_project.html
@@ -13,17 +13,17 @@
   </head>
 
   <body>
 
     <script type="application/javascript">
       window.onload = function() {
         SimpleTest.waitForExplicitFinish();
 
-        (async function () {
+        (async function() {
           DebuggerServer.init();
           DebuggerServer.registerAllActors();
 
           let win = await openWebIDE();
           let docRuntime = getRuntimeDocument(win);
           let docProject = getProjectDocument(win);
 
           let panelNode = docRuntime.querySelector("#runtime-panel");
@@ -97,12 +97,12 @@
           await win.Cmds.disconnectRuntime();
 
           await closeWebIDE(win);
 
           DebuggerServer.destroy();
 
           SimpleTest.finish();
         })();
-      }
+      };
     </script>
   </body>
 </html>
--- a/devtools/client/webide/test/test_basic.html
+++ b/devtools/client/webide/test/test_basic.html
@@ -13,44 +13,44 @@
   </head>
 
   <body>
 
     <script type="application/javascript">
       window.onload = function() {
         SimpleTest.waitForExplicitFinish();
 
-        (async function () {
-            let win = await openWebIDE();
+        (async function() {
+          let win = await openWebIDE();
 
-            const {gDevToolsBrowser} = require("devtools/client/framework/devtools-browser");
-            await gDevToolsBrowser.isWebIDEInitialized.promise;
-            ok(true, "WebIDE was initialized");
+          const {gDevToolsBrowser} = require("devtools/client/framework/devtools-browser");
+          await gDevToolsBrowser.isWebIDEInitialized.promise;
+          ok(true, "WebIDE was initialized");
 
-            ok(win, "Found a window");
-            ok(win.AppManager, "App Manager accessible");
-            let appmgr = win.AppManager;
-            ok(appmgr.connection, "App Manager connection ready");
-            ok(appmgr.runtimeList, "Runtime list ready");
+          ok(win, "Found a window");
+          ok(win.AppManager, "App Manager accessible");
+          let appmgr = win.AppManager;
+          ok(appmgr.connection, "App Manager connection ready");
+          ok(appmgr.runtimeList, "Runtime list ready");
 
             // test error reporting
-            let nbox = win.document.querySelector("#notificationbox");
-            let notification =  nbox.getNotificationWithValue("webide:errornotification");
-            ok(!notification, "No notification yet");
-            let deferred = new Promise((resolve, reject) => {
-              nextTick().then(() => {
-                reject("BOOM!");
-              });
+          let nbox = win.document.querySelector("#notificationbox");
+          let notification =  nbox.getNotificationWithValue("webide:errornotification");
+          ok(!notification, "No notification yet");
+          let deferred = new Promise((resolve, reject) => {
+            nextTick().then(() => {
+              reject("BOOM!");
             });
-            try {
-              await win.UI.busyUntil(deferred, "xx");
-            } catch(e) {/* This *will* fail */}
-            notification =  nbox.getNotificationWithValue("webide:errornotification");
-            ok(notification, "Error has been reported");
+          });
+          try {
+            await win.UI.busyUntil(deferred, "xx");
+          } catch (e) { /* This *will* fail */ }
+          notification =  nbox.getNotificationWithValue("webide:errornotification");
+          ok(notification, "Error has been reported");
 
-            await closeWebIDE(win);
+          await closeWebIDE(win);
 
-            SimpleTest.finish();
+          SimpleTest.finish();
         })();
-      }
+      };
     </script>
   </body>
 </html>
--- a/devtools/client/webide/test/test_device_preferences.html
+++ b/devtools/client/webide/test/test_device_preferences.html
@@ -10,20 +10,21 @@
     <script type="application/javascript" src="head.js"></script>
     <script type="application/javascript" src="device_front_shared.js"></script>
     <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
   </head>
 
   <body>
 
     <script type="application/javascript">
+      /* import-globals-from device_front_shared.js */
       window.onload = function() {
         SimpleTest.waitForExplicitFinish();
 
-        (async function () {
+        (async function() {
           DebuggerServer.init();
           DebuggerServer.registerAllActors();
 
           let win = await openWebIDE();
 
           let prefIframe = win.document.querySelector("#deck-panel-devicepreferences");
           let docRuntime = getRuntimeDocument(win);
 
@@ -74,12 +75,12 @@
 
           await closeWebIDE(win);
 
           SimpleTest.finish();
         })().catch(e => {
           ok(false, "Exception: " + e);
           SimpleTest.finish();
         });
-      }
+      };
     </script>
   </body>
 </html>
--- a/devtools/client/webide/test/test_device_runtime.html
+++ b/devtools/client/webide/test/test_device_runtime.html
@@ -13,17 +13,17 @@
   </head>
 
   <body>
 
     <script type="application/javascript">
       window.onload = function() {
         SimpleTest.waitForExplicitFinish();
 
-        (async function () {
+        (async function() {
           DebuggerServer.init();
           DebuggerServer.registerAllActors();
 
           let win = await openWebIDE();
 
           let detailsIframe = win.document.querySelector("#deck-panel-runtimedetails");
 
           await connectToLocalRuntime(win);
@@ -46,17 +46,17 @@
           // device info and permissions content is checked in other tests
           // We just test one value to make sure we get something
 
           let doc = detailsIframe.contentWindow.document;
           let trs = doc.querySelectorAll("tr");
           let found = false;
 
           for (let tr of trs) {
-            let [name,val] = tr.querySelectorAll("td");
+            let [name, val] = tr.querySelectorAll("td");
             if (name.textContent == "appid") {
               found = true;
               is(val.textContent, Services.appinfo.ID, "appid has the right value");
               break;
             }
           }
           ok(found, "Found appid line");
 
@@ -68,12 +68,12 @@
 
           await closeWebIDE(win);
 
           SimpleTest.finish();
         })().catch(e => {
           ok(false, "Exception: " + e);
           SimpleTest.finish();
         });
-      }
+      };
     </script>
   </body>
 </html>
--- a/devtools/client/webide/test/test_duplicate_import.html
+++ b/devtools/client/webide/test/test_duplicate_import.html
@@ -12,17 +12,17 @@
     <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
   </head>
 
   <body>
     <script type="application/javascript">
       window.onload = function() {
         SimpleTest.waitForExplicitFinish();
 
-        (async function () {
+        (async function() {
           let win = await openWebIDE();
           let docProject = getProjectDocument(win);
           let winProject = getProjectWindow(win);
           let packagedAppLocation = getTestFilePath("app");
           let hostedAppManifest = TEST_BASE + "hosted_app.manifest";
 
           await win.AppProjects.load();
           is(win.AppProjects.projects.length, 0, "IDB is empty");
@@ -65,13 +65,13 @@
 
           await removeAllProjects();
 
           SimpleTest.finish();
         })().catch(e => {
           ok(false, "Exception: " + e);
           SimpleTest.finish();
         });
-      }
+      };
     </script>
   </body>
 </html>
 
--- a/devtools/client/webide/test/test_fullscreenToolbox.html
+++ b/devtools/client/webide/test/test_fullscreenToolbox.html
@@ -22,17 +22,17 @@
               resolve);
           docRuntime.querySelectorAll(".runtime-panel-item-other")[1].click();
         });
       }
 
       window.onload = function() {
         SimpleTest.waitForExplicitFinish();
 
-        (async function () {
+        (async function() {
           let win = await openWebIDE();
           let docProject = getProjectDocument(win);
           let docRuntime = getRuntimeDocument(win);
           win.AppManager.update("runtime-list");
 
           connectToLocal(win, docRuntime);
 
           // Select main process
@@ -56,12 +56,12 @@
           await win.Cmds.disconnectRuntime();
 
           await closeWebIDE(win);
 
           DebuggerServer.destroy();
 
           SimpleTest.finish();
         })();
-      }
+      };
     </script>
   </body>
 </html>
--- a/devtools/client/webide/test/test_import.html
+++ b/devtools/client/webide/test/test_import.html
@@ -12,17 +12,17 @@
     <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
   </head>
 
   <body>
     <script type="application/javascript">
       window.onload = function() {
         SimpleTest.waitForExplicitFinish();
 
-        (async function () {
+        (async function() {
           let win = await openWebIDE();
           let docProject = getProjectDocument(win);
           let winProject = getProjectWindow(win);
           let packagedAppLocation = getTestFilePath("app");
 
           await win.AppProjects.load();
           is(win.AppProjects.projects.length, 0, "IDB is empty");
 
@@ -53,17 +53,17 @@
 
           await nextTick();
 
           hostedAppManifest = TEST_BASE + "/app";
           await winProject.projectList.importHostedApp(hostedAppManifest);
           await waitForUpdate(win, "project-validated");
 
           project = win.AppManager.selectedProject;
-          ok(project.location.endsWith('manifest.webapp'), "The manifest was found and the project was updated");
+          ok(project.location.endsWith("manifest.webapp"), "The manifest was found and the project was updated");
 
           let panelNode = docProject.querySelector("#project-panel");
           let items = panelNode.querySelectorAll(".panel-item");
           // 4 controls, + 2 projects
           is(items.length, 6, "6 projects in panel");
           is(items[3].querySelector("span").textContent, "A name (in app directory)", "Panel text is correct");
           is(items[4].querySelector("span").textContent, "hosted manifest name property", "Panel text is correct");
 
@@ -71,12 +71,12 @@
 
           await removeAllProjects();
 
           SimpleTest.finish();
         })().catch(e => {
           ok(false, "Exception: " + e);
           SimpleTest.finish();
         });
-      }
+      };
     </script>
   </body>
 </html>
--- a/devtools/client/webide/test/test_manifestUpdate.html
+++ b/devtools/client/webide/test/test_manifestUpdate.html
@@ -15,84 +15,83 @@
   <body>
 
     <script type="application/javascript">
       window.onload = function() {
         SimpleTest.waitForExplicitFinish();
 
         let {OS} = ChromeUtils.import("resource://gre/modules/osfile.jsm", {});
 
-        (async function () {
-            let win = await openWebIDE();
-            let winProject = getProjectWindow(win);
-            let AppManager = win.AppManager;
+        (async function() {
+          let win = await openWebIDE();
+          let winProject = getProjectWindow(win);
+          let AppManager = win.AppManager;
 
-            function isProjectMarkedAsValid() {
-              let details = win.frames[1];
-              return !details.document.body.classList.contains("error");
-            }
+          function isProjectMarkedAsValid() {
+            let details = win.frames[1];
+            return !details.document.body.classList.contains("error");
+          }
 
-            let packagedAppLocation = getTestFilePath("app");
+          let packagedAppLocation = getTestFilePath("app");
 
-            let onValidated = waitForUpdate(win, "project-validated");
-            let onDetails = waitForUpdate(win, "details");
-            await winProject.projectList.importPackagedApp(packagedAppLocation);
-            await onValidated;
-            await onDetails;
+          let onValidated = waitForUpdate(win, "project-validated");
+          let onDetails = waitForUpdate(win, "details");
+          await winProject.projectList.importPackagedApp(packagedAppLocation);
+          await onValidated;
+          await onDetails;
 
-            let project = win.AppManager.selectedProject;
+          let project = win.AppManager.selectedProject;
 
-            ok("name" in project.manifest, "manifest includes name");
-            is(project.name, project.manifest.name, "Display name uses manifest name");
-            ok(isProjectMarkedAsValid(), "project is marked as valid");
+          ok("name" in project.manifest, "manifest includes name");
+          is(project.name, project.manifest.name, "Display name uses manifest name");
+          ok(isProjectMarkedAsValid(), "project is marked as valid");
 
             // Change the name
-            let originalName = project.manifest.name;
+          let originalName = project.manifest.name;
 
-            project.manifest.name = "xxx";
+          project.manifest.name = "xxx";
 
             // Write to disk
-            await AppManager.writeManifest(project);
+          await AppManager.writeManifest(project);
 
             // Read file
-            let manifestPath = OS.Path.join(packagedAppLocation, "manifest.webapp");
-            let Decoder = new TextDecoder();
-            let data = await OS.File.read(manifestPath);
-            data = new TextDecoder().decode(data);
-            let json = JSON.parse(data);
-            is(json.name, "xxx", "manifest written on disc");
+          let manifestPath = OS.Path.join(packagedAppLocation, "manifest.webapp");
+          let data = await OS.File.read(manifestPath);
+          data = new TextDecoder().decode(data);
+          let json = JSON.parse(data);
+          is(json.name, "xxx", "manifest written on disc");
 
             // Make the manifest invalid on disk
-            delete json.name;
-            let Encoder = new TextEncoder();
-            data = Encoder.encode(JSON.stringify(json));
-            await OS.File.writeAtomic(manifestPath, data , {tmpPath: manifestPath + ".tmp"});
+          delete json.name;
+          let Encoder = new TextEncoder();
+          data = Encoder.encode(JSON.stringify(json));
+          await OS.File.writeAtomic(manifestPath, data, {tmpPath: manifestPath + ".tmp"});
 
             // Trigger validation
-            await AppManager.validateAndUpdateProject(AppManager.selectedProject);
-            await nextTick();
+          await AppManager.validateAndUpdateProject(AppManager.selectedProject);
+          await nextTick();
 
-            ok(!("name" in project.manifest), "manifest has been updated");
-            is(project.name, "--", "Placeholder is used for display name");
-            ok(!isProjectMarkedAsValid(), "project is marked as invalid");
+          ok(!("name" in project.manifest), "manifest has been updated");
+          is(project.name, "--", "Placeholder is used for display name");
+          ok(!isProjectMarkedAsValid(), "project is marked as invalid");
 
             // Make the manifest valid on disk
-            project.manifest.name = originalName;
-            await AppManager.writeManifest(project);
+          project.manifest.name = originalName;
+          await AppManager.writeManifest(project);
 
             // Trigger validation
-            await AppManager.validateAndUpdateProject(AppManager.selectedProject);
-            await nextTick();
+          await AppManager.validateAndUpdateProject(AppManager.selectedProject);
+          await nextTick();
 
-            ok("name" in project.manifest, "manifest includes name");
-            is(project.name, originalName, "Display name uses original manifest name");
-            ok(isProjectMarkedAsValid(), "project is marked as valid");
+          ok("name" in project.manifest, "manifest includes name");
+          is(project.name, originalName, "Display name uses original manifest name");
+          ok(isProjectMarkedAsValid(), "project is marked as valid");
 
-            await closeWebIDE(win);
+          await closeWebIDE(win);
 
-            await removeAllProjects();
+          await removeAllProjects();
 
-            SimpleTest.finish();
+          SimpleTest.finish();
         })();
-      }
+      };
     </script>
   </body>
 </html>
--- a/devtools/client/webide/test/test_newapp.html
+++ b/devtools/client/webide/test/test_newapp.html
@@ -13,17 +13,17 @@
   </head>
 
   <body>
 
     <script type="application/javascript">
       window.onload = function() {
         SimpleTest.waitForExplicitFinish();
 
-        (async function () {
+        (async function() {
           let win = await openWebIDE();
           let winProject = getProjectWindow(win);
           let tmpDir = FileUtils.getDir("TmpD", []);
           await winProject.projectList.newApp({
             index: 0,
             name: "webideTmpApp",
             folder: tmpDir
           });
@@ -35,12 +35,12 @@
           is(project.name, "webideTmpApp", "name field has been updated");
 
           // Clean up
           tmpDir.remove(true);
           await closeWebIDE(win);
           await removeAllProjects();
           SimpleTest.finish();
         })();
-      }
+      };
     </script>
   </body>
 </html>
--- a/devtools/client/webide/test/test_runtime.html
+++ b/devtools/client/webide/test/test_runtime.html
@@ -16,26 +16,26 @@
 
     <script type="application/javascript">
       window.onload = function() {
         SimpleTest.waitForExplicitFinish();
 
         let win;
 
         SimpleTest.registerCleanupFunction(() => {
-          (async function () {
+          (async function() {
             if (win) {
               await closeWebIDE(win);
             }
             DebuggerServer.destroy();
             await removeAllProjects();
           })();
         });
 
-        (async function () {
+        (async function() {
           function isPlayActive() {
             return !win.document.querySelector("#cmd_play").hasAttribute("disabled");
           }
 
           function isStopActive() {
             return !win.document.querySelector("#cmd_stop").hasAttribute("disabled");
           }
 
@@ -160,17 +160,17 @@
 
           Services.prefs.setIntPref("devtools.webide.busyTimeout", 100);
 
           // Click the infinite runtime
           items[1].click();
           ok(win.document.querySelector("window").className, "busy", "UI is busy");
 
           // Wait for error message since connection never completes
-          let errorDeferred = new Promise(resolve => {
+          await new Promise(resolve => {
             win.UI.reportError = errorName => {
               if (errorName === "error_operationTimeout") {
                 resolve();
               }
             };
           });
 
           // Click the prolonged runtime
@@ -185,14 +185,16 @@
               }
             };
 
             setTimeout(() => {
               resolve();
             }, 1000);
           });
 
+          await noErrorDeferred;
+
           SimpleTest.finish();
         })();
-      }
+      };
     </script>
   </body>
 </html>
--- a/devtools/client/webide/test/test_toolbox.html
+++ b/devtools/client/webide/test/test_toolbox.html
@@ -16,46 +16,48 @@
 
     <script type="application/javascript">
       window.onload = function() {
         SimpleTest.waitForExplicitFinish();
 
         let win;
 
         SimpleTest.registerCleanupFunction(() => {
-          (async function () {
+          (async function() {
             if (win) {
               await closeWebIDE(win);
             }
             DebuggerServer.destroy();
             await removeAllProjects();
           })();
         });
 
-        (async function () {
+        (async function() {
           DebuggerServer.init();
           DebuggerServer.registerAllActors();
 
           win = await openWebIDE();
           let docRuntime = getRuntimeDocument(win);
           let docProject = getProjectDocument(win);
 
           win.AppManager.update("runtime-list");
 
           let deferred = new Promise(resolve => {
-             win.AppManager.connection.once(
+            win.AppManager.connection.once(
                  win.Connection.Events.CONNECTED,
                  resolve);
           });
 
           docRuntime.querySelectorAll(".runtime-panel-item-other")[1].click();
 
           ok(win.document.querySelector("window").className, "busy", "UI is busy");
           await win.UI._busyPromise;
 
+          await deferred;
+
           is(Object.keys(DebuggerServer._connections).length, 1, "Connected");
 
           await waitForUpdate(win, "runtime-global-actors");
 
           ok(win.AppManager.isMainProcessDebuggable(), "Main process available");
 
           // Select main process
           SimpleTest.executeSoon(() => {
@@ -81,12 +83,12 @@
           await win.UI.destroyToolbox();
 
           ok(!win.UI.toolboxPromise, "Toolbox promise is also nullified the second times");
 
           await win.Cmds.disconnectRuntime();
 
           SimpleTest.finish();
         })();
-      }
+      };
     </script>
   </body>
 </html>
--- a/devtools/client/webide/test/test_zoom.html
+++ b/devtools/client/webide/test/test_zoom.html
@@ -13,65 +13,65 @@
   </head>
 
   <body>
 
     <script type="application/javascript">
       window.onload = function() {
         SimpleTest.waitForExplicitFinish();
 
-        (async function () {
-            let win = await openWebIDE();
-            let viewer = win.QueryInterface(Ci.nsIInterfaceRequestor)
+        (async function() {
+          let win = await openWebIDE();
+          let viewer = win.QueryInterface(Ci.nsIInterfaceRequestor)
                             .getInterface(Ci.nsIWebNavigation)
                             .QueryInterface(Ci.nsIDocShell)
                             .contentViewer;
 
-            win.Cmds.zoomOut();
-            win.Cmds.zoomOut();
-            win.Cmds.zoomOut();
-            win.Cmds.zoomOut();
-            win.Cmds.zoomOut();
-            win.Cmds.zoomOut();
-            win.Cmds.zoomOut();
+          win.Cmds.zoomOut();
+          win.Cmds.zoomOut();
+          win.Cmds.zoomOut();
+          win.Cmds.zoomOut();
+          win.Cmds.zoomOut();
+          win.Cmds.zoomOut();
+          win.Cmds.zoomOut();
 
-            let roundZoom = Math.round(10 * viewer.fullZoom) / 10;
-            is(roundZoom, 0.6, "Reach min zoom");
+          let roundZoom = Math.round(10 * viewer.fullZoom) / 10;
+          is(roundZoom, 0.6, "Reach min zoom");
 
-            win.Cmds.zoomIn();
-            win.Cmds.zoomIn();
-            win.Cmds.zoomIn();
-            win.Cmds.zoomIn();
-            win.Cmds.zoomIn();
-            win.Cmds.zoomIn();
-            win.Cmds.zoomIn();
-            win.Cmds.zoomIn();
-            win.Cmds.zoomIn();
-            win.Cmds.zoomIn();
-            win.Cmds.zoomIn();
-            win.Cmds.zoomIn();
+          win.Cmds.zoomIn();
+          win.Cmds.zoomIn();
+          win.Cmds.zoomIn();
+          win.Cmds.zoomIn();
+          win.Cmds.zoomIn();
+          win.Cmds.zoomIn();
+          win.Cmds.zoomIn();
+          win.Cmds.zoomIn();
+          win.Cmds.zoomIn();
+          win.Cmds.zoomIn();
+          win.Cmds.zoomIn();
+          win.Cmds.zoomIn();
 
-            roundZoom = Math.round(10 * viewer.fullZoom) / 10;
-            is(roundZoom, 1.4, "Reach max zoom");
+          roundZoom = Math.round(10 * viewer.fullZoom) / 10;
+          is(roundZoom, 1.4, "Reach max zoom");
 
-            await closeWebIDE(win);
+          await closeWebIDE(win);
 
-            win = await openWebIDE();
-            viewer = win.QueryInterface(Ci.nsIInterfaceRequestor)
+          win = await openWebIDE();
+          viewer = win.QueryInterface(Ci.nsIInterfaceRequestor)
                         .getInterface(Ci.nsIWebNavigation)
                         .QueryInterface(Ci.nsIDocShell)
                         .contentViewer;
 
-            roundZoom = Math.round(10 * viewer.fullZoom) / 10;
-            is(roundZoom, 1.4, "Zoom restored");
+          roundZoom = Math.round(10 * viewer.fullZoom) / 10;
+          is(roundZoom, 1.4, "Zoom restored");
 
-            win.Cmds.resetZoom();
+          win.Cmds.resetZoom();
 
-            is(viewer.fullZoom, 1, "Zoom reset");
+          is(viewer.fullZoom, 1, "Zoom reset");
 
-            await closeWebIDE(win);
+          await closeWebIDE(win);
 
-            SimpleTest.finish();
+          SimpleTest.finish();
         })();
-      }
+      };
     </script>
   </body>
 </html>
--- a/devtools/server/main.js
+++ b/devtools/server/main.js
@@ -11,17 +11,16 @@
 var { Ci, Cc } = require("chrome");
 var Services = require("Services");
 var { ActorPool, OriginalLocation, RegisteredActorFactory,
       ObservedActorFactory } = require("devtools/server/actors/common");
 var { LocalDebuggerTransport, ChildDebuggerTransport, WorkerDebuggerTransport } =
   require("devtools/shared/transport/transport");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
 var { dumpn } = DevToolsUtils;
-var flags = require("devtools/shared/flags");
 
 DevToolsUtils.defineLazyGetter(this, "DebuggerSocket", () => {
   let { DebuggerSocket } = require("devtools/shared/security/socket");
   return DebuggerSocket;
 });
 DevToolsUtils.defineLazyGetter(this, "Authentication", () => {
   return require("devtools/shared/security/auth");
 });
@@ -34,29 +33,16 @@ DevToolsUtils.defineLazyGetter(this, "ge
 // Overload `Components` to prevent DevTools loader exception on Components
 // object usage
 Object.defineProperty(this, "Components", {
   get() {
     return require("chrome").components;
   }
 });
 
-if (isWorker) {
-  flags.wantLogging = true;
-  flags.wantVerbose = true;
-} else {
-  const LOG_PREF = "devtools.debugger.log";
-  const VERBOSE_PREF = "devtools.debugger.log.verbose";
-
-  flags.wantLogging = Services.prefs.getBoolPref(LOG_PREF);
-  flags.wantVerbose =
-    Services.prefs.getPrefType(VERBOSE_PREF) !== Services.prefs.PREF_INVALID &&
-    Services.prefs.getBoolPref(VERBOSE_PREF);
-}
-
 const CONTENT_PROCESS_SERVER_STARTUP_SCRIPT =
   "resource://devtools/server/startup/content-process.js";
 
 function loadSubScript(url) {
   try {
     Services.scriptloader.loadSubScript(url, this);
   } catch (e) {
     let errorStr = "Error loading: " + url + ":\n" +
--- a/devtools/server/tests/mochitest/test_inspector_getImageData-wait-for-load.html
+++ b/devtools/server/tests/mochitest/test_inspector_getImageData-wait-for-load.html
@@ -14,33 +14,34 @@ https://bugzilla.mozilla.org/show_bug.cg
   <title>Test for Bug 1192536</title>
 
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
   <script type="application/javascript" src="inspector-helpers.js"></script>
   <script type="application/javascript">
 "use strict";
 
-const flags = require("devtools/shared/flags");
-const wasTesting = flags.testing;
-SimpleTest.registerCleanupFunction(function() {
-  flags.testing = wasTesting;
-});
-
 const PATH = "http://mochi.test:8888/chrome/devtools/server/tests/mochitest/";
 const BASE_IMAGE = PATH + "inspector-delay-image-response.sjs";
 const DELAYED_IMAGE = BASE_IMAGE + "?delay=300";
 const TIMEOUT_IMAGE = BASE_IMAGE + "?delay=50000";
 const NONEXISTENT_IMAGE = PATH + "this-does-not-exist.png";
 
 window.onload = function() {
   SimpleTest.waitForExplicitFinish();
   runNextTest();
 };
 
+function pushPref(preferenceName, value) {
+  return new Promise(resolve => {
+    let options = {"set": [[preferenceName, value]]};
+    SpecialPowers.pushPrefEnv(options, resolve);
+  });
+}
+
 let gImg = null;
 let gNodeFront = null;
 let gWalker = null;
 
 addTest(function setup() {
   let url = document.getElementById("inspectorContent").href;
   attachURL(url, function(err, client, tab, doc) {
     let {InspectorFront} = require("devtools/shared/fronts/inspector");
@@ -55,45 +56,45 @@ addTest(function setup() {
 
         ok(gNodeFront, "Got the image NodeFront.");
         ok(gImg, "Got the image Node.");
       });
     }).then(runNextTest));
   });
 });
 
-addTest(function testTimeout() {
+addTest(async function testTimeout() {
   info("Testing that the method aborts if the image takes too long to load.");
 
   // imageToImageData() only times out when flags.testing is not set.
-  flags.testing = false;
+  await pushPref("devtools.testing", false);
 
   gImg.src = TIMEOUT_IMAGE;
 
   info("Calling getImageData().");
   ensureRejects(gNodeFront.getImageData(), "Timeout image").then(runNextTest);
 });
 
-addTest(function testNonExistentImage() {
+addTest(async function testNonExistentImage() {
   info("Testing that non-existent image causes a rejection.");
 
   // This test shouldn't hit the timeout.
-  flags.testing = true;
+  await pushPref("devtools.testing", true);
 
   gImg.src = NONEXISTENT_IMAGE;
 
   info("Calling getImageData().");
   ensureRejects(gNodeFront.getImageData(), "Non-existent image").then(runNextTest);
 });
 
-addTest(function testDelayedImage() {
+addTest(async function testDelayedImage() {
   info("Testing that the method waits for an image to load.");
 
   // This test shouldn't hit the timeout.
-  flags.testing = true;
+  await pushPref("devtools.testing", true);
 
   gImg.src = DELAYED_IMAGE;
 
   info("Calling getImageData().");
   checkImageData(gNodeFront.getImageData()).then(runNextTest);
 });
 
 addTest(function cleanup() {
--- a/devtools/server/tests/mochitest/test_inspector_getImageDataFromURL.html
+++ b/devtools/server/tests/mochitest/test_inspector_getImageDataFromURL.html
@@ -13,69 +13,70 @@ https://bugzilla.mozilla.org/show_bug.cg
   <title>Test for Bug 1192536</title>
 
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
   <script type="application/javascript" src="inspector-helpers.js"></script>
   <script type="application/javascript">
 "use strict";
 
-const flags = require("devtools/shared/flags");
-const wasTesting = flags.testing;
-SimpleTest.registerCleanupFunction(function() {
-  flags.testing = wasTesting;
-});
-
 const PATH = "http://mochi.test:8888/chrome/devtools/server/tests/mochitest/";
 const BASE_IMAGE = PATH + "inspector-delay-image-response.sjs";
 const DELAYED_IMAGE = BASE_IMAGE + "?delay=300";
 const TIMEOUT_IMAGE = BASE_IMAGE + "?delay=50000";
 const NONEXISTENT_IMAGE = PATH + "this-does-not-exist.png";
 
 window.onload = function() {
   SimpleTest.waitForExplicitFinish();
   runNextTest();
 };
 
+function pushPref(preferenceName, value) {
+  return new Promise(resolve => {
+    let options = {"set": [[preferenceName, value]]};
+    SpecialPowers.pushPrefEnv(options, resolve);
+  });
+}
+
 let gInspector = null;
 
 addTest(function setup() {
   let url = document.getElementById("inspectorContent").href;
   attachURL(url, function(err, client, tab, doc) {
     let {InspectorFront} = require("devtools/shared/fronts/inspector");
     gInspector = InspectorFront(client, tab);
     runNextTest();
   });
 });
 
-addTest(function testTimeout() {
+addTest(async function testTimeout() {
   info("Testing that the method aborts if the image takes too long to load.");
 
   // imageToImageData() only times out when flags.testing is not set.
-  flags.testing = false;
+  await pushPref("devtools.testing", false);
 
   ensureRejects(gInspector.getImageDataFromURL(TIMEOUT_IMAGE),
     "Image that loads for too long").then(runNextTest);
 });
 
-addTest(function testNonExistentImage() {
+addTest(async function testNonExistentImage() {
   info("Testing that non-existent image causes a rejection.");
 
   // This test shouldn't hit the timeout.
-  flags.testing = true;
+  await pushPref("devtools.testing", true);
 
   ensureRejects(gInspector.getImageDataFromURL(NONEXISTENT_IMAGE),
     "Non-existent image").then(runNextTest);
 });
 
-addTest(function testNormalImage() {
+addTest(async function testNormalImage() {
   info("Testing that the method waits for an image to load.");
 
   // This test shouldn't hit the timeout.
-  flags.testing = true;
+  await pushPref("devtools.testing", true);
 
   checkImageData(gInspector.getImageDataFromURL(DELAYED_IMAGE)).then(runNextTest);
 });
 
 addTest(function cleanup() {
   gInspector = null;
   runNextTest();
 });
--- a/devtools/shared/flags.js
+++ b/devtools/shared/flags.js
@@ -1,26 +1,42 @@
 "use strict";
 
+const Services = require("Services");
+
 /*
  * Create a writable property by tracking it with a private variable.
  * We cannot make a normal property writeable on `exports` because
  * the module system freezes it.
  */
-function makeWritableFlag(exports, name) {
-  let flag = false;
+function makeWritableFlag(exports, name, pref) {
+  let flag;
+  // We don't have access to pref in worker, so disable all logs by default
+  if (isWorker) {
+    flag = false;
+  } else {
+    flag = Services.prefs.getBoolPref(pref, false);
+    let prefObserver = () => {
+      flag = Services.prefs.getBoolPref(pref, false);
+    };
+    Services.prefs.addObserver(pref, prefObserver);
+
+    // Also listen for Loader unload to unregister the pref observer and prevent leaking
+    let unloadObserver = function() {
+      Services.prefs.removeObserver(pref, prefObserver);
+      Services.obs.removeObserver(unloadObserver, "devtools:loader:destroy");
+    };
+    Services.obs.addObserver(unloadObserver, "devtools:loader:destroy");
+  }
   Object.defineProperty(exports, name, {
     get: function() {
       return flag;
-    },
-    set: function(state) {
-      flag = state;
     }
   });
 }
 
-makeWritableFlag(exports, "wantLogging");
-makeWritableFlag(exports, "wantVerbose");
+makeWritableFlag(exports, "wantLogging", "devtools.debugger.log");
+makeWritableFlag(exports, "wantVerbose", "devtools.debugger.log.verbose");
 
 // When the testing flag is set, various behaviors may be altered from
 // production mode, typically to enable easier testing or enhanced
 // debugging.
-makeWritableFlag(exports, "testing");
+makeWritableFlag(exports, "testing", "devtools.testing");
--- a/devtools/shared/heapsnapshot/tests/unit/head_heapsnapshot.js
+++ b/devtools/shared/heapsnapshot/tests/unit/head_heapsnapshot.js
@@ -13,32 +13,33 @@ var CC = Components.Constructor;
 
 const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
 const { Match } = ChromeUtils.import("resource://test/Match.jsm", {});
 const { Census } = ChromeUtils.import("resource://test/Census.jsm", {});
 const { addDebuggerToGlobal } =
   ChromeUtils.import("resource://gre/modules/jsdebugger.jsm", {});
 
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
-const flags = require("devtools/shared/flags");
 const HeapAnalysesClient =
   require("devtools/shared/heapsnapshot/HeapAnalysesClient");
 const Services = require("Services");
 const { censusReportToCensusTreeNode } = require("devtools/shared/heapsnapshot/census-tree-node");
 const CensusUtils = require("devtools/shared/heapsnapshot/CensusUtils");
 const DominatorTreeNode = require("devtools/shared/heapsnapshot/DominatorTreeNode");
 const { deduplicatePaths } = require("devtools/shared/heapsnapshot/shortest-paths");
 const { LabelAndShallowSizeVisitor } = DominatorTreeNode;
 
 // Always log packets when running tests. runxpcshelltests.py will throw
 // the output away anyway, unless you give it the --verbose flag.
 if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_DEFAULT) {
   Services.prefs.setBoolPref("devtools.debugger.log", true);
+  registerCleanupFunction(() => {
+    Services.prefs.clearUserPref("devtools.debugger.log");
+  });
 }
-flags.wantLogging = true;
 
 const SYSTEM_PRINCIPAL = Cc["@mozilla.org/systemprincipal;1"]
   .createInstance(Ci.nsIPrincipal);
 
 function dumpn(msg) {
   dump("HEAPSNAPSHOT-TEST: " + msg + "\n");
 }
 
--- a/devtools/shared/tests/unit/head_devtools.js
+++ b/devtools/shared/tests/unit/head_devtools.js
@@ -3,21 +3,20 @@
 
 /* exported DevToolsUtils, DevToolsLoader */
 
 "use strict";
 
 const { require, DevToolsLoader } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
 const Services = require("Services");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
-const flags = require("devtools/shared/flags");
 
-flags.testing = true;
+Services.prefs.setBoolPref("devtools.testing", true);
 registerCleanupFunction(() => {
-  flags.testing = false;
+  Services.prefs.clearUserPref("devtools.testing");
 });
 
 // Register a console listener, so console messages don't just disappear
 // into the ether.
 
 // If for whatever reason the test needs to post console errors that aren't
 // failures, set this to true.
 var ALLOW_CONSOLE_ERRORS = false;
--- a/devtools/shared/tests/unit/test_assert.js
+++ b/devtools/shared/tests/unit/test_assert.js
@@ -4,19 +4,16 @@
 
 "use strict";
 
 // Test DevToolsUtils.assert
 
 ALLOW_CONSOLE_ERRORS = true;
 
 function run_test() {
-  // Enable assertions.
-  flags.testing = true;
-
   const { assert } = DevToolsUtils;
   equal(typeof assert, "function");
 
   try {
     assert(true, "this assertion should not fail");
   } catch (e) {
     // If you catch assertion failures in practice, I will hunt you down. I get
     // email notifications every time it happens.
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -7979,17 +7979,17 @@ nsContentUtils::DataTransferItemToImage(
   if (!size.width || !size.height) {
     return NS_ERROR_FAILURE;
   }
 
   Shmem data = aItem.data().get_Shmem();
 
   RefPtr<DataSourceSurface> image =
       CreateDataSourceSurfaceFromData(size,
-                                      static_cast<SurfaceFormat>(imageDetails.format()),
+                                      imageDetails.format(),
                                       data.get<uint8_t>(),
                                       imageDetails.stride());
 
   RefPtr<gfxDrawable> drawable = new gfxSurfaceDrawable(image, size);
   nsCOMPtr<imgIContainer> imageContainer =
     image::ImageOps::CreateFromDrawable(drawable);
   imageContainer.forget(aContainer);
 
@@ -8137,17 +8137,17 @@ nsContentUtils::TransferableToIPCTransfe
             // Turn item->data() into an nsCString prior to accessing it.
             item->data() = surfaceData.ref();
 
             IPCDataTransferImage& imageDetails = item->imageDetails();
             mozilla::gfx::IntSize size = dataSurface->GetSize();
             imageDetails.width() = size.width;
             imageDetails.height() = size.height;
             imageDetails.stride() = stride;
-            imageDetails.format() = static_cast<uint8_t>(dataSurface->GetFormat());
+            imageDetails.format() = dataSurface->GetFormat();
 
             continue;
           }
 
           // Otherwise, handle this as a file.
           nsCOMPtr<BlobImpl> blobImpl;
           nsCOMPtr<nsIFile> file = do_QueryInterface(data);
           if (file) {
--- a/dom/ipc/DOMTypes.ipdlh
+++ b/dom/ipc/DOMTypes.ipdlh
@@ -23,16 +23,18 @@ using DesktopToLayoutDeviceScale from "U
 using CSSToLayoutDeviceScale from "Units.h";
 using CSSRect from "Units.h";
 using CSSSize from "Units.h";
 using mozilla::LayoutDeviceIntPoint from "Units.h";
 using mozilla::dom::ScreenOrientationInternal from "mozilla/dom/ScreenOrientation.h";
 using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h";
 using mozilla::layers::CompositorOptions from "mozilla/layers/CompositorOptions.h";
 using mozilla::layers::LayersId from "mozilla/layers/LayersTypes.h";
+using mozilla::gfx::SurfaceFormat from "mozilla/gfx/Types.h";
+
 
 namespace mozilla {
 namespace dom {
 
 struct MessagePortIdentifier
 {
   nsID uuid;
   nsID destinationUuid;
@@ -61,17 +63,17 @@ union IPCDataTransferData
   IPCBlob;   // files
 };
 
 struct IPCDataTransferImage
 {
   uint32_t width;
   uint32_t height;
   uint32_t stride;
-  uint8_t format;
+  SurfaceFormat format;
 };
 
 struct IPCDataTransferItem
 {
   nsCString flavor;
   // The image details are only used when transferring images.
   IPCDataTransferImage imageDetails;
   IPCDataTransferData data;
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -28,16 +28,17 @@ include URIParams;
 include PPrintingTypes;
 include PTabContext;
 
 include "mozilla/GfxMessageUtils.h";
 include "mozilla/layers/LayersMessageUtils.h";
 
 using class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h";
 using mozilla::gfx::Matrix from "mozilla/gfx/Matrix.h";
+using mozilla::gfx::SurfaceFormat from "mozilla/gfx/Types.h";
 using mozilla::LayoutDeviceIntPoint from "Units.h";
 using mozilla::LayoutDevicePoint from "Units.h";
 using mozilla::ScreenIntPoint from "Units.h";
 using ScreenIntSize from "Units.h";
 using struct mozilla::layers::FrameMetrics from "FrameMetrics.h";
 using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h";
 using struct mozilla::layers::ZoomConstraints from "FrameMetrics.h";
 using mozilla::layers::LayersId from "mozilla/layers/LayersTypes.h";
@@ -389,17 +390,17 @@ parent:
      *   Horizontal hotspot of the image, as specified by the css cursor property.
      * @param hotspotY
      *   Vertical hotspot of the image, as specified by the css cursor property.
      * @param force
      *   Invalidate any locally cached cursor settings and force an
      *   update.
      */
     async SetCustomCursor(nsCString cursorData, uint32_t width, uint32_t height,
-                          uint32_t stride, uint8_t format,
+                          uint32_t stride, SurfaceFormat format,
                           uint32_t hotspotX, uint32_t hotspotY, bool force);
 
     /**
      * Used to set the current text of the status tooltip.
      * Nowadays this is mainly used for link locations on hover.
      */
     async SetStatus(uint32_t type, nsString status);
 
@@ -563,17 +564,17 @@ parent:
     async SetDimensions(uint32_t aFlags, int32_t aX, int32_t aY, int32_t aCx, int32_t aCy);
 
     nested(inside_sync) sync DispatchWheelEvent(WidgetWheelEvent event);
     nested(inside_sync) sync DispatchMouseEvent(WidgetMouseEvent event);
     nested(inside_sync) sync DispatchKeyboardEvent(WidgetKeyboardEvent event);
 
     async InvokeDragSession(IPCDataTransfer[] transfers, uint32_t action,
                             OptionalShmem visualData,
-                            uint32_t stride, uint8_t format,
+                            uint32_t stride, SurfaceFormat format,
                             LayoutDeviceIntRect dragRect,
                             nsCString principalURISpec);
 
     // After a compositor reset, it is necessary to reconnect each layers ID to
     // the compositor of the widget that will render those layers. Note that
     // this is sync so we can ensure that messages to the window compositor
     // arrive before the TabChild attempts to use its cross-process compositor
     // bridge.
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -1738,17 +1738,17 @@ TabParent::RecvSetCursor(const nsCursor&
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 TabParent::RecvSetCustomCursor(const nsCString& aCursorData,
                                const uint32_t& aWidth,
                                const uint32_t& aHeight,
                                const uint32_t& aStride,
-                               const uint8_t& aFormat,
+                               const gfx::SurfaceFormat& aFormat,
                                const uint32_t& aHotspotX,
                                const uint32_t& aHotspotY,
                                const bool& aForce)
 {
   mCursor = eCursorInvalid;
 
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (widget) {
@@ -1756,17 +1756,17 @@ TabParent::RecvSetCustomCursor(const nsC
       widget->ClearCachedCursor();
     }
 
     if (mTabSetsCursor) {
       const gfx::IntSize size(aWidth, aHeight);
 
       RefPtr<gfx::DataSourceSurface> customCursor =
           gfx::CreateDataSourceSurfaceFromData(size,
-                                               static_cast<gfx::SurfaceFormat>(aFormat),
+                                               aFormat,
                                                reinterpret_cast<const uint8_t*>(aCursorData.BeginReading()),
                                                aStride);
 
       RefPtr<gfxDrawable> drawable = new gfxSurfaceDrawable(customCursor, size);
       nsCOMPtr<imgIContainer> cursorImage(image::ImageOps::CreateFromDrawable(drawable));
       widget->SetCursor(cursorImage, aHotspotX, aHotspotY);
       mCustomCursor = cursorImage;
       mCustomCursorHotspotX = aHotspotX;
@@ -3290,17 +3290,17 @@ TabParent::RecvAsyncAuthPrompt(const nsC
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 TabParent::RecvInvokeDragSession(nsTArray<IPCDataTransfer>&& aTransfers,
                                  const uint32_t& aAction,
                                  const OptionalShmem& aVisualDnDData,
-                                 const uint32_t& aStride, const uint8_t& aFormat,
+                                 const uint32_t& aStride, const gfx::SurfaceFormat& aFormat,
                                  const LayoutDeviceIntRect& aDragRect,
                                  const nsCString& aPrincipalURISpec)
 {
   mInitialDataTransferItems.Clear();
   nsIPresShell* shell = mFrameElement->OwnerDoc()->GetShell();
   if (!shell) {
     if (Manager()->IsContentParent()) {
       Unused << Manager()->AsContentParent()->SendEndDragSession(true, true,
@@ -3327,17 +3327,17 @@ TabParent::RecvInvokeDragSession(nsTArra
 
   if (aVisualDnDData.type() == OptionalShmem::Tvoid_t ||
       !aVisualDnDData.get_Shmem().IsReadable() ||
       aVisualDnDData.get_Shmem().Size<char>() < aDragRect.height * aStride) {
     mDnDVisualization = nullptr;
   } else {
     mDnDVisualization =
         gfx::CreateDataSourceSurfaceFromData(gfx::IntSize(aDragRect.width, aDragRect.height),
-                                             static_cast<gfx::SurfaceFormat>(aFormat),
+                                             aFormat,
                                              aVisualDnDData.get_Shmem().get<uint8_t>(),
                                              aStride);
   }
 
   mDragValid = true;
   mDragRect = aDragRect;
   mDragPrincipalURISpec = aPrincipalURISpec;
 
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -275,17 +275,17 @@ public:
 
   virtual mozilla::ipc::IPCResult RecvSetCursor(const nsCursor& aValue,
                                                 const bool& aForce) override;
 
   virtual mozilla::ipc::IPCResult RecvSetCustomCursor(const nsCString& aUri,
                                                       const uint32_t& aWidth,
                                                       const uint32_t& aHeight,
                                                       const uint32_t& aStride,
-                                                      const uint8_t& aFormat,
+                                                      const gfx::SurfaceFormat& aFormat,
                                                       const uint32_t& aHotspotX,
                                                       const uint32_t& aHotspotY,
                                                       const bool& aForce) override;
 
   virtual mozilla::ipc::IPCResult RecvSetStatus(const uint32_t& aType,
                                                 const nsString& aStatus) override;
 
   virtual mozilla::ipc::IPCResult RecvIsParentWindowMainWidgetVisible(bool* aIsVisible) override;
@@ -566,17 +566,17 @@ public:
                             const bool& aRunInGlobalScope);
 
   void LayerTreeUpdate(uint64_t aEpoch, bool aActive);
 
   virtual mozilla::ipc::IPCResult
   RecvInvokeDragSession(nsTArray<IPCDataTransfer>&& aTransfers,
                         const uint32_t& aAction,
                         const OptionalShmem& aVisualDnDData,
-                        const uint32_t& aStride, const uint8_t& aFormat,
+                        const uint32_t& aStride, const gfx::SurfaceFormat& aFormat,
                         const LayoutDeviceIntRect& aDragRect,
                         const nsCString& aPrincipalURISpec) override;
 
   void AddInitialDnDDataTo(DataTransfer* aDataTransfer,
                            nsACString& aPrincipalURISpec);
 
   bool TakeDragVisualization(RefPtr<mozilla::gfx::SourceSurface>& aSurface,
                              LayoutDeviceIntRect* aDragRect);
--- a/dom/quota/test/browser_permissionsPrompt.html
+++ b/dom/quota/test/browser_permissionsPrompt.html
@@ -8,18 +8,17 @@
     <title>Persistent-Storage Permission Prompt Test</title>
 
     <script type="text/javascript">
       function* testSteps()
       {
         SpecialPowers.pushPrefEnv({
           "set": [["dom.storageManager.enabled", true],
                   ["dom.storageManager.prompt.testing", false],
-                  ["dom.storageManager.prompt.testing.allow", false],
-                  ["browser.storageManager.enabled", true]]
+                  ["dom.storageManager.prompt.testing.allow", false]]
         }, continueToNextStep);
         yield undefined;
 
         navigator.storage.persist().then(result => {
           testGenerator.next(result);
         });
         testResult = yield undefined;
 
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -8359,19 +8359,24 @@ nsDisplayTransform::ShouldPrerenderTrans
   // Only prerender if the transformed frame's size is <= a multiple of the
   // reference frame size (~viewport), and less than an absolute limit.
   // Both the ratio and the absolute limit are configurable.
   nsSize relativeLimit(nscoord(refSize.width * viewportRatioX),
                        nscoord(refSize.height * viewportRatioY));
   nsSize absoluteLimit(aFrame->PresContext()->DevPixelsToAppUnits(absoluteLimitX),
                        aFrame->PresContext()->DevPixelsToAppUnits(absoluteLimitY));
   nsSize maxSize = Min(relativeLimit, absoluteLimit);
-  gfxSize scale = nsLayoutUtils::GetTransformToAncestorScale(aFrame);
-  nsSize frameSize(overflow.Size().width * scale.width,
-                   overflow.Size().height * scale.height);
+
+  const auto transform = nsLayoutUtils::GetTransformToAncestor(aFrame,
+    nsLayoutUtils::GetDisplayRootFrame(aFrame));
+  const gfxRect transformedBounds = transform.TransformAndClipBounds(
+    gfxRect(overflow.x, overflow.y, overflow.width, overflow.height),
+    gfxRect::MaxIntRect());
+  const nsSize frameSize = nsSize(transformedBounds.width, transformedBounds.height);
+
   uint64_t maxLimitArea = uint64_t(maxSize.width) * maxSize.height;
   uint64_t frameArea = uint64_t(frameSize.width) * frameSize.height;
   if (frameArea <= maxLimitArea && frameSize <= absoluteLimit) {
     *aDirtyRect = overflow;
     return FullPrerender;
   } else if (gfxPrefs::PartiallyPrerenderAnimatedContent()) {
     *aDirtyRect = nsLayoutUtils::ComputePartialPrerenderArea(*aDirtyRect, overflow, maxSize);
     return PartialPrerender;
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -5771,19 +5771,17 @@ pref("security.block_ftp_subresources", 
 #if !defined(MOZ_WIDGET_ANDROID)
 pref("dom.storageManager.enabled", true);
 #else
 pref("dom.storageManager.enabled", false);
 #endif
 pref("dom.storageManager.prompt.testing", false);
 pref("dom.storageManager.prompt.testing.allow", false);
 
-// Enable the Storage management in about:preferences and persistent-storage permission request
-// To enable the DOM implementation, turn on "dom.storageManager.enabled"
-pref("browser.storageManager.enabled", true);
+
 pref("browser.storageManager.pressureNotification.minIntervalMS", 1200000);
 pref("browser.storageManager.pressureNotification.usageThresholdGB", 5);
 
 // When a user cancels this number of authentication dialogs coming from
 // a single web page in a row, all following authentication dialogs will
 // be blocked (automatically canceled) for that page. The counter resets
 // when the page is reloaded. To turn this feature off, just set the limit to 0.
 pref("prompts.authentication_dialog_abuse_limit", 3);
--- a/testing/marionette/doc/Testing.md
+++ b/testing/marionette/doc/Testing.md
@@ -1,111 +1,136 @@
-# Running Tests
+Running tests
+=============
 
-We verify and test Marionette in a couple of different ways.
-While for the server component XPCShell tests are used, the
-client and harness packages both use Python.
+We verify and test Marionette in a couple of different ways.  The
+server uses a combination of xpcshell unit tests and functional
+Python-based tests written with the Marionette harness and WPT; the
+client is tested with the same harness; and the Marionette test
+harness uses a collection of Python tests.
 
-For debugging test failures which are happing in CI, a one-click loaner from
-[Taskcluster](Taskcluster.html) can be used.
+Additionally, for debugging hard-to-reproduce test failures in CI,
+a one-click loaner from [Taskcluster] can be used.
+
+[TaskCluster]: Taskcluster.html
+
 
-## Setting up the tests
+Setup
+-----
 
-Marionette-based tests can be run in-tree with a locally built
-or downloaded Firefox through `mach`, as well as with out-of-tree
-tests with `marionette`.
+Marionette-based tests can be run through `mach` with a local
+checkout of _central_ and a build of Firefox, as well as out-of-tree
+with the `marionette` test harness.
 
 Running in-tree tests, `mach` will automatically manage the Python
 virtual environment in which your tests are run.  The Marionette
 client that is picked up is the one that is in-tree at
 _testing/marionette/client_.
 
 If you want to run tests from a downloaded test archive, you will
-need to download the `target.common.tests.zip` artifact as attached to
-Treeherder [build jobs] `B` for your system.  Extract that file and set up
-the Python Marionette client and harness by executing the following
-command:
+need to download the `target.common.tests.zip` artifact as attached
+to Treeherder [build jobs] `B` for your system.  Extract that file
+and set up the Python Marionette client and harness by executing
+the following command in a virtual environment:
 
-    % pip install -r config/marionette_requirements.txt
+	% pip install -r config/marionette_requirements.txt
 
 The tests can then be found under
-*marionette/tests/testing/marionette/harness/marionette_harness/tests* and
-can be executed with the command `marionette`.  It supports the same options as
-described below for `mach`.
+_marionette/tests/testing/marionette/harness/marionette_harness/tests_ and
+can be executed with the command `marionette`.  It supports the
+same options as described below for `mach`.
 
 [build jobs]: https://treeherder.mozilla.org/#/jobs?repo=mozilla-central&filter-searchStr=build
 
 
-## XPCShell tests
+xpcshell unit tests
+-------------------
 
 Marionette has a set of [xpcshell] unit tests located in
 _testing/marionette/test*.js_.  These can be run this way:
 
-    % ./mach test testing/marionette/test_*.js
+	% ./mach test testing/marionette/test/unit
 
 Because tests are run in parallel and xpcshell itself is quite
 chatty, it can sometimes be useful to run the tests sequentially:
 
-    % ./mach test --sequential testing/marionette/test_error.js
+	% ./mach test --sequential testing/marionette/test/unit/test_error.js
 
 These unit tests run as part of the `X` jobs on Treeherder.
 
 [xpcshell]: https://developer.mozilla.org/en-US/docs/Mozilla/QA/Writing_xpcshell-based_unit_tests
 
 
-## Python functional tests
+Marionette functional tests
+---------------------------
 
 We also have a set of functional tests that make use of the Marionette
 Python client.  These start a Firefox process and tests the Marionette
-protocol input and output.  The following command will run all tests:
+protocol input and output.  The following command will run all
+tests:
 
-    % ./mach marionette-test
+	% ./mach marionette test
 
 But you can also run individual tests:
 
-    % ./mach marionette-test testing/marionette/harness/marionette_harness/tests/unit/test_navigation.py
+	% ./mach marionette test testing/marionette/harness/marionette_harness/tests/unit/test_navigation.py
 
-In case you want to run the tests with eg. a Nightly build of Firefox,
-mach let you do this with the --binary option:
+In case you want to run the tests with another Firefox binary:
 
-    % ./mach marionette-test --binary=/path/to/firefox-executable TEST
+	% ./mach marionette test --binary /path/to/firefox TEST
 
-When working on Marionette code it is often useful to surface the
-stdout from Firefox, which can be achived with the `--gecko-log` option.
-See [Debugging](Debugging.html) for usage instructions.
+When working on Marionette it is often useful to surface the stdout
+from Firefox, which can be achived using the `--gecko-log` option.
+See [Debugging] for usage instructions.
 
 As these are functional integration tests and pop up Firefox windows
-sporadically, a helpful tip is to surpress the window whilst you
+sporadically, a helpful tip is to suppress the window whilst you
 are running them by using Firefox’ [headless mode]:
 
-    % ./mach marionette-test -z TEST
+	% ./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.
+`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.
 
 These functional tests will run as part of the `Mn` job on Treeherder.
 
 In addition to these two test types that specifically test the
 Marionette protocol, Marionette is used as the backend for the
 [geckodriver] WebDriver implementation.  It is served by a WPT test
 suite which effectively tests conformance to the W3C specification.
 
+[Debugging]: Debugging.html
 [headless mode]: https://developer.mozilla.org/en-US/Firefox/Headless_mode
 [geckodriver]: ../geckodriver/README.md
 
 
-## Python harness tests
+WPT functional tests
+--------------------
+
+Marionette is also indirectly tested through [geckodriver] with
+WPT.  WPT tests for WebDriver turn up with the `Wd` try job and can
+be run this way:
+
+	% ./mach wpt testing/web-platform/tests/webdriver/tests
+
+This command supports a `--webdriver-arg '-vv'` argument which
+enables more detailed logging, as well as `--jsdebugger` for opening
+the Browser Toolbox.
+
+
+Harness tests
+-------------
 
 The Marionette harness Python package has a set of unit tests, which
-are written by using the [pytest] framework. The following command will
-run all tests:
+are written by using the [pytest] framework. The following command
+will run all tests:
 
-    % ./mach python-test testing/marionette/
+	% ./mach python-test testing/marionette/
 
 To run a specific test specify the full path to the module:
 
-    % ./mach python-test testing/marionette/harness/marionette_harness/tests/harness_unit/test_serve.py
+	% ./mach python-test testing/marionette/harness/marionette_harness/tests/harness_unit/test_serve.py
 
 [pytest]: https://docs.pytest.org/en/latest/
--- a/testing/marionette/moz.build
+++ b/testing/marionette/moz.build
@@ -4,17 +4,17 @@
 
 DIRS += ["components"]
 
 JAR_MANIFESTS += ["jar.mn"]
 JS_PREFERENCE_FILES += ["prefs/marionette.js"]
 
 MARIONETTE_UNIT_MANIFESTS += ["harness/marionette_harness/tests/unit/unit-tests.ini"]
 MARIONETTE_WEBAPI_MANIFESTS += ["harness/marionette_harness/tests/webapi-tests.ini"]
-XPCSHELL_TESTS_MANIFESTS += ["unit.ini"]
+XPCSHELL_TESTS_MANIFESTS += ["test/unit/xpcshell.ini"]
 
 with Files("**"):
     BUG_COMPONENT = ("Testing", "Marionette")
 
 with Files("harness/**"):
     SCHEDULES.exclusive = ["marionette", "firefox-ui"]
 
 SPHINX_TREES["marionette"] = "doc"
new file mode 100644
--- /dev/null
+++ b/testing/marionette/test/unit/.eslintrc.js
@@ -0,0 +1,13 @@
+"use strict";
+
+module.exports = {
+  "extends": ["plugin:mozilla/xpcshell-test"],
+  "rules": {
+    "camelcase": "off",
+    "max-len": ["error", 100, {
+      "ignoreStrings": true,
+      "ignoreTemplateLiterals": true,
+      "ignoreUrls": true,
+    }],
+  },
+};
new file mode 100644
--- /dev/null
+++ b/testing/marionette/test/unit/README.md
@@ -0,0 +1,14 @@
+To run the tests in this directory, from the top source directory,
+either invoke the test despatcher in mach:
+
+	% ./mach test testing/marionette/test/unit
+
+Or call out the harness specifically:
+
+	% ./mach xpcshell-test testing/marionette/test/unit
+
+The latter gives you the `--sequential` option which can be useful
+when debugging to prevent tests from running in parallel.
+
+When adding new tests you must make sure they are listed in
+xpcshell.ini, otherwise they will not run on try.
rename from testing/marionette/test_action.js
rename to testing/marionette/test/unit/test_action.js
--- a/testing/marionette/test_action.js
+++ b/testing/marionette/test/unit/test_action.js
@@ -1,17 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 ChromeUtils.import("chrome://marionette/content/action.js");
-const {ContentWebElement} = ChromeUtils.import("chrome://marionette/content/element.js", {});
-ChromeUtils.import("chrome://marionette/content/error.js");
+const {InvalidArgumentError} = ChromeUtils.import("chrome://marionette/content/error.js", {});
 
 const XHTMLNS = "http://www.w3.org/1999/xhtml";
 
 const domEl = {
   nodeType: 1,
   ELEMENT_NODE: 1,
   namespaceURI: XHTMLNS,
 };
@@ -41,19 +40,19 @@ add_test(function test_processPointerPar
   let check = (regex, message, arg) => checkErrors(
       regex, action.PointerParameters.fromJSON, [arg], message);
   let parametersData;
   for (let d of ["foo", "", "get", "Get"]) {
     parametersData = {pointerType: d};
     let message = `parametersData: [pointerType: ${parametersData.pointerType}]`;
     check(/Unknown pointerType/, message, parametersData);
   }
-  parametersData.pointerType = "mouse"; //TODO "pen";
+  parametersData.pointerType = "mouse"; // TODO "pen";
   deepEqual(action.PointerParameters.fromJSON(parametersData),
-      {pointerType: "mouse"}); //TODO action.PointerType.Pen});
+      {pointerType: "mouse"}); // TODO action.PointerType.Pen});