Merge mozilla-central to mozilla-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 29 Feb 2016 11:44:03 +0100
changeset 324308 164b34c28be4c5415b9a5c8e09a1c422cabed902
parent 324307 ea315d62656857d0e16cfde598ba14028813f2fc (current diff)
parent 324238 9da51cb4974e03cdd8fa45a34086fe1033abfeaf (diff)
child 324309 b89196c9ac0887763309929a64c45117edef6fd9
push id1128
push userjlund@mozilla.com
push dateWed, 01 Jun 2016 01:31:59 +0000
treeherdermozilla-release@fe0d30de989d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone47.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 mozilla-inbound
devtools/shared/gcli/commands/tools.js
devtools/shared/tests/mochitest/test_loader_paths.html
dom/media/omx/RtspMediaCodecDecoder.cpp
dom/media/omx/RtspMediaCodecDecoder.h
toolkit/components/telemetry/Histograms.json
toolkit/components/telemetry/bucket-whitelist.json
--- a/.eslintignore
+++ b/.eslintignore
@@ -59,17 +59,16 @@ b2g/locales/en-US/b2g-l10n.js
 browser/app/**
 browser/base/content/browser-social.js
 browser/base/content/nsContextMenu.js
 browser/base/content/sanitizeDialog.js
 browser/base/content/test/**
 browser/base/content/newtab/**
 browser/components/downloads/**
 browser/components/feeds/**
-browser/components/pocket/**
 browser/components/preferences/**
 browser/components/privatebrowsing/**
 browser/components/sessionstore/**
 browser/components/shell/**
 browser/components/tabview/**
 browser/components/translation/**
 browser/extensions/pdfjs/**
 browser/extensions/pocket/content/panels/js/vendor/**
@@ -100,17 +99,16 @@ devtools/client/netmonitor/**
 devtools/client/performance/**
 devtools/client/projecteditor/**
 devtools/client/promisedebugger/**
 devtools/client/responsivedesign/**
 devtools/client/scratchpad/**
 devtools/client/shadereditor/**
 devtools/client/shared/**
 devtools/client/sourceeditor/**
-devtools/client/storage/**
 devtools/client/tilt/**
 devtools/client/webaudioeditor/**
 devtools/client/webconsole/**
 devtools/client/webide/**
 devtools/server/**
 devtools/shared/**
 
 # Ignore devtools pre-processed files
@@ -177,17 +175,16 @@ toolkit/components/help/**
 # Intentionally invalid JS
 toolkit/components/workerloader/tests/moduleF-syntax-error.js
 
 # Tests old non-star function generators
 toolkit/modules/tests/xpcshell/test_task.js
 
 # Not yet updated
 toolkit/components/osfile/**
-toolkit/components/passwordmgr/**
 
 # Uses preprocessing
 toolkit/content/widgets/videocontrols.xml
 toolkit/content/widgets/wizard.xml
 toolkit/components/jsdownloads/src/DownloadIntegration.jsm
 toolkit/components/search/nsSearchService.js
 toolkit/components/url-classifier/**
 toolkit/components/urlformatter/nsURLFormatter.js
--- a/browser/base/content/browser-charsetmenu.inc
+++ b/browser/base/content/browser-charsetmenu.inc
@@ -1,19 +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/.
 
 <menu id="charsetMenu"
     label="&charsetMenu2.label;"
-#ifndef OMIT_ACCESSKEYS
     accesskey="&charsetMenu2.accesskey;"
-#endif
     oncommand="BrowserSetForcedCharacterSet(event.target.getAttribute('charset'));"
-#ifdef OMIT_ACCESSKEYS
-    onpopupshowing="CharsetMenu.build(event.target, false);"
-#else
     onpopupshowing="CharsetMenu.build(event.target);"
-#endif
     onpopupshown="UpdateCurrentCharset(this);">
   <menupopup>
   </menupopup>
 </menu>
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -155,17 +155,17 @@ XPCOMUtils.defineLazyGetter(this, "Popup
     Cu.reportError(ex);
     return null;
   }
 });
 
 XPCOMUtils.defineLazyGetter(this, "DeveloperToolbar", function() {
   let { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
   let { DeveloperToolbar } = require("devtools/client/shared/developer-toolbar");
-  return new DeveloperToolbar(window, document.getElementById("developer-toolbar"));
+  return new DeveloperToolbar(window);
 });
 
 XPCOMUtils.defineLazyGetter(this, "BrowserToolboxProcess", function() {
   let tmp = {};
   Cu.import("resource://devtools/client/framework/ToolboxProcess.jsm", tmp);
   return tmp.BrowserToolboxProcess;
 });
 
@@ -5487,19 +5487,19 @@ function BrowserSetForcedCharacterSet(aC
 }
 
 function BrowserCharsetReload()
 {
   BrowserReloadWithFlags(nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE);
 }
 
 function UpdateCurrentCharset(target) {
+  let selectedCharset = CharsetMenu.foldCharset(gBrowser.selectedBrowser.characterSet);
   for (let menuItem of target.getElementsByTagName("menuitem")) {
-    let isSelected = menuItem.getAttribute("charset") ===
-                     CharsetMenu.foldCharset(gBrowser.selectedBrowser.characterSet);
+    let isSelected = menuItem.getAttribute("charset") === selectedCharset;
     menuItem.setAttribute("checked", isSelected);
   }
 }
 
 var gPageStyleMenu = {
   // This maps from a <browser> element (or, more specifically, a
   // browser's permanentKey) to an Object that contains the most recent
   // information about the browser content's stylesheets. That Object
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -1104,39 +1104,16 @@
 #else
             &exitDOMFullscreen.button;
 #endif
     </html:button>
   </html:div>
 
   <vbox id="browser-bottombox" layer="true">
     <notificationbox id="global-notificationbox" notificationside="bottom"/>
-    <toolbar id="developer-toolbar"
-             hidden="true">
-#ifdef XP_MACOSX
-          <toolbarbutton id="developer-toolbar-closebutton"
-                         class="devtools-closebutton"
-                         oncommand="DeveloperToolbar.hide();"
-                         tooltiptext="&devToolbarCloseButton.tooltiptext;"/>
-#endif
-          <stack class="gclitoolbar-stack-node" flex="1">
-            <textbox class="gclitoolbar-input-node" rows="1"/>
-            <hbox class="gclitoolbar-complete-node"/>
-          </stack>
-          <toolbarbutton id="developer-toolbar-toolbox-button"
-                         class="developer-toolbar-button"
-                         observes="devtoolsMenuBroadcaster_DevToolbox"
-                         tooltiptext="&devToolbarToolsButton.tooltip;"/>
-#ifndef XP_MACOSX
-          <toolbarbutton id="developer-toolbar-closebutton"
-                         class="devtools-closebutton"
-                         oncommand="DeveloperToolbar.hide();"
-                         tooltiptext="&devToolbarCloseButton.tooltiptext;"/>
-#endif
-   </toolbar>
   </vbox>
 
   <svg:svg height="0">
 #include tab-shape.inc.svg
     <svg:clipPath id="urlbar-back-button-clip-path">
 #ifndef XP_MACOSX
       <svg:path d="M -9,-4 l 0,1 a 15 15 0 0,1 0,30 l 0,1 l 10000,0 l 0,-32 l -10000,0 z" />
 #else
--- a/browser/base/content/sanitize.js
+++ b/browser/base/content/sanitize.js
@@ -283,39 +283,51 @@ Sanitizer.prototype = {
 
         TelemetryStopwatch.finish("FX_SANITIZE_COOKIES", refObj);
         if (seenException) {
           throw seenException;
         }
       }),
 
       promiseClearPluginCookies: Task.async(function* (range) {
-        const phInterface = Ci.nsIPluginHost;
-        const FLAG_CLEAR_ALL = phInterface.FLAG_CLEAR_ALL;
-        let ph = Cc["@mozilla.org/plugin/host;1"].getService(phInterface);
+        const FLAG_CLEAR_ALL = Ci.nsIPluginHost.FLAG_CLEAR_ALL;
+        let ph = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
 
         // Determine age range in seconds. (-1 means clear all.) We don't know
         // that range[1] is actually now, so we compute age range based
         // on the lower bound. If range results in a negative age, do nothing.
         let age = range ? (Date.now() / 1000 - range[0] / 1000000) : -1;
         if (!range || age >= 0) {
           let tags = ph.getPluginTags();
           for (let tag of tags) {
+            let refObj = {};
+            let probe = "";
+            if (/\bFlash\b/.test(tag.name)) {
+              probe = tag.loaded ? "FX_SANITIZE_LOADED_FLASH"
+                                 : "FX_SANITIZE_UNLOADED_FLASH";
+              TelemetryStopwatch.start(probe, refObj);
+            }
             try {
               let rv = yield new Promise(resolve =>
                 ph.clearSiteData(tag, null, FLAG_CLEAR_ALL, age, resolve)
               );
               // If the plugin doesn't support clearing by age, clear everything.
               if (rv == Components.results.NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED) {
                 yield new Promise(resolve =>
                   ph.clearSiteData(tag, null, FLAG_CLEAR_ALL, -1, resolve)
                 );
               }
+              if (probe) {
+                TelemetryStopwatch.finish(probe, refObj);
+              }
             } catch (ex) {
               // Ignore errors from plug-ins
+              if (probe) {
+                TelemetryStopwatch.cancel(probe, refObj);
+              }
             }
           }
         }
       })
     },
 
     offlineApps: {
       clear: Task.async(function* (range) {
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -137,17 +137,16 @@ skip-if = e10s # Bug 1093153 - no about:
 [browser_action_keyword_override.js]
 [browser_action_searchengine.js]
 [browser_action_searchengine_alias.js]
 [browser_addKeywordSearch.js]
 [browser_search_favicon.js]
 [browser_alltabslistener.js]
 [browser_audioTabIcon.js]
 [browser_autocomplete_a11y_label.js]
-skip-if = e10s # Bug 1101993 - times out for unknown reasons when run in the dir (works on its own)
 [browser_autocomplete_cursor.js]
 [browser_autocomplete_edit_completed.js]
 [browser_autocomplete_enter_race.js]
 [browser_autocomplete_no_title.js]
 [browser_autocomplete_autoselect.js]
 [browser_autocomplete_oldschool_wrap.js]
 [browser_autocomplete_tag_star_visibility.js]
 [browser_backButtonFitts.js]
@@ -362,17 +361,17 @@ support-files =
   permissions.html
 [browser_pinnedTabs.js]
 [browser_plainTextLinks.js]
 [browser_popupUI.js]
 skip-if = buildapp == 'mulet'
 [browser_popup_blocker.js]
 skip-if = (os == 'linux') || (e10s && debug) # Frequent bug 1081925 and bug 1125520 failures
 [browser_printpreview.js]
-skip-if = buildapp == 'mulet' || e10s # Bug 1101973 - breaks the next test in e10s, and may be responsible for later timeout after logging "Error: Channel closing: too late to send/recv, messages will be lost"
+skip-if = buildapp == 'mulet'
 [browser_private_browsing_window.js]
 skip-if = buildapp == 'mulet'
 [browser_private_no_prompt.js]
 skip-if = buildapp == 'mulet'
 [browser_purgehistory_clears_sh.js]
 [browser_PageMetaData_pushstate.js]
 [browser_refreshBlocker.js]
 support-files =
@@ -388,21 +387,21 @@ support-files =
 [browser_restore_isAppTab.js]
 [browser_sanitize-passwordDisabledHosts.js]
 [browser_sanitize-sitepermissions.js]
 [browser_sanitize-timespans.js]
 skip-if = buildapp == 'mulet'
 [browser_sanitizeDialog.js]
 skip-if = buildapp == 'mulet'
 [browser_save_link-perwindowpb.js]
-skip-if = buildapp == 'mulet' || e10s # Bug 933103 - mochitest's EventUtils.synthesizeMouse functions not e10s friendly
+skip-if = buildapp == 'mulet'
 [browser_save_private_link_perwindowpb.js]
-skip-if = buildapp == 'mulet' || e10s # e10s: Bug 933103 - mochitest's EventUtils.synthesizeMouse functions not e10s friendly
+skip-if = buildapp == 'mulet'
 [browser_save_link_when_window_navigates.js]
-skip-if = buildapp == 'mulet' || e10s # Bug 933103 - mochitest's EventUtils.synthesizeMouse functions not e10s friendly
+skip-if = buildapp == 'mulet'
 [browser_save_video.js]
 skip-if = buildapp == 'mulet'
 [browser_save_video_frame.js]
 [browser_scope.js]
 [browser_contentSearchUI.js]
 support-files =
   contentSearchUI.html
   contentSearchUI.js
--- a/browser/base/content/test/general/browser_bookmark_popup.js
+++ b/browser/base/content/test/general/browser_bookmark_popup.js
@@ -46,16 +46,17 @@ function* test_bookmarks_popup({isNewBoo
     is(bookmarkPanelTitle.value,
       isNewBookmark ?
         gNavigatorBundle.getString("editBookmarkPanel.pageBookmarkedTitle") :
         gNavigatorBundle.getString("editBookmarkPanel.editBookmarkTitle"),
       "title should match isEditingBookmark state");
 
     if (!shouldAutoClose) {
       yield new Promise(resolve => setTimeout(resolve, 400));
+      is(bookmarkPanel.state, "open", "Panel should still be 'open' for non-autoclose");
     }
 
     let hiddenPromise = promisePopupHidden(bookmarkPanel);
     if (popupHideFn) {
       yield popupHideFn();
     }
     yield hiddenPromise;
     is(bookmarkStar.hasAttribute("starred"), !isBookmarkRemoved,
@@ -139,31 +140,32 @@ add_task(function* panel_shown_for_new_b
         EventUtils.synthesizeNativeMouseMove(bookmarkPanel, 0, 0, resolve, window);
       });
       info("Waiting for mouseover event");
       yield mouseOverPromise;
       info("Got mouseover event");
 
       yield new Promise(resolve => setTimeout(resolve, 400));
       is(bookmarkPanel.state, "open", "Panel should still be open on mouseover");
-
+    },
+    *popupHideFn() {
       let mouseOutPromise = new Promise(resolve => {
         bookmarkPanel.addEventListener("mouseout", function onmouseout() {
           bookmarkPanel.removeEventListener("mouseout", onmouseout);
           resolve();
         });
       });
       yield new Promise(resolve => {
         EventUtils.synthesizeNativeMouseMove(bookmarkStar, 0, 0, resolve, window);
       });
       info("Waiting for mouseout event");
       yield mouseOutPromise;
       info("Got mouseout event, should autoclose now");
     },
-    shouldAutoClose: true,
+    shouldAutoClose: false,
     isBookmarkRemoved: false,
   });
 });
 
 add_task(function* panel_shown_for_new_bookmark_no_autoclose_close_with_ESC() {
   yield test_bookmarks_popup({
     isNewBookmark: false,
     popupShowFn() {
@@ -203,17 +205,17 @@ add_task(function* panel_shown_for_new_b
     shouldAutoClose: false,
     popupHideFn() {
       bookmarkPanel.hidePopup();
     },
     isBookmarkRemoved: false,
   });
 });
 
-add_task(function* contextmenu_new_bookmark_click_no_autoclose() {
+add_task(function* contextmenu_new_bookmark_keypress_no_autoclose() {
   yield test_bookmarks_popup({
     isNewBookmark: true,
     *popupShowFn(browser) {
       let contextMenu = document.getElementById("contentAreaContextMenu");
       let awaitPopupShown = BrowserTestUtils.waitForEvent(contextMenu,
                                                           "popupshown");
       let awaitPopupHidden = BrowserTestUtils.waitForEvent(contextMenu,
                                                            "popuphidden");
@@ -222,17 +224,17 @@ add_task(function* contextmenu_new_bookm
         button: 2
       }, browser);
       yield awaitPopupShown;
       document.getElementById("context-bookmarkpage").click();
       contextMenu.hidePopup();
       yield awaitPopupHidden;
     },
     popupEditFn() {
-      bookmarkPanelTitle.click();
+      EventUtils.sendChar("VK_TAB", window);
     },
     shouldAutoClose: false,
     popupHideFn() {
       bookmarkPanel.hidePopup();
     },
     isBookmarkRemoved: false,
   });
 });
--- a/browser/base/content/test/general/browser_printpreview.js
+++ b/browser/base/content/test/general/browser_printpreview.js
@@ -1,19 +1,28 @@
+let ourTab;
+
 function test() {
   waitForExplicitFinish();
 
-  ok(!gInPrintPreviewMode,
-     "Should NOT be in print preview mode at starting this tests");
-  // Skip access key test on platforms which don't support access key.
-  if (!/Win|Linux/.test(navigator.platform)) {
-    openPrintPreview(testClosePrintPreviewWithEscKey);
-  } else {
-    openPrintPreview(testClosePrintPreviewWithAccessKey);
-  }
+  BrowserTestUtils.openNewForegroundTab(gBrowser, "about:home", true).then(function(tab) {
+    ourTab = tab;
+    ok(!gInPrintPreviewMode,
+       "Should NOT be in print preview mode at starting this tests");
+    // Skip access key test on platforms which don't support access key.
+    if (!/Win|Linux/.test(navigator.platform)) {
+      openPrintPreview(testClosePrintPreviewWithEscKey);
+    } else {
+      openPrintPreview(testClosePrintPreviewWithAccessKey);
+    }
+  });
+}
+
+function tidyUp() {
+  BrowserTestUtils.removeTab(ourTab).then(finish);
 }
 
 function testClosePrintPreviewWithAccessKey() {
   EventUtils.synthesizeKey("c", { altKey: true });
   checkPrintPreviewClosed(function (aSucceeded) {
     ok(aSucceeded,
        "print preview mode should be finished by access key");
     openPrintPreview(testClosePrintPreviewWithEscKey);
@@ -29,17 +38,17 @@ function testClosePrintPreviewWithEscKey
   });
 }
 
 function testClosePrintPreviewWithClosingWindowShortcutKey() {
   EventUtils.synthesizeKey("w", { accelKey: true });
   checkPrintPreviewClosed(function (aSucceeded) {
     ok(aSucceeded,
        "print preview mode should be finished by closing window shortcut key");
-    finish();
+    tidyUp();
   });
 }
 
 function openPrintPreview(aCallback) {
   document.getElementById("cmd_printPreview").doCommand();
   executeSoon(function () {
     if (gInPrintPreviewMode) {
       executeSoon(aCallback);
--- a/browser/base/content/test/general/browser_save_link-perwindowpb.js
+++ b/browser/base/content/test/general/browser_save_link-perwindowpb.js
@@ -30,17 +30,17 @@ function triggerSave(aWindow, aCallback)
       aWindow.document.addEventListener("popupshown", contextMenuOpened, false);
 
       var link = testBrowser.contentDocument.getElementById("fff");
       info("link: " + link);
       EventUtils.synthesizeMouseAtCenter(link,
                                          { type: "contextmenu", button: 2 },
                                          testBrowser.contentWindow);
       info("right clicked!");
-    }, testBrowser.contentWindow);
+    }, testBrowser);
   }, false);
 
   function contextMenuOpened(event) {
     info("contextMenuOpened");
     aWindow.document.removeEventListener("popupshown", contextMenuOpened);
 
     // Create the folder the link will be saved into.
     var destDir = createTemporarySaveDirectory();
--- a/browser/base/content/test/general/browser_save_private_link_perwindowpb.js
+++ b/browser/base/content/test/general/browser_save_private_link_perwindowpb.js
@@ -101,17 +101,17 @@ function test() {
       aWindow.gBrowser.removeEventListener("pageshow", pageShown);
 
       waitForFocus(function () {
         aWindow.document.addEventListener("popupshown", contextMenuOpened, false);
         var img = aWindow.gBrowser.selectedBrowser.contentDocument.getElementById("img");
         EventUtils.synthesizeMouseAtCenter(img,
                                            { type: "contextmenu", button: 2 },
                                            aWindow.gBrowser.contentWindow);
-      }, aWindow.gBrowser.selectedBrowser.contentWindow);
+      }, aWindow.gBrowser.selectedBrowser);
     });
   }
 
   function testOnWindow(aOptions, aCallback) {
     whenNewWindowLoaded(aOptions, function(aWin) {
       windowsToClose.push(aWin);
       // execute should only be called when need, like when you are opening
       // web pages on the test. If calling executeSoon() is not necesary, then
--- a/browser/components/customizableui/test/browser.ini
+++ b/browser/components/customizableui/test/browser.ini
@@ -94,17 +94,16 @@ skip-if = os == "linux" # Intermittent f
 [browser_947987_removable_default.js]
 [browser_948985_non_removable_defaultArea.js]
 [browser_952963_areaType_getter_no_area.js]
 [browser_956602_remove_special_widget.js]
 [browser_962069_drag_to_overflow_chevron.js]
 [browser_962884_opt_in_disable_hyphens.js]
 [browser_963639_customizing_attribute_non_customizable_toolbar.js]
 [browser_967000_button_charEncoding.js]
-skip-if = e10s # Bug 1088710
 [browser_967000_button_feeds.js]
 [browser_967000_button_sync.js]
 [browser_968447_bookmarks_toolbar_items_in_panel.js]
 skip-if = os == "linux" # Intemittent failures - bug 979207
 [browser_968565_insert_before_hidden_items.js]
 [browser_969427_recreate_destroyed_widget_after_reset.js]
 [browser_969661_character_encoding_navbar_disabled.js]
 [browser_970511_undo_restore_default.js]
@@ -122,17 +121,16 @@ skip-if = os == "linux"
 [browser_984455_bookmarks_items_reparenting.js]
 skip-if = os == "linux"
 [browser_985815_propagate_setToolbarVisibility.js]
 [browser_987177_destroyWidget_xul.js]
 [browser_987177_xul_wrapper_updating.js]
 [browser_987185_syncButton.js]
 [browser_987492_window_api.js]
 [browser_987640_charEncoding.js]
-skip-if = e10s # Bug 1088710
 [browser_988072_sidebar_events.js]
 [browser_989338_saved_placements_not_resaved.js]
 [browser_989751_subviewbutton_class.js]
 [browser_992747_toggle_noncustomizable_toolbar.js]
 [browser_993322_widget_notoolbar.js]
 [browser_995164_registerArea_during_customize_mode.js]
 [browser_996364_registerArea_different_properties.js]
 [browser_996635_remove_non_widgets.js]
--- a/browser/components/customizableui/test/browser_967000_button_charEncoding.js
+++ b/browser/components/customizableui/test/browser_967000_button_charEncoding.js
@@ -1,19 +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 TEST_PAGE = "http://mochi.test:8888/browser/browser/components/customizableui/test/support/test_967000_charEncoding_page.html";
 
-var newTab;
-var initialLocation = gBrowser.currentURI.spec;
-
 add_task(function*() {
   info("Check Character Encoding button functionality");
 
   // add the Character Encoding button to the panel
   CustomizableUI.addWidgetToArea("characterencoding-button",
                                   CustomizableUI.AREA_PANEL);
 
   // check the button's functionality
@@ -23,24 +20,25 @@ add_task(function*() {
   ok(charEncodingButton, "The Character Encoding button was added to the Panel Menu");
   is(charEncodingButton.getAttribute("disabled"), "true",
      "The Character encoding button is initially disabled");
 
   let panelHidePromise = promisePanelHidden(window);
   PanelUI.hide();
   yield panelHidePromise;
 
-  newTab = gBrowser.selectedTab;
-  yield promiseTabLoadEvent(newTab, TEST_PAGE)
+  let newTab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PAGE, true, true);
 
   yield PanelUI.show();
   ok(!charEncodingButton.hasAttribute("disabled"), "The Character encoding button gets enabled");
+  let characterEncodingView = document.getElementById("PanelUI-characterEncodingView");
+  let subviewShownPromise = subviewShown(characterEncodingView);
   charEncodingButton.click();
+  yield subviewShownPromise;
 
-  let characterEncodingView = document.getElementById("PanelUI-characterEncodingView");
   ok(characterEncodingView.hasAttribute("current"), "The Character encoding panel is displayed");
 
   let pinnedEncodings = document.getElementById("PanelUI-characterEncodingView-pinned");
   let charsetsList = document.getElementById("PanelUI-characterEncodingView-charsets");
   ok(pinnedEncodings, "Pinned charsets are available");
   ok(charsetsList, "Charsets list is available");
 
   let checkedButtons = characterEncodingView.querySelectorAll("toolbarbutton[checked='true']");
@@ -48,19 +46,17 @@ add_task(function*() {
   is(checkedButtons[0].getAttribute("label"), "Unicode", "The unicode encoding is correctly selected");
   is(characterEncodingView.querySelectorAll("#PanelUI-characterEncodingView-autodetect toolbarbutton[checked='true']").length,
      1,
      "There should be 1 checked detector.");
 
   panelHidePromise = promisePanelHidden(window);
   PanelUI.hide();
   yield panelHidePromise;
+
+  yield BrowserTestUtils.removeTab(newTab);
 });
 
 add_task(function* asyncCleanup() {
   // reset the panel to the default state
   yield resetCustomization();
   ok(CustomizableUI.inDefaultState, "The UI is in default state again.");
-
-  // restore the initial location
-  gBrowser.addTab(initialLocation);
-  gBrowser.removeTab(newTab);
 });
--- a/browser/components/customizableui/test/browser_987640_charEncoding.js
+++ b/browser/components/customizableui/test/browser_987640_charEncoding.js
@@ -1,32 +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/. */
 
 "use strict";
 
 const TEST_PAGE = "http://mochi.test:8888/browser/browser/components/customizableui/test/support/test_967000_charEncoding_page.html";
-var newTab = null;
 
 add_task(function*() {
   info("Check Character Encoding panel functionality");
 
   // add the Character Encoding button to the panel
   CustomizableUI.addWidgetToArea("characterencoding-button",
                                   CustomizableUI.AREA_PANEL);
 
-  newTab = gBrowser.addTab(TEST_PAGE);
-  yield promiseTabLoadEvent(gBrowser.selectedTab, TEST_PAGE);
+  let newTab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PAGE, true, true);
 
   yield PanelUI.show();
   let charEncodingButton = document.getElementById("characterencoding-button");
+  let characterEncodingView = document.getElementById("PanelUI-characterEncodingView");
+  let subviewShownPromise = subviewShown(characterEncodingView);
   charEncodingButton.click();
+  yield subviewShownPromise;
 
-  let characterEncodingView = document.getElementById("PanelUI-characterEncodingView");
   let checkedButtons = characterEncodingView.querySelectorAll("toolbarbutton[checked='true']");
   let initialEncoding = checkedButtons[0];
   is(initialEncoding.getAttribute("label"), "Unicode", "The unicode encoding is initially selected");
 
   // change the encoding
   let encodings = characterEncodingView.querySelectorAll("toolbarbutton");
   let newEncoding = encodings[0].hasAttribute("checked") ? encodings[1] : encodings[0];
   let tabLoadPromise = promiseTabLoadEvent(gBrowser.selectedTab, TEST_PAGE);
@@ -45,18 +45,16 @@ add_task(function*() {
   charEncodingButton.click();
   tabLoadPromise = promiseTabLoadEvent(gBrowser.selectedTab, TEST_PAGE);
   initialEncoding.click();
   yield tabLoadPromise;
   yield PanelUI.show();
   charEncodingButton.click();
   checkedButtons = characterEncodingView.querySelectorAll("toolbarbutton[checked='true']");
   is(checkedButtons[0].getAttribute("label"), "Unicode", "The encoding was reset to Unicode");
+  yield BrowserTestUtils.removeTab(newTab);
 });
 
 add_task(function* asyncCleanup() {
   // reset the panel to the default state
   yield resetCustomization();
   ok(CustomizableUI.inDefaultState, "The UI is in default state again.");
-
-  // remove the added tab
-  gBrowser.removeTab(newTab);
 });
--- a/browser/components/distribution.js
+++ b/browser/components/distribution.js
@@ -372,44 +372,60 @@ DistributionCustomizer.prototype = {
 
     // We eval() the localizable prefs as well (even though they'll
     // always get set as a string) to keep the INI format consistent:
     // string prefs always need to be in quotes
 
     let localizedStr = Cc["@mozilla.org/pref-localizedstring;1"].
       createInstance(Ci.nsIPrefLocalizedString);
 
-    if (sections["LocalizablePreferences"]) {
-      for (let key of enumerate(this._ini.getKeys("LocalizablePreferences"))) {
+    var usedLocalizablePreferences = [];
+
+    if (sections["LocalizablePreferences-" + this._locale]) {
+      for (let key of enumerate(this._ini.getKeys("LocalizablePreferences-" + this._locale))) {
         try {
-          let value = eval(this._ini.getString("LocalizablePreferences", key));
-          value = value.replace(/%LOCALE%/g, this._locale);
-          value = value.replace(/%LANGUAGE%/g, this._language);
-          localizedStr.data = "data:text/plain," + key + "=" + value;
-          defaults.setComplexValue(key, Ci.nsIPrefLocalizedString, localizedStr);
+          let value = eval(this._ini.getString("LocalizablePreferences-" + this._locale, key));
+          if (value !== undefined) {
+            localizedStr.data = "data:text/plain," + key + "=" + value;
+            defaults.setComplexValue(key, Ci.nsIPrefLocalizedString, localizedStr);
+          }
+          usedLocalizablePreferences.push(key);
         } catch (e) { /* ignore bad prefs and move on */ }
       }
     }
 
     if (sections["LocalizablePreferences-" + this._language]) {
       for (let key of enumerate(this._ini.getKeys("LocalizablePreferences-" + this._language))) {
+        if (usedLocalizablePreferences.indexOf(key) > -1) {
+          continue;
+        }
         try {
           let value = eval(this._ini.getString("LocalizablePreferences-" + this._language, key));
-          localizedStr.data = "data:text/plain," + key + "=" + value;
-          defaults.setComplexValue(key, Ci.nsIPrefLocalizedString, localizedStr);
+          if (value !== undefined) {
+            localizedStr.data = "data:text/plain," + key + "=" + value;
+            defaults.setComplexValue(key, Ci.nsIPrefLocalizedString, localizedStr);
+          }
+          usedLocalizablePreferences.push(key);
         } catch (e) { /* ignore bad prefs and move on */ }
       }
     }
 
-    if (sections["LocalizablePreferences-" + this._locale]) {
-      for (let key of enumerate(this._ini.getKeys("LocalizablePreferences-" + this._locale))) {
+    if (sections["LocalizablePreferences"]) {
+      for (let key of enumerate(this._ini.getKeys("LocalizablePreferences"))) {
+        if (usedLocalizablePreferences.indexOf(key) > -1) {
+          continue;
+        }
         try {
-          let value = eval(this._ini.getString("LocalizablePreferences-" + this._locale, key));
-          localizedStr.data = "data:text/plain," + key + "=" + value;
-          defaults.setComplexValue(key, Ci.nsIPrefLocalizedString, localizedStr);
+          let value = eval(this._ini.getString("LocalizablePreferences", key));
+          if (value !== undefined) {
+            value = value.replace(/%LOCALE%/g, this._locale);
+            value = value.replace(/%LANGUAGE%/g, this._language);
+            localizedStr.data = "data:text/plain," + key + "=" + value;
+            defaults.setComplexValue(key, Ci.nsIPrefLocalizedString, localizedStr);
+          }
         } catch (e) { /* ignore bad prefs and move on */ }
       }
     }
 
     return this._checkCustomizationComplete();
   },
 
   _checkCustomizationComplete: function DIST__checkCustomizationComplete() {
--- a/browser/components/extensions/ext-tabs.js
+++ b/browser/components/extensions/ext-tabs.js
@@ -392,26 +392,30 @@ extensions.registerSchemaAPI("tabs", nul
           AllWindowEvents.removeListener("progress", progressListener);
           AllWindowEvents.removeListener("TabAttrModified", listener);
           AllWindowEvents.removeListener("TabPinned", listener);
           AllWindowEvents.removeListener("TabUnpinned", listener);
         };
       }).api(),
 
       create: function(createProperties) {
-        return new Promise(resolve => {
+        return new Promise((resolve, reject) => {
           function createInWindow(window) {
             let url;
+
             if (createProperties.url !== null) {
               url = context.uri.resolve(createProperties.url);
-            } else {
-              url = window.BROWSER_NEW_TAB_URL;
+
+              if (!context.checkLoadURL(url, {dontReportErrors: true})) {
+                reject({message: `URL not allowed: ${url}`});
+                return;
+              }
             }
 
-            let tab = window.gBrowser.addTab(url);
+            let tab = window.gBrowser.addTab(url || window.BROWSER_NEW_TAB_URL);
 
             let active = true;
             if (createProperties.active !== null) {
               active = createProperties.active;
             }
             if (active) {
               window.gBrowser.selectedTab = tab;
             }
@@ -455,20 +459,33 @@ extensions.registerSchemaAPI("tabs", nul
           tab.ownerDocument.defaultView.gBrowser.removeTab(tab);
         }
 
         return Promise.resolve();
       },
 
       update: function(tabId, updateProperties) {
         let tab = tabId !== null ? TabManager.getTab(tabId) : TabManager.activeTab;
+
+        if (!tab) {
+          return Promise.reject({message: `No tab found with tabId: ${tabId}`});
+        }
+
         let tabbrowser = tab.ownerDocument.defaultView.gBrowser;
+
         if (updateProperties.url !== null) {
-          tab.linkedBrowser.loadURI(updateProperties.url);
+          let url = context.uri.resolve(updateProperties.url);
+
+          if (!context.checkLoadURL(url, {dontReportErrors: true})) {
+            return Promise.reject({message: `URL not allowed: ${url}`});
+          }
+
+          tab.linkedBrowser.loadURI(url);
         }
+
         if (updateProperties.active !== null) {
           if (updateProperties.active) {
             tabbrowser.selectedTab = tab;
           } else {
             // Not sure what to do here? Which tab should we select?
           }
         }
         if (updateProperties.muted !== null) {
@@ -621,25 +638,34 @@ extensions.registerSchemaAPI("tabs", nul
           width: browser.clientWidth,
           height: browser.clientHeight,
         };
 
         return context.sendMessage(browser.messageManager, "Extension:Capture",
                                    message, recipient);
       },
 
-      _execute: function(tabId, details, kind) {
+      _execute: function(tabId, details, kind, method) {
         let tab = tabId !== null ? TabManager.getTab(tabId) : TabManager.activeTab;
         let mm = tab.linkedBrowser.messageManager;
 
         let options = {
           js: [],
           css: [],
         };
 
+        // We require a `code` or a `file` property, but we can't accept both.
+        if ((details.code === null) == (details.file === null)) {
+          return Promise.reject({message: `${method} requires either a 'code' or a 'file' property, but not both`});
+        }
+
+        if (details.frameId !== null && details.allFrames) {
+          return Promise.reject({message: `'frameId' and 'allFrames' are mutually exclusive`});
+        }
+
         let recipient = {
           innerWindowID: tab.linkedBrowser.innerWindowID,
         };
 
         if (TabManager.for(extension).hasActiveTabPermission(tab)) {
           // If we have the "activeTab" permission for this tab, ignore
           // the host whitelist.
           options.matchesHost = ["<all_urls>"];
@@ -655,32 +681,37 @@ extensions.registerSchemaAPI("tabs", nul
           if (!extension.isExtensionURL(url)) {
             return Promise.reject({message: "Files to be injected must be within the extension"});
           }
           options[kind].push(url);
         }
         if (details.allFrames) {
           options.all_frames = details.allFrames;
         }
+        if (details.frameId !== null) {
+          options.frame_id = details.frameId;
+        }
         if (details.matchAboutBlank) {
           options.match_about_blank = details.matchAboutBlank;
         }
         if (details.runAt !== null) {
           options.run_at = details.runAt;
+        } else {
+          options.run_at = "document_idle";
         }
 
         return context.sendMessage(mm, "Extension:Execute", {options}, recipient);
       },
 
       executeScript: function(tabId, details) {
-        return self.tabs._execute(tabId, details, "js");
+        return self.tabs._execute(tabId, details, "js", "executeScript");
       },
 
       insertCSS: function(tabId, details) {
-        return self.tabs._execute(tabId, details, "css");
+        return self.tabs._execute(tabId, details, "css", "insertCSS");
       },
 
       connect: function(tabId, connectInfo) {
         let tab = TabManager.getTab(tabId);
         let mm = tab.linkedBrowser.messageManager;
 
         let name = "";
         if (connectInfo && connectInfo.name !== null) {
--- a/browser/components/extensions/test/browser/browser.ini
+++ b/browser/components/extensions/test/browser/browser.ini
@@ -2,16 +2,18 @@
 support-files =
   head.js
   context.html
   ctxmenu-image.png
   context_tabs_onUpdated_page.html
   context_tabs_onUpdated_iframe.html
   file_popup_api_injection_a.html
   file_popup_api_injection_b.html
+  file_iframe_document.html
+  file_iframe_document.sjs
 
 [browser_ext_simple.js]
 [browser_ext_commands.js]
 [browser_ext_currentWindow.js]
 [browser_ext_browserAction_simple.js]
 [browser_ext_browserAction_pageAction_icon.js]
 [browser_ext_browserAction_context.js]
 [browser_ext_browserAction_disabled.js]
@@ -24,23 +26,26 @@ support-files =
 [browser_ext_lastError.js]
 [browser_ext_runtime_setUninstallURL.js]
 [browser_ext_tabs_audio.js]
 [browser_ext_tabs_captureVisibleTab.js]
 [browser_ext_tabs_events.js]
 [browser_ext_tabs_executeScript.js]
 [browser_ext_tabs_executeScript_good.js]
 [browser_ext_tabs_executeScript_bad.js]
+[browser_ext_tabs_executeScript_runAt.js]
 [browser_ext_tabs_insertCSS.js]
 [browser_ext_tabs_query.js]
 [browser_ext_tabs_getCurrent.js]
 [browser_ext_tabs_create.js]
+[browser_ext_tabs_create_invalid_url.js]
 [browser_ext_tabs_duplicate.js]
 [browser_ext_tabs_update.js]
+[browser_ext_tabs_update_url.js]
 [browser_ext_tabs_onUpdated.js]
 [browser_ext_tabs_sendMessage.js]
 [browser_ext_tabs_move.js]
 [browser_ext_tabs_move_window.js]
 [browser_ext_windows_create_tabId.js]
 [browser_ext_windows_update.js]
 [browser_ext_contentscript_connect.js]
 [browser_ext_tab_runtimeConnect.js]
-[browser_ext_webNavigation_getFrames.js]
\ No newline at end of file
+[browser_ext_webNavigation_getFrames.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_create_invalid_url.js
@@ -0,0 +1,66 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+function* testTabsCreateInvalidURL(tabsCreateURL) {
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      "permissions": ["tabs"],
+    },
+
+    background: function() {
+      browser.test.sendMessage("ready");
+      browser.test.onMessage.addListener((msg, tabsCreateURL) => {
+        browser.tabs.create({url: tabsCreateURL}, (tab) => {
+          browser.test.assertEq(undefined, tab, "on error tab should be undefined");
+          browser.test.assertTrue(/URL not allowed/.test(browser.runtime.lastError.message),
+                                  "runtime.lastError should report the expected error message");
+
+          // Remove the opened tab is any.
+          if (tab) {
+            browser.tabs.remove(tab.id);
+          }
+          browser.test.sendMessage("done");
+        });
+      });
+    },
+  });
+
+  yield extension.startup();
+
+  yield extension.awaitMessage("ready");
+
+  info(`test tab.create on invalid URL "${tabsCreateURL}"`);
+
+  extension.sendMessage("start", tabsCreateURL);
+  yield extension.awaitMessage("done");
+
+  yield extension.unload();
+}
+
+add_task(function* () {
+  info("Start testing tabs.create on invalid URLs");
+
+  let dataURLPage = `data:text/html,
+    <!DOCTYPE html>
+    <html>
+      <head>
+        <meta charset="utf-8">
+      </head>
+      <body>
+        <h1>data url page</h1>
+      </body>
+    </html>`;
+
+  let testCases = [
+    {tabsCreateURL: "about:addons"},
+    {tabsCreateURL: "javascript:console.log('tabs.update execute javascript')"},
+    {tabsCreateURL: dataURLPage},
+  ];
+
+  for (let {tabsCreateURL} of testCases) {
+    yield* testTabsCreateInvalidURL(tabsCreateURL);
+  }
+
+  info("done");
+});
--- a/browser/components/extensions/test/browser/browser_ext_tabs_executeScript.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_executeScript.js
@@ -3,61 +3,160 @@
 "use strict";
 
 add_task(function* testExecuteScript() {
   let {MessageChannel} = Cu.import("resource://gre/modules/MessageChannel.jsm", {});
 
   let messageManagersSize = MessageChannel.messageManagers.size;
   let responseManagersSize = MessageChannel.responseManagers.size;
 
-  let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/", true);
+  const BASE = "http://mochi.test:8888/browser/browser/components/extensions/test/browser/";
+  const URL = BASE + "file_iframe_document.html";
+  let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, URL, true);
 
   function background() {
-    browser.tabs.executeScript({
-      file: "script.js",
-      code: "42",
-    }, result => {
-      browser.test.assertEq(42, result, "Expected callback result");
-      browser.test.sendMessage("got result", result);
-    });
+    browser.tabs.query({active: true, currentWindow: true}).then(tabs => {
+      return browser.webNavigation.getAllFrames({tabId: tabs[0].id});
+    }).then(frames => {
+      browser.test.log(`FRAMES: ${frames[1].frameId} ${JSON.stringify(frames)}\n`);
+      return Promise.all([
+        browser.tabs.executeScript({
+          code: "42",
+        }).then(result => {
+          browser.test.assertEq(42, result, "Expected callback result");
+        }),
+
+        browser.tabs.executeScript({
+          file: "script.js",
+          code: "42",
+        }).then(result => {
+          browser.test.fail("Expected not to be able to execute a script with both file and code");
+        }, error => {
+          browser.test.assertTrue(/a 'code' or a 'file' property, but not both/.test(error.message),
+                                  "Got expected error");
+        }),
+
+        browser.tabs.executeScript({
+          file: "script.js",
+        }).then(result => {
+          browser.test.assertEq(undefined, result, "Expected callback result");
+        }),
+
+        browser.tabs.executeScript({
+          file: "script2.js",
+        }).then(result => {
+          browser.test.assertEq(27, result, "Expected callback result");
+        }),
+
+        browser.tabs.executeScript({
+          code: "location.href;",
+          allFrames: true,
+        }).then(result => {
+          browser.test.assertTrue(Array.isArray(result), "Result is an array");
+
+          browser.test.assertEq(2, result.length, "Result has correct length");
+
+          browser.test.assertTrue(/\/file_iframe_document\.html$/.test(result[0]), "First result is correct");
+          browser.test.assertEq("http://mochi.test:8888/", result[1], "Second result is correct");
+        }),
+
+        browser.tabs.executeScript({
+          code: "location.href;",
+          runAt: "document_end",
+        }).then(result => {
+          browser.test.assertTrue(typeof(result) == "string", "Result is a string");
+
+          browser.test.assertTrue(/\/file_iframe_document\.html$/.test(result), "Result is correct");
+        }),
 
-    browser.tabs.executeScript({
-      file: "script2.js",
-    }, result => {
-      browser.test.assertEq(27, result, "Expected callback result");
-      browser.test.sendMessage("got callback", result);
-    });
+        browser.tabs.executeScript({
+          code: "window",
+        }).then(result => {
+          browser.test.fail("Expected error when returning non-structured-clonable object");
+        }, error => {
+          browser.test.assertEq("Script returned non-structured-clonable data",
+                                error.message, "Got expected error");
+        }),
+
+        browser.tabs.executeScript({
+          code: "Promise.resolve(window)",
+        }).then(result => {
+          browser.test.fail("Expected error when returning non-structured-clonable object");
+        }, error => {
+          browser.test.assertEq("Script returned non-structured-clonable data",
+                                error.message, "Got expected error");
+        }),
+
+        browser.tabs.executeScript({
+          code: "Promise.resolve(42)",
+        }).then(result => {
+          browser.test.assertEq(42, result, "Got expected promise resolution value as result");
+        }),
+
+        browser.tabs.executeScript({
+          code: "location.href;",
+          runAt: "document_end",
+          allFrames: true,
+        }).then(result => {
+          browser.test.assertTrue(Array.isArray(result), "Result is an array");
 
-    browser.runtime.onMessage.addListener(message => {
-      browser.test.assertEq("script ran", message, "Expected runtime message");
-      browser.test.sendMessage("got message", message);
+          browser.test.assertEq(2, result.length, "Result has correct length");
+
+          browser.test.assertTrue(/\/file_iframe_document\.html$/.test(result[0]), "First result is correct");
+          browser.test.assertEq("http://mochi.test:8888/", result[1], "Second result is correct");
+        }),
+
+        browser.tabs.executeScript({
+          code: "location.href;",
+          frameId: frames[0].frameId,
+        }).then(result => {
+          browser.test.assertTrue(/\/file_iframe_document\.html$/.test(result), `Result for frameId[0] is correct: ${result}`);
+        }),
+
+        browser.tabs.executeScript({
+          code: "location.href;",
+          frameId: frames[1].frameId,
+        }).then(result => {
+          browser.test.assertEq("http://mochi.test:8888/", result, "Result for frameId[1] is correct");
+        }),
+
+        new Promise(resolve => {
+          browser.runtime.onMessage.addListener(message => {
+            browser.test.assertEq("script ran", message, "Expected runtime message");
+            resolve();
+          });
+        }),
+      ]);
+    }).then(() => {
+      browser.test.notifyPass("executeScript");
+    }).catch(e => {
+      browser.test.fail(`Error: ${e} :: ${e.stack}`);
+      browser.test.notifyFail("executeScript");
     });
   }
 
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
-      "permissions": ["http://mochi.test/"],
+      "permissions": ["http://mochi.test/", "webNavigation"],
     },
 
     background,
 
     files: {
       "script.js": function() {
         browser.runtime.sendMessage("script ran");
       },
 
       "script2.js": "27",
     },
   });
 
   yield extension.startup();
 
-  yield extension.awaitMessage("got result");
-  yield extension.awaitMessage("got callback");
-  yield extension.awaitMessage("got message");
+  yield extension.awaitFinish("executeScript");
 
   yield extension.unload();
 
   yield BrowserTestUtils.removeTab(tab);
 
   // Make sure that we're not holding on to references to closed message
   // managers.
   is(MessageChannel.messageManagers.size, messageManagersSize, "Message manager count");
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_runAt.js
@@ -0,0 +1,110 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+/**
+ * These tests ensure that the runAt argument to tabs.executeScript delays
+ * script execution until the document has reached the correct state.
+ *
+ * Since tests of this nature are especially race-prone, it relies on a
+ * server-JS script to delay the completion of our test page's load cycle long
+ * enough for us to attempt to load our scripts in the earlies phase we support.
+ *
+ * And since we can't actually rely on that timing, it retries any attempts that
+ * fail to load as early as expected, but don't load at any illegal time.
+ */
+
+add_task(function* testExecuteScript() {
+  let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank", true);
+
+  function background() {
+    let tab;
+
+    const BASE = "http://mochi.test:8888/browser/browser/components/extensions/test/browser/";
+    const URL = BASE + "file_iframe_document.sjs";
+
+    const MAX_TRIES = 10;
+    let tries = 0;
+
+    function again() {
+      if (tries++ == MAX_TRIES) {
+        return Promise.reject(new Error("Max tries exceeded"));
+      }
+
+      let loadingPromise = new Promise(resolve => {
+        browser.tabs.onUpdated.addListener(function listener(tabId, changed, tab_) {
+          if (tabId == tab.id && changed.status == "loading" && tab_.url == URL) {
+            browser.tabs.onUpdated.removeListener(listener);
+            resolve();
+          }
+        });
+      });
+
+      // TODO: Test allFrames and frameId.
+
+      return browser.tabs.update({url: URL}).then(() => {
+        return loadingPromise;
+      }).then(() => {
+        return Promise.all([
+          // Send the executeScript requests in the reverse order that we expect
+          // them to execute in, to avoid them passing only because of timing
+          // races.
+          browser.tabs.executeScript({
+            code: "document.readyState",
+            runAt: "document_idle",
+          }),
+          browser.tabs.executeScript({
+            code: "document.readyState",
+            runAt: "document_end",
+          }),
+          browser.tabs.executeScript({
+            code: "document.readyState",
+            runAt: "document_start",
+          }),
+        ].reverse());
+      }).then(states => {
+        browser.test.log(`Got states: ${states}`);
+
+        // Make sure that none of our scripts executed earlier than expected,
+        // regardless of retries.
+        browser.test.assertTrue(states[1] == "interactive" || states[1] == "complete",
+                                `document_end state is valid: ${states[1]}`);
+        browser.test.assertTrue(states[2] == "complete",
+                                `document_idle state is valid: ${states[2]}`);
+
+        // If we have the earliest valid states for each script, we're done.
+        // Otherwise, try again.
+        if (states[0] != "loading" || states[1] != "interactive" || states[2] != "complete") {
+          return again();
+        }
+      });
+    }
+
+    browser.tabs.query({active: true, currentWindow: true}).then(tabs => {
+      tab = tabs[0];
+
+      return again();
+    }).then(() => {
+      browser.test.notifyPass("executeScript-runAt");
+    }).catch(e => {
+      browser.test.fail(`Error: ${e} :: ${e.stack}`);
+      browser.test.notifyFail("executeScript-runAt");
+    });
+  }
+
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      "permissions": ["http://mochi.test/", "tabs"],
+    },
+
+    background,
+  });
+
+  yield extension.startup();
+
+  yield extension.awaitFinish("executeScript-runAt");
+
+  yield extension.unload();
+
+  yield BrowserTestUtils.removeTab(tab);
+});
--- a/browser/components/extensions/test/browser/browser_ext_tabs_insertCSS.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_insertCSS.js
@@ -8,89 +8,75 @@ add_task(function* testExecuteScript() {
   let messageManagersSize = MessageChannel.messageManagers.size;
   let responseManagersSize = MessageChannel.responseManagers.size;
 
   let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/", true);
 
   function background() {
     let promises = [
       {
-        background: "rgb(0, 0, 0)",
-        foreground: "rgb(255, 192, 203)",
-        promise: resolve => {
-          browser.tabs.insertCSS({
-            file: "file1.css",
-            code: "* { background: black }",
-          }, result => {
-            browser.test.assertEq(undefined, result, "Expected callback result");
-            resolve();
-          });
-        },
-      },
-      {
-        background: "rgb(0, 0, 0)",
+        background: "transparent",
         foreground: "rgb(0, 113, 4)",
-        promise: resolve => {
-          browser.tabs.insertCSS({
+        promise: () => {
+          return browser.tabs.insertCSS({
             file: "file2.css",
-          }, result => {
-            browser.test.assertEq(undefined, result, "Expected callback result");
-            resolve();
           });
         },
       },
       {
         background: "rgb(42, 42, 42)",
         foreground: "rgb(0, 113, 4)",
-        promise: resolve => {
-          browser.tabs.insertCSS({
+        promise: () => {
+          return browser.tabs.insertCSS({
             code: "* { background: rgb(42, 42, 42) }",
-          }, result => {
-            browser.test.assertEq(undefined, result, "Expected callback result");
-            resolve();
           });
         },
       },
     ];
 
     function checkCSS() {
       let computedStyle = window.getComputedStyle(document.body);
       return [computedStyle.backgroundColor, computedStyle.color];
     }
 
     function next() {
       if (!promises.length) {
-        browser.test.notifyPass("insertCSS");
         return;
       }
 
       let {promise, background, foreground} = promises.shift();
-      new Promise(promise).then(() => {
-        browser.tabs.executeScript({
+      return promise().then(result => {
+        browser.test.assertEq(undefined, result, "Expected callback result");
+
+        return browser.tabs.executeScript({
           code: `(${checkCSS})()`,
-        }, result => {
-          browser.test.assertEq(background, result[0], "Expected background color");
-          browser.test.assertEq(foreground, result[1], "Expected foreground color");
-          next();
         });
+      }).then(result => {
+        browser.test.assertEq(background, result[0], "Expected background color");
+        browser.test.assertEq(foreground, result[1], "Expected foreground color");
+        return next();
       });
     }
 
-    next();
+    next().then(() => {
+      browser.test.notifyPass("insertCSS");
+    }).catch(e => {
+      browser.test.fail(`Error: ${e} :: ${e.stack}`);
+      browser.test.notifyFailure("insertCSS");
+    });
   }
 
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       "permissions": ["http://mochi.test/"],
     },
 
     background,
 
     files: {
-      "file1.css": "* { color: pink }",
       "file2.css": "* { color: rgb(0, 113, 4) }",
     },
   });
 
   yield extension.startup();
 
   yield extension.awaitFinish("insertCSS");
 
--- a/browser/components/extensions/test/browser/browser_ext_tabs_onUpdated.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_onUpdated.js
@@ -154,23 +154,23 @@ add_task(function* test_url() {
   yield do_test_update(function background() {
     // Create a new tab for testing update.
     browser.tabs.create({}, function(tab) {
       browser.tabs.onUpdated.addListener(function onUpdated(tabId, changeInfo) {
         // Check callback
         browser.test.assertEq(tabId, tab.id, "Check tab id");
         browser.test.log("onUpdate: " + JSON.stringify(changeInfo));
         if ("url" in changeInfo) {
-          browser.test.assertEq("about:preferences", changeInfo.url,
+          browser.test.assertEq("about:blank", changeInfo.url,
                                 "Check changeInfo.url");
           browser.tabs.onUpdated.removeListener(onUpdated);
           // Remove created tab.
           browser.tabs.remove(tabId);
           browser.test.notifyPass("finish");
           return;
         }
       });
-      browser.tabs.update(tab.id, {url: "about:preferences"});
+      browser.tabs.update(tab.id, {url: "about:blank"});
     });
   });
 });
 
 add_task(forceGC);
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_update_url.js
@@ -0,0 +1,120 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+function* testTabsUpdateURL(existentTabURL, tabsUpdateURL, isErrorExpected) {
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      "permissions": ["tabs"],
+    },
+
+    files: {
+      "tab.html": `
+        <!DOCTYPE html>
+        <html>
+          <head>
+            <meta charset="utf-8">
+          </head>
+          <body>
+            <h1>tab page</h1>
+          </body>
+        </html>
+      `.trim(),
+    },
+    background: function() {
+      browser.test.sendMessage("ready", browser.runtime.getURL("tab.html"));
+
+      browser.test.onMessage.addListener((msg, tabsUpdateURL, isErrorExpected) => {
+        let onTabsUpdated = (tab) => {
+          if (isErrorExpected) {
+            browser.test.fail(`tabs.update with URL ${tabsUpdateURL} should be rejected`);
+          } else {
+            browser.test.assertTrue(tab, "on success the tab should be defined");
+          }
+        };
+
+        let onTabsUpdateError = (error) => {
+          if (!isErrorExpected) {
+            browser.test.fails(`tabs.update with URL ${tabsUpdateURL} should not be rejected`);
+          } else {
+            browser.test.assertTrue(/^URL not allowed/.test(error.message),
+                                    "tabs.update should be rejected with the expected error message");
+          }
+        };
+
+        let onTabsUpdateDone = () => browser.test.sendMessage("done");
+
+        browser.tabs.query({lastFocusedWindow: true}, (tabs) => {
+          browser.tabs.update(tabs[1].id, {url: tabsUpdateURL})
+                      .then(onTabsUpdated, onTabsUpdateError)
+                      .then(onTabsUpdateDone);
+        });
+      });
+    },
+  });
+
+  yield extension.startup();
+
+  let mozExtTabURL = yield extension.awaitMessage("ready");
+
+  if (tabsUpdateURL == "self") {
+    tabsUpdateURL = mozExtTabURL;
+  }
+
+  info(`tab.update URL "${tabsUpdateURL}" on tab with URL "${existentTabURL}"`);
+
+  let tab1 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, existentTabURL);
+
+  extension.sendMessage("start", tabsUpdateURL, isErrorExpected);
+  yield extension.awaitMessage("done");
+
+  yield BrowserTestUtils.removeTab(tab1);
+  yield extension.unload();
+}
+
+add_task(function* () {
+  info("Start testing tabs.update on javascript URLs");
+
+  let dataURLPage = `data:text/html,
+    <!DOCTYPE html>
+    <html>
+      <head>
+        <meta charset="utf-8">
+      </head>
+      <body>
+        <h1>data url page</h1>
+      </body>
+    </html>`;
+
+  let checkList = [
+    {
+      tabsUpdateURL: "http://example.net",
+      isErrorExpected: false,
+    },
+    {
+      tabsUpdateURL: "self",
+      isErrorExpected: false,
+    },
+    {
+      tabsUpdateURL: "about:addons",
+      isErrorExpected: true,
+    },
+    {
+      tabsUpdateURL: "javascript:console.log('tabs.update execute javascript')",
+      isErrorExpected: true,
+    },
+    {
+      tabsUpdateURL: dataURLPage,
+      isErrorExpected: true,
+    },
+  ];
+
+  let testCases = checkList
+        .map((check) => Object.assign({}, check, {existentTabURL: "about:blank"}));
+
+  for (let {existentTabURL, tabsUpdateURL, isErrorExpected} of testCases) {
+    yield* testTabsUpdateURL(existentTabURL, tabsUpdateURL, isErrorExpected);
+  }
+
+  info("done");
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/file_iframe_document.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <title></title>
+</head>
+<body>
+  <iframe src="/"></iframe>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/file_iframe_document.sjs
@@ -0,0 +1,40 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80 ft=javascript: */
+"use strict";
+
+// This script slows the load of an HTML document so that we can reliably test
+// all phases of the load cycle supported by the extension API.
+
+/* eslint-disable no-unused-vars */
+
+const DELAY = 1 * 1000; // Delay one second before completing the request.
+
+const Ci = Components.interfaces;
+
+let nsTimer = Components.Constructor("@mozilla.org/timer;1", "nsITimer", "initWithCallback");
+
+let timer;
+
+function handleRequest(request, response) {
+  response.processAsync();
+
+  response.setHeader("Content-Type", "text/html", false);
+  response.write(`<!DOCTYPE html>
+    <html lang="en">
+    <head>
+      <meta charset="UTF-8">
+      <title></title>
+    </head>
+    <body>
+  `);
+
+  // Note: We need to store a reference to the timer to prevent it from being
+  // canceled when it's GCed.
+  timer = new nsTimer(() => {
+    response.write(`
+        <iframe src="/"></iframe>
+      </body>
+      </html>`);
+    response.finish();
+  }, DELAY, Ci.nsITimer.TYPE_ONE_SHOT);
+}
--- a/browser/components/places/content/controller.js
+++ b/browser/components/places/content/controller.js
@@ -967,17 +967,17 @@ PlacesController.prototype = {
 
     // Do removal in chunks to give some breath to main-thread.
     function* pagesChunkGenerator(aURIs) {
       while (aURIs.length) {
         let URIslice = aURIs.splice(0, REMOVE_PAGES_CHUNKLEN);
         PlacesUtils.bhistory.removePages(URIslice, URIslice.length);
         Services.tm.mainThread.dispatch(() => gen.next(),
                                         Ci.nsIThread.DISPATCH_NORMAL);
-        yield unefined;
+        yield undefined;
       }
     }
     let gen = pagesChunkGenerator(URIs);
     gen.next();
   },
 
   /**
    * Removes history visits for an history container node.
--- a/browser/components/tests/unit/distribution.ini
+++ b/browser/components/tests/unit/distribution.ini
@@ -10,23 +10,26 @@ about=Test distribution file
 distribution.test.string="Test String"
 distribution.test.string.noquotes=Test String
 distribution.test.int=777
 distribution.test.bool.true=true
 distribution.test.bool.false=false
 
 [LocalizablePreferences]
 distribution.test.locale="%LOCALE%"
-distribution.test.reset="Set"
-distribution.test.locale.set="First Set"
-distribution.test.language.set="First Set"
+distribution.test.language.reset="Preference Set"
+distribution.test.locale.reset="Preference Set"
+distribution.test.locale.set="Preference Set"
+distribution.test.language.set="Preference Set"
 
 [LocalizablePreferences-en]
 distribution.test.language.en="en"
-distribution.test.language.set="Second Set"
+distribution.test.language.reset=
+distribution.test.language.set="Language Set"
+distribution.test.locale.set="Language Set"
 
 [LocalizablePreferences-en-US]
 distribution.test.locale.en-US="en-US"
-distribution.test.reset=
-distribution.test.locale.set="Second Set"
+distribution.test.locale.reset=
+distribution.test.locale.set="Locale Set"
 
 [LocalizablePreferences-de]
 distribution.test.locale.de="de"
--- a/browser/components/tests/unit/test_distribution.js
+++ b/browser/components/tests/unit/test_distribution.js
@@ -66,16 +66,19 @@ add_task(function* () {
   Assert.throws(() => Services.prefs.getCharPref("distribution.test.string.noquotes"));
   Assert.equal(Services.prefs.getIntPref("distribution.test.int"), 777);
   Assert.equal(Services.prefs.getBoolPref("distribution.test.bool.true"), true);
   Assert.equal(Services.prefs.getBoolPref("distribution.test.bool.false"), false);
   Assert.equal(Services.prefs.getComplexValue("distribution.test.locale", Ci.nsIPrefLocalizedString).data, "en-US");
   Assert.equal(Services.prefs.getComplexValue("distribution.test.language.en", Ci.nsIPrefLocalizedString).data, "en");
   Assert.equal(Services.prefs.getComplexValue("distribution.test.locale.en-US", Ci.nsIPrefLocalizedString).data, "en-US");
   Assert.throws(() => Services.prefs.getComplexValue("distribution.test.locale.de", Ci.nsIPrefLocalizedString));
+  // This value was never set because of the empty language specific pref
+  Assert.throws(() => Services.prefs.getComplexValue("distribution.test.language.reset", Ci.nsIPrefLocalizedString));
   // This value was never set because of the empty locale specific pref
-  // This testcase currently fails - the value is set to "undefined" - it should not be set at all (throw)
-  // Assert.throws(() => Services.prefs.getComplexValue("distribution.test.reset", Ci.nsIPrefLocalizedString));
-  // This value was overriden by a locale specific setting
-  Assert.equal(Services.prefs.getComplexValue("distribution.test.locale.set", Ci.nsIPrefLocalizedString).data, "Second Set");
-  // This value was overriden by a language specific setting
-  Assert.equal(Services.prefs.getComplexValue("distribution.test.language.set", Ci.nsIPrefLocalizedString).data, "Second Set");
+  Assert.throws(() => Services.prefs.getComplexValue("distribution.test.locale.reset", Ci.nsIPrefLocalizedString));
+  // This value was overridden by a locale specific setting
+  Assert.equal(Services.prefs.getComplexValue("distribution.test.locale.set", Ci.nsIPrefLocalizedString).data, "Locale Set");
+  // This value was overridden by a language specific setting
+  Assert.equal(Services.prefs.getComplexValue("distribution.test.language.set", Ci.nsIPrefLocalizedString).data, "Language Set");
+  // Language should not override locale
+  Assert.notEqual(Services.prefs.getComplexValue("distribution.test.locale.set", Ci.nsIPrefLocalizedString).data, "Language Set");
 });
--- a/browser/extensions/pdfjs/README.mozilla
+++ b/browser/extensions/pdfjs/README.mozilla
@@ -1,3 +1,3 @@
 This is the pdf.js project output, https://github.com/mozilla/pdf.js
 
-Current extension version is: 1.4.83
+Current extension version is: 1.4.95
--- a/browser/extensions/pdfjs/content/PdfStreamConverter.jsm
+++ b/browser/extensions/pdfjs/content/PdfStreamConverter.jsm
@@ -283,17 +283,17 @@ ChromeActions.prototype = {
       // Create a nsIInputStreamChannel so we can set the url on the channel
       // so the filename will be correct.
       var channel = Cc['@mozilla.org/network/input-stream-channel;1'].
                        createInstance(Ci.nsIInputStreamChannel);
       channel.QueryInterface(Ci.nsIChannel);
       try {
         // contentDisposition/contentDispositionFilename is readonly before FF18
         channel.contentDisposition = Ci.nsIChannel.DISPOSITION_ATTACHMENT;
-        if (self.contentDispositionFilename) {
+        if (self.contentDispositionFilename && !data.isAttachment) {
           channel.contentDispositionFilename = self.contentDispositionFilename;
         } else {
           channel.contentDispositionFilename = filename;
         }
       } catch (e) {}
       channel.setURI(originalUri);
       channel.contentStream = aInputStream;
       if ('nsIPrivateBrowsingChannel' in Ci &&
--- a/browser/extensions/pdfjs/content/build/pdf.js
+++ b/browser/extensions/pdfjs/content/build/pdf.js
@@ -23,18 +23,18 @@ define('pdfjs-dist/build/pdf', ['exports
     factory(exports);
   } else {
 factory((root.pdfjsDistBuildPdf = {}));
   }
 }(this, function (exports) {
   // Use strict in our context only - users might not want it
   'use strict';
 
-var pdfjsVersion = '1.4.83';
-var pdfjsBuild = '0629fd0';
+var pdfjsVersion = '1.4.95';
+var pdfjsBuild = '2b813c0';
 
   var pdfjsFilePath =
     typeof document !== 'undefined' && document.currentScript ?
       document.currentScript.src : null;
 
   var pdfjsLibs = {};
 
   (function pdfjsWrapper() {
@@ -402,16 +402,27 @@ var UNSUPPORTED_FEATURES = PDFJS.UNSUPPO
   unknown: 'unknown',
   forms: 'forms',
   javaScript: 'javaScript',
   smask: 'smask',
   shadingPattern: 'shadingPattern',
   font: 'font'
 };
 
+// Gets the file name from a given URL.
+function getFilenameFromUrl(url) {
+  var anchor = url.indexOf('#');
+  var query = url.indexOf('?');
+  var end = Math.min(
+    anchor > 0 ? anchor : url.length,
+    query > 0 ? query : url.length);
+  return url.substring(url.lastIndexOf('/', end) + 1, end);
+}
+PDFJS.getFilenameFromUrl = getFilenameFromUrl;
+
 // Combines two URLs. The baseUrl shall be absolute URL. If the url is an
 // absolute URL, it will be returned as is.
 function combineUrl(baseUrl, url) {
   if (!url) {
     return baseUrl;
   }
   return new URL(url, baseUrl).href;
 }
@@ -1532,16 +1543,17 @@ exports.UnknownErrorException = UnknownE
 exports.Util = Util;
 exports.XRefParseException = XRefParseException;
 exports.assert = assert;
 exports.bytesToString = bytesToString;
 exports.combineUrl = combineUrl;
 exports.createPromiseCapability = createPromiseCapability;
 exports.deprecated = deprecated;
 exports.error = error;
+exports.getFilenameFromUrl = getFilenameFromUrl;
 exports.getLookupTableFactory = getLookupTableFactory;
 exports.info = info;
 exports.isArray = isArray;
 exports.isArrayBuffer = isArrayBuffer;
 exports.isBool = isBool;
 exports.isEmptyObj = isEmptyObj;
 exports.isExternalLinkTargetSet = isExternalLinkTargetSet;
 exports.isInt = isInt;
@@ -1572,26 +1584,28 @@ exports.warn = warn;
       root.pdfjsDisplayDOMUtils);
   }
 }(this, function (exports, sharedUtil, displayDOMUtils) {
 
 var AnnotationBorderStyleType = sharedUtil.AnnotationBorderStyleType;
 var AnnotationType = sharedUtil.AnnotationType;
 var Util = sharedUtil.Util;
 var addLinkAttributes = sharedUtil.addLinkAttributes;
+var getFilenameFromUrl = sharedUtil.getFilenameFromUrl;
 var warn = sharedUtil.warn;
 var CustomStyle = displayDOMUtils.CustomStyle;
 
 /**
  * @typedef {Object} AnnotationElementParameters
  * @property {Object} data
  * @property {HTMLDivElement} layer
  * @property {PDFPage} page
  * @property {PageViewport} viewport
  * @property {IPDFLinkService} linkService
+ * @property {DownloadManager} downloadManager
  */
 
 /**
  * @class
  * @alias AnnotationElementFactory
  */
 function AnnotationElementFactory() {}
 AnnotationElementFactory.prototype =
@@ -1623,16 +1637,19 @@ AnnotationElementFactory.prototype =
         return new UnderlineAnnotationElement(parameters);
 
       case AnnotationType.SQUIGGLY:
         return new SquigglyAnnotationElement(parameters);
 
       case AnnotationType.STRIKEOUT:
         return new StrikeOutAnnotationElement(parameters);
 
+      case AnnotationType.FILEATTACHMENT:
+        return new FileAttachmentAnnotationElement(parameters);
+
       default:
         return new AnnotationElement(parameters);
     }
   }
 };
 
 /**
  * @class
@@ -1641,16 +1658,17 @@ AnnotationElementFactory.prototype =
 var AnnotationElement = (function AnnotationElementClosure() {
   function AnnotationElement(parameters, isRenderable) {
     this.isRenderable = isRenderable || false;
     this.data = parameters.data;
     this.layer = parameters.layer;
     this.page = parameters.page;
     this.viewport = parameters.viewport;
     this.linkService = parameters.linkService;
+    this.downloadManager = parameters.downloadManager;
 
     if (isRenderable) {
       this.container = this._createContainer();
     }
   }
 
   AnnotationElement.prototype = /** @lends AnnotationElement.prototype */ {
     /**
@@ -1740,16 +1758,53 @@ var AnnotationElement = (function Annota
 
       container.style.width = width + 'px';
       container.style.height = height + 'px';
 
       return container;
     },
 
     /**
+     * Create a popup for the annotation's HTML element. This is used for
+     * annotations that do not have a Popup entry in the dictionary, but
+     * are of a type that works with popups (such as Highlight annotations).
+     *
+     * @private
+     * @param {HTMLSectionElement} container
+     * @param {HTMLDivElement|HTMLImageElement|null} trigger
+     * @param {Object} data
+     * @memberof AnnotationElement
+     */
+    _createPopup:
+        function AnnotationElement_createPopup(container, trigger, data) {
+      // If no trigger element is specified, create it.
+      if (!trigger) {
+        trigger = document.createElement('div');
+        trigger.style.height = container.style.height;
+        trigger.style.width = container.style.width;
+        container.appendChild(trigger);
+      }
+
+      var popupElement = new PopupElement({
+        container: container,
+        trigger: trigger,
+        color: data.color,
+        title: data.title,
+        contents: data.contents,
+        hideWrapper: true
+      });
+      var popup = popupElement.render();
+
+      // Position the popup next to the annotation's container.
+      popup.style.left = container.style.width;
+
+      container.appendChild(popup);
+    },
+
+    /**
      * Render the annotation's HTML element in the empty container.
      *
      * @public
      * @memberof AnnotationElement
      */
     render: function AnnotationElement_render() {
       throw new Error('Abstract method AnnotationElement.render called');
     }
@@ -1867,30 +1922,17 @@ var TextAnnotationElement = (function Te
       image.style.width = this.container.style.width;
       image.src = PDFJS.imageResourcesPath + 'annotation-' +
         this.data.name.toLowerCase() + '.svg';
       image.alt = '[{{type}} Annotation]';
       image.dataset.l10nId = 'text_annotation_type';
       image.dataset.l10nArgs = JSON.stringify({type: this.data.name});
 
       if (!this.data.hasPopup) {
-        var popupElement = new PopupElement({
-          container: this.container,
-          trigger: image,
-          color: this.data.color,
-          title: this.data.title,
-          contents: this.data.contents,
-          hideWrapper: true
-        });
-        var popup = popupElement.render();
-
-        // Position the popup next to the Text annotation's container.
-        popup.style.left = image.style.width;
-
-        this.container.appendChild(popup);
+        this._createPopup(this.container, image, this.data);
       }
 
       this.container.appendChild(image);
       return this.container;
     }
   });
 
   return TextAnnotationElement;
@@ -2157,117 +2199,201 @@ var PopupElement = (function PopupElemen
 
 /**
  * @class
  * @alias HighlightAnnotationElement
  */
 var HighlightAnnotationElement = (
     function HighlightAnnotationElementClosure() {
   function HighlightAnnotationElement(parameters) {
-    AnnotationElement.call(this, parameters, true);
+    var isRenderable = !!(parameters.data.hasPopup ||
+                          parameters.data.title || parameters.data.contents);
+    AnnotationElement.call(this, parameters, isRenderable);
   }
 
   Util.inherit(HighlightAnnotationElement, AnnotationElement, {
     /**
      * Render the highlight annotation's HTML element in the empty container.
      *
      * @public
      * @memberof HighlightAnnotationElement
      * @returns {HTMLSectionElement}
      */
     render: function HighlightAnnotationElement_render() {
       this.container.className = 'highlightAnnotation';
+
+      if (!this.data.hasPopup) {
+        this._createPopup(this.container, null, this.data);
+      }
+
       return this.container;
     }
   });
 
   return HighlightAnnotationElement;
 })();
 
 /**
  * @class
  * @alias UnderlineAnnotationElement
  */
 var UnderlineAnnotationElement = (
     function UnderlineAnnotationElementClosure() {
   function UnderlineAnnotationElement(parameters) {
-    AnnotationElement.call(this, parameters, true);
+    var isRenderable = !!(parameters.data.hasPopup ||
+                          parameters.data.title || parameters.data.contents);
+    AnnotationElement.call(this, parameters, isRenderable);
   }
 
   Util.inherit(UnderlineAnnotationElement, AnnotationElement, {
     /**
      * Render the underline annotation's HTML element in the empty container.
      *
      * @public
      * @memberof UnderlineAnnotationElement
      * @returns {HTMLSectionElement}
      */
     render: function UnderlineAnnotationElement_render() {
       this.container.className = 'underlineAnnotation';
+
+      if (!this.data.hasPopup) {
+        this._createPopup(this.container, null, this.data);
+      }
+
       return this.container;
     }
   });
 
   return UnderlineAnnotationElement;
 })();
 
 /**
  * @class
  * @alias SquigglyAnnotationElement
  */
 var SquigglyAnnotationElement = (function SquigglyAnnotationElementClosure() {
   function SquigglyAnnotationElement(parameters) {
-    AnnotationElement.call(this, parameters, true);
+    var isRenderable = !!(parameters.data.hasPopup ||
+                          parameters.data.title || parameters.data.contents);
+    AnnotationElement.call(this, parameters, isRenderable);
   }
 
   Util.inherit(SquigglyAnnotationElement, AnnotationElement, {
     /**
      * Render the squiggly annotation's HTML element in the empty container.
      *
      * @public
      * @memberof SquigglyAnnotationElement
      * @returns {HTMLSectionElement}
      */
     render: function SquigglyAnnotationElement_render() {
       this.container.className = 'squigglyAnnotation';
+
+      if (!this.data.hasPopup) {
+        this._createPopup(this.container, null, this.data);
+      }
+
       return this.container;
     }
   });
 
   return SquigglyAnnotationElement;
 })();
 
 /**
  * @class
  * @alias StrikeOutAnnotationElement
  */
 var StrikeOutAnnotationElement = (
     function StrikeOutAnnotationElementClosure() {
   function StrikeOutAnnotationElement(parameters) {
-    AnnotationElement.call(this, parameters, true);
+    var isRenderable = !!(parameters.data.hasPopup ||
+                          parameters.data.title || parameters.data.contents);
+    AnnotationElement.call(this, parameters, isRenderable);
   }
 
   Util.inherit(StrikeOutAnnotationElement, AnnotationElement, {
     /**
      * Render the strikeout annotation's HTML element in the empty container.
      *
      * @public
      * @memberof StrikeOutAnnotationElement
      * @returns {HTMLSectionElement}
      */
     render: function StrikeOutAnnotationElement_render() {
       this.container.className = 'strikeoutAnnotation';
+
+      if (!this.data.hasPopup) {
+        this._createPopup(this.container, null, this.data);
+      }
+
       return this.container;
     }
   });
 
   return StrikeOutAnnotationElement;
 })();
 
 /**
+ * @class
+ * @alias FileAttachmentAnnotationElement
+ */
+var FileAttachmentAnnotationElement = (
+    function FileAttachmentAnnotationElementClosure() {
+  function FileAttachmentAnnotationElement(parameters) {
+    AnnotationElement.call(this, parameters, true);
+
+    this.filename = getFilenameFromUrl(parameters.data.file.filename);
+    this.content = parameters.data.file.content;
+  }
+
+  Util.inherit(FileAttachmentAnnotationElement, AnnotationElement, {
+    /**
+     * Render the file attachment annotation's HTML element in the empty
+     * container.
+     *
+     * @public
+     * @memberof FileAttachmentAnnotationElement
+     * @returns {HTMLSectionElement}
+     */
+    render: function FileAttachmentAnnotationElement_render() {
+      this.container.className = 'fileAttachmentAnnotation';
+
+      var trigger = document.createElement('div');
+      trigger.style.height = this.container.style.height;
+      trigger.style.width = this.container.style.width;
+      trigger.addEventListener('dblclick', this._download.bind(this));
+
+      if (!this.data.hasPopup && (this.data.title || this.data.contents)) {
+        this._createPopup(this.container, trigger, this.data);
+      }
+
+      this.container.appendChild(trigger);
+      return this.container;
+    },
+
+    /**
+     * Download the file attachment associated with this annotation.
+     *
+     * @private
+     * @memberof FileAttachmentAnnotationElement
+     */
+    _download: function FileAttachmentAnnotationElement_download() {
+      if (!this.downloadManager) {
+        warn('Download cannot be started due to unavailable download manager');
+        return;
+      }
+      this.downloadManager.downloadData(this.content, this.filename, '');
+    }
+  });
+
+  return FileAttachmentAnnotationElement;
+})();
+
+/**
  * @typedef {Object} AnnotationLayerParameters
  * @property {PageViewport} viewport
  * @property {HTMLDivElement} div
  * @property {Array} annotations
  * @property {PDFPage} page
  * @property {IPDFLinkService} linkService
  */
 
@@ -2293,17 +2419,18 @@ var AnnotationLayer = (function Annotati
           continue;
         }
 
         var properties = {
           data: data,
           layer: parameters.div,
           page: parameters.page,
           viewport: parameters.viewport,
-          linkService: parameters.linkService
+          linkService: parameters.linkService,
+          downloadManager: parameters.downloadManager
         };
         var element = annotationElementFactory.create(properties);
         if (element.isRenderable) {
           parameters.div.appendChild(element.render());
         }
       }
     },
 
--- a/browser/extensions/pdfjs/content/build/pdf.worker.js
+++ b/browser/extensions/pdfjs/content/build/pdf.worker.js
@@ -23,18 +23,18 @@ define('pdfjs-dist/build/pdf.worker', ['
     factory(exports);
   } else {
 factory((root.pdfjsDistBuildPdfWorker = {}));
   }
 }(this, function (exports) {
   // Use strict in our context only - users might not want it
   'use strict';
 
-var pdfjsVersion = '1.4.83';
-var pdfjsBuild = '0629fd0';
+var pdfjsVersion = '1.4.95';
+var pdfjsBuild = '2b813c0';
 
   var pdfjsFilePath =
     typeof document !== 'undefined' && document.currentScript ?
       document.currentScript.src : null;
 
   var pdfjsLibs = {};
 
   (function pdfjsWrapper() {
@@ -2361,16 +2361,27 @@ var UNSUPPORTED_FEATURES = PDFJS.UNSUPPO
   unknown: 'unknown',
   forms: 'forms',
   javaScript: 'javaScript',
   smask: 'smask',
   shadingPattern: 'shadingPattern',
   font: 'font'
 };
 
+// Gets the file name from a given URL.
+function getFilenameFromUrl(url) {
+  var anchor = url.indexOf('#');
+  var query = url.indexOf('?');
+  var end = Math.min(
+    anchor > 0 ? anchor : url.length,
+    query > 0 ? query : url.length);
+  return url.substring(url.lastIndexOf('/', end) + 1, end);
+}
+PDFJS.getFilenameFromUrl = getFilenameFromUrl;
+
 // Combines two URLs. The baseUrl shall be absolute URL. If the url is an
 // absolute URL, it will be returned as is.
 function combineUrl(baseUrl, url) {
   if (!url) {
     return baseUrl;
   }
   return new URL(url, baseUrl).href;
 }
@@ -3491,16 +3502,17 @@ exports.UnknownErrorException = UnknownE
 exports.Util = Util;
 exports.XRefParseException = XRefParseException;
 exports.assert = assert;
 exports.bytesToString = bytesToString;
 exports.combineUrl = combineUrl;
 exports.createPromiseCapability = createPromiseCapability;
 exports.deprecated = deprecated;
 exports.error = error;
+exports.getFilenameFromUrl = getFilenameFromUrl;
 exports.getLookupTableFactory = getLookupTableFactory;
 exports.info = info;
 exports.isArray = isArray;
 exports.isArrayBuffer = isArrayBuffer;
 exports.isBool = isBool;
 exports.isEmptyObj = isEmptyObj;
 exports.isExternalLinkTargetSet = isExternalLinkTargetSet;
 exports.isInt = isInt;
@@ -34977,16 +34989,17 @@ var ObjectLoader = (function() {
   };
 
   return ObjectLoader;
 })();
 
 exports.Catalog = Catalog;
 exports.ObjectLoader = ObjectLoader;
 exports.XRef = XRef;
+exports.FileSpec = FileSpec;
 }));
 
 
 (function (root, factory) {
   {
     factory((root.pdfjsCorePattern = {}), root.pdfjsSharedUtil,
       root.pdfjsCorePrimitives, root.pdfjsCoreFunction,
       root.pdfjsCoreColorSpace);
@@ -38787,16 +38800,17 @@ var stringToPDFString = sharedUtil.strin
 var stringToUTF8String = sharedUtil.stringToUTF8String;
 var warn = sharedUtil.warn;
 var Dict = corePrimitives.Dict;
 var isDict = corePrimitives.isDict;
 var isName = corePrimitives.isName;
 var Stream = coreStream.Stream;
 var ColorSpace = coreColorSpace.ColorSpace;
 var ObjectLoader = coreObj.ObjectLoader;
+var FileSpec = coreObj.FileSpec;
 var OperatorList = coreEvaluator.OperatorList;
 
 /**
  * @class
  * @alias AnnotationFactory
  */
 function AnnotationFactory() {}
 AnnotationFactory.prototype = /** @lends AnnotationFactory.prototype */ {
@@ -38812,16 +38826,17 @@ AnnotationFactory.prototype = /** @lends
     }
 
     // Determine the annotation's subtype.
     var subtype = dict.get('Subtype');
     subtype = isName(subtype) ? subtype.name : '';
 
     // Return the right annotation object based on the subtype and field type.
     var parameters = {
+      xref: xref,
       dict: dict,
       ref: ref
     };
 
     switch (subtype) {
       case 'Link':
         return new LinkAnnotation(parameters);
 
@@ -38845,16 +38860,19 @@ AnnotationFactory.prototype = /** @lends
         return new UnderlineAnnotation(parameters);
 
       case 'Squiggly':
         return new SquigglyAnnotation(parameters);
 
       case 'StrikeOut':
         return new StrikeOutAnnotation(parameters);
 
+      case 'FileAttachment':
+        return new FileAttachmentAnnotation(parameters);
+
       default:
         warn('Unimplemented annotation type "' + subtype + '", ' +
              'falling back to base annotation');
         return new Annotation(parameters);
     }
   }
 };
 
@@ -39078,16 +39096,34 @@ var Annotation = (function AnnotationClo
         // specification, we should draw a solid border of width 1 in that
         // case, but Adobe Reader did not implement that part of the
         // specification and instead draws no border at all, so we do the same.
         // See also https://github.com/mozilla/pdf.js/issues/6179.
         this.borderStyle.setWidth(0);
       }
     },
 
+    /**
+     * Prepare the annotation for working with a popup in the display layer.
+     *
+     * @private
+     * @memberof Annotation
+     * @param {Dict} dict - The annotation's data dictionary
+     */
+    _preparePopup: function Annotation_preparePopup(dict) {
+      if (!dict.has('C')) {
+        // Fall back to the default background color.
+        this.data.color = null;
+      }
+
+      this.data.hasPopup = dict.has('Popup');
+      this.data.title = stringToPDFString(dict.get('T') || '');
+      this.data.contents = stringToPDFString(dict.get('Contents') || '');
+    },
+
     loadResources: function Annotation_loadResources(keys) {
       return new Promise(function (resolve, reject) {
         this.appearance.dict.getAsync('Resources').then(function (resources) {
           if (!resources) {
             resolve();
             return;
           }
           var objectLoader = new ObjectLoader(resources.map,
@@ -39395,37 +39431,25 @@ var TextWidgetAnnotation = (function Tex
 var TextAnnotation = (function TextAnnotationClosure() {
   var DEFAULT_ICON_SIZE = 22; // px
 
   function TextAnnotation(parameters) {
     Annotation.call(this, parameters);
 
     this.data.annotationType = AnnotationType.TEXT;
 
-    var dict = parameters.dict;
     if (this.data.hasAppearance) {
       this.data.name = 'NoIcon';
     } else {
       this.data.rect[1] = this.data.rect[3] - DEFAULT_ICON_SIZE;
       this.data.rect[2] = this.data.rect[0] + DEFAULT_ICON_SIZE;
-      this.data.name = dict.has('Name') ? dict.get('Name').name : 'Note';
-    }
-
-    if (!dict.has('C')) {
-      // Fall back to the default background color.
-      this.data.color = null;
-    }
-
-    this.data.hasPopup = dict.has('Popup');
-    if (!this.data.hasPopup) {
-      // There is no associated Popup annotation, so the Text annotation
-      // must create its own popup.
-      this.data.title = stringToPDFString(dict.get('T') || '');
-      this.data.contents = stringToPDFString(dict.get('Contents') || '');
-    }
+      this.data.name = parameters.dict.has('Name') ?
+                       parameters.dict.get('Name').name : 'Note';
+    }
+    this._preparePopup(parameters.dict);
   }
 
   Util.inherit(TextAnnotation, Annotation, {});
 
   return TextAnnotation;
 })();
 
 var LinkAnnotation = (function LinkAnnotationClosure() {
@@ -39534,71 +39558,91 @@ var PopupAnnotation = (function PopupAnn
   return PopupAnnotation;
 })();
 
 var HighlightAnnotation = (function HighlightAnnotationClosure() {
   function HighlightAnnotation(parameters) {
     Annotation.call(this, parameters);
 
     this.data.annotationType = AnnotationType.HIGHLIGHT;
+    this._preparePopup(parameters.dict);
 
     // PDF viewers completely ignore any border styles.
     this.data.borderStyle.setWidth(0);
   }
 
   Util.inherit(HighlightAnnotation, Annotation, {});
 
   return HighlightAnnotation;
 })();
 
 var UnderlineAnnotation = (function UnderlineAnnotationClosure() {
   function UnderlineAnnotation(parameters) {
     Annotation.call(this, parameters);
 
     this.data.annotationType = AnnotationType.UNDERLINE;
+    this._preparePopup(parameters.dict);
 
     // PDF viewers completely ignore any border styles.
     this.data.borderStyle.setWidth(0);
   }
 
   Util.inherit(UnderlineAnnotation, Annotation, {});
 
   return UnderlineAnnotation;
 })();
 
 var SquigglyAnnotation = (function SquigglyAnnotationClosure() {
   function SquigglyAnnotation(parameters) {
     Annotation.call(this, parameters);
 
     this.data.annotationType = AnnotationType.SQUIGGLY;
+    this._preparePopup(parameters.dict);
 
     // PDF viewers completely ignore any border styles.
     this.data.borderStyle.setWidth(0);
   }
 
   Util.inherit(SquigglyAnnotation, Annotation, {});
 
   return SquigglyAnnotation;
 })();
 
 var StrikeOutAnnotation = (function StrikeOutAnnotationClosure() {
   function StrikeOutAnnotation(parameters) {
     Annotation.call(this, parameters);
 
     this.data.annotationType = AnnotationType.STRIKEOUT;
+    this._preparePopup(parameters.dict);
 
     // PDF viewers completely ignore any border styles.
     this.data.borderStyle.setWidth(0);
   }
 
   Util.inherit(StrikeOutAnnotation, Annotation, {});
 
   return StrikeOutAnnotation;
 })();
 
+var FileAttachmentAnnotation = (function FileAttachmentAnnotationClosure() {
+  function FileAttachmentAnnotation(parameters) {
+    Annotation.call(this, parameters);
+
+    var file = new FileSpec(parameters.dict.get('FS'), parameters.xref);
+
+    this.data.annotationType = AnnotationType.FILEATTACHMENT;
+    this.data.file = file.serializable;
+    this._preparePopup(parameters.dict);
+  }
+
+  Util.inherit(FileAttachmentAnnotation, Annotation, {});
+
+  return FileAttachmentAnnotation;
+})();
+
 exports.Annotation = Annotation;
 exports.AnnotationBorderStyle = AnnotationBorderStyle;
 exports.AnnotationFactory = AnnotationFactory;
 }));
 
 
 (function (root, factory) {
   {
--- a/browser/extensions/pdfjs/content/web/viewer.css
+++ b/browser/extensions/pdfjs/content/web/viewer.css
@@ -127,17 +127,18 @@
 
 .annotationLayer .popup p {
   padding-top: 0.2em;
 }
 
 .annotationLayer .highlightAnnotation,
 .annotationLayer .underlineAnnotation,
 .annotationLayer .squigglyAnnotation,
-.annotationLayer .strikeoutAnnotation {
+.annotationLayer .strikeoutAnnotation,
+.annotationLayer .fileAttachmentAnnotation {
   cursor: pointer;
 }
 
 .pdfViewer .canvasWrapper {
   overflow: hidden;
 }
 
 .pdfViewer .page {
--- a/browser/extensions/pdfjs/content/web/viewer.js
+++ b/browser/extensions/pdfjs/content/web/viewer.js
@@ -7,25 +7,25 @@
  *     http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-/* globals PDFJS, PDFBug, FirefoxCom, Stats, ProgressBar,
-           DownloadManager, getFileName, getPDFFileNameFromURL,
-           PDFHistory, Preferences, SidebarView, ViewHistory, Stats,
-           PDFThumbnailViewer, URL, noContextMenuHandler, SecondaryToolbar,
-           PasswordPrompt, PDFPresentationMode, PDFDocumentProperties, HandTool,
-           Promise, PDFLinkService, PDFOutlineView, PDFAttachmentView,
-           OverlayManager, PDFFindController, PDFFindBar, PDFViewer,
-           PDFRenderingQueue, PresentationModeState, parseQueryString,
-           RenderingStates, UNKNOWN_SCALE, DEFAULT_SCALE_VALUE,
+/* globals PDFJS, PDFBug, FirefoxCom, Stats, ProgressBar, DownloadManager,
+           getPDFFileNameFromURL, PDFHistory, Preferences, SidebarView,
+           ViewHistory, Stats, PDFThumbnailViewer, URL, noContextMenuHandler,
+           SecondaryToolbar, PasswordPrompt, PDFPresentationMode,
+           PDFDocumentProperties, HandTool, Promise, PDFLinkService,
+           PDFOutlineView, PDFAttachmentView, OverlayManager,
+           PDFFindController, PDFFindBar, PDFViewer, PDFRenderingQueue,
+           PresentationModeState, parseQueryString, RenderingStates,
+           UNKNOWN_SCALE, DEFAULT_SCALE_VALUE,
            IGNORE_CURRENT_POSITION_ON_ZOOM: true */
 
 'use strict';
 
 var DEFAULT_URL = 'compressed.tracemonkey-pldi-09.pdf';
 var DEFAULT_SCALE_DELTA = 1.1;
 var MIN_SCALE = 0.25;
 var MAX_SCALE = 10.0;
@@ -47,25 +47,16 @@ var mozL10n = document.mozL10n || docume
 var CSS_UNITS = 96.0 / 72.0;
 var DEFAULT_SCALE_VALUE = 'auto';
 var DEFAULT_SCALE = 1.0;
 var UNKNOWN_SCALE = 0;
 var MAX_AUTO_SCALE = 1.25;
 var SCROLLBAR_PADDING = 40;
 var VERTICAL_PADDING = 5;
 
-function getFileName(url) {
-  var anchor = url.indexOf('#');
-  var query = url.indexOf('?');
-  var end = Math.min(
-    anchor > 0 ? anchor : url.length,
-    query > 0 ? query : url.length);
-  return url.substring(url.lastIndexOf('/', end) + 1, end);
-}
-
 /**
  * Returns scale factor for the canvas. It makes sense for the HiDPI displays.
  * @return {Object} The object with horizontal (sx) and vertical (sy)
                     scales. The scaled property is set to false if scaling is
                     not required, true otherwise.
  */
 function getOutputScale(ctx) {
   var devicePixelRatio = window.devicePixelRatio || 1;
@@ -4294,30 +4285,32 @@ DefaultTextLayerFactory.prototype = {
 };
 
 
 /**
  * @typedef {Object} AnnotationLayerBuilderOptions
  * @property {HTMLDivElement} pageDiv
  * @property {PDFPage} pdfPage
  * @property {IPDFLinkService} linkService
+ * @property {DownloadManager} downloadManager
  */
 
 /**
  * @class
  */
 var AnnotationLayerBuilder = (function AnnotationLayerBuilderClosure() {
   /**
    * @param {AnnotationLayerBuilderOptions} options
    * @constructs AnnotationLayerBuilder
    */
   function AnnotationLayerBuilder(options) {
     this.pageDiv = options.pageDiv;
     this.pdfPage = options.pdfPage;
     this.linkService = options.linkService;
+    this.downloadManager = options.downloadManager;
 
     this.div = null;
   }
 
   AnnotationLayerBuilder.prototype =
       /** @lends AnnotationLayerBuilder.prototype */ {
 
     /**
@@ -4332,17 +4325,18 @@ var AnnotationLayerBuilder = (function A
 
       this.pdfPage.getAnnotations(parameters).then(function (annotations) {
         viewport = viewport.clone({ dontFlip: true });
         parameters = {
           viewport: viewport,
           div: self.div,
           annotations: annotations,
           page: self.pdfPage,
-          linkService: self.linkService
+          linkService: self.linkService,
+          downloadManager: self.downloadManager
         };
 
         if (self.div) {
           // If an annotationLayer already exists, refresh its children's
           // transformation matrices.
           PDFJS.AnnotationLayer.update(parameters);
         } else {
           // Create an annotation layer div and render the annotations
@@ -4396,16 +4390,18 @@ DefaultAnnotationLayerFactory.prototype 
 };
 
 
 /**
  * @typedef {Object} PDFViewerOptions
  * @property {HTMLDivElement} container - The container for the viewer element.
  * @property {HTMLDivElement} viewer - (optional) The viewer element.
  * @property {IPDFLinkService} linkService - The navigation/linking service.
+ * @property {DownloadManager} downloadManager - (optional) The download
+ *   manager component.
  * @property {PDFRenderingQueue} renderingQueue - (optional) The rendering
  *   queue object.
  * @property {boolean} removePageBorders - (optional) Removes the border shadow
  *   around the pages. The default is false.
  */
 
 /**
  * Simple viewer control to display PDF content/pages.
@@ -4448,16 +4444,17 @@ var PDFViewer = (function pdfViewer() {
   /**
    * @constructs PDFViewer
    * @param {PDFViewerOptions} options
    */
   function PDFViewer(options) {
     this.container = options.container;
     this.viewer = options.viewer || options.container.firstElementChild;
     this.linkService = options.linkService || new SimpleLinkService();
+    this.downloadManager = options.downloadManager || null;
     this.removePageBorders = options.removePageBorders || false;
 
     this.defaultRenderingQueue = !options.renderingQueue;
     if (this.defaultRenderingQueue) {
       // Custom rendering queue is not specified, using default one
       this.renderingQueue = new PDFRenderingQueue();
       this.renderingQueue.setViewer(this);
     } else {
@@ -5113,17 +5110,18 @@ var PDFViewer = (function pdfViewer() {
      * @param {HTMLDivElement} pageDiv
      * @param {PDFPage} pdfPage
      * @returns {AnnotationLayerBuilder}
      */
     createAnnotationLayerBuilder: function (pageDiv, pdfPage) {
       return new AnnotationLayerBuilder({
         pageDiv: pageDiv,
         pdfPage: pdfPage,
-        linkService: this.linkService
+        linkService: this.linkService,
+        downloadManager: this.downloadManager
       });
     },
 
     setFindController: function (findController) {
       this.findController = findController;
     },
   };
 
@@ -5237,17 +5235,16 @@ var PDFThumbnailView = (function PDFThum
     this.pdfPage = null;
     this.rotation = 0;
     this.viewport = defaultViewport;
     this.pdfPageRotate = defaultViewport.rotation;
 
     this.linkService = linkService;
     this.renderingQueue = renderingQueue;
 
-    this.hasImage = false;
     this.resume = null;
     this.renderingState = RenderingStates.INITIAL;
 
     this.pageWidth = this.viewport.width;
     this.pageHeight = this.viewport.height;
     this.pageRatio = this.pageWidth / this.pageHeight;
 
     this.canvasWidth = THUMBNAIL_WIDTH;
@@ -5293,17 +5290,16 @@ var PDFThumbnailView = (function PDFThum
       this.viewport = pdfPage.getViewport(1, totalRotation);
       this.reset();
     },
 
     reset: function PDFThumbnailView_reset() {
       if (this.renderTask) {
         this.renderTask.cancel();
       }
-      this.hasImage = false;
       this.resume = null;
       this.renderingState = RenderingStates.INITIAL;
 
       this.pageWidth = this.viewport.width;
       this.pageHeight = this.viewport.height;
       this.pageRatio = this.pageWidth / this.pageHeight;
 
       this.canvasHeight = (this.canvasWidth / this.pageRatio) | 0;
@@ -5396,21 +5392,19 @@ var PDFThumbnailView = (function PDFThum
       this.canvas.width = 0;
       this.canvas.height = 0;
       delete this.canvas;
     },
 
     draw: function PDFThumbnailView_draw() {
       if (this.renderingState !== RenderingStates.INITIAL) {
         console.error('Must be in new state before drawing');
-      }
-      if (this.hasImage) {
         return Promise.resolve(undefined);
       }
-      this.hasImage = true;
+
       this.renderingState = RenderingStates.RUNNING;
 
       var resolveRenderPromise, rejectRenderPromise;
       var promise = new Promise(function (resolve, reject) {
         resolveRenderPromise = resolve;
         rejectRenderPromise = reject;
       });
 
@@ -5421,16 +5415,17 @@ var PDFThumbnailView = (function PDFThum
         // triggering this callback.
         if (renderTask === self.renderTask) {
           self.renderTask = null;
         }
         if (error === 'cancelled') {
           rejectRenderPromise(error);
           return;
         }
+
         self.renderingState = RenderingStates.FINISHED;
         self._convertCanvasToImage();
 
         if (!error) {
           resolveRenderPromise(undefined);
         } else {
           rejectRenderPromise(error);
         }
@@ -5464,24 +5459,27 @@ var PDFThumbnailView = (function PDFThum
         function pdfPageRenderError(error) {
           thumbnailDrawCallback(error);
         }
       );
       return promise;
     },
 
     setImage: function PDFThumbnailView_setImage(pageView) {
+      if (this.renderingState !== RenderingStates.INITIAL) {
+        return;
+      }
       var img = pageView.canvas;
-      if (this.hasImage || !img) {
+      if (!img) {
         return;
       }
       if (!this.pdfPage) {
         this.setPdfPage(pageView.pdfPage);
       }
-      this.hasImage = true;
+
       this.renderingState = RenderingStates.FINISHED;
 
       var ctx = this._getPageDrawContext(true);
       var canvas = ctx.canvas;
 
       if (img.width <= 2 * canvas.width) {
         ctx.drawImage(img, 0, 0, img.width, img.height,
                       0, 0, canvas.width, canvas.height);
@@ -5922,17 +5920,17 @@ var PDFAttachmentView = (function PDFAtt
 
       var names = Object.keys(attachments).sort(function(a, b) {
         return a.toLowerCase().localeCompare(b.toLowerCase());
       });
       attachmentsCount = names.length;
 
       for (var i = 0; i < attachmentsCount; i++) {
         var item = attachments[names[i]];
-        var filename = getFileName(item.filename);
+        var filename = PDFJS.getFilenameFromUrl(item.filename);
         var div = document.createElement('div');
         div.className = 'attachmentsItem';
         var button = document.createElement('button');
         this._bindLink(button, item.content, filename);
         button.textContent = PDFJS.removeNullCharacters(filename);
         div.appendChild(button);
         this.container.appendChild(div);
       }
@@ -5988,17 +5986,18 @@ var PDFViewerApplication = {
     this.pdfLinkService = pdfLinkService;
 
     var container = document.getElementById('viewerContainer');
     var viewer = document.getElementById('viewer');
     this.pdfViewer = new PDFViewer({
       container: container,
       viewer: viewer,
       renderingQueue: pdfRenderingQueue,
-      linkService: pdfLinkService
+      linkService: pdfLinkService,
+      downloadManager: new DownloadManager()
     });
     pdfRenderingQueue.setViewer(this.pdfViewer);
     pdfLinkService.setViewer(this.pdfViewer);
 
     var thumbnailContainer = document.getElementById('thumbnailView');
     this.pdfThumbnailViewer = new PDFThumbnailViewer({
       container: thumbnailContainer,
       renderingQueue: pdfRenderingQueue,
@@ -6329,17 +6328,17 @@ var PDFViewerApplication = {
       }
     });
     FirefoxCom.requestSync('initPassiveLoading', null);
   },
 
   setTitleUsingUrl: function pdfViewSetTitleUsingUrl(url) {
     this.url = url;
     try {
-      this.setTitle(decodeURIComponent(getFileName(url)) || url);
+      this.setTitle(decodeURIComponent(PDFJS.getFilenameFromUrl(url)) || url);
     } catch (e) {
       // decodeURIComponent may throw URIError,
       // fall back to using the unprocessed url in that case
       this.setTitle(url);
     }
   },
 
   setTitle: function pdfViewSetTitle(title) {
--- a/browser/extensions/pocket/skin/windows/pocket.css
+++ b/browser/extensions/pocket/skin/windows/pocket.css
@@ -1,12 +1,12 @@
 @import url("chrome://pocket-shared/skin/pocket.css");
 
 #nav-bar #pocket-button > .toolbarbutton-icon {
-  padding: 2px 6px;
+  padding: calc(var(--toolbarbutton-vertical-inner-padding)) 6px;
 }
 
 :-moz-any(#TabsToolbar, .widget-overflow-list) #pocket-button > .toolbarbutton-icon {
     max-width: 18px;
     padding: 0;
 }
 
 @media (-moz-windows-theme: luna-silver) and (max-resolution: 1dppx) {
--- a/devtools/.eslintrc
+++ b/devtools/.eslintrc
@@ -1,9 +1,12 @@
 {
+  "plugins": [
+    "react"
+  ],
   "globals": {
     "Cc": true,
     "Ci": true,
     "Components": true,
     "console": true,
     "Cr": true,
     "Cu": true,
     "devtools": true,
@@ -25,16 +28,33 @@
     // Rules from the mozilla plugin
     "mozilla/mark-test-function-used": 1,
     "mozilla/no-aArgs": 1,
     "mozilla/no-cpows-in-tests": 1,
     // See bug 1224289.
     "mozilla/reject-importGlobalProperties": 1,
     "mozilla/var-only-at-top-level": 1,
 
+    // Rules from the React plugin
+    "react/display-name": 1,
+    "react/no-danger": 1,
+    "react/no-did-mount-set-state": 1,
+    "react/no-did-update-set-state": 1,
+    "react/no-direct-mutation-state": 1,
+    "react/no-unknown-property": 1,
+    "react/prefer-es6-class": 1,
+    "react/prop-types": 1,
+    "react/sort-comp": [1, {
+      order: [
+        "propTypes",
+        "everything-else",
+        "render"
+      ]
+    }],
+
     // Disallow using variables outside the blocks they are defined (especially
     // since only let and const are used, see "no-var").
     "block-scoped-var": 2,
     // Enforce one true brace style (opening brace on the same line) and avoid
     // start and end braces on the same line.
     "brace-style": [2, "1tbs", {"allowSingleLine": false}],
     // Require camel case names
     "camelcase": 2,
--- a/devtools/client/aboutdebugging/components/aboutdebugging.js
+++ b/devtools/client/aboutdebugging/components/aboutdebugging.js
@@ -3,36 +3,43 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* eslint-env browser */
 
 "use strict";
 
 const Services = require("Services");
 
-const React = require("devtools/client/shared/vendor/react");
-const { TabMenu } = require("./tab-menu");
+const { createFactory, createClass, DOM: dom } =
+  require("devtools/client/shared/vendor/react");
 
-loader.lazyRequireGetter(this, "AddonsTab", "./components/addons-tab", true);
-loader.lazyRequireGetter(this, "WorkersTab", "./components/workers-tab", true);
+const TabMenu = createFactory(require("./tab-menu"));
+loader.lazyGetter(this, "AddonsTab",
+  () => createFactory(require("./addons-tab")));
+loader.lazyGetter(this, "WorkersTab",
+  () => createFactory(require("./workers-tab")));
 
 const Strings = Services.strings.createBundle(
   "chrome://devtools/locale/aboutdebugging.properties");
 
-const tabs = [
-  { id: "addons", name: Strings.GetStringFromName("addons"),
-    icon: "chrome://devtools/skin/images/debugging-addons.svg",
-    component: AddonsTab },
-  { id: "workers", name: Strings.GetStringFromName("workers"),
-    icon: "chrome://devtools/skin/images/debugging-workers.svg",
-    component: WorkersTab },
-];
+const tabs = [{
+  id: "addons",
+  name: Strings.GetStringFromName("addons"),
+  icon: "chrome://devtools/skin/images/debugging-addons.svg",
+  component: AddonsTab
+}, {
+  id: "workers",
+  name: Strings.GetStringFromName("workers"),
+  icon: "chrome://devtools/skin/images/debugging-workers.svg",
+  component: WorkersTab
+}];
+
 const defaultTabId = "addons";
 
-exports.AboutDebuggingApp = React.createClass({
+module.exports = createClass({
   displayName: "AboutDebuggingApp",
 
   getInitialState() {
     return {
       selectedTabId: defaultTabId
     };
   },
 
@@ -50,22 +57,21 @@ exports.AboutDebuggingApp = React.create
 
   render() {
     let { client } = this.props;
     let { selectedTabId } = this.state;
     let selectTab = this.selectTab;
 
     let selectedTab = tabs.find(t => t.id == selectedTabId);
 
-    return React.createElement(
-      "div", { className: "app"},
-        React.createElement(TabMenu, { tabs, selectedTabId, selectTab }),
-        React.createElement("div", { className: "main-content" },
-          React.createElement(selectedTab.component, { client }))
-        );
+    return dom.div({ className: "app" },
+      TabMenu({ tabs, selectedTabId, selectTab }),
+      dom.div({ className: "main-content" },
+        selectedTab.component({ client }))
+      );
   },
 
   onHashChange() {
     let tabId = window.location.hash.substr(1);
 
     let isValid = tabs.some(t => t.id == tabId);
     if (isValid) {
       this.setState({ selectedTabId: tabId });
--- a/devtools/client/aboutdebugging/components/addons-controls.js
+++ b/devtools/client/aboutdebugging/components/addons-controls.js
@@ -7,50 +7,49 @@
 "use strict";
 
 loader.lazyImporter(this, "AddonManager",
   "resource://gre/modules/AddonManager.jsm");
 
 const { Cc, Ci } = require("chrome");
 const Services = require("Services");
 
-const React = require("devtools/client/shared/vendor/react");
+const { createClass, DOM: dom } = require("devtools/client/shared/vendor/react");
 
 const Strings = Services.strings.createBundle(
   "chrome://devtools/locale/aboutdebugging.properties");
 
 const MORE_INFO_URL = "https://developer.mozilla.org/docs/Tools" +
                       "/about:debugging#Enabling_add-on_debugging";
 
-exports.AddonsControls = React.createClass({
+module.exports = createClass({
   displayName: "AddonsControls",
 
   render() {
     let { debugDisabled } = this.props;
 
-    return React.createElement(
-      "div", { className: "addons-controls" }, React.createElement(
-        "div", { className: "addons-options" },
-          React.createElement("input", {
+    return dom.div({ className: "addons-controls" },
+        dom.div({ className: "addons-options" },
+          dom.input({
             id: "enable-addon-debugging",
             type: "checkbox",
             checked: !debugDisabled,
             onChange: this.onEnableAddonDebuggingChange,
           }),
-          React.createElement("label", {
+          dom.label({
             className: "addons-debugging-label",
             htmlFor: "enable-addon-debugging",
             title: Strings.GetStringFromName("addonDebugging.tooltip")
           }, Strings.GetStringFromName("addonDebugging.label")),
           "(",
-          React.createElement("a", { href: MORE_INFO_URL, target: "_blank" },
+          dom.a({ href: MORE_INFO_URL, target: "_blank" },
             Strings.GetStringFromName("addonDebugging.moreInfo")),
           ")"
         ),
-        React.createElement("button", {
+        dom.button({
           id: "load-addon-from-file",
           onClick: this.loadAddonFromFile,
         }, Strings.GetStringFromName("loadTemporaryAddon"))
       );
   },
 
   onEnableAddonDebuggingChange(event) {
     let enabled = event.target.checked;
--- a/devtools/client/aboutdebugging/components/addons-tab.js
+++ b/devtools/client/aboutdebugging/components/addons-tab.js
@@ -1,32 +1,31 @@
 /* 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/. */
 
-/* global React */
-
 "use strict";
 
 const { AddonManager } = require("resource://gre/modules/AddonManager.jsm");
 const Services = require("Services");
 
-const React = require("devtools/client/shared/vendor/react");
-const { AddonsControls } = require("./addons-controls");
-const { TabHeader } = require("./tab-header");
-const { TargetList } = require("./target-list");
+const { createFactory, createClass, DOM: dom } =
+  require("devtools/client/shared/vendor/react");
+const AddonsControls = createFactory(require("./addons-controls"));
+const TabHeader = createFactory(require("./tab-header"));
+const TargetList = createFactory(require("./target-list"));
 
 const ExtensionIcon = "chrome://mozapps/skin/extensions/extensionGeneric.svg";
 const Strings = Services.strings.createBundle(
   "chrome://devtools/locale/aboutdebugging.properties");
 
 const CHROME_ENABLED_PREF = "devtools.chrome.enabled";
 const REMOTE_ENABLED_PREF = "devtools.debugger.remote-enabled";
 
-exports.AddonsTab = React.createClass({
+module.exports = createClass({
   displayName: "AddonsTab",
 
   getInitialState() {
     return {
       extensions: [],
       debugDisabled: false,
     };
   },
@@ -51,29 +50,27 @@ exports.AddonsTab = React.createClass({
       this.updateDebugStatus);
   },
 
   render() {
     let { client } = this.props;
     let { debugDisabled, extensions: targets } = this.state;
     let name = Strings.GetStringFromName("extensions");
 
-    return React.createElement(
-      "div", { id: "tab-addons", className: "tab", role: "tabpanel",
-        "aria-labelledby": "tab-addons-header-name" },
-        React.createElement(TabHeader, {
-          id: "tab-addons-header-name",
-          name: Strings.GetStringFromName("addons")}),
-        React.createElement(AddonsControls, { debugDisabled }),
-        React.createElement(
-          "div", { id: "addons" },
-          React.createElement(TargetList,
-            { name, targets, client, debugDisabled })
-      )
-    );
+    return dom.div({
+      id: "tab-addons",
+      className: "tab",
+      role: "tabpanel",
+      "aria-labelledby": "tab-addons-header-name" },
+    TabHeader({
+      id: "tab-addons-header-name",
+      name: Strings.GetStringFromName("addons") }),
+    AddonsControls({ debugDisabled }),
+    dom.div({ id: "addons" },
+      TargetList({ name, targets, client, debugDisabled })));
   },
 
   updateDebugStatus() {
     let debugDisabled =
       !Services.prefs.getBoolPref(CHROME_ENABLED_PREF) ||
       !Services.prefs.getBoolPref(REMOTE_ENABLED_PREF);
 
     this.setState({ debugDisabled });
--- a/devtools/client/aboutdebugging/components/tab-header.js
+++ b/devtools/client/aboutdebugging/components/tab-header.js
@@ -1,19 +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 React = require("devtools/client/shared/vendor/react");
+const { createClass, DOM: dom } =
+  require("devtools/client/shared/vendor/react");
 
-exports.TabHeader = React.createClass({
+module.exports = createClass({
   displayName: "TabHeader",
 
   render() {
     let { name, id } = this.props;
 
-    return React.createElement(
-      "div", { className: "header" }, React.createElement(
-        "h1", { id, className: "header-name" }, name));
+    return dom.div({ className: "header" },
+      dom.h1({ id, className: "header-name" }, name));
   },
 });
--- a/devtools/client/aboutdebugging/components/tab-menu-entry.js
+++ b/devtools/client/aboutdebugging/components/tab-menu-entry.js
@@ -1,30 +1,31 @@
 /* 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 React = require("devtools/client/shared/vendor/react");
+const { createClass, DOM: dom } =
+  require("devtools/client/shared/vendor/react");
 
-exports.TabMenuEntry = React.createClass({
+module.exports = createClass({
   displayName: "TabMenuEntry",
 
   render() {
     let { icon, name, selected } = this.props;
 
     // Here .category, .category-icon, .category-name classnames are used to
     // apply common styles defined.
     let className = "category" + (selected ? " selected" : "");
-    return React.createElement(
-      "div", { className, onClick: this.onClick,
-        "aria-selected": selected, role: "tab" },
-        React.createElement("img", { className: "category-icon", src: icon,
-          role: "presentation" }),
-        React.createElement("div", { className: "category-name" }, name)
-      );
+    return dom.div({
+      "aria-selected": selected,
+      className,
+      onClick: this.onClick,
+      role: "tab" },
+    dom.img({ className: "category-icon", src: icon, role: "presentation" }),
+    dom.div({ className: "category-name" }, name));
   },
 
   onClick() {
     this.props.selectTab(this.props.tabId);
   }
 });
--- a/devtools/client/aboutdebugging/components/tab-menu.js
+++ b/devtools/client/aboutdebugging/components/tab-menu.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/. */
 
-/* global React */
-
 "use strict";
 
-const React = require("devtools/client/shared/vendor/react");
-const { TabMenuEntry } = require("./tab-menu-entry");
+const { createClass, createFactory, DOM: dom } =
+  require("devtools/client/shared/vendor/react");
+const TabMenuEntry = createFactory(require("./tab-menu-entry"));
 
-exports.TabMenu = React.createClass({
+module.exports = createClass({
   displayName: "TabMenu",
 
   render() {
     let { tabs, selectedTabId, selectTab } = this.props;
     let tabLinks = tabs.map(({ id, name, icon }) => {
       let selected = id == selectedTabId;
-      return React.createElement(TabMenuEntry,
-        { tabId: id, name, icon, selected, selectTab });
+      return TabMenuEntry({ tabId: id, name, icon, selected, selectTab });
     });
 
     // "categories" id used for styling purposes
-    return React.createElement("div", { id: "categories" }, tabLinks);
+    return dom.div({ id: "categories" }, tabLinks);
   },
 });
--- a/devtools/client/aboutdebugging/components/target-list.js
+++ b/devtools/client/aboutdebugging/components/target-list.js
@@ -1,37 +1,38 @@
 /* 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/. */
 
-/* global React */
-
 "use strict";
 
 const Services = require("Services");
 
-const React = require("devtools/client/shared/vendor/react");
-const { Target } = require("./target");
+const { createClass, createFactory, DOM: dom } =
+  require("devtools/client/shared/vendor/react");
+const Target = createFactory(require("./target"));
 
 const Strings = Services.strings.createBundle(
   "chrome://devtools/locale/aboutdebugging.properties");
 
 const LocaleCompare = (a, b) => {
   return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
 };
 
-exports.TargetList = React.createClass({
+module.exports = createClass({
   displayName: "TargetList",
 
   render() {
     let { client, debugDisabled } = this.props;
     let targets = this.props.targets.sort(LocaleCompare).map(target => {
-      return React.createElement(Target, { client, target, debugDisabled });
+      return Target({ client, target, debugDisabled });
     });
+
     return (
-      React.createElement("div", { id: this.props.id, className: "targets" },
-        React.createElement("h4", null, this.props.name),
-        targets.length > 0 ? targets :
-          React.createElement("p", null, Strings.GetStringFromName("nothing"))
+      dom.div({ id: this.props.id, className: "targets" },
+        dom.h4(null, this.props.name),
+        targets.length > 0 ?
+          targets :
+          dom.p(null, Strings.GetStringFromName("nothing"))
       )
     );
   },
 });
--- a/devtools/client/aboutdebugging/components/target.js
+++ b/devtools/client/aboutdebugging/components/target.js
@@ -12,38 +12,39 @@ loader.lazyRequireGetter(this, "gDevTool
       "devtools/client/framework/devtools", true);
 loader.lazyRequireGetter(this, "Toolbox",
       "devtools/client/framework/toolbox", true);
 
 loader.lazyImporter(this, "BrowserToolboxProcess",
       "resource://devtools/client/framework/ToolboxProcess.jsm");
 
 const Services = require("Services");
-const React = require("devtools/client/shared/vendor/react");
+const { createClass, DOM: dom } =
+  require("devtools/client/shared/vendor/react");
 
 const Strings = Services.strings.createBundle(
   "chrome://devtools/locale/aboutdebugging.properties");
 
-exports.Target = React.createClass({
+module.exports = createClass({
   displayName: "Target",
 
   render() {
     let { target, debugDisabled } = this.props;
     let isServiceWorker = (target.type === "serviceworker");
     let isRunning = (!isServiceWorker || target.workerActor);
-    return React.createElement("div", { className: "target" },
-      React.createElement("img", {
+    return dom.div({ className: "target" },
+      dom.img({
         className: "target-icon",
         role: "presentation",
         src: target.icon }),
-      React.createElement("div", { className: "target-details" },
-        React.createElement("div", { className: "target-name" }, target.name)
+      dom.div({ className: "target-details" },
+        dom.div({ className: "target-name" }, target.name)
       ),
       (isRunning ?
-        React.createElement("button", {
+        dom.button({
           className: "debug-button",
           onClick: this.debug,
           disabled: debugDisabled,
         }, Strings.GetStringFromName("debug")) :
         null
       )
     );
   },
--- a/devtools/client/aboutdebugging/components/workers-tab.js
+++ b/devtools/client/aboutdebugging/components/workers-tab.js
@@ -3,25 +3,26 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { Ci } = require("chrome");
 const { Task } = require("resource://gre/modules/Task.jsm");
 const Services = require("Services");
 
-const React = require("devtools/client/shared/vendor/react");
-const { TargetList } = require("./target-list");
-const { TabHeader } = require("./tab-header");
+const { createClass, createFactory, DOM: dom } =
+  require("devtools/client/shared/vendor/react");
+const TabHeader = createFactory(require("./tab-header"));
+const TargetList = createFactory(require("./target-list"));
 
 const Strings = Services.strings.createBundle(
   "chrome://devtools/locale/aboutdebugging.properties");
 const WorkerIcon = "chrome://devtools/skin/images/debugging-workers.svg";
 
-exports.WorkersTab = React.createClass({
+module.exports = createClass({
   displayName: "WorkersTab",
 
   getInitialState() {
     return {
       workers: {
         service: [],
         shared: [],
         other: []
@@ -43,37 +44,40 @@ exports.WorkersTab = React.createClass({
     client.removeListener("serviceWorkerRegistrationListChanged", this.update);
     client.removeListener("workerListChanged", this.update);
   },
 
   render() {
     let { client } = this.props;
     let { workers } = this.state;
 
-    return React.createElement(
-      "div", { id: "tab-workers", className: "tab", role: "tabpanel",
-        "aria-labelledby": "tab-workers-header-name" },
-        React.createElement(TabHeader, {
-          id: "tab-workers-header-name",
-          name: Strings.GetStringFromName("workers")}),
-        React.createElement(
-          "div", { id: "workers", className: "inverted-icons" },
-          React.createElement(TargetList, {
-            id: "service-workers",
-            name: Strings.GetStringFromName("serviceWorkers"),
-            targets: workers.service, client }),
-          React.createElement(TargetList, {
-            id: "shared-workers",
-            name: Strings.GetStringFromName("sharedWorkers"),
-            targets: workers.shared, client }),
-          React.createElement(TargetList, {
-            id: "other-workers",
-            name: Strings.GetStringFromName("otherWorkers"),
-            targets: workers.other, client }))
-      );
+    return dom.div({
+      id: "tab-workers",
+      className: "tab",
+      role: "tabpanel",
+      "aria-labelledby": "tab-workers-header-name" },
+    TabHeader({
+      id: "tab-workers-header-name",
+      name: Strings.GetStringFromName("workers") }),
+    dom.div({ id: "workers", className: "inverted-icons" },
+      TargetList({
+        id: "service-workers",
+        name: Strings.GetStringFromName("serviceWorkers"),
+        targets: workers.service,
+        client }),
+      TargetList({
+        id: "shared-workers",
+        name: Strings.GetStringFromName("sharedWorkers"),
+        targets: workers.shared,
+        client }),
+      TargetList({
+        id: "other-workers",
+        name: Strings.GetStringFromName("otherWorkers"),
+        targets: workers.other,
+        client })));
   },
 
   update() {
     let workers = this.getInitialState().workers;
 
     this.getWorkerForms().then(forms => {
       forms.registrations.forEach(form => {
         workers.service.push({
--- a/devtools/client/aboutdebugging/initializer.js
+++ b/devtools/client/aboutdebugging/initializer.js
@@ -1,14 +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/. */
 
 /* eslint-env browser */
-/* global DebuggerClient, DebuggerServer */
 
 "use strict";
 
 const { loader } = Components.utils.import(
   "resource://devtools/shared/Loader.jsm", {});
 
 loader.lazyRequireGetter(this, "DebuggerClient",
   "devtools/shared/client/main", true);
@@ -17,39 +16,42 @@ loader.lazyRequireGetter(this, "Debugger
 loader.lazyRequireGetter(this, "Telemetry",
   "devtools/client/shared/telemetry");
 
 const { BrowserLoader } = Components.utils.import(
   "resource://devtools/client/shared/browser-loader.js", {});
 const { require } =
   BrowserLoader("resource://devtools/client/aboutdebugging/", window);
 
-const React = require("devtools/client/shared/vendor/react");
-const { AboutDebuggingApp } = require("./components/aboutdebugging");
+const {
+  createFactory,
+  render,
+  unmountComponentAtNode } = require("devtools/client/shared/vendor/react");
+const AboutDebuggingApp = createFactory(require("./components/aboutdebugging"));
 
 var AboutDebugging = {
   init() {
     if (!DebuggerServer.initialized) {
       DebuggerServer.init();
       DebuggerServer.addBrowserActors();
     }
     DebuggerServer.allowChromeProcess = true;
     this.client = new DebuggerClient(DebuggerServer.connectPipe());
 
     this.client.connect().then(() => {
       let client = this.client;
       let telemetry = new Telemetry();
 
-      let app = React.createElement(AboutDebuggingApp, { client, telemetry });
-      React.render(app, document.querySelector("#body"));
+      render(AboutDebuggingApp({ client, telemetry }),
+        document.querySelector("#body"));
     });
   },
 
   destroy() {
-    React.unmountComponentAtNode(document.querySelector("#body"));
+    unmountComponentAtNode(document.querySelector("#body"));
 
     this.client.close();
     this.client = null;
   },
 };
 
 window.addEventListener("DOMContentLoaded", function load() {
   window.removeEventListener("DOMContentLoaded", load);
--- a/devtools/client/canvasdebugger/canvasdebugger.js
+++ b/devtools/client/canvasdebugger/canvasdebugger.js
@@ -1,23 +1,23 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
-Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://devtools/client/shared/widgets/SideMenuWidget.jsm");
 Cu.import("resource://devtools/client/shared/widgets/ViewHelpers.jsm");
 Cu.import("resource://gre/modules/Console.jsm");
 
 const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 const promise = require("promise");
+const Services = require("Services");
 const EventEmitter = require("devtools/shared/event-emitter");
 const { CallWatcherFront } = require("devtools/server/actors/call-watcher");
 const { CanvasFront } = require("devtools/server/actors/canvas");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 
 const Telemetry = require("devtools/client/shared/telemetry");
 const telemetry = new Telemetry();
 
--- a/devtools/client/canvasdebugger/test/head.js
+++ b/devtools/client/canvasdebugger/test/head.js
@@ -1,25 +1,19 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
 var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
-var { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
-
-// Disable logging for all the tests. Both the debugger server and frontend will
-// be affected by this pref.
-var gEnableLogging = Services.prefs.getBoolPref("devtools.debugger.log");
-Services.prefs.setBoolPref("devtools.debugger.log", false);
-
 var { generateUUID } = Cc['@mozilla.org/uuid-generator;1'].getService(Ci.nsIUUIDGenerator);
 var { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
 var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 
+var Services = require("Services");
 var promise = require("promise");
 var { gDevTools } = require("devtools/client/framework/devtools");
 var { DebuggerClient } = require("devtools/shared/client/main");
 var { DebuggerServer } = require("devtools/server/main");
 var { CallWatcherFront } = require("devtools/server/actors/call-watcher");
 var { CanvasFront } = require("devtools/server/actors/canvas");
 var { setTimeout } = require("sdk/timers");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
@@ -38,16 +32,21 @@ const SIMPLE_BITMASKS_URL = EXAMPLE_URL 
 const SIMPLE_CANVAS_TRANSPARENT_URL = EXAMPLE_URL + "doc_simple-canvas-transparent.html";
 const SIMPLE_CANVAS_DEEP_STACK_URL = EXAMPLE_URL + "doc_simple-canvas-deep-stack.html";
 const WEBGL_ENUM_URL = EXAMPLE_URL + "doc_webgl-enum.html";
 const WEBGL_BINDINGS_URL = EXAMPLE_URL + "doc_webgl-bindings.html";
 const WEBGL_DRAW_ARRAYS = EXAMPLE_URL + "doc_webgl-drawArrays.html";
 const WEBGL_DRAW_ELEMENTS = EXAMPLE_URL + "doc_webgl-drawElements.html";
 const RAF_BEGIN_URL = EXAMPLE_URL + "doc_raf-begin.html";
 
+// Disable logging for all the tests. Both the debugger server and frontend will
+// be affected by this pref.
+var gEnableLogging = Services.prefs.getBoolPref("devtools.debugger.log");
+Services.prefs.setBoolPref("devtools.debugger.log", false);
+
 // All tests are asynchronous.
 waitForExplicitFinish();
 
 var gToolEnabled = Services.prefs.getBoolPref("devtools.canvasdebugger.enabled");
 
 DevToolsUtils.testing = true;
 
 registerCleanupFunction(() => {
--- a/devtools/client/commandline/test/browser_cmd_commands.js
+++ b/devtools/client/commandline/test/browser_cmd_commands.js
@@ -1,14 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Test various GCLI commands
 
 const TEST_URI = "data:text/html;charset=utf-8,gcli-commands";
+const HUDService = require("devtools/client/webconsole/hudservice");
 
 function test() {
   return Task.spawn(spawnTest).then(finish, helpers.handleError);
 }
 
 function* spawnTest() {
   let options = yield helpers.openTab(TEST_URI);
   yield helpers.openToolbar(options);
--- a/devtools/client/debugger/debugger-controller.js
+++ b/devtools/client/debugger/debugger-controller.js
@@ -88,17 +88,16 @@ const EVENTS = {
 // Descriptions for what a stack frame represents after the debugger pauses.
 const FRAME_TYPE = {
   NORMAL: 0,
   CONDITIONAL_BREAKPOINT_EVAL: 1,
   WATCH_EXPRESSIONS_EVAL: 2,
   PUBLIC_CLIENT_EVAL: 3
 };
 
-Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://devtools/shared/event-emitter.js");
 Cu.import("resource://devtools/client/shared/widgets/SimpleListWidget.jsm");
 Cu.import("resource://devtools/client/shared/widgets/BreadcrumbsWidget.jsm");
 Cu.import("resource://devtools/client/shared/widgets/SideMenuWidget.jsm");
 Cu.import("resource://devtools/client/shared/widgets/VariablesView.jsm");
 Cu.import("resource://devtools/client/shared/widgets/VariablesViewController.jsm");
 Cu.import("resource://devtools/client/shared/widgets/ViewHelpers.jsm");
@@ -132,16 +131,17 @@ const { bindActionCreators } = require('
 const reducers = require("./content/reducers/index");
 const { onReducerEvents } = require("./content/utils");
 
 const waitUntilService = require("devtools/client/shared/redux/middleware/wait-service");
 var services = {
   WAIT_UNTIL: waitUntilService.NAME
 };
 
+var Services = require("Services");
 var {TargetFactory} = require("devtools/client/framework/target");
 var {Toolbox} = require("devtools/client/framework/toolbox");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
 var promise = require("devtools/shared/deprecated-sync-thenables");
 var Editor = require("devtools/client/sourceeditor/editor");
 var DebuggerEditor = require("devtools/client/sourceeditor/debugger");
 var {Tooltip} = require("devtools/client/shared/widgets/Tooltip");
 var FastListWidget = require("devtools/client/shared/widgets/FastListWidget");
--- a/devtools/client/debugger/debugger.xul
+++ b/devtools/client/debugger/debugger.xul
@@ -1,15 +1,15 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!-- 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/. -->
 <?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/content/shared/widgets/widgets.css" type="text/css"?>
-<?xml-stylesheet href="debugger.css" type="text/css"?>
+<?xml-stylesheet href="chrome://devtools/content/debugger/debugger.css" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/skin/widgets.css" type="text/css"?>
 <?xml-stylesheet href="chrome://devtools/skin/debugger.css" type="text/css"?>
 <!DOCTYPE window [
   <!ENTITY % debuggerDTD SYSTEM "chrome://devtools/locale/debugger.dtd">
   %debuggerDTD;
 ]>
 <?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
 
index b77ec953125d620c5746638272be32d1bc341a90..689689ebe8a87bb743257fbe4d93512ba93f98df
GIT binary patch
literal 5577
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
NeZ{}F#{FmC{{!Z}@ag~n
index 460eaca8a287d1d45ecf18ece23d07536403df0f..8f6ec6dc16722fd3c3d07de677473636ff071fa4
GIT binary patch
literal 5578
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
I`|p4M4+?@00{{R3
index 673b31b9d1748647f72e94801e16591fecf7122b..cf17a2f80edc9102c9e2317889f10f864349c210
GIT binary patch
literal 11643
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
index 56dc98f6e01e766f60b9eeb08c5ce4a3c56d4b02..1f6f106f3c361729bd200633c783b1652101fbcd
GIT binary patch
literal 7340
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
Kr>&bmX#5W^SdL@>
index 16991f7a06aeaf2bb32a045d7c925f1d41372d9e..56b38761d211ae7ee09f1773a207e8ba83b60d30
GIT binary patch
literal 7224
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
M2Y#1S;eXKhFHsyk2mk;8
--- a/devtools/client/definitions.js
+++ b/devtools/client/definitions.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 {Cc, Ci} = require("chrome");
-
-const { Services } = require("resource://gre/modules/Services.jsm");
+const Services = require("Services");
 
 loader.lazyGetter(this, "osString", () => Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS);
 
 // Panels
 loader.lazyGetter(this, "OptionsPanel", () => require("devtools/client/framework/toolbox-options").OptionsPanel);
 loader.lazyGetter(this, "InspectorPanel", () => require("devtools/client/inspector/inspector-panel").InspectorPanel);
 loader.lazyGetter(this, "WebConsolePanel", () => require("devtools/client/webconsole/panel").WebConsolePanel);
 loader.lazyGetter(this, "DebuggerPanel", () => require("devtools/client/debugger/panel").DebuggerPanel);
--- a/devtools/client/eyedropper/eyedropper.js
+++ b/devtools/client/eyedropper/eyedropper.js
@@ -2,20 +2,19 @@
  * 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} = require("chrome");
 const {rgbToHsl} = require("devtools/shared/css-color").colorUtils;
 const Telemetry = require("devtools/client/shared/telemetry");
 const {EventEmitter} = Cu.import("resource://devtools/shared/event-emitter.js");
 const promise = require("promise");
+const Services = require("Services");
 const {setTimeout, clearTimeout} = Cu.import("resource://gre/modules/Timer.jsm", {});
 
-Cu.import("resource://gre/modules/Services.jsm");
-
 loader.lazyGetter(this, "clipboardHelper", function() {
   return Cc["@mozilla.org/widget/clipboardhelper;1"]
     .getService(Ci.nsIClipboardHelper);
 });
 
 loader.lazyGetter(this, "ssService", function() {
   return Cc["@mozilla.org/content/style-sheet-service;1"]
     .getService(Ci.nsIStyleSheetService);
--- a/devtools/client/framework/ToolboxProcess.jsm
+++ b/devtools/client/framework/ToolboxProcess.jsm
@@ -6,27 +6,27 @@
 
 "use strict";
 
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 const DBG_XUL = "chrome://devtools/content/framework/toolbox-process-window.xul";
 const CHROME_DEBUGGER_PROFILE_NAME = "chrome_debugger_profile";
 
-Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm")
 const { require, DevToolsLoader } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 
 XPCOMUtils.defineLazyGetter(this, "Telemetry", function () {
   return require("devtools/client/shared/telemetry");
 });
 XPCOMUtils.defineLazyGetter(this, "EventEmitter", function () {
   return require("devtools/shared/event-emitter");
 });
 const promise = require("promise");
+const Services = require("Services");
 
 this.EXPORTED_SYMBOLS = ["BrowserToolboxProcess"];
 
 var processes = new Set();
 
 /**
  * Constructor for creating a process that will hold a chrome toolbox.
  *
--- a/devtools/client/framework/attach-thread.js
+++ b/devtools/client/framework/attach-thread.js
@@ -1,16 +1,16 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* 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} = require("chrome");
-const Services = Cu.import("resource://gre/modules/Services.jsm", {}).Services;
+const Services = require("Services");
 const promise = require("promise");
 
 function l10n(name) {
   const bundle = Services.strings.createBundle("chrome://devtools/locale/toolbox.properties");
   try {
     return bundle.GetStringFromName(name);
   } catch (e) {
     throw new Error("Failed loading l10n string: " + name);
--- a/devtools/client/framework/connect/connect.js
+++ b/devtools/client/framework/connect/connect.js
@@ -3,19 +3,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";
 
 var Cu = Components.utils;
 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
-Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 var {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
+var Services = require("Services");
 var {gDevTools} = require("devtools/client/framework/devtools");
 var {TargetFactory} = require("devtools/client/framework/target");
 var {Toolbox} = require("devtools/client/framework/toolbox")
 var promise = require("promise");
 var {DebuggerClient} = require("devtools/shared/client/main");
 
 var gClient;
 var gConnectionTimeout;
--- a/devtools/client/framework/sidebar.js
+++ b/devtools/client/framework/sidebar.js
@@ -2,19 +2,19 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* 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");
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 
+var Services = require("Services");
 var EventEmitter = require("devtools/shared/event-emitter");
 var Telemetry = require("devtools/client/shared/telemetry");
 
 const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
 /**
  * ToolSidebar provides methods to register tabs in the sidebar.
  * It's assumed that the sidebar contains a xul:tabbox.
--- a/devtools/client/framework/test/shared-head.js
+++ b/devtools/client/framework/test/shared-head.js
@@ -12,25 +12,25 @@
 var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr, Constructor: CC} = Components;
 
 function scopedCuImport(path) {
   const scope = {};
   Cu.import(path, scope);
   return scope;
 }
 
-const {Services} = scopedCuImport("resource://gre/modules/Services.jsm");
 const {console} = scopedCuImport("resource://gre/modules/Console.jsm");
 const {ScratchpadManager} = scopedCuImport("resource://devtools/client/scratchpad/scratchpad-manager.jsm");
 const {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");
 let promise = require("promise");
+const Services = require("Services");
 
 const TEST_DIR = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
 const CHROME_URL_ROOT = TEST_DIR + "/";
 const URL_ROOT = CHROME_URL_ROOT.replace("chrome://mochitests/content/",
                                          "http://example.com/");
 const URL_ROOT_SSL = CHROME_URL_ROOT.replace("chrome://mochitests/content/",
                                              "https://example.com/");
 
--- a/devtools/client/framework/toolbox-hosts.js
+++ b/devtools/client/framework/toolbox-hosts.js
@@ -4,17 +4,17 @@
  * 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 {Cu} = require("chrome");
 const EventEmitter = require("devtools/shared/event-emitter");
 const promise = require("promise");
-Cu.import("resource://gre/modules/Services.jsm");
+const Services = require("Services");
 Cu.import("resource://devtools/client/shared/DOMHelpers.jsm");
 
 loader.lazyRequireGetter(this, "system", "devtools/shared/system");
 
 /* A host should always allow this much space for the page to be displayed.
  * There is also a min-height on the browser, but we still don't want to set
  * frame.height to be larger than that, since it can cause problems with
  * resizing the toolbox and panel layout. */
--- a/devtools/client/framework/toolbox-process-window.js
+++ b/devtools/client/framework/toolbox-process-window.js
@@ -11,17 +11,17 @@ var { classes: Cc, interfaces: Ci, utils
 var { loader, require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 // Require this module just to setup things like themes and tools
 // devtools-browser is special as it loads main module
 // To be cleaned up in bug 1247203.
 require("devtools/client/framework/devtools-browser");
 var { gDevTools } = require("devtools/client/framework/devtools");
 var { TargetFactory } = require("devtools/client/framework/target");
 var { Toolbox } = require("devtools/client/framework/toolbox");
-var { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
+var Services = require("Services");
 var { DebuggerClient } = require("devtools/shared/client/main");
 var { ViewHelpers } =
   Cu.import("resource://devtools/client/shared/widgets/ViewHelpers.jsm", {});
 var { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
 
 /**
  * Shortcuts for accessing various debugger preferences.
  */
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -13,24 +13,24 @@ const SPLITCONSOLE_HEIGHT_PREF = "devtoo
 const MIN_ZOOM = 0.5;
 const MAX_ZOOM = 2;
 const OS_HISTOGRAM = "DEVTOOLS_OS_ENUMERATED_PER_USER";
 const OS_IS_64_BITS = "DEVTOOLS_OS_IS_64_BITS_PER_USER";
 const SCREENSIZE_HISTOGRAM = "DEVTOOLS_SCREEN_RESOLUTION_ENUMERATED_PER_USER";
 
 var {Cc, Ci, Cu} = require("chrome");
 var promise = require("promise");
+var Services = require("Services");
 var {gDevTools} = require("devtools/client/framework/devtools");
 var EventEmitter = require("devtools/shared/event-emitter");
 var Telemetry = require("devtools/client/shared/telemetry");
 var HUDService = require("devtools/client/webconsole/hudservice");
 var viewSource = require("devtools/client/shared/view-source");
 var { attachThread, detachThread } = require("./attach-thread");
 
-Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://devtools/client/scratchpad/scratchpad-manager.jsm");
 Cu.import("resource://devtools/client/shared/DOMHelpers.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 
 loader.lazyGetter(this, "toolboxStrings", () => {
   const properties = "chrome://devtools/locale/toolbox.properties";
   const bundle = Services.strings.createBundle(properties);
   return (name, ...args) => {
@@ -558,21 +558,16 @@ Toolbox.prototype = {
     prevKey.addEventListener("command", this.selectPreviousTool.bind(this), true);
 
     let minimizeKey = this.doc.getElementById("toolbox-minimize-key");
     minimizeKey.addEventListener("command", this._toggleMinimizeMode, true);
 
     let toggleKey = this.doc.getElementById("toolbox-toggle-host-key");
     toggleKey.addEventListener("command", this.switchToPreviousHost.bind(this), true);
 
-    if (Services.prefs.prefHasUserValue("devtools.loader.srcdir")) {
-      let reloadKey = this.doc.getElementById("tools-reload-key");
-      reloadKey.addEventListener("command", this.reload.bind(this), true);
-    }
-
     // Split console uses keypress instead of command so the event can be
     // cancelled with stopPropagation on the keypress, and not preventDefault.
     this.doc.addEventListener("keypress", this._splitConsoleOnKeypress, false);
 
     this.doc.addEventListener("focus", this._onFocus, true);
   },
 
   _registerOverlays: function() {
@@ -1697,21 +1692,16 @@ Toolbox.prototype = {
     }
 
     // clean up the toolbox if its window is closed
     let newHost = new Hosts[hostType](this.target.tab, options);
     newHost.on("window-closed", this.destroy);
     return newHost;
   },
 
-  reload: function () {
-    const {devtools} = Cu.import("resource://devtools/shared/Loader.jsm", {});
-    devtools.reload(true);
-  },
-
   /**
    * Switch to the last used host for the toolbox UI.
    * This is determined by the devtools.toolbox.previousHost pref.
    */
   switchToPreviousHost: function() {
     let hostType = Services.prefs.getCharPref(this._prefs.PREVIOUS_HOST);
 
     // Handle the case where the previous host happens to match the current
--- a/devtools/client/framework/toolbox.xul
+++ b/devtools/client/framework/toolbox.xul
@@ -89,20 +89,16 @@
     <key id="toolbox-minimize-key"
          key="&toolboxToggleMinimize.key;"
          oncommand="void(0);"
          modifiers="shift, accel"/>
     <key id="toolbox-toggle-host-key"
          key="&toolboxToggle.key;"
          oncommand="void(0);"
          modifiers="accel shift"/>
-    <key id="tools-reload-key"
-         key="&toolboxReload.key;"
-         oncommand="void(0);"
-         modifiers="accel alt"/>
   </keyset>
 
   <popupset>
     <menupopup id="toolbox-textbox-context-popup">
       <menuitem id="cMenu_undo"/>
       <menuseparator/>
       <menuitem id="cMenu_cut"/>
       <menuitem id="cMenu_copy"/>
--- a/devtools/client/inspector/breadcrumbs.js
+++ b/devtools/client/inspector/breadcrumbs.js
@@ -3,17 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {Cu, Ci} = require("chrome");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
+const Services = require("Services");
 const promise = require("promise");
 
 const ENSURE_SELECTION_VISIBLE_DELAY = 50; // ms
 const ELLIPSIS = Services.prefs.getComplexValue("intl.ellipsis", Ci.nsIPrefLocalizedString).data;
 const MAX_LABEL_LENGTH = 40;
 const LOW_PRIORITY_ELEMENTS = {
   "HEAD": true,
   "BASE": true,
--- a/devtools/client/inspector/computed/computed.js
+++ b/devtools/client/inspector/computed/computed.js
@@ -9,28 +9,28 @@
 "use strict";
 
 const {Cc, Ci, Cu} = require("chrome");
 
 const ToolDefinitions = require("devtools/client/main").Tools;
 const {CssLogic} = require("devtools/shared/inspector/css-logic");
 const {ELEMENT_STYLE} = require("devtools/server/actors/styles");
 const promise = require("promise");
+const Services = require("Services");
 const {setTimeout, clearTimeout} = Cu.import("resource://gre/modules/Timer.jsm", {});
 const {OutputParser} = require("devtools/client/shared/output-parser");
 const {PrefObserver, PREF_ORIG_SOURCES} = require("devtools/client/styleeditor/utils");
 const {createChild} = require("devtools/client/inspector/shared/utils");
 const {gDevTools} = require("devtools/client/framework/devtools");
 
 loader.lazyRequireGetter(this, "overlays",
   "devtools/client/inspector/shared/style-inspector-overlays");
 loader.lazyRequireGetter(this, "StyleInspectorMenu",
   "devtools/client/inspector/shared/style-inspector-menu");
 
-Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
                                   "resource://gre/modules/PluralForm.jsm");
 
 XPCOMUtils.defineLazyGetter(CssComputedView, "_strings", function() {
   return Services.strings.createBundle(
     "chrome://devtools-shared/locale/styleinspector.properties");
--- a/devtools/client/inspector/inspector-panel.js
+++ b/devtools/client/inspector/inspector-panel.js
@@ -3,18 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {Cc, Ci, Cu} = require("chrome");
 
-Cu.import("resource://gre/modules/Services.jsm");
-
+var Services = require("Services");
 var promise = require("promise");
 var EventEmitter = require("devtools/shared/event-emitter");
 var clipboard = require("sdk/clipboard");
 var {HostType} = require("devtools/client/framework/toolbox").Toolbox;
 
 loader.lazyRequireGetter(this, "CSS", "CSS");
 
 loader.lazyGetter(this, "MarkupView", () => require("devtools/client/inspector/markup/markup").MarkupView);
--- a/devtools/client/inspector/markup/html-editor.js
+++ b/devtools/client/inspector/markup/html-editor.js
@@ -1,17 +1,17 @@
 /* vim:set ts=2 sw=2 sts=2 et tw=80:
  * 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 {Cu} = require("chrome");
 const Editor = require("devtools/client/sourceeditor/editor");
-Cu.import("resource://gre/modules/Services.jsm");
+const Services = require("Services");
 Cu.import("resource://devtools/shared/event-emitter.js");
 
 /**
  * A wrapper around the Editor component, that allows editing of HTML.
  *
  * The main functionality this provides around the Editor is the ability
  * to show/hide/position an editor inplace. It only appends once to the
  * body, and uses CSS to position the editor.  The reason it is done this
--- a/devtools/client/inspector/markup/markup.js
+++ b/devtools/client/inspector/markup/markup.js
@@ -37,17 +37,16 @@ const {parseAttribute} =
       require("devtools/client/shared/node-attribute-parser");
 const ELLIPSIS = Services.prefs.getComplexValue("intl.ellipsis",
       Ci.nsIPrefLocalizedString).data;
 const {Task} = require("resource://gre/modules/Task.jsm");
 const {scrollIntoViewIfNeeded} = require("devtools/shared/layout/utils");
 const {PrefObserver} = require("devtools/client/styleeditor/utils");
 
 Cu.import("resource://devtools/shared/gcli/Templater.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 loader.lazyRequireGetter(this, "CSS", "CSS");
 loader.lazyGetter(this, "DOMParser", () => {
   return Cc["@mozilla.org/xmlextras/domparser;1"]
     .createInstance(Ci.nsIDOMParser);
 });
 loader.lazyGetter(this, "AutocompletePopup", () => {
--- a/devtools/client/inspector/rules/models/element-style.js
+++ b/devtools/client/inspector/rules/models/element-style.js
@@ -8,18 +8,16 @@
 
 const {Cc, Ci, Cu} = require("chrome");
 const promise = require("promise");
 const {Rule} = require("devtools/client/inspector/rules/models/rule");
 const {promiseWarn} = require("devtools/client/inspector/shared/utils");
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
-loader.lazyImporter(this, "Services", "resource://gre/modules/Services.jsm");
-
 loader.lazyGetter(this, "PSEUDO_ELEMENTS", () => {
   return domUtils.getCSSPseudoElementNames();
 });
 
 XPCOMUtils.defineLazyGetter(this, "domUtils", function() {
   return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
 });
 
--- a/devtools/client/inspector/rules/rules.js
+++ b/devtools/client/inspector/rules/rules.js
@@ -4,16 +4,17 @@
  * 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/. */
 /* globals gDevTools */
 
 "use strict";
 
 const {Cc, Ci, Cu} = require("chrome");
 const promise = require("promise");
+const Services = require("Services");
 const {Tools} = require("devtools/client/main");
 const {setTimeout, clearTimeout} =
       Cu.import("resource://gre/modules/Timer.jsm", {});
 const {CssLogic} = require("devtools/shared/inspector/css-logic");
 const {ELEMENT_STYLE} = require("devtools/server/actors/styles");
 const {OutputParser} = require("devtools/client/shared/output-parser");
 const {PrefObserver, PREF_ORIG_SOURCES} =
       require("devtools/client/styleeditor/utils");
@@ -29,17 +30,16 @@ const {gDevTools} = require("devtools/cl
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 loader.lazyRequireGetter(this, "overlays",
   "devtools/client/inspector/shared/style-inspector-overlays");
 loader.lazyRequireGetter(this, "EventEmitter",
   "devtools/shared/event-emitter");
 loader.lazyRequireGetter(this, "StyleInspectorMenu",
   "devtools/client/inspector/shared/style-inspector-menu");
-loader.lazyImporter(this, "Services", "resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "clipboardHelper", function() {
   return Cc["@mozilla.org/widget/clipboardhelper;1"]
     .getService(Ci.nsIClipboardHelper);
 });
 
 XPCOMUtils.defineLazyGetter(this, "_strings", function() {
   return Services.strings.createBundle(
--- a/devtools/client/inspector/rules/test/browser.ini
+++ b/devtools/client/inspector/rules/test/browser.ini
@@ -9,16 +9,18 @@ support-files =
   doc_content_stylesheet_linked.css
   doc_content_stylesheet_script.css
   doc_copystyles.css
   doc_copystyles.html
   doc_cssom.html
   doc_custom.html
   doc_filter.html
   doc_frame_script.js
+  doc_invalid_sourcemap.css
+  doc_invalid_sourcemap.html
   doc_keyframeanimation.css
   doc_keyframeanimation.html
   doc_keyframeLineNumbers.html
   doc_media_queries.html
   doc_pseudoelement.html
   doc_ruleLineNumbers.html
   doc_sourcemaps.css
   doc_sourcemaps.css.map
@@ -114,16 +116,17 @@ skip-if = os == "mac" # Bug 1245996 : cl
 [browser_rules_filtereditor-appears-on-swatch-click.js]
 [browser_rules_filtereditor-commit-on-ENTER.js]
 [browser_rules_filtereditor-revert-on-ESC.js]
 skip-if = (os == "win" && debug) # bug 963492: win.
 [browser_rules_guessIndentation.js]
 [browser_rules_inherited-properties_01.js]
 [browser_rules_inherited-properties_02.js]
 [browser_rules_inherited-properties_03.js]
+[browser_rules_invalid-source-map.js]
 [browser_rules_keybindings.js]
 [browser_rules_keyframes-rule_01.js]
 [browser_rules_keyframes-rule_02.js]
 [browser_rules_keyframeLineNumbers.js]
 [browser_rules_lineNumbers.js]
 [browser_rules_livepreview.js]
 [browser_rules_mark_overridden_01.js]
 [browser_rules_mark_overridden_02.js]
--- a/devtools/client/inspector/rules/test/browser_rules_colorpicker-hides-on-tooltip.js
+++ b/devtools/client/inspector/rules/test/browser_rules_colorpicker-hides-on-tooltip.js
@@ -20,29 +20,28 @@ const TEST_URI = `
 
 add_task(function*() {
   yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
   let {view} = yield openRuleView();
 
   let swatch = getRuleViewProperty(view, "body", "color").valueSpan
     .querySelector(".ruleview-colorswatch");
 
-  yield testColorPickerHidesWhenImageTooltipAppears(view, swatch);
-});
-
-function* testColorPickerHidesWhenImageTooltipAppears(view, swatch) {
   let bgImageSpan = getRuleViewProperty(view, "body", "background-image")
     .valueSpan;
   let uriSpan = bgImageSpan.querySelector(".theme-link");
   let tooltip = view.tooltips.colorPicker.tooltip;
 
   info("Showing the color picker tooltip by clicking on the color swatch");
   let onShown = tooltip.once("shown");
   swatch.click();
   yield onShown;
 
   info("Now showing the image preview tooltip to hide the color picker");
   let onHidden = tooltip.once("hidden");
+  // Hiding the color picker refreshes the value.
+  let onRuleViewChanged = view.once("ruleview-changed");
   yield assertHoverTooltipOn(view.tooltips.previewTooltip, uriSpan);
   yield onHidden;
+  yield onRuleViewChanged;
 
   ok(true, "The color picker closed when the image preview tooltip appeared");
-}
+});
--- a/devtools/client/inspector/rules/test/browser_rules_completion-existing-property_02.js
+++ b/devtools/client/inspector/rules/test/browser_rules_completion-existing-property_02.js
@@ -8,37 +8,38 @@
 // correctly when editing existing properties in the rule view.
 
 // format :
 //  [
 //    what key to press,
 //    modifers,
 //    expected input box value after keypress,
 //    selectedIndex of the popup,
-//    total items in the popup
+//    total items in the popup,
+//    expect ruleview-changed
 //  ]
 var testData = [
-  ["b", {}, "beige", 0, 8],
-  ["l", {}, "black", 0, 4],
-  ["VK_DOWN", {}, "blanchedalmond", 1, 4],
-  ["VK_DOWN", {}, "blue", 2, 4],
-  ["VK_RIGHT", {}, "blue", -1, 0],
-  [" ", {}, "blue !important", 0, 10],
-  ["!", {}, "blue !important", 0, 0],
-  ["VK_BACK_SPACE", {}, "blue !", -1, 0],
-  ["VK_BACK_SPACE", {}, "blue ", -1, 0],
-  ["VK_BACK_SPACE", {}, "blue", -1, 0],
-  ["VK_TAB", {shiftKey: true}, "color", -1, 0],
-  ["VK_BACK_SPACE", {}, "", -1, 0],
-  ["d", {}, "direction", 0, 3],
-  ["i", {}, "direction", 0, 2],
-  ["s", {}, "display", -1, 0],
-  ["VK_TAB", {}, "blue", -1, 0],
-  ["n", {}, "none", -1, 0],
-  ["VK_RETURN", {}, null, -1, 0]
+  ["b", {}, "beige", 0, 8, true],
+  ["l", {}, "black", 0, 4, true],
+  ["VK_DOWN", {}, "blanchedalmond", 1, 4, true],
+  ["VK_DOWN", {}, "blue", 2, 4, true],
+  ["VK_RIGHT", {}, "blue", -1, 0, false],
+  [" ", {}, "blue !important", 0, 10, true],
+  ["!", {}, "blue !important", 0, 0, true],
+  ["VK_BACK_SPACE", {}, "blue !", -1, 0, true],
+  ["VK_BACK_SPACE", {}, "blue ", -1, 0, true],
+  ["VK_BACK_SPACE", {}, "blue", -1, 0, true],
+  ["VK_TAB", {shiftKey: true}, "color", -1, 0, true],
+  ["VK_BACK_SPACE", {}, "", -1, 0, false],
+  ["d", {}, "direction", 0, 3, false],
+  ["i", {}, "direction", 0, 2, false],
+  ["s", {}, "display", -1, 0, false],
+  ["VK_TAB", {}, "blue", -1, 0, true],
+  ["n", {}, "none", -1, 0, true],
+  ["VK_RETURN", {}, null, -1, 0, true]
 ];
 
 const TEST_URI = "<h1 style='color: red'>Header</h1>";
 
 add_task(function*() {
   yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
   let {toolbox, inspector, view, testActor} = yield openRuleView();
 
@@ -49,53 +50,53 @@ add_task(function*() {
   yield reloadPage(inspector, testActor);
   yield runAutocompletionTest(toolbox, inspector, view);
 });
 
 function* runAutocompletionTest(toolbox, inspector, view) {
   info("Selecting the test node");
   yield selectNode("h1", inspector);
 
+  let rule = getRuleViewRuleEditor(view, 0).rule;
+  let prop = rule.textProps[0];
+
   info("Focusing the css property editable value");
-  let value = view.styleDocument.querySelectorAll(".ruleview-propertyvalue")[0];
-  let editor = yield focusEditableField(view, value);
+  let editor = yield focusEditableField(view, prop.editor.valueSpan);
 
   info("Starting to test for css property completion");
   for (let i = 0; i < testData.length; i++) {
     // Re-define the editor at each iteration, because the focus may have moved
     // from property to value and back
     editor = inplaceEditor(view.styleDocument.activeElement);
     yield testCompletion(testData[i], editor, view);
   }
 }
 
-function* testCompletion([key, modifiers, completion, index, total], editor,
-    view) {
+function* testCompletion([key, modifiers, completion, index, total, willChange],
+                         editor, view) {
   info("Pressing key " + key);
   info("Expecting " + completion + ", " + index + ", " + total);
 
-  let onKeyPress;
-
-  if (/tab/ig.test(key)) {
-    info("Waiting for the new property or value editor to get focused");
-    let brace = view.styleDocument.querySelector(".ruleview-ruleclose");
-    onKeyPress = once(brace.parentNode, "focus", true);
-  } else if (/(right|return|back_space)/ig.test(key)) {
-    info("Adding event listener for right|return|back_space keys");
-    onKeyPress = once(editor.input, "keypress");
+  let onDone;
+  if (willChange) {
+    // If the key triggers a ruleview-changed, wait for that event, it will
+    // always be the last to be triggered and tells us when the preview has
+    // been done.
+    onDone = view.once("ruleview-changed");
   } else {
-    info("Waiting for after-suggest event on the editor");
-    onKeyPress = editor.once("after-suggest");
+    // Otherwise, expect an after-suggest event (except if the popup gets
+    // closed).
+    onDone = key !== "VK_RIGHT" && key !== "VK_BACK_SPACE"
+             ? editor.once("after-suggest")
+             : null;
   }
 
   info("Synthesizing key " + key + ", modifiers: " + Object.keys(modifiers));
   EventUtils.synthesizeKey(key, modifiers, view.styleWindow);
-
-  yield onKeyPress;
-  yield waitForTick();
+  yield onDone;
 
   // The key might have been a TAB or shift-TAB, in which case the editor will
   // be a new one
   editor = inplaceEditor(view.styleDocument.activeElement);
 
   info("Checking the state");
   if (completion != null) {
     is(editor.input.value, completion, "Correct value is autocompleted");
--- a/devtools/client/inspector/rules/test/browser_rules_completion-new-property_02.js
+++ b/devtools/client/inspector/rules/test/browser_rules_completion-new-property_02.js
@@ -8,40 +8,40 @@
 // correctly when editing new properties in the rule view.
 
 // format :
 //  [
 //    what key to press,
 //    modifers,
 //    expected input box value after keypress,
 //    selectedIndex of the popup,
-//    total items in the popup
+//    total items in the popup,
+//    expect ruleview-changed
 //  ]
 var testData = [
-  ["a", {accelKey: true, ctrlKey: true}, "", -1, 0],
-  ["d", {}, "direction", 0, 3],
-  ["VK_DOWN", {}, "display", 1, 3],
-  ["VK_TAB", {}, "", -1, 10],
-  ["VK_DOWN", {}, "-moz-box", 0, 10],
-  ["n", {}, "none", -1, 0],
-  ["VK_TAB", {shiftKey: true}, "display", -1, 0],
-  ["VK_BACK_SPACE", {}, "", -1, 0],
-  ["c", {}, "caption-side", 0, 10],
-  ["o", {}, "color", 0, 7],
-  ["VK_TAB", {}, "none", -1, 0],
-  ["r", {}, "rebeccapurple", 0, 6],
-  ["VK_DOWN", {}, "red", 1, 6],
-  ["VK_DOWN", {}, "rgb", 2, 6],
-  ["VK_DOWN", {}, "rgba", 3, 6],
-  ["VK_DOWN", {}, "rosybrown", 4, 6],
-  ["VK_DOWN", {}, "royalblue", 5, 6],
-  ["VK_RIGHT", {}, "royalblue", -1, 0],
-  [" ", {}, "royalblue !important", 0, 10],
-  ["!", {}, "royalblue !important", 0, 0],
-  ["VK_ESCAPE", {}, null, -1, 0]
+  ["d", {}, "direction", 0, 3, false],
+  ["VK_DOWN", {}, "display", 1, 3, false],
+  ["VK_TAB", {}, "", -1, 10, true],
+  ["VK_DOWN", {}, "-moz-box", 0, 10, true],
+  ["n", {}, "none", -1, 0, true],
+  ["VK_TAB", {shiftKey: true}, "display", -1, 0, true],
+  ["VK_BACK_SPACE", {}, "", -1, 0, false],
+  ["c", {}, "caption-side", 0, 10, false],
+  ["o", {}, "color", 0, 7, false],
+  ["VK_TAB", {}, "none", -1, 0, true],
+  ["r", {}, "rebeccapurple", 0, 6, true],
+  ["VK_DOWN", {}, "red", 1, 6, true],
+  ["VK_DOWN", {}, "rgb", 2, 6, true],
+  ["VK_DOWN", {}, "rgba", 3, 6, true],
+  ["VK_DOWN", {}, "rosybrown", 4, 6, true],
+  ["VK_DOWN", {}, "royalblue", 5, 6, true],
+  ["VK_RIGHT", {}, "royalblue", -1, 0, false],
+  [" ", {}, "royalblue !important", 0, 10, true],
+  ["!", {}, "royalblue !important", 0, 0, true],
+  ["VK_ESCAPE", {}, null, -1, 0, true]
 ];
 
 const TEST_URI = `
   <style type="text/css">
     h1 {
       border: 1px solid red;
     }
   </style>
@@ -72,41 +72,38 @@ function* runAutocompletionTest(toolbox,
   for (let i = 0; i < testData.length; i++) {
     // Re-define the editor at each iteration, because the focus may have moved
     // from property to value and back
     editor = inplaceEditor(view.styleDocument.activeElement);
     yield testCompletion(testData[i], editor, view);
   }
 }
 
-function* testCompletion([key, modifiers, completion, index, total], editor,
-    view) {
+function* testCompletion([key, modifiers, completion, index, total, willChange],
+                         editor, view) {
   info("Pressing key " + key);
   info("Expecting " + completion + ", " + index + ", " + total);
 
-  let onKeyPress;
-
-  if (/tab/ig.test(key)) {
-    info("Waiting for the new property or value editor to get focused");
-    let brace = view.styleDocument.querySelectorAll(".ruleview-ruleclose")[1];
-    onKeyPress = once(brace.parentNode, "focus", true);
-  } else if (/(right|back_space|escape|return)/ig.test(key) ||
-             (modifiers.accelKey || modifiers.ctrlKey)) {
-    info("Adding event listener for right|escape|back_space|return keys");
-    onKeyPress = once(editor.input, "keypress");
+  let onDone;
+  if (willChange) {
+    // If the key triggers a ruleview-changed, wait for that event, it will
+    // always be the last to be triggered and tells us when the preview has
+    // been done.
+    onDone = view.once("ruleview-changed");
   } else {
-    info("Waiting for after-suggest event on the editor");
-    onKeyPress = editor.once("after-suggest");
+    // Otherwise, expect an after-suggest event (except if the popup gets
+    // closed).
+    onDone = key !== "VK_RIGHT" && key !== "VK_BACK_SPACE"
+             ? editor.once("after-suggest")
+             : null;
   }
 
   info("Synthesizing key " + key + ", modifiers: " + Object.keys(modifiers));
   EventUtils.synthesizeKey(key, modifiers, view.styleWindow);
-
-  yield onKeyPress;
-  yield waitForTick();
+  yield onDone;
 
   info("Checking the state");
   if (completion != null) {
     // The key might have been a TAB or shift-TAB, in which case the editor will
     // be a new one
     editor = inplaceEditor(view.styleDocument.activeElement);
     is(editor.input.value, completion, "Correct value is autocompleted");
   }
--- a/devtools/client/inspector/rules/test/browser_rules_completion-new-property_04.js
+++ b/devtools/client/inspector/rules/test/browser_rules_completion-new-property_04.js
@@ -5,28 +5,28 @@
 "use strict";
 
 // Test that a new property editor supports the following flow:
 // - type first character of property name
 // - select an autocomplete suggestion !!with a mouse click!!
 // - press RETURN to move to the property value
 // - blur the input to commit
 
-const TEST_URI = "<style>.title {margin: 0;}</style>" +
-  "<h1 class=title style='color: red'>Header</h1>";
+const TEST_URI = "<style>.title {color: red;}</style>" +
+                 "<h1 class=title>Header</h1>";
 
 add_task(function*() {
   yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
   let { inspector, view} = yield openRuleView();
 
   info("Selecting the test node");
   yield selectNode("h1", inspector);
 
   info("Focusing the new property editable field");
-  let ruleEditor = getRuleViewRuleEditor(view, 0);
+  let ruleEditor = getRuleViewRuleEditor(view, 1);
   let editor = yield focusNewRuleViewProperty(ruleEditor);
 
   info("Sending \"background\" to the editable field.");
   for (let key of "background") {
     let onSuggest = editor.once("after-suggest");
     EventUtils.synthesizeKey(key, {}, view.styleWindow);
     yield onSuggest;
   }
@@ -40,28 +40,27 @@ add_task(function*() {
   info("Select the background-color suggestion with a mouse click.");
   let onInputFocus = once(editor.input, "focus", true);
   let node = editor.popup._list.childNodes[itemIndex];
   EventUtils.synthesizeMouseAtCenter(node, {}, view.styleWindow);
   yield onInputFocus;
   is(editor.input.value, "background-color", "Correct value is autocompleted");
 
   info("Press RETURN to move the focus to a property value editor.");
-  let onModifications = view.once("ruleview-changed");
+  let onModifications = waitForNEvents(view, "ruleview-changed", 2);
   EventUtils.synthesizeKey("VK_RETURN", {}, view.styleWindow);
   yield onModifications;
 
   // Getting the new value editor after focus
-  let elementRuleEditor = getRuleViewRuleEditor(view, 0);
   editor = inplaceEditor(view.styleDocument.activeElement);
-  let textProp = elementRuleEditor.rule.textProps[1];
+  let textProp = ruleEditor.rule.textProps[1];
 
-  is(elementRuleEditor.rule.textProps.length, 2,
+  is(ruleEditor.rule.textProps.length, 2,
     "Created a new text property.");
-  is(elementRuleEditor.propertyList.children.length, 2,
+  is(ruleEditor.propertyList.children.length, 2,
     "Created a property editor.");
   is(editor, inplaceEditor(textProp.editor.valueSpan),
     "Editing the value span now.");
 
   info("Entering a value and blurring the field to expect a rule change");
   editor.input.value = "#F00";
 
   onModifications = view.once("ruleview-changed");
--- a/devtools/client/inspector/rules/test/browser_rules_edit-property-commit.js
+++ b/devtools/client/inspector/rules/test/browser_rules_edit-property-commit.js
@@ -67,25 +67,23 @@ function* runTestData(view, {value, comm
 
   let editor = yield focusEditableField(view, propEditor.valueSpan);
   is(inplaceEditor(propEditor.valueSpan), editor,
     "Focused editor should be the value span.");
 
   info("Entering test data " + value);
   let onRuleViewChanged = view.once("ruleview-changed");
   EventUtils.sendString(value, view.styleWindow);
-
-  info("Waiting for focus on the field");
-  let onBlur = once(editor.input, "blur");
+  yield onRuleViewChanged;
 
   info("Entering the commit key " + commitKey + " " + modifiers);
+  onRuleViewChanged = view.once("ruleview-changed");
+  let onBlur = once(editor.input, "blur");
   EventUtils.synthesizeKey(commitKey, modifiers);
   yield onBlur;
-  // No matter if we escape or commit the change, the preview throttle is going
-  // to update the property value
   yield onRuleViewChanged;
 
   if (commitKey === "VK_ESCAPE") {
     is(propEditor.valueSpan.textContent, expected,
       "Value is as expected: " + expected);
   } else {
     is(propEditor.valueSpan.textContent, expected,
       "Value is as expected: " + expected);
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/rules/test/browser_rules_invalid-source-map.js
@@ -0,0 +1,43 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that when a source map is missing/invalid, the rule view still loads
+// correctly.
+
+const TESTCASE_URI = URL_ROOT + "doc_invalid_sourcemap.html";
+const PREF = "devtools.styleeditor.source-maps-enabled";
+const CSS_LOC = "doc_invalid_sourcemap.css:1";
+
+add_task(function*() {
+  Services.prefs.setBoolPref(PREF, true);
+
+  yield addTab(TESTCASE_URI);
+  let {inspector, view} = yield openRuleView();
+
+  yield selectNode("div", inspector);
+
+  let ruleEl = getRuleViewRule(view, "div");
+  ok(ruleEl, "The 'div' rule exists in the rule-view");
+
+  let prop = getRuleViewProperty(view, "div", "color");
+  ok(prop, "The 'color' property exists in this rule");
+
+  let value = getRuleViewPropertyValue(view, "div", "color");
+  is(value, "gold", "The 'color' property has the right value");
+
+  yield verifyLinkText(view, CSS_LOC);
+
+  Services.prefs.clearUserPref(PREF);
+});
+
+function verifyLinkText(view, text) {
+  info("Verifying that the rule-view stylesheet link is " + text);
+  let label = getRuleViewLinkByIndex(view, 1).querySelector("label");
+  return waitForSuccess(
+    () => label.getAttribute("value") == text,
+    "Link text changed to display correct location: " + text
+  );
+}
--- a/devtools/client/inspector/rules/test/browser_rules_multiple_properties_02.js
+++ b/devtools/client/inspector/rules/test/browser_rules_multiple_properties_02.js
@@ -8,34 +8,35 @@
 // unfinished properties/values in inplace-editors
 
 const TEST_URI = "<div>Test Element</div>";
 
 add_task(function*() {
   yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
   let {inspector, view} = yield openRuleView();
   yield selectNode("div", inspector);
-  yield testMultiValues(inspector, view);
-});
 
-function* testMultiValues(inspector, view) {
   let ruleEditor = getRuleViewRuleEditor(view, 0);
+  let onDone = view.once("ruleview-changed");
   yield createNewRuleViewProperty(ruleEditor, "width:");
+  yield onDone;
 
   is(ruleEditor.rule.textProps.length, 1,
     "Should have created a new text property.");
   is(ruleEditor.propertyList.children.length, 1,
     "Should have created a property editor.");
 
   // Value is focused, lets add multiple rules here and make sure they get added
+  onDone = view.once("ruleview-changed");
   let onMutation = inspector.once("markupmutation");
-  let valueEditor = ruleEditor.propertyList.children[0].querySelector("input");
-  valueEditor.value = "height: 10px;color:blue";
+  let input = view.styleDocument.activeElement;
+  input.value = "height: 10px;color:blue";
   EventUtils.synthesizeKey("VK_RETURN", {}, view.styleWindow);
   yield onMutation;
+  yield onDone;
 
   is(ruleEditor.rule.textProps.length, 2,
     "Should have added the changed value.");
   is(ruleEditor.propertyList.children.length, 3,
     "Should have added the changed value editor.");
 
   EventUtils.synthesizeKey("VK_ESCAPE", {}, view.styleWindow);
   is(ruleEditor.propertyList.children.length, 2,
@@ -45,9 +46,9 @@ function* testMultiValues(inspector, vie
     "Should have correct property name");
   is(ruleEditor.rule.textProps[0].value, "height: 10px",
     "Should have correct property value");
 
   is(ruleEditor.rule.textProps[1].name, "color",
     "Should have correct property name");
   is(ruleEditor.rule.textProps[1].value, "blue",
     "Should have correct property value");
-}
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/rules/test/doc_invalid_sourcemap.css
@@ -0,0 +1,3 @@
+div { color: gold; }
+
+/*# sourceMappingURL=this-source-map-does-not-exist.css.map */
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/rules/test/doc_invalid_sourcemap.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Invalid source map</title>
+  <link rel="stylesheet" type="text/css" href="doc_invalid_sourcemap.css">
+</head>
+<body>
+  <div>invalid source map</div>
+</body>
+</html>
--- a/devtools/client/inspector/shared/style-inspector-menu.js
+++ b/devtools/client/inspector/shared/style-inspector-menu.js
@@ -3,20 +3,20 @@
 /* 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/. */
 /* global _strings */
 
 "use strict";
 
 const {PREF_ORIG_SOURCES} = require("devtools/client/styleeditor/utils");
+const Services = require("Services");
 
 loader.lazyRequireGetter(this, "overlays",
   "devtools/client/inspector/shared/style-inspector-overlays");
-loader.lazyImporter(this, "Services", "resource://gre/modules/Services.jsm");
 loader.lazyServiceGetter(this, "clipboardHelper",
   "@mozilla.org/widget/clipboardhelper;1", "nsIClipboardHelper");
 loader.lazyGetter(this, "_strings", () => {
   return Services.strings
   .createBundle("chrome://devtools-shared/locale/styleinspector.properties");
 });
 
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
--- a/devtools/client/inspector/shared/style-inspector-overlays.js
+++ b/devtools/client/inspector/shared/style-inspector-overlays.js
@@ -18,17 +18,17 @@ const {
   SwatchColorPickerTooltip,
   SwatchCubicBezierTooltip,
   CssDocsTooltip,
   SwatchFilterTooltip
 } = require("devtools/client/shared/widgets/Tooltip");
 const EventEmitter = require("devtools/shared/event-emitter");
 const promise = require("promise");
 Cu.import("resource://gre/modules/Task.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
+const Services = require("Services");
 
 const PREF_IMAGE_TOOLTIP_SIZE = "devtools.inspector.imagePreviewTooltipSize";
 
 // Types of existing tooltips
 const TOOLTIP_IMAGE_TYPE = "image";
 const TOOLTIP_FONTFAMILY_TYPE = "font-family";
 
 // Types of nodes in the rule/computed-view
--- a/devtools/client/jsonview/converter-child.js
+++ b/devtools/client/jsonview/converter-child.js
@@ -2,28 +2,28 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* 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 {Cu, Cc, Ci, components} = require("chrome");
+const Services = require("Services");
 const {Class} = require("sdk/core/heritage");
 const {Unknown} = require("sdk/platform/xpcom");
 const xpcom = require("sdk/platform/xpcom");
 const Events = require("sdk/dom/events");
 const Clipboard = require("sdk/clipboard");
 
 loader.lazyRequireGetter(this, "NetworkHelper",
                                "devtools/shared/webconsole/network-helper");
 loader.lazyRequireGetter(this, "JsonViewUtils",
                                "devtools/client/jsonview/utils");
 
-const {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
 
 const childProcessMessageManager =
   Cc["@mozilla.org/childprocessmessagemanager;1"].
     getService(Ci.nsISyncMessageSender);
 
 // Amount of space that will be allocated for the stream's backing-store.
 // Must be power of 2. Used to copy the data stream in onStopRequest.
 const SEGMENT_SIZE = Math.pow(2, 17);
--- a/devtools/client/jsonview/main.js
+++ b/devtools/client/jsonview/main.js
@@ -2,19 +2,19 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* 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 {Cu, Ci, Cc} = require("chrome");
+const Services = require("Services");
 
 const {XPCOMUtils} = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
-const {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
 
 XPCOMUtils.defineLazyGetter(this, "JsonViewService", function() {
   return require("devtools/client/jsonview/utils");
 });
 
 /**
  * Singleton object that represents the JSON View in-content tool.
  * It has the same lifetime as the browser. Initialization done by
--- a/devtools/client/locales/en-US/webide.dtd
+++ b/devtools/client/locales/en-US/webide.dtd
@@ -70,19 +70,16 @@
 <!ENTITY key_toggleToolbox "VK_F12">
 <!-- toggle sidebar -->
 <!ENTITY key_toggleEditor "B">
 <!-- zoom -->
 <!ENTITY key_zoomin "+">
 <!ENTITY key_zoomin2 "=">
 <!ENTITY key_zoomout "-">
 <!ENTITY key_resetzoom "0">
-<!-- reload WebIDE and devtools from local checkout -->
-<!-- this binding is with accel+alt, whereas all others are just accel -->
-<!ENTITY key_reload_devtools "R">
 
 <!ENTITY projectPanel_myProjects "My Projects">
 <!ENTITY projectPanel_runtimeApps "Runtime Apps">
 <!ENTITY projectPanel_tabs "Tabs">
 <!ENTITY runtimePanel_usb "USB Devices">
 <!ENTITY runtimePanel_wifi "Wi-Fi Devices">
 <!ENTITY runtimePanel_simulator "Simulators">
 <!ENTITY runtimePanel_other "Other">
--- a/devtools/client/main.js
+++ b/devtools/client/main.js
@@ -1,16 +1,15 @@
 /* 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 { Cu } = require("chrome");
-Cu.import("resource://gre/modules/Services.jsm");
+const Services = require("Services");
 const { gDevTools } = require("devtools/client/framework/devtools");
 
 const { defaultTools, defaultThemes } = require("devtools/client/definitions");
 
 defaultTools.forEach(definition => gDevTools.registerTool(definition));
 defaultThemes.forEach(definition => gDevTools.registerTheme(definition));
 
 // Re-export for backwards compatibility, but we should probably the
--- a/devtools/client/memory/test/chrome/head.js
+++ b/devtools/client/memory/test/chrome/head.js
@@ -1,20 +1,20 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
 var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 Cu.import("resource://testing-common/Assert.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
 
 Cu.import("resource://devtools/client/shared/browser-loader.js");
 var { require } = BrowserLoader("resource://devtools/client/memory/", this);
+var Services = require("Services");
 
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
 DevToolsUtils.testing = true;
 var { immutableUpdate } = DevToolsUtils;
 
 var constants = require("devtools/client/memory/constants");
 var {
   breakdowns,
--- a/devtools/client/memory/test/unit/head.js
+++ b/devtools/client/memory/test/unit/head.js
@@ -1,18 +1,18 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
-var { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
 var { console } = Cu.import("resource://gre/modules/Console.jsm", {});
 var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 
+var Services = require("Services");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
 DevToolsUtils.testing = true;
 DevToolsUtils.dumpn.wantLogging = true;
 DevToolsUtils.dumpv.wantVerbose = false;
 
 var { OS } = require("resource://gre/modules/osfile.jsm");
 var { FileUtils } = require("resource://gre/modules/FileUtils.jsm");
 var { TargetFactory } = require("devtools/client/framework/target");
--- a/devtools/client/netmonitor/har/har-automation.js
+++ b/devtools/client/netmonitor/har/har-automation.js
@@ -1,17 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const { Cu, Ci, Cc } = require("chrome");
 const { Class } = require("sdk/core/heritage");
 const { defer, resolve } = require("promise");
-const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
+const Services = require("Services");
 
 Cu.import("resource://gre/modules/Task.jsm");
 
 loader.lazyRequireGetter(this, "HarCollector", "devtools/client/netmonitor/har/har-collector", true);
 loader.lazyRequireGetter(this, "HarExporter", "devtools/client/netmonitor/har/har-exporter", true);
 loader.lazyRequireGetter(this, "HarUtils", "devtools/client/netmonitor/har/har-utils", true);
 
 const prefDomain = "devtools.netmonitor.har.";
--- a/devtools/client/netmonitor/har/har-builder.js
+++ b/devtools/client/netmonitor/har/har-builder.js
@@ -1,16 +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 { Cu, Ci, Cc } = require("chrome");
 const { defer, all, resolve } = require("promise");
-const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
+const Services = require("Services");
 
 loader.lazyImporter(this, "ViewHelpers", "resource://devtools/client/shared/widgets/ViewHelpers.jsm");
 loader.lazyRequireGetter(this, "NetworkHelper", "devtools/shared/webconsole/network-helper");
 
 loader.lazyGetter(this, "appInfo", () => {
   return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo);
 });
 
--- a/devtools/client/netmonitor/har/har-collector.js
+++ b/devtools/client/netmonitor/har/har-collector.js
@@ -2,18 +2,17 @@
  * 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 { Cu, Ci, Cc } = require("chrome");
 const { defer, all } = require("promise");
 const { setTimeout, clearTimeout } = require("sdk/timers");
 const { makeInfallible } = require("devtools/shared/DevToolsUtils");
-
-const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
+const Services = require("Services");
 
 // Helper tracer. Should be generic sharable by other modules (bug 1171927)
 const trace = {
   log: function(...args) {
   }
 }
 
 /**
--- a/devtools/client/netmonitor/har/har-exporter.js
+++ b/devtools/client/netmonitor/har/har-exporter.js
@@ -1,15 +1,15 @@
 /* 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 { Cu, Cc, Ci } = require("chrome");
-const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
+const Services = require("Services");
 const { defer, resolve } = require("promise");
 const { HarUtils } = require("./har-utils.js");
 const { HarBuilder } = require("./har-builder.js");
 
 XPCOMUtils.defineLazyGetter(this, "clipboardHelper", function() {
   return Cc["@mozilla.org/widget/clipboardhelper;1"].
     getService(Ci.nsIClipboardHelper);
 });
--- a/devtools/client/netmonitor/har/har-utils.js
+++ b/devtools/client/netmonitor/har/har-utils.js
@@ -1,15 +1,15 @@
 /* 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 { Cu, Ci, Cc, CC } = require("chrome");
-const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
+const Services = require("Services");
 
 XPCOMUtils.defineLazyGetter(this, "dirService", function() {
   return Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
 });
 
 XPCOMUtils.defineLazyGetter(this, "ZipWriter", function() {
   return CC("@mozilla.org/zipwriter;1", "nsIZipWriter");
 });
--- a/devtools/client/netmonitor/netmonitor-controller.js
+++ b/devtools/client/netmonitor/netmonitor-controller.js
@@ -105,25 +105,25 @@ const ACTIVITY_TYPE = {
     WITH_CACHE_DEFAULT: 3
   },
 
   // Enabling or disabling the cache without triggering a reload.
   ENABLE_CACHE: 3,
   DISABLE_CACHE: 4
 };
 
-Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://devtools/client/shared/widgets/SideMenuWidget.jsm");
 Cu.import("resource://devtools/client/shared/widgets/VariablesView.jsm");
 Cu.import("resource://devtools/client/shared/widgets/VariablesViewController.jsm");
 Cu.import("resource://devtools/client/shared/widgets/ViewHelpers.jsm");
 
 const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
 const promise = require("promise");
+const Services = require("Services");
 const EventEmitter = require("devtools/shared/event-emitter");
 const Editor = require("devtools/client/sourceeditor/editor");
 const {Tooltip} = require("devtools/client/shared/widgets/Tooltip");
 const {ToolSidebar} = require("devtools/client/framework/sidebar");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 const {TimelineFront} = require("devtools/server/actors/timeline");
 
 XPCOMUtils.defineConstant(this, "EVENTS", EVENTS);
--- a/devtools/client/netmonitor/test/head.js
+++ b/devtools/client/netmonitor/test/head.js
@@ -1,19 +1,19 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
 var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
-var { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
 var { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
 var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 var { gDevTools } = require("devtools/client/framework/devtools");
 var { CurlUtils } = Cu.import("resource://devtools/client/shared/Curl.jsm", {});
+var Services = require("Services");
 var promise = require("promise");
 var NetworkHelper = require("devtools/shared/webconsole/network-helper");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
 var { TargetFactory } = require("devtools/client/framework/target");
 var { Toolbox } = require("devtools/client/framework/toolbox");
 
 const EXAMPLE_URL = "http://example.com/browser/devtools/client/netmonitor/test/";
 
--- a/devtools/client/performance/test/head.js
+++ b/devtools/client/performance/test/head.js
@@ -1,20 +1,20 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
 var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
-var { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
 var { Preferences } = Cu.import("resource://gre/modules/Preferences.jsm", {});
 var { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
 var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 var { gDevTools } = require("devtools/client/framework/devtools");
 var { console } = require("resource://gre/modules/Console.jsm");
+var Services = require("Services");
 var { TargetFactory } = require("devtools/client/framework/target");
 var Promise = require("promise");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
 var { DebuggerServer } = require("devtools/server/main");
 var { merge } = require("sdk/util/object");
 var { createPerformanceFront } = require("devtools/server/actors/performance");
 var RecordingUtils = require("devtools/shared/performance/recording-utils");
 var {
--- a/devtools/client/performance/test/unit/head.js
+++ b/devtools/client/performance/test/unit/head.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
 var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
-var { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
+var Services = require("Services");
 var { console } = require("resource://gre/modules/Console.jsm");
 const RecordingUtils = require("devtools/shared/performance/recording-utils");
 const PLATFORM_DATA_PREF = "devtools.performance.ui.show-platform-data";
 
 /**
  * Get a path in a FrameNode call tree.
  */
 function getFrameNodePath(root, path) {
--- a/devtools/client/projecteditor/lib/plugins/app-manager/plugin.js
+++ b/devtools/client/projecteditor/lib/plugins/app-manager/plugin.js
@@ -1,17 +1,17 @@
 const { Cu } = require("chrome");
 const { Class } = require("sdk/core/heritage");
 const { EventTarget } = require("sdk/event/target");
 const { emit } = require("sdk/event/core");
 const promise = require("promise");
 var { registerPlugin, Plugin } = require("devtools/client/projecteditor/lib/plugins/core");
 const { AppProjectEditor } = require("./app-project-editor");
 const OPTION_URL = "chrome://devtools/skin/images/tool-options.svg";
-const {Services} = Cu.import("resource://gre/modules/Services.jsm");
+const Services = require("Services");
 const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties");
 
 var AppManagerRenderer = Class({
   extends: Plugin,
 
   isAppManagerProject: function() {
     return !!this.host.project.appManagerOpts;
   },
--- a/devtools/client/projecteditor/lib/projecteditor.js
+++ b/devtools/client/projecteditor/lib/projecteditor.js
@@ -13,17 +13,17 @@ const { Resource } = require("devtools/c
 const { registeredPlugins } = require("devtools/client/projecteditor/lib/plugins/core");
 const { EventTarget } = require("sdk/event/target");
 const { on, forget } = require("devtools/client/projecteditor/lib/helpers/event");
 const { emit } = require("sdk/event/core");
 const { merge } = require("sdk/util/object");
 const promise = require("promise");
 const { ViewHelpers } = Cu.import("resource://devtools/client/shared/widgets/ViewHelpers.jsm", {});
 const { DOMHelpers } = Cu.import("resource://devtools/client/shared/DOMHelpers.jsm");
-const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
+const Services = require("Services");
 const { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
 const ITCHPAD_URL = "chrome://devtools/content/projecteditor/chrome/content/projecteditor.xul";
 const { confirm } = require("devtools/client/projecteditor/lib/helpers/prompts");
 const { getLocalizedString } = require("devtools/client/projecteditor/lib/helpers/l10n");
 
 // Enabled Plugins
 require("devtools/client/projecteditor/lib/plugins/dirty/dirty");
 require("devtools/client/projecteditor/lib/plugins/delete/delete");
--- a/devtools/client/projecteditor/lib/stores/local.js
+++ b/devtools/client/projecteditor/lib/stores/local.js
@@ -6,19 +6,19 @@
 
 const { Cc, Ci, Cu, ChromeWorker } = require("chrome");
 const { Class } = require("sdk/core/heritage");
 const { OS } = Cu.import("resource://gre/modules/osfile.jsm", {});
 const { emit } = require("sdk/event/core");
 const { Store } = require("devtools/client/projecteditor/lib/stores/base");
 const { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
 const promise = require("promise");
+const Services = require("Services");
 const { on, forget } = require("devtools/client/projecteditor/lib/helpers/event");
 const { FileResource } = require("devtools/client/projecteditor/lib/stores/resource");
-const {Services} = Cu.import("resource://gre/modules/Services.jsm");
 const {setTimeout, clearTimeout} = Cu.import("resource://gre/modules/Timer.jsm", {});
 
 const CHECK_LINKED_DIRECTORY_DELAY = 5000;
 const SHOULD_LIVE_REFRESH = true;
 // XXX: Ignores should be customizable
 const IGNORE_REGEX = /(^\.)|(\~$)|(^node_modules$)/;
 
 /**
--- a/devtools/client/responsive.html/index.js
+++ b/devtools/client/responsive.html/index.js
@@ -25,18 +25,16 @@ const { addViewport } = require("./actio
 
 let bootstrap = {
 
   telemetry: new Telemetry(),
 
   store: null,
 
   init() {
-    // TODO: Should we track this as a separate tool from the old version?
-    // See bug 1242057.
     this.telemetry.toolOpened("responsive");
     let store = this.store = Store();
     let app = createElement(App);
     let provider = createElement(Provider, { store }, app);
     ReactDOM.render(provider, document.querySelector("#app"));
   },
 
   destroy() {
--- a/devtools/client/responsive.html/test/browser/head.js
+++ b/devtools/client/responsive.html/test/browser/head.js
@@ -1,47 +1,47 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 /* eslint no-unused-vars: [2, {"vars": "local"}] */
 /* import-globals-from ../../../framework/test/shared-head.js */
 /* import-globals-from ../../../framework/test/shared-redux-head.js */
-/* global ResponsiveUI */
 
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js",
   this);
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/framework/test/shared-redux-head.js",
   this);
 
 Services.prefs.setBoolPref("devtools.responsive.html.enabled", true);
 registerCleanupFunction(() => {
   Services.prefs.clearUserPref("devtools.responsive.html.enabled");
 });
+const { ResponsiveUIManager } = Cu.import("resource://devtools/client/responsivedesign/responsivedesign.jsm", {});
 
 /**
  * Open responsive design mode for the given tab.
  */
 var openRDM = Task.async(function*(tab) {
   info("Opening responsive design mode");
-  let manager = ResponsiveUI.ResponsiveUIManager;
+  let manager = ResponsiveUIManager;
   let ui = yield manager.openIfNeeded(window, tab);
   info("Responsive design mode opened");
   return { ui, manager };
 });
 
 /**
  * Close responsive design mode for the given tab.
  */
 var closeRDM = Task.async(function*(tab) {
   info("Closing responsive design mode");
-  let manager = ResponsiveUI.ResponsiveUIManager;
+  let manager = ResponsiveUIManager;
   manager.closeIfNeeded(window, tab);
   info("Responsive design mode closed");
 });
 
 /**
  * Adds a new test task that adds a tab with the given URL, opens responsive
  * design mode, runs the given generator, closes responsive design mode, and
  * removes the tab.
--- a/devtools/client/responsivedesign/resize-commands.js
+++ b/devtools/client/responsivedesign/resize-commands.js
@@ -1,16 +1,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { Cc, Ci, Cu } = require("chrome");
 
+loader.lazyImporter(this, "ResponsiveUIManager", "resource://devtools/client/responsivedesign/responsivedesign.jsm");
+
 const BRAND_SHORT_NAME = Cc["@mozilla.org/intl/stringbundle;1"].
                          getService(Ci.nsIStringBundleService).
                          createBundle("chrome://branding/locale/brand.properties").
                          GetStringFromName("brandShortName");
 
 const l10n = require("gcli/l10n");
 
 exports.items = [
@@ -43,34 +45,28 @@ exports.items = [
     tooltipText: l10n.lookup("resizeModeToggleTooltip"),
     description: l10n.lookup('resizeModeToggleDesc'),
     manual: l10n.lookupFormat('resizeModeManual2', [BRAND_SHORT_NAME]),
     state: {
       isChecked: function(aTarget) {
         if (!aTarget.tab) {
           return false;
         }
-        let browserWindow = aTarget.tab.ownerDocument.defaultView;
-        let mgr = browserWindow.ResponsiveUI.ResponsiveUIManager;
-        return mgr.isActiveForTab(aTarget.tab);
+        return ResponsiveUIManager.isActiveForTab(aTarget.tab);
       },
       onChange: function(aTarget, aChangeHandler) {
         if (aTarget.tab) {
-          let browserWindow = aTarget.tab.ownerDocument.defaultView;
-          let mgr = browserWindow.ResponsiveUI.ResponsiveUIManager;
-          mgr.on("on", aChangeHandler);
-          mgr.on("off", aChangeHandler);
+          ResponsiveUIManager.on("on", aChangeHandler);
+          ResponsiveUIManager.on("off", aChangeHandler);
         }
       },
       offChange: function(aTarget, aChangeHandler) {
         if (aTarget.tab) {
-          let browserWindow = aTarget.tab.ownerDocument.defaultView;
-          let mgr = browserWindow.ResponsiveUI.ResponsiveUIManager;
-          mgr.off("on", aChangeHandler);
-          mgr.off("off", aChangeHandler);
+          ResponsiveUIManager.off("on", aChangeHandler);
+          ResponsiveUIManager.off("off", aChangeHandler);
         }
       },
     },
     exec: gcli_cmd_resize
   },
   {
     item: "command",
     runAt: "client",
@@ -89,14 +85,13 @@ exports.items = [
       },
     ],
     exec: gcli_cmd_resize
   }
 ];
 
 function* gcli_cmd_resize(args, context) {
   let browserWindow = context.environment.chromeWindow;
-  let mgr = browserWindow.ResponsiveUI.ResponsiveUIManager;
-  yield mgr.handleGcliCommand(browserWindow,
-                              browserWindow.gBrowser.selectedTab,
-                              this.name,
-                              args);
+  yield ResponsiveUIManager.handleGcliCommand(browserWindow,
+                                              browserWindow.gBrowser.selectedTab,
+                                              this.name,
+                                              args);
 }
--- a/devtools/client/responsivedesign/test/browser_responsive_cmd.js
+++ b/devtools/client/responsivedesign/test/browser_responsive_cmd.js
@@ -6,17 +6,17 @@
 ///////////////////
 //
 // Whitelisting this test.
 // As part of bug 1077403, the leaking uncaught rejection should be fixed.
 //
 thisTestLeaksUncaughtRejectionsAndShouldBeFixed("destroy");
 
 function test() {
-  let manager = ResponsiveUI.ResponsiveUIManager;
+  let manager = ResponsiveUIManager;
   let done;
 
   function isOpen() {
     return gBrowser.getBrowserContainer(gBrowser.selectedBrowser)
                    .hasAttribute("responsivemode");
   }
 
   helpers.addTabWithToolbar("data:text/html;charset=utf-8,hi", (options) => {
--- a/devtools/client/responsivedesign/test/head.js
+++ b/devtools/client/responsivedesign/test/head.js
@@ -19,25 +19,27 @@ registerCleanupFunction(() => {
   Services.prefs.clearUserPref("devtools.responsiveUI.customHeight");
   Services.prefs.clearUserPref("devtools.responsiveUI.customWidth");
   Services.prefs.clearUserPref("devtools.responsiveUI.presets");
   Services.prefs.clearUserPref("devtools.responsiveUI.rotate");
 });
 
 SimpleTest.requestCompleteLog();
 
+const { ResponsiveUIManager } = Cu.import("resource://devtools/client/responsivedesign/responsivedesign.jsm", {});
+
 /**
  * Open the Responsive Design Mode
  * @param {Tab} The browser tab to open it into (defaults to the selected tab).
  * @param {method} The method to use to open the RDM (values: menu, keyboard)
  * @return {rdm, manager} Returns the RUI instance and the manager
  */
 var openRDM = Task.async(function*(tab = gBrowser.selectedTab,
                                    method = "menu") {
-  let manager = ResponsiveUI.ResponsiveUIManager;
+  let manager = ResponsiveUIManager;
 
   let opened = once(manager, "on");
   let resized = once(manager, "contentResize");
   if (method == "menu") {
     document.getElementById("Tools:ResponsiveUI").doCommand();
   } else {
     synthesizeKeyFromKeyTag(document.getElementById("key_responsiveUI"));
   }
@@ -56,17 +58,17 @@ var openRDM = Task.async(function*(tab =
   return {rdm, manager};
 });
 
 /**
  * Close a responsive mode instance
  * @param {rdm} ResponsiveUI instance for the tab
  */
 var closeRDM = Task.async(function*(rdm) {
-  let manager = ResponsiveUI.ResponsiveUIManager;
+  let manager = ResponsiveUIManager;
   if (!rdm) {
     rdm = manager.getResponsiveUIForTab(gBrowser.selectedTab);
   }
   let closed = once(manager, "off");
   let resized = once(manager, "contentResize");
   rdm.close();
   yield resized;
   yield closed;
--- a/devtools/client/responsivedesign/test/touch.html
+++ b/devtools/client/responsivedesign/test/touch.html
@@ -28,11 +28,13 @@
   div.addEventListener("touchmove", function(evt) {
     var touch = evt.changedTouches[0];
     var deltaX = touch.pageX - initX;
     var deltaY = touch.pageY - initY;
     div.style.transform = "translate(" + deltaX + "px, " + deltaY + "px)";
   }, true);
 
   div.addEventListener("touchend", function(evt) {
-    div.style.transform = "none";
+    if (!evt.touches.length) {
+      div.style.transform = "none";
+    }
   }, true);
 </script>
--- a/devtools/client/scratchpad/scratchpad-commands.js
+++ b/devtools/client/scratchpad/scratchpad-commands.js
@@ -1,21 +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 l10n = require("gcli/l10n");
+const {Cu} = require("chrome");
 
 exports.items = [{
   item: "command",
   runAt: "client",
   name: "scratchpad",
   buttonId: "command-button-scratchpad",
   buttonClass: "command-button command-button-invertable",
   tooltipText: l10n.lookup("scratchpadOpenTooltip"),
   hidden: true,
   exec: function(args, context) {
-    let Scratchpad = context.environment.chromeWindow.Scratchpad;
-    Scratchpad.ScratchpadManager.openScratchpad();
+    const {ScratchpadManager} = Cu.import("resource://devtools/client/scratchpad/scratchpad-manager.jsm", {});
+    ScratchpadManager.openScratchpad();
   }
 }];
--- a/devtools/client/scratchpad/scratchpad-manager.jsm
+++ b/devtools/client/scratchpad/scratchpad-manager.jsm
@@ -9,19 +9,18 @@ this.EXPORTED_SYMBOLS = ["ScratchpadMana
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 const SCRATCHPAD_WINDOW_URL = "chrome://devtools/content/scratchpad/scratchpad.xul";
 const SCRATCHPAD_WINDOW_FEATURES = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
 
-Cu.import("resource://gre/modules/Services.jsm");
 const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
-
+const Services = require("Services");
 const Telemetry = require("devtools/client/shared/telemetry");
 
 
 /**
  * The ScratchpadManager object opens new Scratchpad windows and manages the state
  * of open scratchpads for session restore. There's only one ScratchpadManager in
  * the life of the browser.
  */
--- a/devtools/client/scratchpad/scratchpad.js
+++ b/devtools/client/scratchpad/scratchpad.js
@@ -47,20 +47,20 @@ const {require, loader} = Cu.import("res
 
 const Telemetry = require("devtools/client/shared/telemetry");
 const Editor    = require("devtools/client/sourceeditor/editor");
 const TargetFactory = require("devtools/client/framework/target").TargetFactory;
 const EventEmitter = require("devtools/shared/event-emitter");
 const {DevToolsWorker} = require("devtools/shared/worker/worker");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 const promise = require("promise");
+const Services = require("Services");
 const {gDevTools} = require("devtools/client/framework/devtools");
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/NetUtil.jsm");
 Cu.import("resource://devtools/client/scratchpad/scratchpad-manager.jsm");
 Cu.import("resource://gre/modules/jsdebugger.jsm");
 Cu.import("resource://gre/modules/osfile.jsm");
 Cu.import("resource://devtools/client/shared/widgets/ViewHelpers.jsm");
 Cu.import("resource://gre/modules/reflect.jsm");
 
 XPCOMUtils.defineConstant(this, "SCRATCHPAD_CONTEXT_CONTENT", SCRATCHPAD_CONTEXT_CONTENT);
@@ -76,16 +76,17 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "VariablesViewController",
   "resource://devtools/client/shared/widgets/VariablesViewController.jsm");
 
 loader.lazyRequireGetter(this, "DebuggerServer", "devtools/server/main", true);
 
 loader.lazyRequireGetter(this, "DebuggerClient", "devtools/shared/client/main", true);
 loader.lazyRequireGetter(this, "EnvironmentClient", "devtools/shared/client/main", true);
 loader.lazyRequireGetter(this, "ObjectClient", "devtools/shared/client/main", true);
+loader.lazyRequireGetter(this, "HUDService", "devtools/client/webconsole/hudservice");
 
 XPCOMUtils.defineLazyGetter(this, "REMOTE_TIMEOUT", () =>
   Services.prefs.getIntPref("devtools.debugger.remote-timeout"));
 
 XPCOMUtils.defineLazyModuleGetter(this, "ShortcutUtils",
   "resource://gre/modules/ShortcutUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Reflect",
@@ -1586,17 +1587,17 @@ var Scratchpad = {
     }
   },
 
   /**
    * Open the Error Console.
    */
   openErrorConsole: function SP_openErrorConsole()
   {
-    this.browserWindow.HUDService.toggleBrowserConsole();
+    HUDService.toggleBrowserConsole();
   },
 
   /**
    * Open the Web Console.
    */
   openWebConsole: function SP_openWebConsole()
   {
     let target = TargetFactory.forTab(this.gBrowser.selectedTab);
--- a/devtools/client/scratchpad/test/browser_scratchpad_chrome_context_pref.js
+++ b/devtools/client/scratchpad/test/browser_scratchpad_chrome_context_pref.js
@@ -10,18 +10,16 @@ function test()
   waitForExplicitFinish();
 
   Services.prefs.setBoolPref(DEVTOOLS_CHROME_ENABLED, true);
 
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
     gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
 
-    ok(window.Scratchpad, "Scratchpad variable exists");
-
     openScratchpad(runTests);
   }, true);
 
   content.location = "data:text/html,Scratchpad test for bug 646070 - chrome context preference";
 }
 
 function runTests()
 {
--- a/devtools/client/scratchpad/test/browser_scratchpad_help_key.js
+++ b/devtools/client/scratchpad/test/browser_scratchpad_help_key.js
@@ -7,18 +7,16 @@ function test()
 {
   waitForExplicitFinish();
 
   gBrowser.selectedTab = gBrowser.addTab();
   content.location = "data:text/html,Test keybindings for opening Scratchpad MDN Documentation, bug 650760";
   gBrowser.selectedBrowser.addEventListener("load", function onTabLoad() {
     gBrowser.selectedBrowser.removeEventListener("load", onTabLoad, true);
 
-    ok(window.Scratchpad, "Scratchpad variable exists");
-
     openScratchpad(runTest);
   }, true);
 }
 
 function runTest()
 {
   let sp = gScratchpadWindow.Scratchpad;
   ok(sp, "Scratchpad object exists in new window");
--- a/devtools/client/scratchpad/test/browser_scratchpad_initialization.js
+++ b/devtools/client/scratchpad/test/browser_scratchpad_initialization.js
@@ -8,18 +8,16 @@ function test()
 {
   waitForExplicitFinish();
 
   Services.prefs.setBoolPref(DEVTOOLS_CHROME_ENABLED, false);
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
     gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
 
-    ok(window.Scratchpad, "Scratchpad variable exists");
-
     openScratchpad(runTests);
   }, true);
 
   content.location = "data:text/html,initialization test for Scratchpad";
 }
 
 function runTests()
 {
--- a/devtools/client/scratchpad/test/browser_scratchpad_open_error_console.js
+++ b/devtools/client/scratchpad/test/browser_scratchpad_open_error_console.js
@@ -1,12 +1,14 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
+const HUDService = require("devtools/client/webconsole/hudservice");
+
 function test()
 {
   waitForExplicitFinish();
 
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
     gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
     openScratchpad(runTests);
--- a/devtools/client/scratchpad/test/browser_scratchpad_restore.js
+++ b/devtools/client/scratchpad/test/browser_scratchpad_restore.js
@@ -1,14 +1,12 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
-var ScratchpadManager = Scratchpad.ScratchpadManager;
-
 /* Call the iterator for each item in the list,
    calling the final callback with all the results
    after every iterator call has sent its result */
 function asyncMap(items, iterator, callback)
 {
   let expected = items.length;
   let results = [];
 
--- a/devtools/client/scratchpad/test/browser_scratchpad_tab.js
+++ b/devtools/client/scratchpad/test/browser_scratchpad_tab.js
@@ -6,18 +6,16 @@
 function test()
 {
   waitForExplicitFinish();
 
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.selectedBrowser.addEventListener("load", function onTabLoad() {
     gBrowser.selectedBrowser.removeEventListener("load", onTabLoad, true);
 
-    ok(window.Scratchpad, "Scratchpad variable exists");
-
     Services.prefs.setIntPref("devtools.editor.tabsize", 5);
 
     openScratchpad(runTests);
   }, true);
 
   content.location = "data:text/html,Scratchpad test for the Tab key, bug 660560";
 }
 
--- a/devtools/client/scratchpad/test/browser_scratchpad_unsaved.js
+++ b/devtools/client/scratchpad/test/browser_scratchpad_unsaved.js
@@ -8,18 +8,16 @@ const expected = 4;
 var count = 0;
 function done()
 {
   if (++count == expected) {
     finish();
   }
 }
 
-var ScratchpadManager = Scratchpad.ScratchpadManager;
-
 
 function test()
 {
   waitForExplicitFinish();
 
   testListeners();
   testRestoreNotFromFile();
   testRestoreFromFileSaved();
--- a/devtools/client/scratchpad/test/head.js
+++ b/devtools/client/scratchpad/test/head.js
@@ -2,18 +2,19 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 const {NetUtil} = Cu.import("resource://gre/modules/NetUtil.jsm", {});
 const {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm", {});
 const {console} = Cu.import("resource://gre/modules/Console.jsm", {});
+const {ScratchpadManager} = Cu.import("resource://devtools/client/scratchpad/scratchpad-manager.jsm", {});
 const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
-const {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
+const Services = require("Services");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 const promise = require("promise");
 
 
 var gScratchpadWindow; // Reference to the Scratchpad chrome window object
 
 DevToolsUtils.testing = true;
 SimpleTest.registerCleanupFunction(() => {
@@ -39,17 +40,17 @@ SimpleTest.registerCleanupFunction(() =>
  * @return nsIDOMWindow
  *         The new window object that holds Scratchpad. Note that the
  *         gScratchpadWindow global is also updated to reference the new window
  *         object.
  */
 function openScratchpad(aReadyCallback, aOptions = {})
 {
   let win = aOptions.window ||
-            Scratchpad.ScratchpadManager.openScratchpad(aOptions.state);
+            ScratchpadManager.openScratchpad(aOptions.state);
   if (!win) {
     return;
   }
 
   let onLoad = function() {
     win.removeEventListener("load", onLoad, false);
 
     win.Scratchpad.addObserver({
--- a/devtools/client/shadereditor/shadereditor.js
+++ b/devtools/client/shadereditor/shadereditor.js
@@ -1,24 +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/. */
 "use strict";
 
 var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
-Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource://devtools/client/shared/widgets/SideMenuWidget.jsm");
 Cu.import("resource://devtools/client/shared/widgets/ViewHelpers.jsm");
 Cu.import("resource://gre/modules/Console.jsm");
 
 const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
 const promise = require("promise");
+const Services = require("Services");
 const EventEmitter = require("devtools/shared/event-emitter");
 const {Tooltip} = require("devtools/client/shared/widgets/Tooltip");
 const Editor = require("devtools/client/sourceeditor/editor");
 const Telemetry = require("devtools/client/shared/telemetry");
 const telemetry = new Telemetry();
 
 // The panel's window global is an EventEmitter firing the following events:
 const EVENTS = {
--- a/devtools/client/shadereditor/test/head.js
+++ b/devtools/client/shadereditor/test/head.js
@@ -1,23 +1,18 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
 var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
-var { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
-
-var gEnableLogging = Services.prefs.getBoolPref("devtools.debugger.log");
-// To enable logging for try runs, just set the pref to true.
-Services.prefs.setBoolPref("devtools.debugger.log", false);
-
 var { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
 var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 
+var Services = require("Services");
 var promise = require("promise");
 var { gDevTools } = require("devtools/client/framework/devtools");
 var { DebuggerClient } = require("devtools/shared/client/main");
 var { DebuggerServer } = require("devtools/server/main");
 var { WebGLFront } = require("devtools/server/actors/webgl");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
 var TiltGL = require("devtools/client/tilt/tilt-gl");
 var {TargetFactory} = require("devtools/client/framework/target");
@@ -27,16 +22,20 @@ var mm = null;
 const FRAME_SCRIPT_UTILS_URL = "chrome://devtools/content/shared/frame-script-utils.js"
 const EXAMPLE_URL = "http://example.com/browser/devtools/client/shadereditor/test/";
 const SIMPLE_CANVAS_URL = EXAMPLE_URL + "doc_simple-canvas.html";
 const SHADER_ORDER_URL = EXAMPLE_URL + "doc_shader-order.html";
 const MULTIPLE_CONTEXTS_URL = EXAMPLE_URL + "doc_multiple-contexts.html";
 const OVERLAPPING_GEOMETRY_CANVAS_URL = EXAMPLE_URL + "doc_overlapping-geometry.html";
 const BLENDED_GEOMETRY_CANVAS_URL = EXAMPLE_URL + "doc_blended-geometry.html";
 
+var gEnableLogging = Services.prefs.getBoolPref("devtools.debugger.log");
+// To enable logging for try runs, just set the pref to true.
+Services.prefs.setBoolPref("devtools.debugger.log", false);
+
 // All tests are asynchronous.
 waitForExplicitFinish();
 
 var gToolEnabled = Services.prefs.getBoolPref("devtools.shadereditor.enabled");
 
 DevToolsUtils.testing = true;
 
 registerCleanupFunction(() => {
--- a/devtools/client/shared/AppCacheUtils.jsm
+++ b/devtools/client/shared/AppCacheUtils.jsm
@@ -23,20 +23,20 @@
  *  - etc.
  */
 
 "use strict";
 
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 var { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
-var { Services }   = Cu.import("resource://gre/modules/Services.jsm", {});
 var { NetUtil }    = Cu.import("resource://gre/modules/NetUtil.jsm", {});
 var { LoadContextInfo } = Cu.import("resource://gre/modules/LoadContextInfo.jsm", {});
 var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
+var Services = require("Services");
 var promise = require("promise");
 
 this.EXPORTED_SYMBOLS = ["AppCacheUtils"];
 
 function AppCacheUtils(documentOrUri) {
   this._parseManifest = this._parseManifest.bind(this);
 
   if (documentOrUri) {
--- a/devtools/client/shared/autocomplete-popup.js
+++ b/devtools/client/shared/autocomplete-popup.js
@@ -2,18 +2,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {Cc, Ci, Cu} = require("chrome");
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-
-loader.lazyImporter(this, "Services", "resource://gre/modules/Services.jsm");
+const Services = require("Services");
 const {gDevTools} = require("devtools/client/framework/devtools");
 const events  = require("devtools/shared/event-emitter");
 
 /**
  * Autocomplete popup UI implementation.
  *
  * @constructor
  * @param nsIDOMDocument aDocument
--- a/devtools/client/shared/browser-loader.js
+++ b/devtools/client/shared/browser-loader.js
@@ -3,26 +3,30 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 var { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 const loaders = Cu.import("resource://gre/modules/commonjs/toolkit/loader.js", {});
 const { devtools } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 const { joinURI } = devtools.require("devtools/shared/path");
-const { Services } = devtools.require("resource://gre/modules/Services.jsm");
+const Services = devtools.require("Services");
 Cu.import("resource://gre/modules/AppConstants.jsm");
 
 const BROWSER_BASED_DIRS = [
   "resource://devtools/client/jsonview",
   "resource://devtools/client/shared/vendor",
   "resource://devtools/client/shared/components",
   "resource://devtools/client/shared/redux"
 ];
 
+function clearCache() {
+  Services.obs.notifyObservers(null, "startupcache-invalidate", null);
+}
+
 /*
  * Create a loader to be used in a browser environment. This evaluates
  * modules in their own environment, but sets window (the normal
  * global object) as the sandbox prototype, so when a variable is not
  * defined it checks `window` before throwing an error. This makes all
  * browser APIs available to modules by default, like a normal browser
  * environment, but modules are still evaluated in their own scope.
  *
@@ -146,18 +150,19 @@ function BrowserLoaderBuilder(baseURI, w
   }
 
   const mainModule = loaders.Module(baseURI, joinURI(baseURI, "main.js"));
   this.loader = loaders.Loader(opts);
   this.require = loaders.Require(this.loader, mainModule);
 
   if (hotReloadEnabled) {
     const watcher = devtools.require("devtools/client/shared/file-watcher");
-    const onFileChanged = (_, fileURI) => {
-      this.hotReloadFile(window, componentProxies, fileURI);
+    const onFileChanged = (_, relativePath) => {
+      this.hotReloadFile(window, componentProxies,
+                         "resource://devtools/" + relativePath);
     };
     watcher.on("file-changed", onFileChanged);
 
     window.addEventListener("unload", () => {
       watcher.off("file-changed", onFileChanged);
     });
   }
 }
@@ -180,46 +185,26 @@ BrowserLoaderBuilder.prototype = {
   lazyRequireGetter: function(obj, property, module, destructure) {
     devtools.lazyGetter(obj, property, () => {
       return destructure
           ? this.require(module)[property]
           : this.require(module || property);
     });
   },
 
-  clearCache: function() {
-    Services.obs.notifyObservers(null, "startupcache-invalidate", null);
-  },
-
   hotReloadFile: function(window, componentProxies, fileURI) {
-    dump("Hot reloading: " + fileURI + "\n");
-
     if (fileURI.match(/\.js$/)) {
       // Test for React proxy components
       const proxy = componentProxies.get(fileURI);
       if (proxy) {
         // Remove the old module and re-require the new one; the require
         // hook in the loader will take care of the rest
         delete this.loader.modules[fileURI];
-        this.clearCache();
+        clearCache();
         this.require(fileURI);
       }
-    } else if (fileURI.match(/\.css$/)) {
-      const links = [...window.document.getElementsByTagNameNS("http://www.w3.org/1999/xhtml", "link")];
-      links.forEach(link => {
-        if (link.href.indexOf(fileURI) === 0) {
-          const parentNode = link.parentNode;
-          const newLink = window.document.createElementNS("http://www.w3.org/1999/xhtml", "link");
-          newLink.rel = "stylesheet";
-          newLink.type = "text/css";
-          newLink.href = fileURI + "?s=" + Math.random();
-
-          parentNode.insertBefore(newLink, link);
-          parentNode.removeChild(link);
-        }
-      });
     }
   }
 };
 
 this.BrowserLoader = BrowserLoader;
 
 this.EXPORTED_SYMBOLS = ["BrowserLoader"];
--- a/devtools/client/shared/components/test/mochitest/head.js
+++ b/devtools/client/shared/components/test/mochitest/head.js
@@ -1,22 +1,22 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
 var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 Cu.import("resource://testing-common/Assert.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
-var { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
 
 var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 var { gDevTools } = require("devtools/client/framework/devtools");
 var { BrowserLoader } = Cu.import("resource://devtools/client/shared/browser-loader.js", {});
 var promise = require("promise");
+var Services = require("Services");
 var { DebuggerServer } = require("devtools/server/main");
 var { DebuggerClient } = require("devtools/shared/client/main");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
 var { TargetFactory } = require("devtools/client/framework/target");
 var { Toolbox } = require("devtools/client/framework/toolbox");
 
 DevToolsUtils.testing = true;
 var { require: browserRequire } = BrowserLoader("resource://devtools/client/shared/", this);
new file mode 100644
--- /dev/null
+++ b/devtools/client/shared/css-reload.js
@@ -0,0 +1,142 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+const { Services } = require("resource://gre/modules/Services.jsm");
+const { getTheme } = require("devtools/client/shared/theme");
+
+function iterStyleNodes(window, func) {
+  for (let node of window.document.childNodes) {
+    // Look for ProcessingInstruction nodes.
+    if (node.nodeType === 7) {
+      func(node);
+    }
+  }
+
+  const links = window.document.getElementsByTagNameNS(
+    "http://www.w3.org/1999/xhtml", "link"
+  );
+  for (let node of links) {
+    func(node);
+  }
+}
+
+function replaceCSS(window, fileURI) {
+  const document = window.document;
+  const randomKey = Math.random();
+  Services.obs.notifyObservers(null, "startupcache-invalidate", null);
+
+  // Scan every CSS tag and reload ones that match the file we are
+  // looking for.
+  iterStyleNodes(window, node => {
+    if (node.nodeType === 7) {
+      // xml-stylesheet declaration
+      if (node.data.includes(fileURI)) {
+        const newNode = window.document.createProcessingInstruction(
+          "xml-stylesheet",
+          `href="${fileURI}?s=${randomKey}" type="text/css"`
+        );
+        document.insertBefore(newNode, node);
+        document.removeChild(node);
+      }
+    } else if (node.href.includes(fileURI)) {
+      const parentNode = node.parentNode;
+      const newNode = window.document.createElementNS(
+        "http://www.w3.org/1999/xhtml",
+        "link"
+      );
+      newNode.rel = "stylesheet";
+      newNode.type = "text/css";
+      newNode.href = fileURI + "?s=" + randomKey;
+
+      parentNode.insertBefore(newNode, node);
+      parentNode.removeChild(node);
+    }
+  });
+}
+
+function _replaceResourceInSheet(sheet, filename, randomKey) {
+  for (let i = 0; i < sheet.cssRules.length; i++) {
+    const rule = sheet.cssRules[i];
+    if (rule.type === rule.IMPORT_RULE) {
+      _replaceResourceInSheet(rule.styleSheet, filename);
+    } else if (rule.cssText.includes(filename)) {
+      // Strip off any existing query strings. This might lose
+      // updates for files if there are multiple resources
+      // referenced in the same rule, but the chances of someone hot
+      // reloading multiple resources in the same rule is very low.
+      const text = rule.cssText.replace(/\?s=0.\d+/g, "");
+      const newRule = (
+        text.replace(filename, filename + "?s=" + randomKey)
+      );
+
+      sheet.deleteRule(i);
+      sheet.insertRule(newRule, i);
+    }
+  }
+}
+
+function replaceCSSResource(window, fileURI) {
+  const document = window.document;
+  const randomKey = Math.random();
+
+  // Only match the filename. False positives are much better than
+  // missing updates, as all that would happen is we reload more
+  // resources than we need. We do this because many resources only
+  // use relative paths.
+  const parts = fileURI.split("/");
+  const file = parts[parts.length - 1];
+
+  // Scan every single rule in the entire page for any reference to
+  // this resource, and re-insert the rule to force it to update.
+  for (let sheet of document.styleSheets) {
+    _replaceResourceInSheet(sheet, file, randomKey);
+  }
+
+  for (let node of document.querySelectorAll("img,image")) {
+    if (node.src.startsWith(fileURI)) {
+      node.src = fileURI + "?s=" + randomKey;
+    }
+  }
+}
+
+function watchCSS(window) {
+  if (Services.prefs.getBoolPref("devtools.loader.hotreload")) {
+    const watcher = require("devtools/client/shared/file-watcher");
+
+    function onFileChanged(_, relativePath) {
+      if (relativePath.match(/\.css$/)) {
+        if (relativePath.startsWith("client/themes")) {
+          let path = relativePath.replace(/^client\/themes\//, "");
+
+          // Special-case a few files that get imported from other CSS
+          // files. We just manually hot reload the parent CSS file.
+          if (path === "variables.css" || path === "toolbars.css" ||
+              path === "common.css" || path === "splitters.css") {
+            replaceCSS(window, "chrome://devtools/skin/" + getTheme() + "-theme.css");
+          } else {
+            replaceCSS(window, "chrome://devtools/skin/" + path);
+          }
+          return;
+        }
+
+        replaceCSS(
+          window,
+          "chrome://devtools/content/" +  relativePath.replace(/^client\//, "")
+        );
+        replaceCSS(window, "resource://devtools/" + relativePath);
+      } else if (relativePath.match(/\.(svg|png)$/)) {
+        relativePath = relativePath.replace(/^client\/themes\//, "");
+        replaceCSSResource(window, "chrome://devtools/skin/" + relativePath);
+      }
+    }
+    watcher.on("file-changed", onFileChanged);
+
+    window.addEventListener("unload", () => {
+      watcher.off("file-changed", onFileChanged);
+    });
+  }
+}
+
+module.exports = { watchCSS };
--- a/devtools/client/shared/developer-toolbar.js
+++ b/devtools/client/shared/developer-toolbar.js
@@ -208,46 +208,42 @@ exports.CommandUtils = CommandUtils;
  * Linux. See the comments for TooltipPanel and OutputPanel for further details.
  *
  * When bug 780102 is fixed all isLinux checks can be removed and we can revert
  * to using panels.
  */
 loader.lazyGetter(this, "isLinux", function() {
   return OS == "Linux";
 });
+loader.lazyGetter(this, "isMac", function() {
+  return OS == "Darwin";
+});
 
 loader.lazyGetter(this, "OS", function() {
   let os = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
   return os;
 });
 
 /**
  * A component to manage the global developer toolbar, which contains a GCLI
  * and buttons for various developer tools.
  * @param aChromeWindow The browser window to which this toolbar is attached
- * @param aToolbarElement See browser.xul:<toolbar id="developer-toolbar">
  */
-function DeveloperToolbar(aChromeWindow, aToolbarElement)
+function DeveloperToolbar(aChromeWindow)
 {
   this._chromeWindow = aChromeWindow;
 
   this.target = null; // Will be setup when show() is called
 
-  this._element = aToolbarElement;
-  this._element.hidden = true;
-  this._doc = this._element.ownerDocument;
+  this._doc = aChromeWindow.document;
 
   this._telemetry = new Telemetry();
   this._errorsCount = {};
   this._warningsCount = {};
   this._errorListeners = {};
-  this._errorCounterButton = this._doc
-                             .getElementById("developer-toolbar-toolbox-button");
-  this._errorCounterButton._defaultTooltipText =
-      this._errorCounterButton.getAttribute("tooltiptext");
 
   EventEmitter.decorate(this);
 }
 exports.DeveloperToolbar = DeveloperToolbar;
 
 /**
  * Inspector notifications dispatched through the nsIObserverService
  */
@@ -268,17 +264,17 @@ const NOTIFICATIONS = {
  */
 DeveloperToolbar.prototype.NOTIFICATIONS = NOTIFICATIONS;
 
 /**
  * Is the toolbar open?
  */
 Object.defineProperty(DeveloperToolbar.prototype, "visible", {
   get: function() {
-    return !this._element.hidden;
+    return this._element && !this._element.hidden;
   },
   enumerable: true
 });
 
 var _gSequenceId = 0;
 
 /**
  * Getter for a unique ID.
@@ -286,16 +282,72 @@ var _gSequenceId = 0;
 Object.defineProperty(DeveloperToolbar.prototype, "sequenceId", {
   get: function() {
     return _gSequenceId++;
   },
   enumerable: true
 });
 
 /**
+ * Create the <toolbar> element to insert within browser UI
+ */
+DeveloperToolbar.prototype.createToolbar = function() {
+  if (this._element) {
+    return;
+  }
+  let toolbar = this._doc.createElement("toolbar");
+  toolbar.setAttribute("id", "developer-toolbar");
+  toolbar.setAttribute("hidden", "true");
+
+  let close = this._doc.createElement("toolbarbutton");
+  close.setAttribute("id", "developer-toolbar-closebutton");
+  close.setAttribute("class", "devtools-closebutton");
+  close.setAttribute("oncommand", "DeveloperToolbar.hide();");
+  close.setAttribute("tooltiptext", "developerToolbarCloseButton.tooltiptext");
+
+  let stack = this._doc.createElement("stack");
+  stack.setAttribute("flex", "1");
+
+  let input = this._doc.createElement("textbox");
+  input.setAttribute("class", "gclitoolbar-input-node");
+  input.setAttribute("rows", "1");
+  stack.appendChild(input);
+
+  let hbox = this._doc.createElement("hbox");
+  hbox.setAttribute("class", "gclitoolbar-complete-node");
+  stack.appendChild(hbox);
+
+  let toolboxBtn = this._doc.createElement("toolbarbutton");
+  toolboxBtn.setAttribute("id", "developer-toolbar-toolbox-button");
+  toolboxBtn.setAttribute("class", "developer-toolbar-button");
+  toolboxBtn.setAttribute("observes", "devtoolsMenuBroadcaster_DevToolbox");
+  toolboxBtn.setAttribute("tooltiptext", "devToolbarToolsButton.tooltip");
+
+  // On Mac, the close button is on the left,
+  // while it is on the right on every other platforms.
+  if (isMac) {
+    toolbar.appendChild(close);
+    toolbar.appendChild(stack);
+    toolbar.appendChild(toolboxBtn);
+  } else {
+    toolbar.appendChild(stack);
+    toolbar.appendChild(toolboxBtn);
+    toolbar.appendChild(close);
+  }
+
+  let bottomBox = this._doc.getElementById("browser-bottombox");
+  this._element = toolbar;
+  bottomBox.appendChild(this._element);
+
+  this._errorCounterButton = toolboxBtn
+  this._errorCounterButton._defaultTooltipText =
+      this._errorCounterButton.getAttribute("tooltiptext");
+};
+
+/**
  * Called from browser.xul in response to menu-click or keyboard shortcut to
  * toggle the toolbar
  */
 DeveloperToolbar.prototype.toggle = function() {
   if (this.visible) {
     return this.hide().catch(console.error);
   } else {
     return this.show(true).catch(console.error);
@@ -351,16 +403,18 @@ DeveloperToolbar.prototype.show = functi
   if (this._showPromise != null) {
     return this._showPromise;
   }
 
   // hide() is async, so ensure we don't need to wait for hide() to finish
   var waitPromise = this._hidePromise || promise.resolve();
 
   this._showPromise = waitPromise.then(() => {
+    this.createToolbar();
+
     Services.prefs.setBoolPref("devtools.toolbar.visible", true);
 
     this._telemetry.toolOpened("developertoolbar");
 
     this._notify(NOTIFICATIONS.LOAD);
 
     this._input = this._doc.querySelector(".gclitoolbar-input-node");
 
@@ -378,17 +432,20 @@ DeveloperToolbar.prototype.show = functi
       this.target = TargetFactory.forTab(this._chromeWindow.gBrowser.selectedTab);
       const options = {
         environment: CommandUtils.createEnvironment(this, "target"),
         document: this.outputPanel.document,
       };
       return CommandUtils.createRequisition(this.target, options).then(requisition => {
         this.requisition = requisition;
 
-        return this.requisition.update(this._input.value).then(() => {
+        // The <textbox> `value` may still be undefined on the XUL binding if
+        // we fetch it early
+        let value = this._input.value || "";
+        return this.requisition.update(value).then(() => {
           const Inputter = require('gcli/mozui/inputter').Inputter;
           const Completer = require('gcli/mozui/completer').Completer;
           const Tooltip = require('gcli/mozui/tooltip').Tooltip;
           const FocusManager = require('gcli/ui/focus').FocusManager;
 
           this.onOutput = this.requisition.commandOutputManager.onOutput;
 
           this.focusManager = new FocusManager(this._doc, requisition.system.settings);
@@ -434,21 +491,32 @@ DeveloperToolbar.prototype.show = functi
           this._devtoolsUnloaded = this._devtoolsUnloaded.bind(this);
           this._devtoolsLoaded = this._devtoolsLoaded.bind(this);
           Services.obs.addObserver(this._devtoolsUnloaded, "devtools-unloaded", false);
           Services.obs.addObserver(this._devtoolsLoaded, "devtools-loaded", false);
 
           this._element.hidden = false;
 
           if (focus) {
-            this._input.focus();
+            // If the toolbar was just inserted, the <textbox> may still have
+            // its binding in process of being applied and not be focusable yet
+            let waitForBinding = () => {
+              // mInputField is a xbl field of <xul:textbox>
+              if (typeof this._input.mInputField != "undefined") {
+                this._input.focus();
+                this._notify(NOTIFICATIONS.SHOW);
+              } else {
+                this._input.ownerDocument.defaultView.setTimeout(waitForBinding, 50);
+              }
+            };
+            waitForBinding();
+          } else {
+            this._notify(NOTIFICATIONS.SHOW);
           }
 
-          this._notify(NOTIFICATIONS.SHOW);
-
           if (!DeveloperToolbar.introShownThisSession) {
             let intro = require("gcli/ui/intro");
             intro.maybeShowIntro(this.requisition.commandOutputManager,
                                  this.requisition.conversionContext);
             DeveloperToolbar.introShownThisSession = true;
           }
 
           this._showPromise = null;
@@ -594,16 +662,19 @@ DeveloperToolbar.prototype.destroy = fun
   this.focusManager.destroy();
 
   this.outputPanel.destroy();
   this.tooltipPanel.destroy();
   delete this._input;
 
   CommandUtils.destroyRequisition(this.requisition, this.target);
   this.target = undefined;
+
+  this._element.remove();
+  delete this._element;
 };
 
 /**
  * Utility for sending notifications
  * @param topic a NOTIFICATION constant
  */
 DeveloperToolbar.prototype._notify = function(topic) {
   let data = { toolbar: this };
--- a/devtools/client/shared/devices.js
+++ b/devtools/client/shared/devices.js
@@ -1,17 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { Ci, Cc } = require("chrome");
 const { getJSON } = require("devtools/client/shared/getjson");
-const { Services } = require("resource://gre/modules/Services.jsm");
+const Services = require("Services");
 const promise = require("promise");
 
 const DEVICES_URL = "devtools.devices.url";
 const Strings = Services.strings.createBundle("chrome://devtools/locale/device.properties");
 
 /* This is a catalog of common web-enabled devices and their properties,
  * intended for (mobile) device emulation.
  *
--- a/devtools/client/shared/doorhanger.js
+++ b/devtools/client/shared/doorhanger.js
@@ -1,16 +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 { Ci, Cc } = require("chrome");
-const { Services } = require("resource://gre/modules/Services.jsm");
+const Services = require("Services");
 const { DOMHelpers } = require("resource://devtools/client/shared/DOMHelpers.jsm");
 const { Task } = require("resource://gre/modules/Task.jsm");
 const { Promise } = require("resource://gre/modules/Promise.jsm");
 const { setTimeout } = require("sdk/timers");
 const { getMostRecentBrowserWindow } = require("sdk/window/utils");
 
 const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 const DEV_EDITION_PROMO_URL = "chrome://devtools/content/framework/dev-edition-promo/dev-edition-promo.xul";
--- a/devtools/client/shared/file-watcher-worker.js
+++ b/devtools/client/shared/file-watcher-worker.js
@@ -4,16 +4,27 @@
 "use strict";
 
 /* eslint-env worker */
 /* global OS */
 importScripts("resource://gre/modules/osfile.jsm");
 
 const modifiedTimes = new Map();
 
+function findSourceDir(path) {
+  if (path === "" || path === "/") {
+    return null;
+  } else if (OS.File.exists(
+    OS.Path.join(path, "devtools/client/shared/file-watcher.js")
+  )) {
+    return path;
+  }
+  return findSourceDir(OS.Path.dirname(path));
+}
+
 function gatherFiles(path, fileRegex) {
   let files = [];
   const iterator = new OS.File.DirectoryIterator(path);
 
   try {
     for (let child in iterator) {
       // Don't descend into test directories. Saves us some time and
       // there's no reason to.
@@ -55,24 +66,49 @@ function scanFiles(files, onChangedFile)
       modifiedTimes.set(file, info.lastModificationDate.getTime());
       onChangedFile(file);
     }
   });
 }
 
 onmessage = function(event) {
   const { path, fileRegex } = event.data;
-  let info = OS.File.stat(path);
+  const devtoolsPath = event.data.devtoolsPath.replace(/\/$/, "");
+
+  // We need to figure out a src dir to watch. These are the actual
+  // files the user is working with, not the files in the obj dir. We
+  // do this by walking up the filesystem and looking for the devtools
+  // directories, and falling back to the raw path. This means none of
+  // this will work for users who store their obj dirs outside of the
+  // src dir.
+  //
+  // We take care not to mess with the `devtoolsPath` if that's what
+  // we end up using, because it might be intentionally mapped to a
+  // specific place on the filesystem for loading devtools externally.
+  //
+  // `devtoolsPath` is currently the devtools directory inside of the
+  // obj dir, and we search for `devtools/client`, so go up 2 levels
+  // to skip that devtools dir and start searching for the src dir.
+  const searchPoint = OS.Path.dirname(OS.Path.dirname(devtoolsPath));
+  const srcPath = findSourceDir(searchPoint);
+  const rootPath = srcPath ? OS.Path.join(srcPath, "devtools") : devtoolsPath;
+  const watchPath = OS.Path.join(rootPath, path.replace(/^devtools\//, ""));
+
+  const info = OS.File.stat(watchPath);
   if (!info.isDir) {
-    throw new Error("watcher expects a directory as root path");
+    throw new Error("Watcher expects a directory as root path");
   }
 
   // We get a list of all the files upfront, which means we don't
   // support adding new files. But you need to rebuild Firefox when
   // adding a new file anyway.
-  const files = gatherFiles(path, fileRegex || /.*/);
+  const files = gatherFiles(watchPath, fileRegex || /.*/);
 
   // Every second, scan for file changes by stat-ing each of them and
   // comparing modification time.
   setInterval(() => {
-    scanFiles(files, changedFile => postMessage(changedFile));
+    scanFiles(files, changedFile => {
+      postMessage({ fullPath: changedFile,
+                    relativePath: changedFile.replace(rootPath + "/", "") });
+    });
   }, 1000);
 };
+
--- a/devtools/client/shared/file-watcher.js
+++ b/devtools/client/shared/file-watcher.js
@@ -1,71 +1,62 @@
 /* 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 { Ci, ChromeWorker } = require("chrome");
-const { Services } = require("resource://gre/modules/Services.jsm");
+const Services = require("Services");
 const EventEmitter = require("devtools/shared/event-emitter");
 
 const HOTRELOAD_PREF = "devtools.loader.hotreload";
 
-function resolveResourceURI(uri) {
+function resolveResourcePath(uri) {
   const handler = Services.io.getProtocolHandler("resource")
         .QueryInterface(Ci.nsIResProtocolHandler);
-  return handler.resolveURI(Services.io.newURI(uri, null, null));
+  const resolved = handler.resolveURI(Services.io.newURI(uri, null, null));
+  return resolved.replace(/file:\/\//, "");
 }
 
 function watchFiles(path, onFileChanged) {
   if (!path.startsWith("devtools/")) {
     throw new Error("`watchFiles` expects a devtools path");
   }
 
-  // We need to figure out a local path to watch. We start with
-  // whatever devtools points to.
-  let resolvedRootURI = resolveResourceURI("resource://devtools");
-  if (resolvedRootURI.match(/\/obj\-.*/)) {
-    // Move from the built directory to the user's local files
-    resolvedRootURI = resolvedRootURI.replace(/\/obj\-.*/, "") + "/devtools";
-  }
-  resolvedRootURI = resolvedRootURI.replace(/^file:\/\//, "");
-  const localURI = resolvedRootURI + "/" + path.replace(/^devtools\//, "");
-
   const watchWorker = new ChromeWorker(
     "resource://devtools/client/shared/file-watcher-worker.js"
   );
 
   watchWorker.onmessage = event => {
     // We need to turn a local path back into a resource URI (or
     // chrome). This means that this system will only work when built
     // files are symlinked, so that these URIs actually read from
     // local sources. There might be a better way to do this.
-    const relativePath = event.data.replace(resolvedRootURI + "/", "");
-    if (relativePath.startsWith("client/themes")) {
-      onFileChanged(relativePath.replace("client/themes",
-                                         "chrome://devtools/skin"));
-    }
-    onFileChanged("resource://devtools/" + relativePath);
+    const { relativePath, fullPath } = event.data;
+    onFileChanged(relativePath, fullPath);
   };
 
-  watchWorker.postMessage({ path: localURI, fileRegex: /\.(js|css)$/ });
+  watchWorker.postMessage({
+    path: path,
+    // We must do this here because we can't access the needed APIs in
+    // a worker.
+    devtoolsPath: resolveResourcePath("resource://devtools"),
+    fileRegex: /\.(js|css|svg|png)$/ });
   return watchWorker;
 }
 
 EventEmitter.decorate(module.exports);
 
 let watchWorker;
 function onPrefChange() {
   if (Services.prefs.getBoolPref(HOTRELOAD_PREF) && !watchWorker) {
-    watchWorker = watchFiles("devtools/client", changedFile => {
-      module.exports.emit("file-changed", changedFile);
+    watchWorker = watchFiles("devtools/client", (relativePath, fullPath) => {
+      module.exports.emit("file-changed", relativePath, fullPath);
     });
-  }
-  else if(watchWorker) {
+  } else if (watchWorker) {
     watchWorker.terminate();
     watchWorker = null;
   }
 }
 
 Services.prefs.addObserver(HOTRELOAD_PREF, {
   observe: onPrefChange
 }, false);
--- a/devtools/client/shared/getjson.js
+++ b/devtools/client/shared/getjson.js
@@ -1,15 +1,15 @@
 /* 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, CC} = require("chrome");
 const promise = require("promise");
-const {Services} = Cu.import("resource://gre/modules/Services.jsm");
+const Services = require("Services");
 
 const XMLHttpRequest = CC("@mozilla.org/xmlextras/xmlhttprequest;1");
 
 // Downloads and caches a JSON file from a URL given by the pref.
 exports.getJSON = function (prefName, bypassCache) {
   if (!bypassCache) {
     try {
       let str = Services.prefs.getCharPref(prefName + "_cache");
--- a/devtools/client/shared/inplace-editor.js
+++ b/devtools/client/shared/inplace-editor.js
@@ -19,30 +19,30 @@
  * });
  *
  * See editableField() for more options.
  */
 
 "use strict";
 
 const {Ci, Cu, Cc} = require("chrome");
+const Services = require("Services");
 
 const HTML_NS = "http://www.w3.org/1999/xhtml";
 const CONTENT_TYPES = {
   PLAIN_TEXT: 0,
   CSS_VALUE: 1,
   CSS_MIXED: 2,
   CSS_PROPERTY: 3,
 };
 const MAX_POPUP_ENTRIES = 10;
 
 const FOCUS_FORWARD = Ci.nsIFocusManager.MOVEFOCUS_FORWARD;
 const FOCUS_BACKWARD = Ci.nsIFocusManager.MOVEFOCUS_BACKWARD;
 
-Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://devtools/shared/event-emitter.js");
 
 /**
  * Mark a span editable.  |editableField| will listen for the span to
  * be focused and create an InlineEditor to handle text input.
  * Changes will be committed when the InlineEditor's input is blurred
  * or dropped when the user presses escape.
--- a/devtools/client/shared/moz.build
+++ b/devtools/client/shared/moz.build
@@ -14,16 +14,17 @@ DIRS += [
     'widgets',
 ]
 
 DevToolsModules(
     'AppCacheUtils.jsm',
     'autocomplete-popup.js',
     'browser-loader.js',
     'css-parsing-utils.js',
+    'css-reload.js',
     'Curl.jsm',
     'demangle.js',
     'developer-toolbar.js',
     'devices.js',
     'DOMHelpers.jsm',
     'doorhanger.js',
     'file-watcher-worker.js',
     'file-watcher.js',
--- a/devtools/client/shared/options-view.js
+++ b/devtools/client/shared/options-view.js
@@ -1,10 +1,10 @@
 const EventEmitter = require("devtools/shared/event-emitter");
-const { Services } = require("resource://gre/modules/Services.jsm");
+const Services = require("Services");
 const { Preferences } = require("resource://gre/modules/Preferences.jsm");
 const OPTIONS_SHOWN_EVENT = "options-shown";
 const OPTIONS_HIDDEN_EVENT = "options-hidden";
 const PREF_CHANGE_EVENT = "pref-changed";
 
 /**
  * OptionsView constructor. Takes several options, all required:
  * - branchName: The name of the prefs branch, like "devtools.debugger."
--- a/devtools/client/shared/output-parser.js
+++ b/devtools/client/shared/output-parser.js
@@ -1,17 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {Cc, Ci, Cu} = require("chrome");
 const {colorUtils} = require("devtools/shared/css-color");
-const {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
+const Services = require("Services");
 
 const HTML_NS = "http://www.w3.org/1999/xhtml";
 
 const BEZIER_KEYWORDS = ["linear", "ease-in-out", "ease-in", "ease-out",
                          "ease"];
 
 // Functions that accept a color argument.
 const COLOR_TAKING_FUNCTIONS = ["linear-gradient",
--- a/devtools/client/shared/telemetry.js
+++ b/devtools/client/shared/telemetry.js
@@ -50,17 +50,17 @@ this.Telemetry = function() {
   this.destroy = this.destroy.bind(this);
 
   this._timers = new Map();
 };
 
 module.exports = Telemetry;
 
 var {Cc, Ci, Cu} = require("chrome");
-var {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
+var Services = require("Services");
 var {XPCOMUtils} = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
 
 Telemetry.prototype = {
   _histograms: {
     toolbox: {
       histogram: "DEVTOOLS_TOOLBOX_OPENED_COUNT",
       userHistogram: "DEVTOOLS_TOOLBOX_OPENED_PER_USER_FLAG",
       timerHistogram: "DEVTOOLS_TOOLBOX_TIME_ACTIVE_SECONDS"
--- a/devtools/client/shared/test/browser_telemetry_button_responsive.js
+++ b/devtools/client/shared/test/browser_telemetry_button_responsive.js
@@ -3,16 +3,18 @@
 
 const TEST_URI = "data:text/html;charset=utf-8," +
   "<p>browser_telemetry_button_responsive.js</p>";
 
 // Because we need to gather stats for the period of time that a tool has been
 // opened we make use of setTimeout() to create tool active times.
 const TOOL_DELAY = 200;
 
+const { ResponsiveUIManager } = Cu.import("resource://devtools/client/responsivedesign/responsivedesign.jsm", {});
+
 add_task(function*() {
   yield addTab(TEST_URI);
   let Telemetry = loadTelemetryAndRecordLogs();
 
   let target = TargetFactory.forTab(gBrowser.selectedTab);
   let toolbox = yield gDevTools.showToolbox(target, "inspector");
   info("inspector opened");
 
@@ -33,23 +35,22 @@ function* testButton(toolbox, Telemetry)
   yield delayedClicks(button, 4);
 
   checkResults("_RESPONSIVE_", Telemetry);
 }
 
 function waitForToggle() {
   return new Promise(resolve => {
     let handler = () => {
-      manager.off("on", handler);
-      manager.off("off", handler);
+      ResponsiveUIManager.off("on", handler);
+      ResponsiveUIManager.off("off", handler);
       resolve();
     };
-    let manager = ResponsiveUI.ResponsiveUIManager;
-    manager.on("on", handler);
-    manager.on("off", handler);
+    ResponsiveUIManager.on("on", handler);
+    ResponsiveUIManager.on("off", handler);
   });
 }
 
 var delayedClicks = Task.async(function*(node, clicks) {
   for (let i = 0; i < clicks; i++) {
     info("Clicking button " + node.id);
     let toggled = waitForToggle();
     node.click();
--- a/devtools/client/shared/test/browser_toolbar_webconsole_errors_count.js
+++ b/devtools/client/shared/test/browser_toolbar_webconsole_errors_count.js
@@ -1,18 +1,18 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that the developer toolbar errors count works properly.
 
 function test() {
   const TEST_URI = TEST_URI_ROOT + "browser_toolbar_webconsole_errors_count.html";
 
-  let webconsole = document.getElementById("developer-toolbar-toolbox-button");
-  let tab1, tab2;
+
+  let tab1, tab2, webconsole;
 
   Services.prefs.setBoolPref("javascript.options.strict", true);
 
   registerCleanupFunction(() => {
     Services.prefs.clearUserPref("javascript.options.strict");
   });
 
   ignoreAllUncaughtExceptions();
@@ -29,16 +29,17 @@ function test() {
     }
     else {
       onOpenToolbar();
     }
   }
 
   function onOpenToolbar() {
     ok(DeveloperToolbar.visible, "DeveloperToolbar is visible");
+    webconsole = document.getElementById("developer-toolbar-toolbox-button");
 
     waitForButtonUpdate({
       name: "web console button shows page errors",
       errors: 3,
       warnings: 0,
       callback: addErrors,
     });
   }
--- a/devtools/client/shared/theme-switching.js
+++ b/devtools/client/shared/theme-switching.js
@@ -153,24 +153,27 @@
 
   function handlePrefChange(event, data) {
     if (data.pref == "devtools.theme") {
       switchTheme(data.newValue, data.oldValue);
     }
   }
 
   const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
-  Cu.import("resource://gre/modules/Services.jsm");
-  const {require} = Components.utils.import("resource://devtools/shared/Loader.jsm", {});
-  const {gDevTools} = require("devtools/client/framework/devtools");
+  const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
+  const Services = require("Services");
+  const { gDevTools } = require("devtools/client/framework/devtools");
   const StylesheetUtils = require("sdk/stylesheet/utils");
+  const { watchCSS } = require("devtools/client/shared/css-reload");
 
   if (documentElement.hasAttribute("force-theme")) {
     switchTheme(documentElement.getAttribute("force-theme"));
   } else {
     switchTheme(Services.prefs.getCharPref("devtools.theme"));
 
     gDevTools.on("pref-changed", handlePrefChange);
     window.addEventListener("unload", function() {
       gDevTools.off("pref-changed", handlePrefChange);
     });
   }
+
+  watchCSS(window);
 })();
--- a/devtools/client/shared/theme.js
+++ b/devtools/client/shared/theme.js
@@ -6,17 +6,17 @@
 
 /**
  * Colors for themes taken from:
  * https://developer.mozilla.org/en-US/docs/Tools/DevToolsColors
  */
 
 const { Ci, Cu } = require("chrome");
 const { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {});
-loader.lazyRequireGetter(this, "Services");
+const Services = require("Services");
 const { gDevTools } = require("devtools/client/framework/devtools");
 
 const VARIABLES_URI = "chrome://devtools/skin/variables.css";
 const THEME_SELECTOR_STRINGS = {
   light: ":root.theme-light {",
   dark: ":root.theme-dark {"
 }
 
--- a/devtools/client/shared/view-source.js
+++ b/devtools/client/shared/view-source.js
@@ -1,17 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-loader.lazyRequireGetter(this, "Services");
 loader.lazyImporter(this, "Task", "resource://gre/modules/Task.jsm");
 
+var Services = require("Services");
 var {gDevTools} = require("devtools/client/framework/devtools");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
 
 /**
  * Tries to open a Stylesheet file in the Style Editor. If the file is not found,
  * it is opened in source view instead.
  * Returns a promise resolving to a boolean indicating whether or not
  * the source was able to be displayed in the StyleEditor, as the built-in Firefox
--- a/devtools/client/shared/widgets/MdnDocsWidget.js
+++ b/devtools/client/shared/widgets/MdnDocsWidget.js
@@ -20,17 +20,17 @@
  * - the MdnDocsWidget class, that manages and updates a tooltip
  * document whose content is taken from MDN. If you want to embed
  * the content in a tooltip, use this in conjunction with Tooltip.js.
  */
 
 "use strict";
 
 const {Cc, Cu, Ci} = require("chrome");
-Cu.import("resource://gre/modules/Services.jsm");
+const Services = require("Services");
 const Promise = require("promise");
 const DOMUtils = Cc["@mozilla.org/inspector/dom-utils;1"]
                  .getService(Ci.inIDOMUtils);
 
 // Parameters for the XHR request
 // see https://developer.mozilla.org/en-US/docs/MDN/Kuma/API#Document_parameters
 const XHR_PARAMS = "?raw&macros";
 // URL for the XHR request
--- a/devtools/client/shared/widgets/Tooltip.js
+++ b/devtools/client/shared/widgets/Tooltip.js
@@ -11,20 +11,20 @@ const {CubicBezierWidget} =
       require("devtools/client/shared/widgets/CubicBezierWidget");
 const {MdnDocsWidget} = require("devtools/client/shared/widgets/MdnDocsWidget");
 const {CSSFilterEditorWidget} = require("devtools/client/shared/widgets/FilterWidget");
 const EventEmitter = require("devtools/shared/event-emitter");
 const {colorUtils} = require("devtools/shared/css-color");
 const Heritage = require("sdk/core/heritage");
 const {Eyedropper} = require("devtools/client/eyedropper/eyedropper");
 const Editor = require("devtools/client/sourceeditor/editor");
+const Services = require("Services");
 
 loader.lazyRequireGetter(this, "beautify", "devtools/shared/jsbeautify/beautify");
 
-Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "setNamedTimeout",
   "resource://devtools/client/shared/widgets/ViewHelpers.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "clearNamedTimeout",
   "resource://devtools/client/shared/widgets/ViewHelpers.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "VariablesView",
   "resource://devtools/client/shared/widgets/VariablesView.jsm");
--- a/devtools/client/shared/widgets/VariablesView.jsm
+++ b/devtools/client/shared/widgets/VariablesView.jsm
@@ -12,23 +12,23 @@ const DBG_STRINGS_URI = "chrome://devtoo
 const LAZY_EMPTY_DELAY = 150; // ms
 const LAZY_EXPAND_DELAY = 50; // ms
 const SCROLL_PAGE_SIZE_DEFAULT = 0;
 const PAGE_SIZE_SCROLL_HEIGHT_RATIO = 100;
 const PAGE_SIZE_MAX_JUMPS = 30;
 const SEARCH_ACTION_MAX_DELAY = 300; // ms
 const ITEM_FLASH_DURATION = 300 // ms
 
-Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://devtools/client/shared/widgets/ViewHelpers.jsm");
 Cu.import("resource://devtools/shared/event-emitter.js");
 Cu.import("resource://gre/modules/Task.jsm");
 const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+const Services = require("Services");
 const promise = require("promise");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
   "resource://gre/modules/PluralForm.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "clipboardHelper",
   "@mozilla.org/widget/clipboardhelper;1",
   "nsIClipboardHelper");
--- a/devtools/client/shared/widgets/VariablesViewController.jsm
+++ b/devtools/client/shared/widgets/VariablesViewController.jsm
@@ -2,21 +2,21 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* 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 { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
-Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://devtools/client/shared/widgets/VariablesView.jsm");
 Cu.import("resource://devtools/client/shared/widgets/ViewHelpers.jsm");
 var {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
+var Services = require("Services");
 var promise = require("promise");
 
 Object.defineProperty(this, "WebConsoleUtils", {
   get: function() {
     return require("devtools/shared/webconsole/utils").Utils;
   },
   configurable: true,
   enumerable: true
--- a/devtools/client/shared/widgets/ViewHelpers.jsm
+++ b/devtools/client/shared/widgets/ViewHelpers.jsm
@@ -8,20 +8,20 @@
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 const PANE_APPEARANCE_DELAY = 50;
 const PAGE_SIZE_ITEM_COUNT_RATIO = 5;
 const WIDGET_FOCUSABLE_NODES = new Set(["vbox", "hbox"]);
 
-Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Timer.jsm");
 const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
+const Services = require("Services");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 Cu.import("resource://devtools/shared/event-emitter.js");
 
 this.EXPORTED_SYMBOLS = [
   "Heritage", "ViewHelpers", "WidgetMethods",
   "setNamedTimeout", "clearNamedTimeout",
   "setConditionalTimeout", "clearConditionalTimeout",
 ];
--- a/devtools/client/shims/gDevTools.jsm
+++ b/devtools/client/shims/gDevTools.jsm
@@ -6,17 +6,17 @@
 
 /**
  * This file only exists to support add-ons which import this module at a
  * specific path.
  */
 
 const Cu = Components.utils;
 
-const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
+const Services = require("Services");
 
 const WARNING_PREF = "devtools.migration.warnings";
 if (Services.prefs.getBoolPref(WARNING_PREF)) {
   const { Deprecated } = Cu.import("resource://gre/modules/Deprecated.jsm", {});
   Deprecated.warning("This path to gDevTools.jsm is deprecated.  Please use " +
                      "Cu.import(\"resource://devtools/client/" +
                      "framework/gDevTools.jsm\") to load this module.",
                      "https://bugzil.la/912121");
--- a/devtools/client/sourceeditor/editor.js
+++ b/devtools/client/sourceeditor/editor.js
@@ -27,21 +27,21 @@ const VALID_KEYMAPS = new Set(["emacs", 
 // while shifting to a line which was initially out of view.
 const MAX_VERTICAL_OFFSET = 3;
 
 // Match @Scratchpad/N:LINE[:COLUMN] or (LINE[:COLUMN]) anywhere at an end of
 // line in text selection.
 const RE_SCRATCHPAD_ERROR = /(?:@Scratchpad\/\d+:|\()(\d+):?(\d+)?(?:\)|\n)/;
 const RE_JUMP_TO_LINE = /^(\d+):?(\d+)?/;
 
+const Services = require("Services");
 const promise = require("promise");
 const events = require("devtools/shared/event-emitter");
 const { PrefObserver } = require("devtools/client/styleeditor/utils");
 
-Cu.import("resource://gre/modules/Services.jsm");
 const L10N = Services.strings.createBundle(L10N_BUNDLE);
 
 const { OS } = Services.appinfo;
 
 // CM_STYLES, CM_SCRIPTS and CM_IFRAME represent the HTML,
 // JavaScript and CSS that is injected into an iframe in
 // order to initialize a CodeMirror instance.
 
--- a/devtools/client/styleeditor/StyleEditorUtil.jsm
+++ b/devtools/client/styleeditor/StyleEditorUtil.jsm
@@ -16,21 +16,20 @@ this.EXPORTED_SYMBOLS = [
   "wire",
   "showFilePicker"
 ];
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
-Cu.import("resource://gre/modules/Services.jsm");
-
 const PROPERTIES_URL = "chrome://devtools/locale/styleeditor.properties";
 
 const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
+const Services = require("Services");
 const console = require("resource://gre/modules/Console.jsm").console;
 const gStringBundle = Services.strings.createBundle(PROPERTIES_URL);
 
 /**
  * Returns a localized string with the given key name from the string bundle.
  *
  * @param name
  * @param ...rest
--- a/devtools/client/styleeditor/StyleSheetEditor.jsm
+++ b/devtools/client/styleeditor/StyleSheetEditor.jsm
@@ -11,18 +11,18 @@ const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
 const Editor = require("devtools/client/sourceeditor/editor");
 const promise = require("promise");
 const {CssLogic} = require("devtools/shared/inspector/css-logic");
 const {console} = require("resource://gre/modules/Console.jsm");
+const Services = require("Services");
 
-Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/FileUtils.jsm");
 Cu.import("resource://gre/modules/NetUtil.jsm");
 const { TextDecoder, OS } = Cu.import("resource://gre/modules/osfile.jsm", {});
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource://devtools/shared/event-emitter.js");
 /* import-globals-from StyleEditorUtil.jsm */
 Cu.import("resource://devtools/client/styleeditor/StyleEditorUtil.jsm");
 
--- a/devtools/client/styleeditor/styleeditor-panel.js
+++ b/devtools/client/styleeditor/styleeditor-panel.js
@@ -4,18 +4,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 {Cu} = require("chrome");
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
 
+var Services = require("Services");
 var promise = require("promise");
 var EventEmitter = require("devtools/shared/event-emitter");
 
 Cu.import("resource://devtools/client/styleeditor/StyleEditorUI.jsm");
 /* import-globals-from StyleEditorUtil.jsm */
 Cu.import("resource://devtools/client/styleeditor/StyleEditorUtil.jsm");
 
 loader.lazyGetter(this, "StyleSheetsFront",
--- a/devtools/client/styleeditor/utils.js
+++ b/devtools/client/styleeditor/utils.js
@@ -2,18 +2,18 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* 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 {Cu} = require("chrome");
+const Services = require("Services");
 
-Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://devtools/shared/event-emitter.js");
 
 exports.PREF_ORIG_SOURCES = "devtools.styleeditor.source-maps-enabled";
 
 /**
  * A PreferenceObserver observes a pref branch for pref changes.
  * It emits an event for each preference change.
  */
--- a/devtools/client/themes/dark-theme.css
+++ b/devtools/client/themes/dark-theme.css
@@ -314,33 +314,45 @@ div.CodeMirror span.eval-text {
 .theme-tooltip-panel .panel-arrow {
   --arrow-margin: -4px;
 }
 
 :root[platform="win"] .theme-tooltip-panel .panel-arrow {
   --arrow-margin: -7px;
 }
 
+.theme-tooltip-panel .panel-arrow[side="top"],
+.theme-tooltip-panel .panel-arrow[side="bottom"] {
+  list-style-image: url("chrome://devtools/skin/tooltip/arrow-vertical-dark.png");
+  /* !important is needed to override the popup.css rules in toolkit/themes */
+  width: 39px !important;
+  height: 16px !important;
+}
+
+.theme-tooltip-panel .panel-arrow[side="left"],
+.theme-tooltip-panel .panel-arrow[side="right"] {
+  list-style-image: url("chrome://devtools/skin/tooltip/arrow-horizontal-dark.png");
+  /* !important is needed to override the popup.css rules in toolkit/themes */
+  width: 16px !important;
+  height: 39px !important;
+}
+
 .theme-tooltip-panel .panel-arrow[side="top"] {
-  list-style-image: url("chrome://devtools/skin/tooltip/arrow-vertical-dark.png");
   margin-bottom: var(--arrow-margin);
 }
 
 .theme-tooltip-panel .panel-arrow[side="bottom"] {
-  list-style-image: url("chrome://devtools/skin/tooltip/arrow-vertical-dark.png");
   margin-top: var(--arrow-margin);
 }
 
 .theme-tooltip-panel .panel-arrow[side="left"] {
-  list-style-image: url("chrome://devtools/skin/tooltip/arrow-horizontal-dark.png");
   margin-right: var(--arrow-margin);
 }
 
 .theme-tooltip-panel .panel-arrow[side="right"] {
-  list-style-image: url("chrome://devtools/skin/tooltip/arrow-horizontal-dark.png");
   margin-left: var(--arrow-margin);
 }
 
 @media (min-resolution: 1.1dppx) {
   .theme-tooltip-panel .panel-arrow[side="top"],
   .theme-tooltip-panel .panel-arrow[side="bottom"] {
     list-style-image: url("chrome://devtools/skin/tooltip/arrow-vertical-dark@2x.png");
   }
--- a/devtools/client/themes/light-theme.css
+++ b/devtools/client/themes/light-theme.css
@@ -323,33 +323,45 @@ div.CodeMirror span.eval-text {
 .theme-tooltip-panel .panel-arrow {
   --arrow-margin: -4px;
 }
 
 :root[platform="win"] .theme-tooltip-panel .panel-arrow {
   --arrow-margin: -7px;
 }
 
+.theme-tooltip-panel .panel-arrow[side="top"],
+.theme-tooltip-panel .panel-arrow[side="bottom"] {
+  list-style-image: url("chrome://devtools/skin/tooltip/arrow-vertical-light.png");
+  /* !important is needed to override the popup.css rules in toolkit/themes */
+  width: 39px !important;
+  height: 16px !important;
+}
+
+.theme-tooltip-panel .panel-arrow[side="left"],
+.theme-tooltip-panel .panel-arrow[side="right"] {
+  list-style-image: url("chrome://devtools/skin/tooltip/arrow-horizontal-light.png");
+  /* !important is needed to override the popup.css rules in toolkit/themes */
+  width: 16px !important;
+  height: 39px !important;
+}
+
 .theme-tooltip-panel .panel-arrow[side="top"] {
-  list-style-image: url("chrome://devtools/skin/tooltip/arrow-vertical-light.png");
   margin-bottom: var(--arrow-margin);
 }
 
 .theme-tooltip-panel .panel-arrow[side="bottom"] {
-  list-style-image: url("chrome://devtools/skin/tooltip/arrow-vertical-light.png");
   margin-top: var(--arrow-margin);
 }
 
 .theme-tooltip-panel .panel-arrow[side="left"] {
-  list-style-image: url("chrome://devtools/skin/tooltip/arrow-horizontal-light.png");
   margin-right: var(--arrow-margin);
 }
 
 .theme-tooltip-panel .panel-arrow[side="right"] {
-  list-style-image: url("chrome://devtools/skin/tooltip/arrow-horizontal-light.png");
   margin-left: var(--arrow-margin);
 }
 
 @media (min-resolution: 1.1dppx) {
   .theme-tooltip-panel .panel-arrow[side="top"],
   .theme-tooltip-panel .panel-arrow[side="bottom"] {
     list-style-image: url("chrome://devtools/skin/tooltip/arrow-vertical-light@2x.png");
   }
--- a/devtools/client/themes/splitters.css
+++ b/devtools/client/themes/splitters.css
@@ -1,19 +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/. */
 
 /* Splitters */
-:root[devtoolstheme="light"] .devtools-horizontal-splitter {
+:root[devtoolstheme="light"] {
   /* These variables are used in browser.xul but inside the toolbox they are overridden by --theme-splitter-color */
   --devtools-splitter-color: #dde1e4;
 }
 
-:root[devtoolstheme="dark"] .devtools-horizontal-splitter {
+:root[devtoolstheme="dark"] {
   --devtools-splitter-color: #42484f;
 }
 
 .devtools-horizontal-splitter {
   -moz-appearance: none;
   background-image: none;
   background-color: transparent;
   border: 0;
--- a/devtools/client/tilt/tilt-gl.js
+++ b/devtools/client/tilt/tilt-gl.js
@@ -4,18 +4,17 @@
  * 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 {Cc, Ci, Cu} = require("chrome");
 
 var TiltUtils = require("devtools/client/tilt/tilt-utils");
 var {TiltMath, mat4} = require("devtools/client/tilt/tilt-math");
-
-Cu.import("resource://gre/modules/Services.jsm");
+var Services = require("Services");
 
 const WEBGL_CONTEXT_NAME = "experimental-webgl";
 
 
 /**
  * Module containing thin wrappers around low-level WebGL functions.
  */
 var TiltGL = {};
--- a/devtools/client/tilt/tilt-utils.js
+++ b/devtools/client/tilt/tilt-utils.js
@@ -2,18 +2,18 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* 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 {Cc, Ci, Cu} = require("chrome");
 const {getRect} = require("devtools/shared/layout/utils");
+const Services = require("Services");
 
-Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 const STACK_THICKNESS = 15;
 
 /**
  * Module containing various helper functions used throughout Tilt.
  */
 this.TiltUtils = {};
--- a/devtools/client/tilt/tilt-visualizer.js
+++ b/devtools/client/tilt/tilt-visualizer.js
@@ -2,23 +2,22 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* 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 {Cu, Ci, ChromeWorker} = require("chrome");
 
+var Services = require("Services");
 var TiltGL = require("devtools/client/tilt/tilt-gl");
 var TiltUtils = require("devtools/client/tilt/tilt-utils");
 var TiltVisualizerStyle = require("devtools/client/tilt/tilt-visualizer-style");
 var {EPSILON, TiltMath, vec3, mat4, quat4} = require("devtools/client/tilt/tilt-math");
 var {TargetFactory} = require("devtools/client/framework/target");
-
-Cu.import("resource://gre/modules/Services.jsm");
 var {gDevTools} = require("devtools/client/framework/devtools");
 
 const ELEMENT_MIN_SIZE = 4;
 const INVISIBLE_ELEMENTS = {
   "head": true,
   "base": true,
   "basefont": true,
   "isindex": true,
--- a/devtools/client/tilt/tilt.js
+++ b/devtools/client/tilt/tilt.js
@@ -7,18 +7,17 @@
 
 const {Cu} = require("chrome");
 
 var {TiltVisualizer} = require("devtools/client/tilt/tilt-visualizer");
 var TiltGL = require("devtools/client/tilt/tilt-gl");
 var TiltUtils = require("devtools/client/tilt/tilt-utils");
 var EventEmitter = require("devtools/shared/event-emitter");
 var Telemetry = require("devtools/client/shared/telemetry");
-
-Cu.import("resource://gre/modules/Services.jsm");
+var Services = require("Services");
 
 // Tilt notifications dispatched through the nsIObserverService.
 const TILT_NOTIFICATIONS = {
   // Called early in the startup of a new tilt instance
   STARTUP: "tilt-startup",
 
   // Fires when Tilt starts the initialization.
   INITIALIZING: "tilt-initializing",
--- a/devtools/client/webaudioeditor/includes.js
+++ b/devtools/client/webaudioeditor/includes.js
@@ -1,32 +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/. */
 "use strict";
 
 var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
-Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://devtools/client/shared/widgets/ViewHelpers.jsm");
 
 const { loader, require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 
 var { console } = Cu.import("resource://gre/modules/Console.jsm", {});
 var { EventTarget } = require("sdk/event/target");
 
 const { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
 const { Class } = require("sdk/core/heritage");
 const EventEmitter = require("devtools/shared/event-emitter");
 const STRINGS_URI = "chrome://devtools/locale/webaudioeditor.properties"
 const L10N = new ViewHelpers.L10N(STRINGS_URI);
 const Telemetry = require("devtools/client/shared/telemetry");
 const telemetry = new Telemetry();
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+const Services = require("Services");
 const { gDevTools } = require("devtools/client/framework/devtools");
 
 loader.lazyRequireGetter(this, "LineGraphWidget",
   "devtools/client/shared/widgets/LineGraphWidget");
 
 // `AUDIO_NODE_DEFINITION` defined in the controller's initialization,
 // which describes all the properties of an AudioNode
 var AUDIO_NODE_DEFINITION;
--- a/devtools/client/webaudioeditor/test/head.js
+++ b/devtools/client/webaudioeditor/test/head.js
@@ -1,29 +1,24 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
 var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
-var { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
-
-// Enable logging for all the tests. Both the debugger server and frontend will
-// be affected by this pref.
-var gEnableLogging = Services.prefs.getBoolPref("devtools.debugger.log");
-Services.prefs.setBoolPref("devtools.debugger.log", false);
-
 var { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
 var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
+var Services = require("Services");
 var { gDevTools } = require("devtools/client/framework/devtools");
 var { TargetFactory } = require("devtools/client/framework/target");
 var { DebuggerServer } = require("devtools/server/main");
 var { generateUUID } = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
 
 var Promise = require("promise");
+var Services = require("Services");
 var { WebAudioFront } = require("devtools/server/actors/webaudio");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
 var audioNodes = require("devtools/server/actors/utils/audionodes.json");
 var mm = null;
 
 const FRAME_SCRIPT_UTILS_URL = "chrome://devtools/content/shared/frame-script-utils.js";
 const EXAMPLE_URL = "http://example.com/browser/devtools/client/webaudioeditor/test/";
 const SIMPLE_CONTEXT_URL = EXAMPLE_URL + "doc_simple-context.html";
@@ -32,16 +27,21 @@ const SIMPLE_NODES_URL = EXAMPLE_URL + "
 const MEDIA_NODES_URL = EXAMPLE_URL + "doc_media-node-creation.html";
 const BUFFER_AND_ARRAY_URL = EXAMPLE_URL + "doc_buffer-and-array.html";
 const DESTROY_NODES_URL = EXAMPLE_URL + "doc_destroy-nodes.html";
 const CONNECT_PARAM_URL = EXAMPLE_URL + "doc_connect-param.html";
 const CONNECT_MULTI_PARAM_URL = EXAMPLE_URL + "doc_connect-multi-param.html";
 const IFRAME_CONTEXT_URL = EXAMPLE_URL + "doc_iframe-context.html";
 const AUTOMATION_URL = EXAMPLE_URL + "doc_automation.html";
 
+// Enable logging for all the tests. Both the debugger server and frontend will
+// be affected by this pref.
+var gEnableLogging = Services.prefs.getBoolPref("devtools.debugger.log");
+Services.prefs.setBoolPref("devtools.debugger.log", false);
+
 // All tests are asynchronous.
 waitForExplicitFinish();
 
 var gToolEnabled = Services.prefs.getBoolPref("devtools.webaudioeditor.enabled");
 
 DevToolsUtils.testing = true;
 
 registerCleanupFunction(() => {
--- a/devtools/client/webconsole/console-output.js
+++ b/devtools/client/webconsole/console-output.js
@@ -3,17 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {Cc, Ci, Cu} = require("chrome");
 
-const { Services } = require("resource://gre/modules/Services.jsm");
+const Services = require("Services");
 
 loader.lazyImporter(this, "VariablesView", "resource://devtools/client/shared/widgets/VariablesView.jsm");
 loader.lazyImporter(this, "escapeHTML", "resource://devtools/client/shared/widgets/VariablesView.jsm");
 loader.lazyImporter(this, "Task", "resource://gre/modules/Task.jsm");
 loader.lazyImporter(this, "PluralForm", "resource://gre/modules/PluralForm.jsm");
 
 loader.lazyRequireGetter(this, "promise");
 loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
--- a/devtools/client/webconsole/hudservice.js
+++ b/devtools/client/webconsole/hudservice.js
@@ -9,18 +9,18 @@
 const {Cc, Ci, Cu} = require("chrome");
 
 var WebConsoleUtils = require("devtools/shared/webconsole/utils").Utils;
 var Heritage = require("sdk/core/heritage");
 var {TargetFactory} = require("devtools/client/framework/target");
 var {Tools} = require("devtools/client/definitions");
 const { Task } = require("resource://gre/modules/Task.jsm");
 var promise = require("promise");
+var Services = require("Services");
 
-loader.lazyImporter(this, "Services", "resource://gre/modules/Services.jsm");
 loader.lazyRequireGetter(this, "Telemetry", "devtools/client/shared/telemetry");
 loader.lazyRequireGetter(this, "WebConsoleFrame", "devtools/client/webconsole/webconsole", true);
 loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
 loader.lazyRequireGetter(this, "DebuggerServer", "devtools/server/main", true);
 loader.lazyRequireGetter(this, "DebuggerClient", "devtools/shared/client/main", true);
 loader.lazyRequireGetter(this, "showDoorhanger", "devtools/client/shared/doorhanger", true);
 loader.lazyRequireGetter(this, "viewSource", "devtools/client/shared/view-source");
 
--- a/devtools/client/webconsole/jsterm.js
+++ b/devtools/client/webconsole/jsterm.js
@@ -7,21 +7,21 @@
 "use strict";
 
 const {Cc, Ci, Cu} = require("chrome");
 
 const {Utils: WebConsoleUtils, CONSOLE_WORKER_IDS} =
   require("devtools/shared/webconsole/utils");
 const promise = require("promise");
 const Debugger = require("Debugger");
+const Services = require("Services");
 
 loader.lazyServiceGetter(this, "clipboardHelper",
                          "@mozilla.org/widget/clipboardhelper;1",
                          "nsIClipboardHelper");
-loader.lazyImporter(this, "Services", "resource://gre/modules/Services.jsm");
 loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
 loader.lazyRequireGetter(this, "AutocompletePopup", "devtools/client/shared/autocomplete-popup", true);
 loader.lazyRequireGetter(this, "ToolSidebar", "devtools/client/framework/sidebar", true);
 loader.lazyRequireGetter(this, "Messages", "devtools/client/webconsole/console-output", true);
 loader.lazyRequireGetter(this, "asyncStorage", "devtools/shared/async-storage");
 loader.lazyRequireGetter(this, "EnvironmentClient", "devtools/shared/client/main", true);
 loader.lazyRequireGetter(this, "ObjectClient", "devtools/shared/client/main", true);
 loader.lazyImporter(this, "VariablesView", "resource://devtools/client/shared/widgets/VariablesView.jsm");
--- a/devtools/client/webconsole/test/browser_console_private_browsing.js
+++ b/devtools/client/webconsole/test/browser_console_private_browsing.js
@@ -122,17 +122,17 @@ function test() {
       messages: expectedMessages,
     }).then(testBrowserConsole);
   }
 
   function testBrowserConsole() {
     info("testBrowserConsole()");
     closeConsole(privateTab).then(() => {
       info("web console closed");
-      privateWindow.HUDService.toggleBrowserConsole().then(onBrowserConsoleOpen);
+      HUDService.toggleBrowserConsole().then(onBrowserConsoleOpen);
     });
   }
 
   // Make sure that the cached messages from private tabs are not displayed in
   // the browser console.
   function checkNoPrivateMessages() {
     let text = hud.outputNode.textContent;
     is(text.indexOf("fooBazBaz"), -1, "no exception displayed");
@@ -158,17 +158,17 @@ function test() {
   function testPrivateWindowClose() {
     info("close the private window and check if the private messages are removed");
     hud.jsterm.once("private-messages-cleared", () => {
       isnot(hud.outputNode.textContent.indexOf("bug874061-not-private"), -1,
             "non-private messages are still shown after private window closed");
       checkNoPrivateMessages();
 
       info("close the browser console");
-      privateWindow.HUDService.toggleBrowserConsole().then(() => {
+      HUDService.toggleBrowserConsole().then(() => {
         info("reopen the browser console");
         executeSoon(() =>
           HUDService.toggleBrowserConsole().then(onBrowserConsoleReopen));
       });
     });
     privateWindow.BrowserTryToCloseWindow();
   }
 
--- a/devtools/client/webconsole/test/head.js
+++ b/devtools/client/webconsole/test/head.js
@@ -7,16 +7,17 @@
 
 // shared-head.js handles imports, constants, and utility functions
 Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js", this);
 
 var {Task} = Cu.import("resource://gre/modules/Task.jsm", {});
 var {Utils: WebConsoleUtils} = require("devtools/shared/webconsole/utils");
 var {Messages} = require("devtools/client/webconsole/console-output");
 const asyncStorage = require("devtools/shared/async-storage");
+const HUDService = require("devtools/client/webconsole/hudservice");
 
 // Services.prefs.setBoolPref("devtools.debugger.log", true);
 
 var gPendingOutputTest = 0;
 
 // The various categories of messages.
 const CATEGORY_NETWORK = 0;
 const CATEGORY_CSS = 1;
--- a/devtools/client/webconsole/webconsole.js
+++ b/devtools/client/webconsole/webconsole.js
@@ -6,21 +6,22 @@
 
 "use strict";
 
 const {Cc, Ci, Cu} = require("chrome");
 
 const {Utils: WebConsoleUtils, CONSOLE_WORKER_IDS} =
   require("devtools/shared/webconsole/utils");
 const promise = require("promise");
+const Debugger = require("Debugger");
+const Services = require("Services");
 
 loader.lazyServiceGetter(this, "clipboardHelper",
                          "@mozilla.org/widget/clipboardhelper;1",
                          "nsIClipboardHelper");
-loader.lazyImporter(this, "Services", "resource://gre/modules/Services.jsm");
 loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
 loader.lazyRequireGetter(this, "AutocompletePopup", "devtools/client/shared/autocomplete-popup", true);
 loader.lazyRequireGetter(this, "ToolSidebar", "devtools/client/framework/sidebar", true);
 loader.lazyRequireGetter(this, "ConsoleOutput", "devtools/client/webconsole/console-output", true);
 loader.lazyRequireGetter(this, "Messages", "devtools/client/webconsole/console-output", true);
 loader.lazyRequireGetter(this, "EnvironmentClient", "devtools/shared/client/main", true);
 loader.lazyRequireGetter(this, "ObjectClient", "devtools/shared/client/main", true);
 loader.lazyRequireGetter(this, "system", "devtools/shared/system");
--- a/devtools/client/webide/content/addons.js
+++ b/devtools/client/webide/content/addons.js
@@ -1,15 +1,15 @@
 /* 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/. */
 
 var Cu = Components.utils;
-const {Services} = Cu.import("resource://gre/modules/Services.jsm");
 const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
+const Services = require("Services");
 const {GetAvailableAddons, ForgetAddonsList} = require("devtools/client/webide/modules/addons");
 const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties");
 
 window.addEventListener("load", function onLoad() {
   window.removeEventListener("load", onLoad);
   document.querySelector("#aboutaddons").onclick = function() {
     let browserWin = Services.wm.getMostRecentWindow("navigator:browser");
     browserWin.BrowserOpenAddonsMgr("addons://list/extension");
--- a/devtools/client/webide/content/details.js
+++ b/devtools/client/webide/content/details.js
@@ -1,15 +1,15 @@
 /* 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/. */
 
 var Cu = Components.utils;
-const {Services} = Cu.import("resource://gre/modules/Services.jsm");
 const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
+const Services = require("Services");
 const {AppManager} = require("devtools/client/webide/modules/app-manager");
 const {ProjectBuilding} = require("devtools/client/webide/modules/build");
 
 window.addEventListener("load", function onLoad() {
   window.removeEventListener("load", onLoad);
   document.addEventListener("visibilitychange", updateUI, true);
   AppManager.on("app-manager-update", onAppManagerUpdate);
   updateUI();
--- a/devtools/client/webide/content/monitor.js
+++ b/devtools/client/webide/content/monitor.js
@@ -1,15 +1,15 @@
 /* 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/. */
 
 var Cu = Components.utils;
 const {require} = Cu.import('resource://devtools/shared/Loader.jsm', {});
-const {Services} = Cu.import('resource://gre/modules/Services.jsm');
+const Services = require('Services');
 const {AppManager} = require('devtools/client/webide/modules/app-manager');
 const {AppActorFront} = require('devtools/shared/apps/app-actor-front');
 const {Connection} = require('devtools/shared/client/connection-manager');
 const EventEmitter = require('devtools/shared/event-emitter');
 
 window.addEventListener('load', function onLoad() {
   window.removeEventListener('load', onLoad);
   window.addEventListener('resize', Monitor.resize);
--- a/devtools/client/webide/content/newapp.js
+++ b/devtools/client/webide/content/newapp.js
@@ -1,23 +1,23 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 var Cc = Components.classes;
 var Cu = Components.utils;
 var Ci = Components.interfaces;
 
-Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "ZipUtils", "resource://gre/modules/ZipUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Downloads", "resource://gre/modules/Downloads.jsm");
 
 const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
+const Services = require("Services");
 const {FileUtils} = Cu.import("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");
 
 const TEMPLATES_URL = "devtools.webide.templatesURL";
 
 var gTemplateList = null;
--- a/devtools/client/webide/content/permissionstable.js
+++ b/devtools/client/webide/content/permissionstable.js
@@ -1,15 +1,15 @@
 /* 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/. */
 
 var Cu = Components.utils;
-const {Services} = Cu.import("resource://gre/modules/Services.jsm");
 const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
+const Services = require("Services");
 const {AppManager} = require("devtools/client/webide/modules/app-manager");
 const {Connection} = require("devtools/shared/client/connection-manager");
 
 window.addEventListener("load", function onLoad() {
   window.removeEventListener("load", onLoad);
   document.querySelector("#close").onclick = CloseUI;
   AppManager.on("app-manager-update", OnAppManagerUpdate);
   BuildUI();
--- a/devtools/client/webide/content/runtimedetails.js
+++ b/devtools/client/webide/content/runtimedetails.js
@@ -1,15 +1,15 @@
 /* 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/. */
 
 var Cu = Components.utils;
-const {Services} = Cu.import("resource://gre/modules/Services.jsm");
 const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
+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 onLoad() {
--- a/devtools/client/webide/content/simulator.js
+++ b/devtools/client/webide/content/simulator.js
@@ -2,18 +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/. */
 
 var Cu = Components.utils;
 var Ci = Components.interfaces;
 
 const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 const { GetDevices, GetDeviceString } = require("devtools/client/shared/devices");
-const { Services } = Cu.import("resource://gre/modules/Services.jsm");
 const { Simulators, Simulator } = require("devtools/client/webide/modules/simulators");
+const Services = require("Services");
 const EventEmitter = require('devtools/shared/event-emitter');
 const promise = require("promise");
 const utils = require("devtools/client/webide/modules/utils");
 
 const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties");
 
 var SimulatorEditor = {
 
--- a/devtools/client/webide/content/webide.js
+++ b/devtools/client/webide/content/webide.js
@@ -1150,17 +1150,10 @@ var Cmds = {
       UI.contentViewer.fullZoom -= 0.1;
       Services.prefs.setCharPref("devtools.webide.zoom", UI.contentViewer.fullZoom);
     }
   },
 
   resetZoom: function() {
     UI.contentViewer.fullZoom = 1;
     Services.prefs.setCharPref("devtools.webide.zoom", 1);
-  },
-
-  reloadDevtools: function(event) {
-    if (Services.prefs.prefHasUserValue("devtools.loader.srcdir")) {
-      let {devtools} = Cu.import("resource://devtools/shared/Loader.jsm", {});
-      devtools.reload();
-    }
   }
 };
--- a/devtools/client/webide/content/webide.xul
+++ b/devtools/client/webide/content/webide.xul
@@ -53,17 +53,16 @@
       <command id="cmd_showPrefs" oncommand="Cmds.showPrefs()"/>
       <command id="cmd_showTroubleShooting" oncommand="Cmds.showTroubleShooting()"/>
       <command id="cmd_play" oncommand="Cmds.play()"/>
       <command id="cmd_stop" oncommand="Cmds.stop()" label="&projectMenu_stop_label;"/>
       <command id="cmd_toggleToolbox" oncommand="Cmds.toggleToolbox()"/>
       <command id="cmd_zoomin" label="&viewMenu_zoomin_label;" oncommand="Cmds.zoomIn()"/>
       <command id="cmd_zoomout" label="&viewMenu_zoomout_label;" oncommand="Cmds.zoomOut()"/>
       <command id="cmd_resetzoom" label="&viewMenu_resetzoom_label;" oncommand="Cmds.resetZoom()"/>
-      <command id="cmd_reload_devtools" oncommand="Cmds.reloadDevtools()"/>
     </commandset>
   </commandset>
 
   <menubar id="main-menubar">
     <menu id="menu-project" label="&projectMenu_label;" accesskey="&projectMenu_accesskey;">
       <menupopup id="menu-project-popup">
         <menuitem command="cmd_newApp" accesskey="&projectMenu_newApp_accesskey;"/>
         <menuitem command="cmd_importPackagedApp" accesskey="&projectMenu_importPackagedApp_accesskey;"/>
@@ -111,17 +110,16 @@
     <key key="&key_showProjectPanel;" id="key_showProjectPanel" command="cmd_showProjectPanel" modifiers="accel"/>
     <key key="&key_play;" id="key_play" command="cmd_play" modifiers="accel"/>
     <key key="&key_toggleEditor;" id="key_toggleEditor" command="cmd_toggleEditor" modifiers="accel"/>
     <key keycode="&key_toggleToolbox;" id="key_toggleToolbox" command="cmd_toggleToolbox"/>
     <key key="&key_zoomin;" id="key_zoomin" command="cmd_zoomin" modifiers="accel"/>
     <key key="&key_zoomin2;" id="key_zoomin2" command="cmd_zoomin" modifiers="accel"/>
     <key key="&key_zoomout;" id="key_zoomout" command="cmd_zoomout" modifiers="accel"/>
     <key key="&key_resetzoom;" id="key_resetzoom" command="cmd_resetzoom" modifiers="accel"/>
-    <key key="&key_reload_devtools;" id="key_reload_devtools" command="cmd_reload_devtools" modifiers="accel alt"/>
   </keyset>
 
   <tooltip id="aHTMLTooltip" page="true"/>
 
   <toolbar id="main-toolbar">
 
     <vbox flex="1">
       <hbox id="action-buttons-container" class="busy">
--- a/devtools/client/webide/content/wifi-auth.js
+++ b/devtools/client/webide/content/wifi-auth.js
@@ -1,18 +1,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 var Cu = Components.utils;
-const { Services } = Cu.import("resource://gre/modules/Services.jsm");
 const { require } =
   Cu.import("resource://devtools/shared/Loader.jsm", {});
+const Services = require("Services");
 const QR = require("devtools/shared/qrcode/index");
 
 window.addEventListener("load", function onLoad() {
   window.removeEventListener("load", onLoad);
   document.getElementById("close").onclick = () => window.close();
   document.getElementById("no-scanner").onclick = showToken;
   document.getElementById("yes-scanner").onclick = hideToken;
   buildUI();
--- a/devtools/client/webide/modules/addons.js
+++ b/devtools/client/webide/modules/addons.js
@@ -1,16 +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/. */
 
 const {Cu} = require("chrome");
 const promise = require("promise");
 const {AddonManager} = Cu.import("resource://gre/modules/AddonManager.jsm");
-const {Services} = Cu.import("resource://gre/modules/Services.jsm");
+const Services = require("Services");
 const {getJSON} = require("devtools/client/shared/getjson");
 const EventEmitter = require("devtools/shared/event-emitter");
 
 const ADDONS_URL = "devtools.webide.addonsURL";
 
 var SIMULATOR_LINK = Services.prefs.getCharPref("devtools.webide.simulatorAddonsURL");
 var ADB_LINK = Services.prefs.getCharPref("devtools.webide.adbAddonURL");
 var ADAPTERS_LINK = Services.prefs.getCharPref("devtools.webide.adaptersAddonURL");
--- a/devtools/client/webide/modules/config-view.js
+++ b/devtools/client/webide/modules/config-view.js
@@ -1,16 +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/. */
 
 const {Cu} = require("chrome");
 
 const EventEmitter = require("devtools/shared/event-emitter");
-const {Services} = Cu.import("resource://gre/modules/Services.jsm");
+const Services = require("Services");
 const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties");
 
 var ConfigView;
 
 module.exports = ConfigView = function(window) {
   EventEmitter.decorate(this);
   this._doc = window.document;
   this._keys = [];
--- a/devtools/client/webide/modules/runtime-list.js
+++ b/devtools/client/webide/modules/runtime-list.js
@@ -1,15 +1,15 @@
 /* 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} = Cu.import("resource://gre/modules/Services.jsm");
+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} = Cu.import("resource://devtools/shared/apps/Devices.jsm");
 const {Task} = Cu.import("resource://gre/modules/Task.jsm", {});
 const utils = require("devtools/client/webide/modules/utils");
 
 const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties");
--- a/devtools/client/webide/modules/runtimes.js
+++ b/devtools/client/webide/modules/runtimes.js
@@ -1,15 +1,15 @@
 /* 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, Ci} = require("chrome");
+const Services = require("Services");
 const {Devices} = Cu.import("resource://devtools/shared/apps/Devices.jsm");
-const {Services} = Cu.import("resource://gre/modules/Services.jsm");
 const {Connection} = require("devtools/shared/client/connection-manager");
 const {DebuggerServer} = require("devtools/server/main");
 const {Simulators} = require("devtools/client/webide/modules/simulators");
 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);
--- a/devtools/client/webide/modules/simulator-process.js
+++ b/devtools/client/webide/modules/simulator-process.js
@@ -6,17 +6,17 @@
 'use strict';
 
 const { Cc, Ci, Cu } = require("chrome");
 
 const Environment = require("sdk/system/environment").env;
 const EventEmitter = require("devtools/shared/event-emitter");
 const promise = require("promise");
 const Subprocess = require("sdk/system/child_process/subprocess");
-const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
+const Services = require("Services");
 
 loader.lazyGetter(this, "OS", () => {
   const Runtime = require("sdk/system/runtime");
   switch (Runtime.OS) {
     case "Darwin":
       return "mac64";
     case "Linux":
       if (Runtime.XPCOMABI.indexOf("x86_64") === 0) {
--- a/devtools/client/webide/modules/utils.js
+++ b/devtools/client/webide/modules/utils.js
@@ -1,15 +1,15 @@
 /* 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 { FileUtils } = Cu.import("resource://gre/modules/FileUtils.jsm", {});
-const { Services } = Cu.import("resource://gre/modules/Services.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();
 }
 exports.doesFileExist = doesFileExist;
 
--- a/devtools/client/webide/test/head.js
+++ b/devtools/client/webide/test/head.js
@@ -1,22 +1,22 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 var {utils: Cu, classes: Cc, interfaces: Ci} = Components;
 
-Cu.import('resource://gre/modules/Services.jsm');
 Cu.import("resource://gre/modules/FileUtils.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 
 const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
 const {gDevTools} = require("devtools/client/framework/devtools");
 const promise = require("promise");
+const Services = require("Services");
 const {AppProjects} = require("devtools/client/webide/modules/app-projects");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 DevToolsUtils.testing = true;
 
 var TEST_BASE;
 if (window.location === "chrome://browser/content/browser.xul") {
   TEST_BASE = "chrome://mochitests/content/browser/devtools/client/webide/test/";
 } else {
--- a/devtools/client/webide/test/test_app_validator.html
+++ b/devtools/client/webide/test/test_app_validator.html
@@ -16,17 +16,17 @@
     <script type="application/javascript;version=1.8">
     const Cu = Components.utils;
     const Cc = Components.classes;
     const Ci = Components.interfaces;
     Cu.import("resource://testing-common/httpd.js");
     const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
 
     const {AppValidator} = require("devtools/client/webide/modules/app-validator");
-    const {Services} = Cu.import("resource://gre/modules/Services.jsm");
+    const Services = require("Services");
     const nsFile = Components.Constructor("@mozilla.org/file/local;1",
                                            "nsILocalFile", "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;
 
     window.onload = function() {
--- a/devtools/server/actors/preference.js
+++ b/devtools/server/actors/preference.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/. */
 
 const {Cc, Ci, Cu, CC} = require("chrome");
 const protocol = require("devtools/server/protocol");
 const {Arg, method, RetVal} = protocol;
-
-Cu.import("resource://gre/modules/Services.jsm");
+const Services = require("Services");
 
 exports.register = function(handle) {
   handle.addGlobalActor(PreferenceActor, "preferenceActor");
 };
 
 exports.unregister = function(handle) {
 };
 
--- a/devtools/server/actors/settings.js
+++ b/devtools/server/actors/settings.js
@@ -2,20 +2,20 @@
  * 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, CC} = require("chrome");
 const protocol = require("devtools/server/protocol");
 const {Arg, method, RetVal} = protocol;
 const {DebuggerServer} = require("devtools/server/main");
 const promise = require("promise");
+const Services = require("Services");
 
 Cu.import("resource://gre/modules/FileUtils.jsm");
 Cu.import("resource://gre/modules/NetUtil.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
 
 var defaultSettings = {};
 var settingsFile;
 
 exports.register = function(handle) {
   handle.addGlobalActor(SettingsActor, "settingsActor");
 };
 
--- a/devtools/server/actors/stylesheets.js
+++ b/devtools/server/actors/stylesheets.js
@@ -729,39 +729,48 @@ var StyleSheetActor = protocol.ActorClas
    * Fetch the source map for this stylesheet.
    *
    * @return {Promise}
    *         A promise that resolves with a SourceMapConsumer, or null.
    */
   _fetchSourceMap: function() {
     let deferred = promise.defer();
 
-    this._getText().then((content) => {
-      let url = this._extractSourceMapUrl(content);
+    this._getText().then(sheetContent => {
+      let url = this._extractSourceMapUrl(sheetContent);
       if (!url) {
         // no source map for this stylesheet
         deferred.resolve(null);
         return;
-      };
+      }
 
       url = normalize(url, this.href);
       let options = {
         loadFromCache: false,
         policy: Ci.nsIContentPolicy.TYPE_INTERNAL_STYLESHEET,
         window: this.window
       };
-      let map = fetch(url, options)
-        .then(({content}) => {
-          let map = new SourceMapConsumer(content);
-          this._setSourceMapRoot(map, url, this.href);
-          this._sourceMap = promise.resolve(map);
 
-          deferred.resolve(map);
-          return map;
-        }, deferred.reject);
+      let map = fetch(url, options).then(({content}) => {
+        // Fetching the source map might have failed with a 404 or other. When
+        // this happens, SourceMapConsumer may fail with a JSON.parse error.
+        let consumer;
+        try {
+          consumer = new SourceMapConsumer(content);
+        } catch (e) {
+          deferred.reject(new Error(
+            `Source map at ${url} not found or invalid`));
+          return null;
+        }
+        this._setSourceMapRoot(consumer, url, this.href);
+        this._sourceMap = promise.resolve(consumer);
+
+        deferred.resolve(consumer);
+        return consumer;
+      }, deferred.reject);
 
       this._sourceMap = map;
     }, deferred.reject);
 
     return deferred.promise;
   },
 
   /**
--- a/devtools/server/tests/browser/head.js
+++ b/devtools/server/tests/browser/head.js
@@ -1,23 +1,23 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 var Cc = Components.classes;
 var Ci = Components.interfaces;
 var Cu = Components.utils;
 
-Cu.import("resource://gre/modules/Services.jsm");
 const {console} = Cu.import("resource://gre/modules/Console.jsm", {});
 const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
 const {DebuggerClient} = require("devtools/shared/client/main");
 const {DebuggerServer} = require("devtools/server/main");
 const {defer} = require("promise");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+const Services = require("Services");
 
 const PATH = "browser/devtools/server/tests/browser/";
 const MAIN_DOMAIN = "http://test1.example.org/" + PATH;
 const ALT_DOMAIN = "http://sectest1.example.org/" + PATH;
 const ALT_DOMAIN_SECURED = "https://sectest1.example.org:443/" + PATH;
 
 // All tests are asynchronous.
 waitForExplicitFinish();
--- a/devtools/server/tests/mochitest/memory-helpers.js
+++ b/devtools/server/tests/mochitest/memory-helpers.js
@@ -1,27 +1,26 @@
 var Cu = Components.utils;
 var Cc = Components.classes;
 var Ci = Components.interfaces;
 
-Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/Task.jsm");
+var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
+var Services = require("Services");
+var { DebuggerClient } = require("devtools/shared/client/main");
+var { DebuggerServer } = require("devtools/server/main");
+
+var { MemoryFront } = require("devtools/server/actors/memory");
 
 // Always log packets when running tests.
 Services.prefs.setBoolPref("devtools.debugger.log", true);
 SimpleTest.registerCleanupFunction(function() {
   Services.prefs.clearUserPref("devtools.debugger.log");
 });
 
-Cu.import("resource://gre/modules/Task.jsm");
-var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
-var { DebuggerClient } = require("devtools/shared/client/main");
-var { DebuggerServer } = require("devtools/server/main");
-
-var { MemoryFront } = require("devtools/server/actors/memory");
-
 function startServerAndGetSelectedTabMemory() {
   DebuggerServer.init();
   DebuggerServer.addBrowserActors();
   var client = new DebuggerClient(DebuggerServer.connectPipe());
 
   return client.connect()
     .then(() => client.listTabs())
     .then(response => {
--- a/devtools/server/tests/mochitest/memprof-helpers.js
+++ b/devtools/server/tests/mochitest/memprof-helpers.js
@@ -1,28 +1,27 @@
 var Cu = Components.utils;
 var Cc = Components.classes;
 var Ci = Components.interfaces;
 
-var { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
+var { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
+var { require } =
+  Cu.import("resource://devtools/shared/Loader.jsm", {});
+
+var Services = require("Services");
+var { DebuggerClient } = require("devtools/shared/client/main");
+var { DebuggerServer } = require("devtools/server/main");
+var { MemprofFront } = require("devtools/server/actors/memprof");
 
 // Always log packets when running tests.
 Services.prefs.setBoolPref("devtools.debugger.log", true);
 SimpleTest.registerCleanupFunction(function() {
   Services.prefs.clearUserPref("devtools.debugger.log");
 });
 
-var { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
-var { require } =
-  Cu.import("resource://devtools/shared/Loader.jsm", {});
-
-var { DebuggerClient } = require("devtools/shared/client/main");
-var { DebuggerServer } = require("devtools/server/main");
-var { MemprofFront } = require("devtools/server/actors/memprof");
-
 function startServerAndGetSelectedTabMemprof() {
   DebuggerServer.init();
   DebuggerServer.addBrowserActors();
   var client = new DebuggerClient(DebuggerServer.connectPipe());
 
   return client.connect()
     .then(() => client.listTabs())
     .then(response => {
--- a/devtools/server/tests/mochitest/test_connection-manager.html
+++ b/devtools/server/tests/mochitest/test_connection-manager.html
@@ -15,17 +15,17 @@ Bug 898485 - [app manager] Implement an 
 
 window.onload = function() {
   SimpleTest.waitForExplicitFinish();
 
   var Cu = Components.utils;
 
   var {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
   var {DebuggerServer} = require("devtools/server/main");
-  Cu.import("resource://gre/modules/Services.jsm");
+  var Services = require("Services");
 
   if (!DebuggerServer.initialized) {
     DebuggerServer.init();
     DebuggerServer.addBrowserActors();
   }
 
   var {ConnectionManager, Connection} = require("devtools/shared/client/connection-manager");
 
--- a/devtools/server/tests/mochitest/test_css-logic-inheritance.html
+++ b/devtools/server/tests/mochitest/test_css-logic-inheritance.html
@@ -14,17 +14,17 @@ Test that css-logic handles inherited pr
     <div id="innerdiv">Inner div</div>
   </div>
   <script type="application/javascript;version=1.8">
 
   window.onload = function() {
     var Cu = Components.utils;
 
     const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
-    Cu.import("resource://gre/modules/Services.jsm");
+    var Services = require("Services");
     const {CssLogic} = require("devtools/shared/inspector/css-logic");
 
     SimpleTest.waitForExplicitFinish();
 
     let cssLogic = new CssLogic();
     cssLogic.highlight(document.getElementById("innerdiv"));
 
     let marginProp = cssLogic.getPropertyInfo("margin-left");
--- a/devtools/server/tests/mochitest/test_css-logic-media-queries.html
+++ b/devtools/server/tests/mochitest/test_css-logic-media-queries.html
@@ -25,17 +25,17 @@ Test that css-logic handles media-querie
 <body>
   <div></div>
   <script type="application/javascript;version=1.8">
 
   window.onload = function() {
     var Cu = Components.utils;
 
     var {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
-    Cu.import("resource://gre/modules/Services.jsm");
+    var Services = require("Services");
     const {CssLogic} = require("devtools/shared/inspector/css-logic");
 
     SimpleTest.waitForExplicitFinish();
 
     let div = document.querySelector("div");
     let cssLogic = new CssLogic();
     cssLogic.highlight(div);
     cssLogic.processMatchedSelectors();
--- a/devtools/server/tests/mochitest/test_device.html
+++ b/devtools/server/tests/mochitest/test_device.html
@@ -17,17 +17,17 @@ window.onload = function() {
   var Cu = Components.utils;
   var Cc = Components.classes;
   var Ci = Components.interfaces;
 
   Cu.import("resource://gre/modules/PermissionsTable.jsm");
   var {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
   var {DebuggerClient} = require("devtools/shared/client/main");
   var {DebuggerServer} = require("devtools/server/main");
-  Cu.import("resource://gre/modules/Services.jsm");
+  var Services = require("Services");
 
   SimpleTest.waitForExplicitFinish();
 
   var {getDeviceFront} = require("devtools/server/actors/device");
 
   if (!DebuggerServer.initialized) {
     DebuggerServer.init();
     DebuggerServer.addBrowserActors();
--- a/devtools/server/tests/mochitest/test_framerate_01.html
+++ b/devtools/server/tests/mochitest/test_framerate_01.html
@@ -13,28 +13,27 @@ Bug 1007200 - Create a framerate actor
 <pre id="test">
 <script>
 
 window.onload = function() {
   var Cu = Components.utils;
   var Cc = Components.classes;
   var Ci = Components.interfaces;
 
-  Cu.import("resource://gre/modules/Services.jsm");
+  var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
+  var Services = require("Services");
+  var { DebuggerClient } = require("devtools/shared/client/main");
+  var { DebuggerServer } = require("devtools/server/main");
 
   // Always log packets when running tests.
   Services.prefs.setBoolPref("devtools.debugger.log", true);
   SimpleTest.registerCleanupFunction(function() {
     Services.prefs.clearUserPref("devtools.debugger.log");
   });
 
-  var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
-  var { DebuggerClient } = require("devtools/shared/client/main");
-  var { DebuggerServer } = require("devtools/server/main");
-
   SimpleTest.waitForExplicitFinish();
 
   var {FramerateFront} = require("devtools/server/actors/framerate");
 
   function plotFPS(ticks, interval = 100, clamp = 60) {
     var timeline = [];
     var totalTicks = ticks.length;
 
--- a/devtools/server/tests/mochitest/test_framerate_02.html
+++ b/devtools/server/tests/mochitest/test_framerate_02.html
@@ -13,28 +13,27 @@ Bug 1007200 - Create a framerate actor
 <pre id="test">
 <script>
 
 window.onload = function() {
   var Cu = Components.utils;
   var Cc = Components.classes;
   var Ci = Components.interfaces;
 
-  Cu.import("resource://gre/modules/Services.jsm");
+  var {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
+  var {DebuggerClient} = require("devtools/shared/client/main");
+  var {DebuggerServer} = require("devtools/server/main");
+  var Services = require("Services");
 
   // Always log packets when running tests.
   Services.prefs.setBoolPref("devtools.debugger.log", true);
   SimpleTest.registerCleanupFunction(function() {
     Services.prefs.clearUserPref("devtools.debugger.log");
   });
 
-  var {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
-  var {DebuggerClient} = require("devtools/shared/client/main");
-  var {DebuggerServer} = require("devtools/server/main");
-
   SimpleTest.waitForExplicitFinish();
 
   var {FramerateFront} = require("devtools/server/actors/framerate");
 
   function plotFPS(ticks, interval = 100, clamp = 60) {
     var timeline = [];
     var totalTicks = ticks.length;
 
--- a/devtools/server/tests/mochitest/test_framerate_03.html
+++ b/devtools/server/tests/mochitest/test_framerate_03.html
@@ -13,28 +13,27 @@ Bug 1023018 - Tests whether or not the f
 <pre id="test">
 <script>
 
 window.onload = function() {
   var Cu = Components.utils;
   var Cc = Components.classes;
   var Ci = Components.interfaces;
 
-  Cu.import("resource://gre/modules/Services.jsm");
+  var {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
+  var {DebuggerClient} = require("devtools/shared/client/main");
+  var {DebuggerServer} = require("devtools/server/main");
+  var Services = require("Services");
 
   // Always log packets when running tests.
   Services.prefs.setBoolPref("devtools.debugger.log", true);
   SimpleTest.registerCleanupFunction(function() {
     Services.prefs.clearUserPref("devtools.debugger.log");
   });
 
-  var {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
-  var {DebuggerClient} = require("devtools/shared/client/main");
-  var {DebuggerServer} = require("devtools/server/main");
-
   SimpleTest.waitForExplicitFinish();
 
   var {FramerateFront} = require("devtools/server/actors/framerate");
   var START_TICK = 2000;
   var STOP_TICK = 3000;
   var TOTAL_TIME = 5000;
 
   if (!DebuggerServer.initialized) {
--- a/devtools/server/tests/mochitest/test_framerate_05.html
+++ b/devtools/server/tests/mochitest/test_framerate_05.html
@@ -13,28 +13,27 @@ Bug 1034648 - Tests whether a framerate 
 <pre id="test">
 <script>
 
 window.onload = function() {
   var Cu = Components.utils;
   var Cc = Components.classes;
   var Ci = Components.interfaces;
 
-  Cu.import("resource://gre/modules/Services.jsm");
+  var {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
+  var {DebuggerClient} = require("devtools/shared/client/main");
+  var {DebuggerServer} = require("devtools/server/main");
+  var Services = require("Services");
 
   // Always log packets when running tests.
   Services.prefs.setBoolPref("devtools.debugger.log", true);
   SimpleTest.registerCleanupFunction(function() {
     Services.prefs.clearUserPref("devtools.debugger.log");
   });
 
-  var {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
-  var {DebuggerClient} = require("devtools/shared/client/main");
-  var {DebuggerServer} = require("devtools/server/main");
-
   SimpleTest.waitForExplicitFinish();
 
   var {FramerateFront} = require("devtools/server/actors/framerate");
 
   DebuggerServer.init();
   DebuggerServer.addBrowserActors();
 
   var client = new DebuggerClient(DebuggerServer.connectPipe());
--- a/devtools/server/tests/mochitest/test_preference.html
+++ b/devtools/server/tests/mochitest/test_preference.html
@@ -16,17 +16,17 @@ Bug 943251 - Allow accessing about:confi
 function runTests() {
   var Cu = Components.utils;
   var Cc = Components.classes;
   var Ci = Components.interfaces;
 
   var {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
   var {DebuggerClient} = require("devtools/shared/client/main");
   var {DebuggerServer} = require("devtools/server/main");
-  Cu.import("resource://gre/modules/Services.jsm");
+  var Services = require("Services");
 
   SimpleTest.waitForExplicitFinish();
 
   var {getPreferenceFront} = require("devtools/server/actors/preference");
 
   DebuggerServer.init();
   DebuggerServer.addBrowserActors();
 
--- a/devtools/server/tests/mochitest/test_setupInParentChild.html
+++ b/devtools/server/tests/mochitest/test_setupInParentChild.html
@@ -12,20 +12,20 @@ Bug 1181100 - Test DebuggerServerConnect
 <body>
 <pre id="test">
 <script type="application/javascript;version=1.8">
 
 let Cu = Components.utils;
 let Cc = Components.classes;
 let Ci = Components.interfaces;
 
-Cu.import("resource://gre/modules/Services.jsm");
 let {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
 let {DebuggerClient} = require("devtools/shared/client/main");
 let {DebuggerServer} = require("devtools/server/main");
+let Services = require("Services");
 
 window.onload = function() {
   SimpleTest.waitForExplicitFinish();
 
   SpecialPowers.pushPrefEnv({
     "set": [
       // Always log packets when running tests.
       ["devtools.debugger.log", true],
--- a/devtools/shared/Loader.jsm
+++ b/devtools/shared/Loader.jsm
@@ -17,17 +17,17 @@ Cu.import("resource://gre/modules/Servic
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", "resource://gre/modules/FileUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
 
 var { Loader } = Cu.import("resource://gre/modules/commonjs/toolkit/loader.js", {});
 var promise = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;
 
 this.EXPORTED_SYMBOLS = ["DevToolsLoader", "devtools", "BuiltinProvider",
-                         "SrcdirProvider", "require", "loader"];
+                         "require", "loader"];
 
 /**
  * Providers are different strategies for loading the devtools.
  */
 
 var loaderModules = {
   "Services": Object.create(Services),
   "toolkit/loader": Loader,
@@ -83,18 +83,16 @@ var sharedGlobalBlocklist = ["sdk/indexe
  */
 function BuiltinProvider() {}
 BuiltinProvider.prototype = {
   load: function() {
     this.loader = new Loader.Loader({
       id: "fx-devtools",
       modules: loaderModules,
       paths: {
-        // When you add a line to this mapping, don't forget to make a
-        // corresponding addition to the SrcdirProvider mapping below as well.
         // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
         "": "resource://gre/modules/commonjs/",
         // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
         "devtools": "resource://devtools",
         // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
         "gcli": "resource://devtools/shared/gcli/source/lib/gcli",
         // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
         "promise": "resource://gre/modules/Promise-backend.js",
@@ -119,135 +117,16 @@ BuiltinProvider.prototype = {
   },
 
   unload: function(reason) {
     Loader.unload(this.loader, reason);
     delete this.loader;
   },
 };
 
-/**
- * Used when the tools should be loaded from a mozilla-central checkout.  In
- * addition to different paths, it needs to write chrome.manifest files to
- * override chrome urls from the builtin tools.
- */
-function SrcdirProvider() {}
-SrcdirProvider.prototype = {
-  fileURI: function(path) {
-    let file = new FileUtils.File(path);
-    return Services.io.newFileURI(file).spec;
-  },
-
-  load: function() {
-    let srcDir = Services.prefs.getComplexValue("devtools.loader.srcdir",
-                                                Ci.nsISupportsString);
-    srcDir = OS.Path.normalize(srcDir.data.trim());
-    let devtoolsDir = OS.Path.join(srcDir, "devtools");
-    let sharedDir = OS.Path.join(devtoolsDir, "shared");
-    let modulesDir = OS.Path.join(srcDir, "toolkit", "modules");
-    let devtoolsURI = this.fileURI(devtoolsDir);
-    let gcliURI = this.fileURI(OS.Path.join(sharedDir,
-                                            "gcli", "source", "lib", "gcli"));
-    let promiseURI = this.fileURI(OS.Path.join(modulesDir,
-                                               "Promise-backend.js"));
-    let acornURI = this.fileURI(OS.Path.join(sharedDir, "acorn"));
-    let acornWalkURI = OS.Path.join(acornURI, "walk.js");
-    let sourceMapURI = this.fileURI(OS.Path.join(sharedDir,
-                                                 "sourcemap", "source-map.js"));
-    this.loader = new Loader.Loader({
-      id: "fx-devtools",
-      modules: loaderModules,
-      paths: {
-        // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
-        "": "resource://gre/modules/commonjs/",
-        // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
-        "devtools": devtoolsURI,
-        // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
-        "gcli": gcliURI,
-        // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
-        "promise": promiseURI,
-        // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
-        "acorn": acornURI,
-        // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
-        "acorn/util/walk": acornWalkURI,
-        // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
-        "source-map": sourceMapURI,
-        // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
-      },
-      globals: this.globals,
-      invisibleToDebugger: this.invisibleToDebugger,
-      sharedGlobal: true,
-      sharedGlobalBlocklist,
-    });
-
-    return this._writeManifest(srcDir).then(null, Cu.reportError);
-  },
-
-  unload: function(reason) {
-    Loader.unload(this.loader, reason);
-    delete this.loader;
-  },
-
-  _readFile: function(filename) {
-    let deferred = promise.defer();
-    let file = new FileUtils.File(filename);
-    NetUtil.asyncFetch({
-      uri: NetUtil.newURI(file),
-      loadUsingSystemPrincipal: true
-    }, (inputStream, status) => {
-        if (!Components.isSuccessCode(status)) {
-          deferred.reject(new Error("Couldn't load manifest: " + filename + "\n"));
-          return;
-        }
-        var data = NetUtil.readInputStreamToString(inputStream, inputStream.available());
-        deferred.resolve(data);
-      });
-
-    return deferred.promise;
-  },
-
-  _writeFile: function(filename, data) {
-    let promise = OS.File.writeAtomic(filename, data, {encoding: "utf-8"});
-    return promise.then(null, (ex) => new Error("Couldn't write manifest: " + ex + "\n"));
-  },
-
-  _writeManifest: function(srcDir) {
-    let clientDir = OS.Path.join(srcDir, "devtools", "client");
-    return this._readFile(OS.Path.join(clientDir, "jar.mn")).then((data) => {
-      // The file data is contained within inputStream.
-      // You can read it into a string with
-      let entries = [];
-      let lines = data.split(/\n/);
-      let preprocessed = /^\s*\*/;
-      let contentEntry = /^\s+content\/(\S+)\s+\((\S+)\)/;
-      for (let line of lines) {
-        if (preprocessed.test(line)) {
-          dump("Unable to override preprocessed file: " + line + "\n");
-          continue;
-        }
-        let match = contentEntry.exec(line);
-        if (match) {
-          let pathComponents = match[2].split("/");
-          pathComponents.unshift(clientDir);
-          let path = OS.Path.join.apply(OS.Path, pathComponents);
-          let uri = this.fileURI(path);
-          let chromeURI = "chrome://devtools/content/" + match[1];
-          let entry = "override " + chromeURI + "\t" + uri;
-          entries.push(entry);
-        }
-      }
-      return this._writeFile(OS.Path.join(clientDir, "chrome.manifest"),
-                             entries.join("\n"));
-    }).then(() => {
-      let clientDirFile = new FileUtils.File(clientDir);
-      Components.manager.addBootstrappedManifestLocation(clientDirFile);
-    });
-  }
-};
-
 var gNextLoaderID = 0;
 
 /**
  * The main devtools API.
  * In addition to a few loader-related details, this object will also include all
  * exports from the main module.  The standard instance of this loader is
  * exported as |devtools| below, but if a fresh copy of the loader is needed,
  * then a new one can also be created.
@@ -259,17 +138,17 @@ this.DevToolsLoader = function DevToolsL
   this.lazyServiceGetter = XPCOMUtils.defineLazyServiceGetter.bind(XPCOMUtils);
   this.lazyRequireGetter = this.lazyRequireGetter.bind(this);
   this.main = this.main.bind(this);
 };
 
 DevToolsLoader.prototype = {
   get provider() {
     if (!this._provider) {
-      this._chooseProvider();
+      this._loadProvider();
     }
     return this._provider;
   },
 
   _provider: null,
 
   get id() {
     if (this._id) {
@@ -281,17 +160,17 @@ DevToolsLoader.prototype = {
 
   /**
    * A dummy version of require, in case a provider hasn't been chosen yet when
    * this is first called.  This will then be replaced by the real version.
    * @see setProvider
    */
   require: function() {
     if (!this._provider) {
-      this._chooseProvider();
+      this._loadProvider();
     }
     return this.require.apply(this, arguments);
   },
 
   /**
    * Define a getter property on the given object that requires the given
    * module. This enables delaying importing modules until the module is
    * actually used.
@@ -426,36 +305,32 @@ DevToolsLoader.prototype = {
     if (this._mainid) {
       this.main(this._mainid);
     }
   },
 
   /**
    * Choose a default tools provider based on the preferences.
    */
-  _chooseProvider: function() {
-    if (Services.prefs.prefHasUserValue("devtools.loader.srcdir")) {
-      this.setProvider(new SrcdirProvider());
-    } else {
-      this.setProvider(new BuiltinProvider());
-    }
+  _loadProvider: function() {
+    this.setProvider(new BuiltinProvider());
   },
 
   /**
    * Reload the current provider.
    */
   reload: function() {
     var events = this.require("sdk/system/events");
     events.emit("startupcache-invalidate", {});
     events.emit("devtools-unloaded", {});
 
     this._provider.unload("reload");
     delete this._provider;
     delete this._mainid;
-    this._chooseProvider();
+    this._loadProvider();
     this.main("devtools/client/main");
   },
 
   /**
    * Sets whether the compartments loaded by this instance should be invisible
    * to the debugger.  Invisibility is needed for loaders that support debugging
    * of chrome code.  This is true of remote target environments, like Fennec or
    * B2G.  It is not the default case for desktop Firefox because we offer the
--- a/devtools/shared/apps/app-actor-front.js
+++ b/devtools/shared/apps/app-actor-front.js
@@ -1,13 +1,13 @@
 const {Ci, Cc, Cu, Cr} = require("chrome");
 Cu.import("resource://gre/modules/osfile.jsm");
-const {Services} = Cu.import("resource://gre/modules/Services.jsm");
 const {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm");
 const {NetUtil} = Cu.import("resource://gre/modules/NetUtil.jsm");
+const Services = require("Services");
 const promise = require("promise");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 const EventEmitter = require("devtools/shared/event-emitter");
 
 // Bug 1188401: When loaded from xpcshell tests, we do not have browser/ files
 // and can't load target.js. Should be fixed by bug 912121.
 loader.lazyRequireGetter(this, "TargetFactory", "devtools/client/framework/target", true);
 
--- a/devtools/shared/apps/tests/debugger-protocol-helper.js
+++ b/devtools/shared/apps/tests/debugger-protocol-helper.js
@@ -3,18 +3,18 @@
 
 var Cc = Components.classes;
 var Ci = Components.interfaces;
 var Cu = Components.utils;
 
 const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 const { DebuggerClient } = require("devtools/shared/client/main");
 const { DebuggerServer } = require("devtools/server/main");
+const Services = require("Services");
 const { FileUtils } = Cu.import("resource://gre/modules/FileUtils.jsm");
-const { Services } = Cu.import("resource://gre/modules/Services.jsm");
 const { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm");
 
 var gClient, gActor;
 
 function connect(onDone) {
 
   if (Services.appinfo.name == "B2G") {
     // On b2g, we try to exercice the code that launches the production debugger server
--- a/devtools/shared/apps/tests/unit/head_apps.js
+++ b/devtools/shared/apps/tests/unit/head_apps.js
@@ -2,20 +2,20 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 var Cc = Components.classes;
 var Ci = Components.interfaces;
 var Cu = Components.utils;
 var Cr = Components.results;
 var CC = Components.Constructor;
 
-Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/FileUtils.jsm");
 const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
+const Services = require("Services");
 const {DebuggerClient} = require("devtools/shared/client/main");
 const {DebuggerServer} = require("devtools/server/main");
 const {AppActorFront} = require("devtools/shared/apps/app-actor-front");
 
 var gClient, gActor, gActorFront;
 
 function connect(onDone) {
   // Initialize a loopback remote protocol connection
--- a/devtools/shared/client/connection-manager.js
+++ b/devtools/shared/client/connection-manager.js
@@ -7,18 +7,18 @@
 "use strict";
 
 const {Cc, Ci, Cu, Cr} = require("chrome");
 const {setTimeout, clearTimeout} = require('sdk/timers');
 const EventEmitter = require("devtools/shared/event-emitter");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 const { DebuggerServer } = require("devtools/server/main");
 const { DebuggerClient } = require("devtools/shared/client/main");
+const Services = require("Services");
 
-Cu.import("resource://gre/modules/Services.jsm");
 DevToolsUtils.defineLazyModuleGetter(this, "Task",
   "resource://gre/modules/Task.jsm");
 
 const REMOTE_TIMEOUT = "devtools.debugger.remote-timeout";
 
 /**
  * Connection Manager.
  *
--- a/devtools/shared/content-observer.js
+++ b/devtools/shared/content-observer.js
@@ -1,15 +1,15 @@
 /* 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 {Cc, Ci, Cu, Cr} = require("chrome");
-const {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
+const Services = require("Services");
 
 const events = require("sdk/event/core");
 
 /**
  * Handles adding an observer for the creation of content document globals,
  * event sent immediately after a web content document window has been set up,
  * but before any script code has been executed.
  */
--- a/devtools/shared/css-color.js
+++ b/devtools/shared/css-color.js
@@ -1,16 +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 {Cc, Ci, Cu} = require("chrome");
-const {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
+const Services = require("Services");
 
 const COLOR_UNIT_PREF = "devtools.defaultColorUnit";
 
 const SPECIALVALUES = new Set([
   "currentcolor",
   "initial",
   "inherit",
   "transparent",
--- a/devtools/shared/discovery/discovery.js
+++ b/devtools/shared/discovery/discovery.js
@@ -29,28 +29,28 @@
  * When a service is registered, is supplies a regular object with any details
  * about itself (a port number, for example) in a service-defined format, which
  * is then available to scanning devices.
  */
 
 const { Cu, CC, Cc, Ci } = require("chrome");
 const EventEmitter = require("devtools/shared/event-emitter");
 const { setTimeout, clearTimeout } = require("sdk/timers");
+const Services = require("Services");
 
 const UDPSocket = CC("@mozilla.org/network/udp-socket;1",
                      "nsIUDPSocket",
                      "init");
 
 const SCAN_PORT = 50624;
 const UPDATE_PORT = 50625;
 const ADDRESS = "224.0.0.115";
 const REPLY_TIMEOUT = 5000;
 
 const { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
-const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
 
 XPCOMUtils.defineLazyGetter(this, "converter", () => {
   let conv = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
              createInstance(Ci.nsIScriptableUnicodeConverter);
   conv.charset = "utf8";
   return conv;
 });
 
--- a/devtools/shared/discovery/tests/unit/test_discovery.js
+++ b/devtools/shared/discovery/tests/unit/test_discovery.js
@@ -1,29 +1,29 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 var Cu = Components.utils;
 
-const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
+const { require } =
+  Cu.import("resource://devtools/shared/Loader.jsm", {});
+const Services = require("Services");
+const promise = require("promise");
+const EventEmitter = require("devtools/shared/event-emitter");
+const discovery = require("devtools/shared/discovery/discovery");
+const { setTimeout, clearTimeout } = require("sdk/timers");
+
 Services.prefs.setBoolPref("devtools.discovery.log", true);
 
 do_register_cleanup(() => {
   Services.prefs.clearUserPref("devtools.discovery.log");
 });
 
-const { require } =
-  Cu.import("resource://devtools/shared/Loader.jsm", {});
-const promise = require("promise");
-const EventEmitter = require("devtools/shared/event-emitter");
-const discovery = require("devtools/shared/discovery/discovery");
-const { setTimeout, clearTimeout } = require("sdk/timers");
-
 function log(msg) {
   do_print("DISCOVERY: " + msg);
 }
 
 // Global map of actively listening ports to TestTransport instances
 var gTestTransports = {};
 
 /**
--- a/devtools/shared/gcli/commands/folder.js
+++ b/devtools/shared/gcli/commands/folder.js
@@ -1,16 +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 { Cc, Ci, Cu, CC } = require("chrome");
-const { Services } = require("resource://gre/modules/Services.jsm");
+const Services = require("Services");
 const l10n = require("gcli/l10n");
 const dirService = Cc["@mozilla.org/file/directory_service;1"]
                       .getService(Ci.nsIProperties);
 
 function showFolder(aPath) {
   let nsLocalFile = CC("@mozilla.org/file/local;1", "nsILocalFile",
                         "initWithPath");
 
--- a/devtools/shared/gcli/commands/index.js
+++ b/devtools/shared/gcli/commands/index.js
@@ -70,17 +70,16 @@ exports.devtoolsModules = [
   "devtools/shared/gcli/commands/media",
   "devtools/shared/gcli/commands/pagemod",
   "devtools/shared/gcli/commands/paintflashing",
   "devtools/shared/gcli/commands/qsa",
   "devtools/shared/gcli/commands/restart",
   "devtools/shared/gcli/commands/rulers",
   "devtools/shared/gcli/commands/screenshot",
   "devtools/shared/gcli/commands/security",
-  "devtools/shared/gcli/commands/tools",
 ];
 
 /**
  * Register commands from tools with 'command: [ "some/module" ]' definitions.
  * The map/reduce incantation squashes the array of arrays to a single array.
  */
 try {
   const defaultTools = require("devtools/client/definitions").defaultTools;
--- a/devtools/shared/gcli/commands/inject.js
+++ b/devtools/shared/gcli/commands/inject.js
@@ -1,15 +1,15 @@
 /* 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("resource://gre/modules/Services.jsm");
+const Services = require("Services");
 const { listenOnce } = require("devtools/shared/async-utils");
 const l10n = require("gcli/l10n");
 
 exports.items = [
   {
     item: "command",
     runAt: "server",
     name: "inject",
--- a/devtools/shared/gcli/commands/jsb.js
+++ b/devtools/shared/gcli/commands/jsb.js
@@ -4,16 +4,17 @@
 
 "use strict";
 
 const { Cc, Ci, Cu } = require("chrome");
 const l10n = require("gcli/l10n");
 const XMLHttpRequest = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"];
 
 loader.lazyImporter(this, "Preferences", "resource://gre/modules/Preferences.jsm");
+loader.lazyImporter(this, "ScratchpadManager", "resource://devtools/client/scratchpad/scratchpad-manager.jsm");
 
 loader.lazyRequireGetter(this, "beautify", "devtools/shared/jsbeautify/beautify");
 
 exports.items = [
   {
     item: "command",
     runAt: "client",
     name: "jsb",
@@ -111,22 +112,19 @@ exports.items = [
         return l10n.lookup("jsbInvalidURL");
       }
 
       let deferred = context.defer();
 
       xhr.onreadystatechange = function() {
         if (xhr.readyState == 4) {
           if (xhr.status == 200 || xhr.status == 0) {
-            let browserDoc = context.environment.chromeDocument;
-            let browserWindow = browserDoc.defaultView;
-            let gBrowser = browserWindow.gBrowser;
             let result = beautify.js(xhr.responseText, opts);
 
-            browserWindow.Scratchpad.ScratchpadManager.openScratchpad({text: result});
+            ScratchpadManager.openScratchpad({text: result});
 
             deferred.resolve();
           } else {
             deferred.reject("Unable to load page to beautify: " + args.url + " " +
                             xhr.status + " " + xhr.statusText);
           }
         };
       }
--- a/devtools/shared/gcli/commands/moz.build
+++ b/devtools/shared/gcli/commands/moz.build
@@ -22,10 +22,9 @@ DevToolsModules(
     'media.js',
     'pagemod.js',
     'paintflashing.js',
     'qsa.js',
     'restart.js',
     'rulers.js',
     'screenshot.js',
     'security.js',
-    'tools.js',
 )
--- a/devtools/shared/gcli/commands/screenshot.js
+++ b/devtools/shared/gcli/commands/screenshot.js
@@ -1,17 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { Cc, Ci, Cu } = require("chrome");
 const l10n = require("gcli/l10n");
-const { Services } = require("resource://gre/modules/Services.jsm");
+const Services = require("Services");
 const { getRect } = require("devtools/shared/layout/utils");
 
 loader.lazyImporter(this, "Downloads", "resource://gre/modules/Downloads.jsm");
 loader.lazyImporter(this, "Task", "resource://gre/modules/Task.jsm");
 loader.lazyImporter(this, "OS", "resource://gre/modules/osfile.jsm");
 
 const BRAND_SHORT_NAME = Cc["@mozilla.org/intl/stringbundle;1"]
                            .getService(Ci.nsIStringBundleService)
deleted file mode 100644
--- a/devtools/shared/gcli/commands/tools.js
+++ /dev/null
@@ -1,99 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const { Cc, Ci, Cu } = require("chrome");
-const Services = require("Services");
-const { OS } = require("resource://gre/modules/osfile.jsm");
-const { devtools } = require("resource://devtools/shared/Loader.jsm");
-const gcli = require("gcli/index");
-const l10n = require("gcli/l10n");
-
-const BRAND_SHORT_NAME = Cc["@mozilla.org/intl/stringbundle;1"]
-                           .getService(Ci.nsIStringBundleService)
-                           .createBundle("chrome://branding/locale/brand.properties")
-                           .GetStringFromName("brandShortName");
-
-exports.items = [
-  {
-    name: "tools",
-    description: l10n.lookupFormat("toolsDesc2", [ BRAND_SHORT_NAME ]),
-    manual: l10n.lookupFormat("toolsManual2", [ BRAND_SHORT_NAME ]),
-    get hidden() {
-      return gcli.hiddenByChromePref();
-    }
-  },
-  {
-    item: "command",
-    runAt: "client",
-    name: "tools srcdir",
-    description: l10n.lookup("toolsSrcdirDesc"),
-    manual: l10n.lookupFormat("toolsSrcdirManual2", [ BRAND_SHORT_NAME ]),
-    get hidden() {
-      return gcli.hiddenByChromePref();
-    },
-    params: [
-      {
-        name: "srcdir",
-        type: "string" /* {
-          name: "file",
-          filetype: "directory",
-          existing: "yes"
-        } */,
-        description: l10n.lookup("toolsSrcdirDir")
-      }
-    ],
-    returnType: "string",
-    exec: function(args, context) {
-      let clobber = OS.Path.join(args.srcdir, "CLOBBER");
-      return OS.File.exists(clobber).then(function(exists) {
-        if (exists) {
-          let str = Cc["@mozilla.org/supports-string;1"]
-                      .createInstance(Ci.nsISupportsString);
-          str.data = args.srcdir;
-          Services.prefs.setComplexValue("devtools.loader.srcdir",
-                                         Ci.nsISupportsString, str);
-          devtools.reload();
-
-          return l10n.lookupFormat("toolsSrcdirReloaded2", [ args.srcdir ]);
-        }
-
-        return l10n.lookupFormat("toolsSrcdirNotFound2", [ args.srcdir ]);
-      });
-    }
-  },
-  {
-    item: "command",
-    runAt: "client",
-    name: "tools builtin",
-    description: l10n.lookup("toolsBuiltinDesc"),
-    manual: l10n.lookup("toolsBuiltinManual"),
-    get hidden() {
-      return gcli.hiddenByChromePref();
-    },
-    returnType: "string",
-    exec: function(args, context) {
-      Services.prefs.clearUserPref("devtools.loader.srcdir");
-      devtools.reload();
-      return l10n.lookup("toolsBuiltinReloaded");
-    }
-  },
-  {
-    item: "command",
-    runAt: "client",
-    name: "tools reload",
-    description: l10n.lookup("toolsReloadDesc"),
-    get hidden() {
-      return gcli.hiddenByChromePref() ||
-             !Services.prefs.prefHasUserValue("devtools.loader.srcdir");
-    },
-
-    returnType: "string",
-    exec: function(args, context) {
-      devtools.reload();
-      return l10n.lookup("toolsReloaded2");
-    }
-  }
-];
--- a/devtools/shared/gcli/source/lib/gcli/l10n.js
+++ b/devtools/shared/gcli/source/lib/gcli/l10n.js
@@ -19,17 +19,17 @@
 var Cc = require('chrome').Cc;
 var Ci = require('chrome').Ci;
 var Cu = require('chrome').Cu;
 
 var prefSvc = Cc['@mozilla.org/preferences-service;1']
                         .getService(Ci.nsIPrefService);
 var prefBranch = prefSvc.getBranch(null).QueryInterface(Ci.nsIPrefBranch);
 
-var Services = Cu.import('resource://gre/modules/Services.jsm', {}).Services;
+var Services = require("Services");
 var stringBundle = Services.strings.createBundle(
         'chrome://devtools-shared/locale/gclicommands.properties');
 
 /**
  * Lookup a string in the GCLI string bundle
  */
 exports.lookup = function(name) {
   try {
--- a/devtools/shared/gcli/source/lib/gcli/settings.js
+++ b/devtools/shared/gcli/source/lib/gcli/settings.js
@@ -18,17 +18,17 @@
 
 var imports = {};
 
 var Cc = require('chrome').Cc;
 var Ci = require('chrome').Ci;
 var Cu = require('chrome').Cu;
 
 var XPCOMUtils = Cu.import('resource://gre/modules/XPCOMUtils.jsm', {}).XPCOMUtils;
-var Services = Cu.import('resource://gre/modules/Services.jsm', {}).Services;
+var Services = require("Services");
 
 XPCOMUtils.defineLazyGetter(imports, 'prefBranch', function() {
   var prefService = Cc['@mozilla.org/preferences-service;1']
           .getService(Ci.nsIPrefService);
   return prefService.getBranch(null).QueryInterface(Ci.nsIPrefBranch2);
 });
 
 XPCOMUtils.defineLazyGetter(imports, 'supportsString', function() {
--- a/devtools/shared/gcli/source/lib/gcli/util/l10n.js
+++ b/devtools/shared/gcli/source/lib/gcli/util/l10n.js
@@ -14,17 +14,17 @@
  * limitations under the License.
  */
 
 'use strict';
 
 var Cu = require('chrome').Cu;
 
 var XPCOMUtils = Cu.import('resource://gre/modules/XPCOMUtils.jsm', {}).XPCOMUtils;
-var Services = Cu.import('resource://gre/modules/Services.jsm', {}).Services;
+var Services = require("Services");
 
 var imports = {};
 XPCOMUtils.defineLazyGetter(imports, 'stringBundle', function () {
   return Services.strings.createBundle('chrome://devtools-shared/locale/gcli.properties');
 });
 
 /*
  * Not supported when embedded - we're doing things the Mozilla way not the
--- a/devtools/shared/heapsnapshot/CensusUtils.js
+++ b/devtools/shared/heapsnapshot/CensusUtils.js
@@ -354,17 +354,17 @@ DiffVisitor.prototype.results = function
  *                                        census.
  *            - {Number} basisTotalCount: the total count in the start census.
  */
 function diff(breakdown, startCensus, endCensus) {
   const visitor = new DiffVisitor(endCensus);
   walk(breakdown, startCensus, visitor);
   return visitor.results();
 };
-exports.diff = diff
+exports.diff = diff;
 
 /**
  * Creates a hash map mapping node IDs to its parent node.
  *
  * @param {CensusTreeNode} node
  * @param {Object<number, TreeNode>} aggregator
  *
  * @return {Object<number, TreeNode>}
--- a/devtools/shared/heapsnapshot/census-tree-node.js
+++ b/devtools/shared/heapsnapshot/census-tree-node.js
@@ -515,28 +515,16 @@ function invert(tree) {
       for (let i = path.length - 1; i >= 0; i--) {
         currentCacheValue = insertOrMergeNode(currentCacheValue, path[i]);
       }
     }
 
     path.pop();
   }(tree));
 
-  // Next, do a depth-first search of the inverted tree and ensure that siblings
-  // are sorted by their self bytes/count.
-
-  (function ensureSorted(node) {
-    if (node.children) {
-      node.children.sort(compareBySelf);
-      for (let i = 0, length = node.children.length; i < length; i++) {
-        ensureSorted(node.children[i]);
-      }
-    }
-  }(inverted.node));
-
   // Ensure that the root node always has the totals.
   inverted.node.totalBytes = tree.totalBytes;
   inverted.node.totalCount = tree.totalCount;
 
   return inverted.node;
 }
 
 /**
@@ -672,10 +660,22 @@ exports.censusReportToCensusTreeNode = f
   // If the report is a delta report that was generated by diffing two other
   // reports, make sure to use the basis totals rather than the totals of the
   // difference.
   if (typeof report[basisTotalBytes] === "number") {
     result.totalBytes = report[basisTotalBytes];
     result.totalCount = report[basisTotalCount];
   }
 
+  // Inverting and filtering could have messed up the sort order, so do a
+  // depth-first search of the tree and ensure that siblings are sorted.
+  const comparator = options.invert ? compareBySelf : compareByTotal;
+  (function ensureSorted(node) {
+    if (node.children) {
+      node.children.sort(comparator);
+      for (let i = 0, length = node.children.length; i < length; i++) {
+        ensureSorted(node.children[i]);
+      }
+    }
+  }(result));
+
   return result;
 };
new file mode 100644
--- /dev/null
+++ b/devtools/shared/heapsnapshot/tests/unit/test_census_filtering_05.js
@@ -0,0 +1,71 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that filtered and inverted allocation stack census trees are sorted
+// properly.
+
+function run_test() {
+  const countBreakdown = { by: "count", count: true, bytes: true };
+
+  const BREAKDOWN = {
+    by: "allocationStack",
+    then: countBreakdown,
+    noStack: countBreakdown,
+  };
+
+  const stacks = [];
+
+  function foo(depth = 1) {
+    stacks.push(saveStack(depth));
+    bar(depth + 1);
+    baz(depth + 1);
+    stacks.push(saveStack(depth));
+  }
+
+  function bar(depth = 1) {
+    stacks.push(saveStack(depth));
+    stacks.push(saveStack(depth));
+  }
+
+  function baz(depth = 1) {
+    stacks.push(saveStack(depth));
+    bang(depth + 1);
+    stacks.push(saveStack(depth));
+  }
+
+  function bang(depth = 1) {
+    stacks.push(saveStack(depth));
+    stacks.push(saveStack(depth));
+    stacks.push(saveStack(depth));
+  }
+
+  foo();
+  bar();
+  baz();
+  bang();
+
+  const REPORT = new Map(stacks.map((s, i) => {
+    return [s, {
+      count: i + 1,
+      bytes: (i + 1) * 10
+    }];
+  }));
+
+  const tree = censusReportToCensusTreeNode(BREAKDOWN, REPORT, {
+    filter: "baz",
+    invert: true
+  });
+
+  dumpn("tree = " + JSON.stringify(tree, savedFrameReplacer, 4));
+
+  (function assertSortedBySelf(node) {
+    if (node.children) {
+      let lastSelfBytes = Infinity;
+      for (let child of node.children) {
+        ok(child.bytes <= lastSelfBytes, `${child.bytes} <= ${lastSelfBytes}`);
+        lastSelfBytes = child.bytes;
+        assertSortedBySelf(child);
+      }
+    }
+  }(tree));
+}
--- a/devtools/shared/heapsnapshot/tests/unit/xpcshell.ini
+++ b/devtools/shared/heapsnapshot/tests/unit/xpcshell.ini
@@ -16,16 +16,17 @@ support-files =
 [test_census_diff_03.js]
 [test_census_diff_04.js]
 [test_census_diff_05.js]
 [test_census_diff_06.js]
 [test_census_filtering_01.js]
 [test_census_filtering_02.js]
 [test_census_filtering_03.js]
 [test_census_filtering_04.js]
+[test_census_filtering_05.js]
 [test_census-tree-node-01.js]
 [test_census-tree-node-02.js]
 [test_census-tree-node-03.js]
 [test_census-tree-node-04.js]
 [test_census-tree-node-05.js]
 [test_census-tree-node-06.js]
 [test_census-tree-node-07.js]
 [test_census-tree-node-08.js]
--- a/devtools/shared/indentation.js
+++ b/devtools/shared/indentation.js
@@ -2,17 +2,17 @@
 /* vim:set ts=2 sw=2 sts=2 et tw=80:
  * 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 { Cu } = require("chrome");
-Cu.import("resource://gre/modules/Services.jsm");
+const Services = require("Services");
 
 const EXPAND_TAB = "devtools.editor.expandtab";
 const TAB_SIZE = "devtools.editor.tabsize";
 const DETECT_INDENT = "devtools.editor.detectindentation";
 const DETECT_INDENT_MAX_LINES = 500;
 
 /**
  * Get the indentation to use in an editor, or return false if the user has
--- a/devtools/shared/shims/Loader.jsm
+++ b/devtools/shared/shims/Loader.jsm
@@ -21,17 +21,16 @@ if (Services.prefs.getBoolPref(WARNING_P
                      "Loader.jsm\") to load this module.",
                      "https://bugzil.la/912121");
 }
 
 this.EXPORTED_SYMBOLS = [
   "DevToolsLoader",
   "devtools",
   "BuiltinProvider",
-  "SrcdirProvider",
   "require",
   "loader"
 ];
 
 const module =
   Cu.import("resource://devtools/shared/Loader.jsm", {});
 
 for (let symbol of this.EXPORTED_SYMBOLS) {
--- a/devtools/shared/tests/mochitest/chrome.ini
+++ b/devtools/shared/tests/mochitest/chrome.ini
@@ -1,8 +1,7 @@
 [DEFAULT]
 tags = devtools
 skip-if = buildapp == 'b2g' || os == 'android'
 
 [test_eventemitter_basic.html]
 [test_devtools_extensions.html]
 skip-if = os == 'linux' && debug # Bug 1205739
-[test_loader_paths.html]
--- a/devtools/shared/tests/mochitest/test_devtools_extensions.html
+++ b/devtools/shared/tests/mochitest/test_devtools_extensions.html
@@ -12,20 +12,20 @@
 
     <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;version=1.8">
       const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
-      const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
 
       let { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
       const contentGlobals  = require("devtools/server/content-globals");
+      const Services = require("Services");
       const tabs = require('sdk/tabs');
       const { getMostRecentBrowserWindow, getInnerId } = require('sdk/window/utils');
       const { PageMod } = require('sdk/page-mod');
 
       var _tests = [];
       function addTest(test) {
         _tests.push(test);
       }
deleted file mode 100644
--- a/devtools/shared/tests/mochitest/test_loader_paths.html
+++ /dev/null
@@ -1,52 +0,0 @@
-<!DOCTYPE html>
-<!--
-  Any copyright is dedicated to the Public Domain.
-  http://creativecommons.org/publicdomain/zero/1.0/
--->
-
-<html>
-
-  <head>
-    <meta charset="utf8">
-    <title></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">
-  </head>
-
-  <body>
-
-    <script type="application/javascript;version=1.8">
-      const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
-
-      const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
-
-      const SRCDIR_PREF = "devtools.loader.srcdir";
-      let srcDir = Cc["@mozilla.org/file/directory_service;1"]
-                   .getService(Components.interfaces.nsIProperties)
-                   .get("CurWorkD", Components.interfaces.nsIFile).path;
-
-      let srcDirStr = Cc["@mozilla.org/supports-string;1"]
-                      .createInstance(Ci.nsISupportsString);
-      srcDirStr.data = srcDir;
-      Services.prefs.setComplexValue(SRCDIR_PREF, Ci.nsISupportsString,
-                                     srcDirStr);
-
-      const { BuiltinProvider, SrcdirProvider } =
-        Cu.import("resource://devtools/shared/Loader.jsm", {});
-
-      let builtin = new BuiltinProvider();
-      builtin.load();
-      let srcdir = new SrcdirProvider();
-      srcdir.load();
-
-      is(builtin.loader.mapping.length,
-         srcdir.loader.mapping.length + 1,
-         "The built-in loader has one additional mappings.");
-
-      Services.prefs.clearUserPref(SRCDIR_PREF);
-    </script>
-  </body>
-</html>
--- a/devtools/shared/touch/simulator-content.js
+++ b/devtools/shared/touch/simulator-content.js
@@ -279,19 +279,26 @@ var simulator = {
     }
 
     let touchEvent = document.createEvent("touchevent");
     let point = document.createTouch(content, target, 0,
                                      evt.pageX, evt.pageY,
                                      evt.screenX, evt.screenY,
                                      evt.clientX, evt.clientY,
                                      1, 1, 0, 0);
+
     let touches = document.createTouchList(point);
     let targetTouches = touches;
     let changedTouches = touches;
+    if (name === "touchend" || name === "touchcancel") {
+      // "touchend" and "touchcancel" events should not have the removed touch
+      // neither in touches nor in targetTouches
+      touches = targetTouches = document.createTouchList();
+    }
+
     touchEvent.initTouchEvent(name, true, true, content, 0,
                               false, false, false, false,
                               touches, targetTouches, changedTouches);
     target.dispatchEvent(touchEvent);
   },
 
   getContent(target) {
     let win = (target && target.ownerDocument)
--- a/devtools/shared/webconsole/network-monitor.js
+++ b/devtools/shared/webconsole/network-monitor.js
@@ -2,22 +2,22 @@
 /* vim: set ft= javascript ts=2 et sw=2 tw=80: */
 /* 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 {Cc, Ci, Cu, Cr} = require("chrome");
+const Services = require("Services");
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 loader.lazyRequireGetter(this, "NetworkHelper",
                          "devtools/shared/webconsole/network-helper");
-loader.lazyImporter(this, "Services", "resource://gre/modules/Services.jsm");
 loader.lazyRequireGetter(this, "DevToolsUtils",
                          "devtools/shared/DevToolsUtils");
 loader.lazyImporter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm");
 loader.lazyServiceGetter(this, "gActivityDistributor",
                          "@mozilla.org/network/http-activity-distributor;1",
                          "nsIHttpActivityDistributor");
 
 ///////////////////////////////////////////////////////////////////////////////
--- a/devtools/shared/webconsole/test/common.js
+++ b/devtools/shared/webconsole/test/common.js
@@ -5,23 +5,23 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 const XHTML_NS = "http://www.w3.org/1999/xhtml";
 
-Cu.import("resource://gre/modules/Services.jsm");
 const {Task} = Cu.import("resource://gre/modules/Task.jsm", {});
 
 // This gives logging to stdout for tests
 var {console} = Cu.import("resource://gre/modules/Console.jsm", {});
 
 var {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
+var Services = require("Services");
 var WebConsoleUtils = require("devtools/shared/webconsole/utils").Utils;
 
 var ConsoleAPIStorage = Cc["@mozilla.org/consoleAPI-storage;1"]
                           .getService(Ci.nsIConsoleAPIStorage);
 var {DebuggerServer} = require("devtools/server/main");
 var {DebuggerClient, ObjectClient} = require("devtools/shared/client/main");
 
 var {ConsoleServiceListener, ConsoleAPIListener} =
--- a/devtools/shared/webconsole/test/unit/test_security-info-static-hpkp.js
+++ b/devtools/shared/webconsole/test/unit/test_security-info-static-hpkp.js
@@ -2,17 +2,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
 // Test that NetworkHelper.parseSecurityInfo correctly detects static hpkp pins
 
 const { require } = Components.utils.import("resource://devtools/shared/Loader.jsm", {});
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
-Components.utils.import("resource://gre/modules/Services.jsm");
+const Services = require("Services");
 
 Object.defineProperty(this, "NetworkHelper", {
   get: function() {
     return require("devtools/shared/webconsole/network-helper");
   },
   configurable: true,
   writeable: false,
   enumerable: true
--- a/devtools/shared/webconsole/utils.js
+++ b/devtools/shared/webconsole/utils.js
@@ -3,21 +3,20 @@
 /* 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 {Cc, Ci, Cu, components} = require("chrome");
 const {isWindowIncluded} = require("devtools/shared/layout/utils");
+const Services = require("Services");
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
-loader.lazyImporter(this, "Services", "resource://gre/modules/Services.jsm");
-
 // TODO: Bug 842672 - browser/ imports modules from toolkit/.
 // Note that these are only used in WebConsoleCommands, see $0 and pprint().
 loader.lazyImporter(this, "VariablesView", "resource://devtools/client/shared/widgets/VariablesView.jsm");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 
 XPCOMUtils.defineLazyServiceGetter(this,
                                    "swm",
                                    "@mozilla.org/serviceworkers/manager;1",
deleted file mode 100644
--- a/dom/media/omx/RtspMediaCodecDecoder.cpp
+++ /dev/null
@@ -1,56 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et cindent: */
-/* 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/. */
-
-#include "RtspMediaCodecDecoder.h"
-
-#include "MediaDecoderStateMachine.h"
-#include "RtspMediaResource.h"
-#include "RtspMediaCodecReader.h"
-
-namespace mozilla {
-
-MediaDecoder*
-RtspMediaCodecDecoder::Clone(MediaDecoderOwner* aOwner)
-{
-  return new RtspMediaCodecDecoder(aOwner);
-}
-
-MediaOmxCommonReader*
-RtspMediaCodecDecoder::CreateReader()
-{
-  return new RtspMediaCodecReader(this);
-}
-
-MediaDecoderStateMachine*
-RtspMediaCodecDecoder::CreateStateMachineFromReader(MediaOmxCommonReader* aReader)
-{
-  return new MediaDecoderStateMachine(this, aReader,
-                                      mResource->IsRealTime());
-}
-
-void
-RtspMediaCodecDecoder::ChangeState(PlayState aState)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  MediaDecoder::ChangeState(aState);
-
-  // Notify RTSP controller if the play state is ended.
-  // This is necessary for RTSP controller to transit its own state.
-  if (mPlayState == PLAY_STATE_ENDED) {
-    RefPtr<RtspMediaResource> resource = mResource->GetRtspPointer();
-    if (resource) {
-      nsIStreamingProtocolController* controller =
-        resource->GetMediaStreamController();
-      if (controller) {
-        controller->PlaybackEnded();
-      }
-    }
-  }
-}
-
-} // namespace mozilla
-
deleted file mode 100644
--- a/dom/media/omx/RtspMediaCodecDecoder.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et cindent: */
-/* 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/. */
-
-#if !defined(RtspMediaCodecDecoder_h_)
-#define RtspMediaCodecDecoder_h_
-
-#include "MediaOmxCommonDecoder.h"
-
-namespace mozilla {
-
-class RtspMediaCodecDecoder final : public MediaOmxCommonDecoder
-{
-public:
-  explicit RtspMediaCodecDecoder(MediaDecoderOwner* aOwner)
-    : MediaOmxCommonDecoder(aOwner) {}
-
-  MediaDecoder* Clone(MediaDecoderOwner* aOwner) override;
-
-  MediaOmxCommonReader* CreateReader() override;
-
-  MediaDecoderStateMachine* CreateStateMachineFromReader(MediaOmxCommonReader* aReader) override;
-
-  void ChangeState(PlayState aState) override;
-};
-
-} // namespace mozilla
-
-#endif
--- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
@@ -28,17 +28,16 @@ import org.mozilla.gecko.favicons.Favico
 import org.mozilla.gecko.favicons.OnFaviconLoadedListener;
 import org.mozilla.gecko.favicons.decoders.IconDirectoryEntry;
 import org.mozilla.gecko.firstrun.FirstrunAnimationContainer;
 import org.mozilla.gecko.gfx.DynamicToolbarAnimator;
 import org.mozilla.gecko.gfx.ImmutableViewportMetrics;
 import org.mozilla.gecko.gfx.LayerView;
 import org.mozilla.gecko.home.BrowserSearch;
 import org.mozilla.gecko.home.HomeBanner;
-import org.mozilla.gecko.home.HomeConfig;
 import org.mozilla.gecko.home.HomeConfig.PanelType;
 import org.mozilla.gecko.home.HomeConfigPrefsBackend;
 import org.mozilla.gecko.home.HomePager;
 import org.mozilla.gecko.home.HomePager.OnUrlOpenInBackgroundListener;
 import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
 import org.mozilla.gecko.home.HomePanelsManager;
 import org.mozilla.gecko.home.SearchEngine;
 import org.mozilla.gecko.javaaddons.JavaAddonManager;
@@ -189,17 +188,17 @@ public class BrowserApp extends GeckoApp
     private static final String BROWSER_SEARCH_TAG = "browser_search";
 
     // Request ID for startActivityForResult.
     private static final int ACTIVITY_REQUEST_PREFERENCES = 1001;
     private static final int ACTIVITY_REQUEST_TAB_QUEUE = 2001;
 
     @RobocopTarget
     public static final String EXTRA_SKIP_STARTPANE = "skipstartpane";
-    private static final String HONEYCOMB_EOL_NOTIFIED = "honeycomb_eol_notified";
+    private static final String EOL_NOTIFIED = "eol_notified";
 
     private BrowserSearch mBrowserSearch;
     private View mBrowserSearchContainer;
 
     public ViewGroup mBrowserChrome;
     public ViewFlipper mActionBarFlipper;
     public ActionModeCompatView mActionBar;
     private BrowserToolbar mBrowserToolbar;
@@ -793,21 +792,21 @@ public class BrowserApp extends GeckoApp
 
         SnackbarHelper.showSnackbarWithAction(this,
                 getString(R.string.updater_permission_text),
                 Snackbar.LENGTH_INDEFINITE,
                 getString(R.string.updater_permission_allow),
                 allowCallback);
     }
 
-    private void conditionallyNotifyHCEOL() {
+    private void conditionallyNotifyEOL() {
         final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
         try {
             final SharedPreferences prefs = GeckoSharedPrefs.forProfile(this);
-            if (!prefs.getBoolean(HONEYCOMB_EOL_NOTIFIED, false)) {
+            if (!prefs.contains(EOL_NOTIFIED)) {
 
                 // Launch main App to load SUMO url on EOL notification.
                 final String link = getString(R.string.eol_notification_url,
                                               AppConstants.MOZ_APP_VERSION,
                                               AppConstants.OS_TARGET,
                                               Locales.getLanguageTag(Locale.getDefault()));
 
                 final Intent intent = new Intent(Intent.ACTION_VIEW);
@@ -819,22 +818,22 @@ public class BrowserApp extends GeckoApp
                         .setContentTitle(getString(R.string.eol_notification_title))
                         .setContentText(getString(R.string.eol_notification_summary))
                         .setSmallIcon(R.drawable.ic_status_logo)
                         .setAutoCancel(true)
                         .setContentIntent(pendingIntent)
                         .build();
 
                 final NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
-                final int notificationID = HONEYCOMB_EOL_NOTIFIED.hashCode();
+                final int notificationID = EOL_NOTIFIED.hashCode();
                 notificationManager.notify(notificationID, notification);
 
                 GeckoSharedPrefs.forProfile(this)
                                 .edit()
-                                .putBoolean(HONEYCOMB_EOL_NOTIFIED, true)
+                                .putBoolean(EOL_NOTIFIED, true)
                                 .apply();
             }
         } finally {
             StrictMode.setThreadPolicy(savedPolicy);
         }
     }
 
     /**
@@ -2513,18 +2512,18 @@ public class BrowserApp extends GeckoApp
 
         HomePanelsManager.getInstance().onLocaleReady(locale);
 
         if (mMenu != null) {
             mMenu.clear();
             onCreateOptionsMenu(mMenu);
         }
 
-        if (!Versions.preHC && !Versions.feature14Plus) {
-            conditionallyNotifyHCEOL();
+        if (!Versions.feature14Plus) {
+            conditionallyNotifyEOL();
         }
     }
 
     @Override
     public void onActivityResult(int requestCode, int resultCode, Intent data) {
         Log.d(LOGTAG, "onActivityResult: " + requestCode + ", " + resultCode + ", " + data);
         switch (requestCode) {
             case ACTIVITY_REQUEST_PREFERENCES:
--- a/mobile/android/base/java/org/mozilla/gecko/db/BrowserContract.java
+++ b/mobile/android/base/java/org/mozilla/gecko/db/BrowserContract.java
@@ -480,15 +480,43 @@ public class BrowserContract {
 
     @RobocopTarget
     public static final class SuggestedSites implements CommonColumns, URLColumns {
         private SuggestedSites() {}
 
         public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "suggestedsites");
     }
 
+    @RobocopTarget
+    public static final class UrlAnnotations implements CommonColumns, DateSyncColumns {
+        private UrlAnnotations() {}
+
+        public static final String TABLE_NAME = "urlannotations";
+        public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, TABLE_NAME);
+
+        public static final String URL = "url";
+        public static final String KEY = "key";
+        public static final String VALUE = "value";
+        public static final String SYNC_STATUS = "sync_status";
+
+        public enum SyncStatus {
+            // We use a parameter, rather than ordinal(), as defensive coding: we can't let the
+            // ordinal values change because we've already stored values into the DB.
+            NEW (0);
+
+            // Value stored into the database for this column.
+            private final int dbValue;
+
+            SyncStatus(final int dbValue) {
+                this.dbValue = dbValue;
+            }
+
+            public int getDBValue() { return dbValue; }
+        }
+    }
+
     // We refer to the service by name to decouple services from the rest of the code base.
     public static final String TAB_RECEIVED_SERVICE_CLASS_NAME = "org.mozilla.gecko.tabqueue.TabReceivedService";
 
     public static final String SKIP_TAB_QUEUE_FLAG = "skip_tab_queue";
 
     public static final String EXTRA_CLIENT_GUID = "org.mozilla.gecko.extra.CLIENT_ID";
 }
--- a/mobile/android/base/java/org/mozilla/gecko/db/BrowserDB.java
+++ b/mobile/android/base/java/org/mozilla/gecko/db/BrowserDB.java
@@ -5,16 +5,17 @@
 package org.mozilla.gecko.db;
 
 import java.io.File;
 import java.util.Collection;
 import java.util.EnumSet;
 import java.util.List;
 
 import org.mozilla.gecko.GeckoProfile;
+import org.mozilla.gecko.annotation.RobocopTarget;
 import org.mozilla.gecko.db.BrowserContract.ExpirePriority;
 import org.mozilla.gecko.distribution.Distribution;
 import org.mozilla.gecko.favicons.decoders.LoadFaviconResult;
 
 import android.content.ContentProviderOperation;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.ContentObserver;
@@ -38,16 +39,17 @@ public interface BrowserDB {
     public static enum FilterFlags {
         EXCLUDE_PINNED_SITES
     }
 
     public abstract Searches getSearches();
     public abstract TabsAccessor getTabsAccessor();
     public abstract URLMetadata getURLMetadata();
     public abstract ReadingListAccessor getReadingListAccessor();
+    @RobocopTarget UrlAnnotations getUrlAnnotations();
 
     /**
      * Add default bookmarks to the database.
      * Takes an offset; returns a new offset.
      */
     public abstract int addDefaultBookmarks(Context context, ContentResolver cr, int offset);
 
     /**
--- a/mobile/android/base/java/org/mozilla/gecko/db/BrowserDatabaseHelper.java
+++ b/mobile/android/base/java/org/mozilla/gecko/db/BrowserDatabaseHelper.java
@@ -1,29 +1,29 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- */
 /* 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/. */
 
 package org.mozilla.gecko.db;
 
 import java.io.File;
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 
 import org.mozilla.gecko.GeckoProfile;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.db.BrowserContract.Bookmarks;
 import org.mozilla.gecko.db.BrowserContract.Combined;
 import org.mozilla.gecko.db.BrowserContract.Favicons;
 import org.mozilla.gecko.db.BrowserContract.History;
 import org.mozilla.gecko.db.BrowserContract.ReadingListItems;
 import org.mozilla.gecko.db.BrowserContract.SearchHistory;
 import org.mozilla.gecko.db.BrowserContract.Thumbnails;
+import org.mozilla.gecko.db.BrowserContract.UrlAnnotations;
 import org.mozilla.gecko.util.FileUtils;
 
 import static org.mozilla.gecko.db.DBUtils.qualifyColumn;
 
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
 import android.database.DatabaseUtils;
@@ -31,22 +31,23 @@ import android.database.SQLException;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteException;
 import android.database.sqlite.SQLiteOpenHelper;
 import android.net.Uri;
 import android.os.Build;
 import android.util.Log;
 
 
-final class BrowserDatabaseHelper extends SQLiteOpenHelper {
+// public for robocop testing
+public final class BrowserDatabaseHelper extends SQLiteOpenHelper {
     private static final String LOGTAG = "GeckoBrowserDBHelper";
 
     // Replace the Bug number below with your Bug that is conducting a DB upgrade, as to force a merge conflict with any
     // other patches that require a DB upgrade.
-    public static final int DATABASE_VERSION = 27; // Bug 826400
+    public static final int DATABASE_VERSION = 28; // Bug 1250707
     public static final String DATABASE_NAME = "browser.db";
 
     final protected Context mContext;
 
     static final String TABLE_BOOKMARKS = Bookmarks.TABLE_NAME;
     static final String TABLE_HISTORY = History.TABLE_NAME;
     static final String TABLE_FAVICONS = Favicons.TABLE_NAME;
     static final String TABLE_THUMBNAILS = Thumbnails.TABLE_NAME;
@@ -353,16 +354,17 @@ final class BrowserDatabaseHelper extend
         createOrUpdateSpecialFolder(db, Bookmarks.PLACES_FOLDER_GUID,
             R.string.bookmarks_folder_places, 0);
 
         createOrUpdateAllSpecialFolders(db);