Merge m-c to inbound, a=merge
authorWes Kocher <wkocher@mozilla.com>
Thu, 06 Aug 2015 18:11:16 -0700
changeset 288389 efc97cd68267c35d255daa6c2aa2baeddc4daf94
parent 288388 8fc2a69b16c0392f21eaec7fcfe9148f6c4b7872 (current diff)
parent 288294 84bfbe34da654487199a5010d6aa443f7e0523b5 (diff)
child 288390 bd9983f167c2658d1a9f5863fc43f2c0860f0e45
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone42.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 m-c to inbound, a=merge
browser/app/profile/firefox.js
browser/base/content/sync/quota.js
browser/base/content/sync/quota.xul
browser/devtools/shared/test/browser_require_basic.js
browser/locales/en-US/chrome/browser/browser.dtd
configure.in
mobile/android/base/mozglue/generatorannotations/OptionalGeneratedParameter.java
mobile/android/base/mozglue/generatorannotations/WrapEntireClassForJNI.java
modules/libpref/init/all.js
toolkit/devtools/Require.jsm
toolkit/devtools/sourcemap/SourceMap.jsm
--- a/b2g/components/B2GComponents.manifest
+++ b/b2g/components/B2GComponents.manifest
@@ -113,8 +113,11 @@ contract @mozilla.org/services/mobileid-
 # B2GAppMigrator.js
 component {7211ece0-b458-4635-9afc-f8d7f376ee95} B2GAppMigrator.js
 contract @mozilla.org/app-migrator;1 {7211ece0-b458-4635-9afc-f8d7f376ee95}
 
 # B2GPresentationDevicePrompt.js
 component {4a300c26-e99b-4018-ab9b-c48cf9bc4de1} B2GPresentationDevicePrompt.js
 contract @mozilla.org/presentation-device/prompt;1 {4a300c26-e99b-4018-ab9b-c48cf9bc4de1}
 
+# PresentationRequestUIGlue.js
+component {ccc8a839-0b64-422b-8a60-fb2af0e376d0} PresentationRequestUIGlue.js
+contract @mozilla.org/presentation/requestuiglue;1 {ccc8a839-0b64-422b-8a60-fb2af0e376d0}
new file mode 100644
--- /dev/null
+++ b/b2g/components/PresentationRequestUIGlue.js
@@ -0,0 +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 { interfaces: Ci, utils: Cu, classes: Cc } = Components;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "SystemAppProxy",
+                                  "resource://gre/modules/SystemAppProxy.jsm");
+
+function PresentationRequestUIGlue() {
+  // This is to store the session ID / resolver binding.
+  // An example of the object literal is shown below:
+  //
+  // {
+  //   "sessionId1" : resolver1,
+  //   ...
+  // }
+  this._resolvers = {};
+
+  // Listen to the result for the opened iframe from front-end.
+  SystemAppProxy.addEventListener("mozPresentationContentEvent", aEvent => {
+    let detail = aEvent.detail;
+
+    if (detail.type != "presentation-receiver-launched") {
+      return;
+    }
+
+    let sessionId = detail.sessionId;
+    let resolver = this._resolvers[sessionId];
+    if (!resolver) {
+      return;
+    }
+
+    delete this._resolvers[sessionId];
+    resolver(detail.frame);
+  });
+}
+
+PresentationRequestUIGlue.prototype = {
+
+  sendRequest: function(aUrl, aSessionId) {
+    SystemAppProxy._sendCustomEvent("mozPresentationChromeEvent",
+                                    { type: "presentation-launch-receiver",
+                                      url: aUrl,
+                                      id: aSessionId });
+
+    return new Promise(function(aResolve, aReject) {
+      this._resolvers[aSessionId] = aResolve;
+    }.bind(this));
+  },
+
+  classID: Components.ID("{ccc8a839-0b64-422b-8a60-fb2af0e376d0}"),
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationRequestUIGlue])
+};
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PresentationRequestUIGlue]);
--- a/b2g/components/moz.build
+++ b/b2g/components/moz.build
@@ -18,16 +18,17 @@ EXTRA_COMPONENTS += [
     'FxAccountsUIGlue.js',
     'HelperAppDialog.js',
     'InterAppCommUIGlue.js',
     'MailtoProtocolHandler.js',
     'MobileIdentityUIGlue.js',
     'OMAContentHandler.js',
     'PaymentGlue.js',
     'PaymentProviderStrategy.js',
+    'PresentationRequestUIGlue.js',
     'ProcessGlobal.js',
     'SmsProtocolHandler.js',
     'SystemMessageGlue.js',
     'TelProtocolHandler.js',
     'WebappsUpdateTimer.js',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk':
--- a/b2g/components/test/mochitest/mochitest.ini
+++ b/b2g/components/test/mochitest/mochitest.ini
@@ -2,18 +2,20 @@
 skip-if = toolkit != "gonk"
 support-files =
   permission_handler_chrome.js
   SandboxPromptTest.html
   filepicker_path_handler_chrome.js
   screenshot_helper.js
   systemapp_helper.js
   presentation_prompt_handler_chrome.js
+  presentation_ui_glue_handler_chrome.js
 
 [test_filepicker_path.html]
 [test_permission_deny.html]
 [test_permission_gum_remember.html]
 skip-if = true # Bug 1019572 - frequent timeouts
 [test_sandbox_permission.html]
 [test_screenshot.html]
 [test_systemapp.html]
 [test_presentation_device_prompt.html]
 [test_permission_visibilitychange.html]
+[test_presentation_request_ui_glue.html]
new file mode 100644
--- /dev/null
+++ b/b2g/components/test/mochitest/presentation_ui_glue_handler_chrome.js
@@ -0,0 +1,35 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+'use strict';
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+const { XPCOMUtils } = Cu.import('resource://gre/modules/XPCOMUtils.jsm');
+const { SystemAppProxy } = Cu.import('resource://gre/modules/SystemAppProxy.jsm');
+
+const glue = Cc["@mozilla.org/presentation/requestuiglue;1"]
+             .createInstance(Ci.nsIPresentationRequestUIGlue);
+
+SystemAppProxy.addEventListener('mozPresentationChromeEvent', function(aEvent) {
+  if (!aEvent.detail || aEvent.detail.type !== 'presentation-launch-receiver') {
+    return;
+  }
+  sendAsyncMessage('presentation-launch-receiver', aEvent.detail);
+});
+
+addMessageListener('trigger-ui-glue', function(aData) {
+  var promise = glue.sendRequest(aData.url, aData.sessionId);
+  promise.then(function(aFrame){
+    sendAsyncMessage('iframe-resolved', aFrame);
+  });
+});
+
+addMessageListener('trigger-presentation-content-event', function(aData) {
+  var detail = {
+    type: 'presentation-receiver-launched',
+    sessionId: aData.sessionId,
+    frame: aData.frame
+  };
+  SystemAppProxy._sendCustomEvent('mozPresentationContentEvent', detail);
+});
new file mode 100644
--- /dev/null
+++ b/b2g/components/test/mochitest/test_presentation_request_ui_glue.html
@@ -0,0 +1,78 @@
+<!DOCTYPE HTML>
+<html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Presentation Device Selection</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Test for Presentation UI Glue</a>
+<script type="application/javascript;version=1.8">
+
+'use strict';
+
+SimpleTest.waitForExplicitFinish();
+
+var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('presentation_ui_glue_handler_chrome.js'));
+
+var obs = SpecialPowers.Cc["@mozilla.org/observer-service;1"]
+          .getService(SpecialPowers.Ci.nsIObserverService);
+
+var url = 'http://example.com';
+var sessionId = 'sessionId';
+
+function testLaunchReceiver() {
+  return new Promise(function(aResolve, aReject) {
+    gScript.addMessageListener('presentation-launch-receiver', function launchReceiverHandler(aDetail) {
+      gScript.removeMessageListener('presentation-launch-receiver', launchReceiverHandler);
+      ok(true, "A presentation-launch-receiver mozPresentationChromeEvent should be received.");
+      is(aDetail.url, url, "Url should be the same.");
+      is(aDetail.id, sessionId, "Session ID should be the same.");
+
+      aResolve();
+    });
+
+    gScript.sendAsyncMessage('trigger-ui-glue',
+                             { url: url,
+                               sessionId : sessionId });
+  });
+}
+
+function testReceiverLaunched() {
+  return new Promise(function(aResolve, aReject) {
+    gScript.addMessageListener('iframe-resolved', function iframeResolvedHandler(aFrame) {
+      gScript.removeMessageListener('iframe-resolved', iframeResolvedHandler);
+      ok(true, "The promise should be resolved.");
+
+      aResolve();
+    });
+
+    var iframe = document.createElement('iframe');
+    iframe.setAttribute('remote', 'true');
+    iframe.setAttribute('mozbrowser', 'true');
+    iframe.setAttribute('src', 'http://example.com');
+    document.body.appendChild(iframe);
+
+    gScript.sendAsyncMessage('trigger-presentation-content-event',
+                             { sessionId : sessionId,
+                               frame: iframe });
+  });
+}
+
+function runTests() {
+  testLaunchReceiver()
+  .then(testReceiverLaunched)
+  .then(function() {
+    info('test finished, teardown');
+    gScript.destroy();
+    SimpleTest.finish();
+  });
+}
+
+window.addEventListener('load', runTests);
+</script>
+</body>
+</html>
--- a/b2g/config/aries/sources.xml
+++ b/b2g/config/aries/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="7f387f859d48f9ad0761637c78447dc524747738"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="91068221506ff05692aa187ac314e15443db68fd"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4d9fbc08e87731447c19e96e13d8c7444baafcca"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="735805df70bc64af1e5b709133afb76499a92ee1"/>
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="7f387f859d48f9ad0761637c78447dc524747738"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="91068221506ff05692aa187ac314e15443db68fd"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4d9fbc08e87731447c19e96e13d8c7444baafcca"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="735805df70bc64af1e5b709133afb76499a92ee1"/>
@@ -133,12 +133,12 @@
   <project name="platform/frameworks/av" path="frameworks/av" revision="8bb69db127112fc66da75f8ca7a1158614b919f6"/>
   <project name="platform/hardware/akm" path="hardware/akm" revision="6d3be412647b0eab0adff8a2768736cf4eb68039"/>
   <project groups="invensense" name="platform/hardware/invensense" path="hardware/invensense" revision="e6d9ab28b4f4e7684f6c07874ee819c9ea0002a2"/>
   <project name="platform/hardware/ril" path="hardware/ril" revision="865ce3b4a2ba0b3a31421ca671f4d6c5595f8690"/>
   <project name="kernel/common" path="kernel" revision="0f36762ab0c1d8ce10c6a5eda948b05d5d6cc379"/>
   <project name="platform/system/core" path="system/core" revision="4b989b1bec28b0838420c4d5bb454c78afa62bea"/>
   <project name="u-boot" path="u-boot" revision="f1502910977ac88f43da7bf9277c3523ad4b0b2f"/>
   <project name="vendor/sprd/gps" path="vendor/sprd/gps" revision="7d6e1269be7186b2073fa568958b357826692c4b"/>
-  <project name="vendor/sprd/open-source" path="vendor/sprd/open-source" revision="17ea4b64fb0144e0cffeb52344d10215976945fe"/>
+  <project name="vendor/sprd/open-source" path="vendor/sprd/open-source" revision="295ff253b74353751a99aafd687196a28c84a58e"/>
   <project name="vendor/sprd/partner" path="vendor/sprd/partner" revision="8649c7145972251af11b0639997edfecabfc7c2e"/>
   <project name="vendor/sprd/proprietories" path="vendor/sprd/proprietories" revision="d2466593022f7078aaaf69026adf3367c2adb7bb"/>
 </manifest>
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="1b0db93fb6b870b03467aff50d6419771ba0d88c">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="7f387f859d48f9ad0761637c78447dc524747738"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="91068221506ff05692aa187ac314e15443db68fd"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4d9fbc08e87731447c19e96e13d8c7444baafcca"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="2d58f4b9206b50b8fda0d5036da6f0c62608db7c"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="d70e4bfdcb65e7514de0f9315b74aea1c811678d"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="660169a3d7e034a892359e39135e8c2785a6ad6f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="7f387f859d48f9ad0761637c78447dc524747738"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="91068221506ff05692aa187ac314e15443db68fd"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4d9fbc08e87731447c19e96e13d8c7444baafcca"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="735805df70bc64af1e5b709133afb76499a92ee1"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="7f387f859d48f9ad0761637c78447dc524747738"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="91068221506ff05692aa187ac314e15443db68fd"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4d9fbc08e87731447c19e96e13d8c7444baafcca"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="735805df70bc64af1e5b709133afb76499a92ee1"/>
--- a/b2g/config/emulator-l/sources.xml
+++ b/b2g/config/emulator-l/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="05a36844c1046a1eb07d5b1325f85ed741f961ea">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="7f387f859d48f9ad0761637c78447dc524747738"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="91068221506ff05692aa187ac314e15443db68fd"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4d9fbc08e87731447c19e96e13d8c7444baafcca"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="735805df70bc64af1e5b709133afb76499a92ee1"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="1b0db93fb6b870b03467aff50d6419771ba0d88c">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="7f387f859d48f9ad0761637c78447dc524747738"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="91068221506ff05692aa187ac314e15443db68fd"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4d9fbc08e87731447c19e96e13d8c7444baafcca"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="2d58f4b9206b50b8fda0d5036da6f0c62608db7c"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="d70e4bfdcb65e7514de0f9315b74aea1c811678d"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="7f387f859d48f9ad0761637c78447dc524747738"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="91068221506ff05692aa187ac314e15443db68fd"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4d9fbc08e87731447c19e96e13d8c7444baafcca"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="735805df70bc64af1e5b709133afb76499a92ee1"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "7f387f859d48f9ad0761637c78447dc524747738", 
+        "git_revision": "91068221506ff05692aa187ac314e15443db68fd", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "16423131f4a9b03659d92e8ffad7a6f80a8eae37", 
+    "revision": "ab36b7edaadc88ece94cc213db139d1615dadf64", 
     "repo_path": "integration/gaia-central"
 }
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="660169a3d7e034a892359e39135e8c2785a6ad6f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="7f387f859d48f9ad0761637c78447dc524747738"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="91068221506ff05692aa187ac314e15443db68fd"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4d9fbc08e87731447c19e96e13d8c7444baafcca"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="735805df70bc64af1e5b709133afb76499a92ee1"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="05a36844c1046a1eb07d5b1325f85ed741f961ea">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="7f387f859d48f9ad0761637c78447dc524747738"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="91068221506ff05692aa187ac314e15443db68fd"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4d9fbc08e87731447c19e96e13d8c7444baafcca"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="735805df70bc64af1e5b709133afb76499a92ee1"/>
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -955,16 +955,17 @@ bin/libfreebl_32int64_3.so
 @RESPATH@/components/B2GAboutRedirector.js
 @RESPATH@/components/FilePicker.js
 @RESPATH@/components/HelperAppDialog.js
 @RESPATH@/components/DownloadsUI.js
 @RESPATH@/components/InterAppCommUIGlue.js
 @RESPATH@/components/SystemMessageGlue.js
 @RESPATH@/components/B2GAppMigrator.js
 @RESPATH@/components/B2GPresentationDevicePrompt.js
+@RESPATH@/components/PresentationRequestUIGlue.js
 
 #ifndef MOZ_WIDGET_GONK
 @RESPATH@/components/SimulatorScreen.js
 #endif
 
 @RESPATH@/components/FxAccountsUIGlue.js
 @RESPATH@/components/services_fxaccounts.xpt
 
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -266,18 +266,23 @@ pref("browser.urlbar.doubleClickSelectsA
 #else
 pref("browser.urlbar.doubleClickSelectsAll", false);
 #endif
 
 // Control autoFill behavior
 pref("browser.urlbar.autoFill", true);
 pref("browser.urlbar.autoFill.typed", true);
 
+#ifdef NIGHTLY_BUILD
 // Use the new unifiedComplete component
 pref("browser.urlbar.unifiedcomplete", true);
+#else
+// Don't use the new unifiedComplete component
+pref("browser.urlbar.unifiedcomplete", false);
+#endif
 
 // 0: Match anywhere (e.g., middle of words)
 // 1: Match on word boundaries and then try matching anywhere
 // 2: Match only on word boundaries (e.g., after / or .)
 // 3: Match at the beginning of the url or title
 pref("browser.urlbar.matchBehavior", 1);
 pref("browser.urlbar.filter.javascript", true);
 
@@ -300,21 +305,17 @@ pref("browser.urlbar.restrict.searches",
 pref("browser.urlbar.match.title", "#");
 pref("browser.urlbar.match.url", "@");
 
 // The default behavior for the urlbar can be configured to use any combination
 // of the match filters with each additional filter adding more results (union).
 pref("browser.urlbar.suggest.history",              true);
 pref("browser.urlbar.suggest.bookmark",             true);
 pref("browser.urlbar.suggest.openpage",             true);
-#ifdef NIGHTLY_BUILD
 pref("browser.urlbar.suggest.searches",             true);
-#else
-pref("browser.urlbar.suggest.searches",             false);
-#endif
 
 // Limit the number of characters sent to the current search engine to fetch
 // suggestions.
 pref("browser.urlbar.maxCharsForSearchSuggestions", 20);
 
 // Restrictions to current suggestions can also be applied (intersection).
 // Typed suggestion works only if history is set to true.
 pref("browser.urlbar.suggest.history.onlyTyped",    false);
@@ -1924,16 +1925,20 @@ pref("dom.ipc.reportProcessHangs", false
 #else
 pref("dom.ipc.reportProcessHangs", true);
 #endif
 
 pref("browser.reader.detectedFirstArticle", false);
 // Don't limit how many nodes we care about on desktop:
 pref("reader.parse-node-limit", 0);
 
+// On desktop, we want the URLs to be included here for ease of debugging,
+// and because (normally) these errors are not persisted anywhere.
+pref("reader.errors.includeURLs", true);
+
 pref("browser.pocket.enabled", true);
 pref("browser.pocket.api", "api.getpocket.com");
 pref("browser.pocket.site", "getpocket.com");
 pref("browser.pocket.oAuthConsumerKey", "40249-e88c401e1b1f2242d9e441c4");
 pref("browser.pocket.useLocaleList", true);
 pref("browser.pocket.enabledLocales", "cs de en-GB en-US en-ZA es-ES es-MX fr hu it ja ja-JP-mac ko nl pl pt-BR pt-PT ru zh-CN zh-TW");
 
 pref("view_source.tab", true);
--- a/browser/base/content/abouthealthreport/abouthealth.js
+++ b/browser/base/content/abouthealthreport/abouthealth.js
@@ -6,16 +6,23 @@
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 const prefs = new Preferences("datareporting.healthreport.");
 
+const PREF_UNIFIED = "toolkit.telemetry.unified";
+const PREF_UNIFIED_OPTIN = "toolkit.telemetry.unifiedIsOptIn";
+
+// Whether v4 behavior is enabled, i.e. unified Telemetry features are on by default.
+const IS_V4 = Preferences.get(PREF_UNIFIED, false) &&
+              !Preferences.get(PREF_UNIFIED_OPTIN, false);
+
 let healthReportWrapper = {
   init: function () {
     let iframe = document.getElementById("remote-report");
     iframe.addEventListener("load", healthReportWrapper.initRemotePage, false);
     iframe.src = this._getReportURI().spec;
     iframe.onload = () => {
       MozSelfSupport.getHealthReportPayload().then(this.updatePayload,
                                                    this.handleInitFailure);
@@ -23,17 +30,19 @@ let healthReportWrapper = {
     prefs.observe("uploadEnabled", this.updatePrefState, healthReportWrapper);
   },
 
   uninit: function () {
     prefs.ignore("uploadEnabled", this.updatePrefState, healthReportWrapper);
   },
 
   _getReportURI: function () {
-    let url = Services.urlFormatter.formatURLPref("datareporting.healthreport.about.reportUrl");
+    const pref = IS_V4 ? "datareporting.healthreport.about.reportUrl"
+                       : "datareporting.healthreport.about.reportUrlUnified";
+    let url = Services.urlFormatter.formatURLPref(pref);
     return Services.io.newURI(url, null, null);
   },
 
   setDataSubmission: function (enabled) {
     MozSelfSupport.healthReportDataSubmissionEnabled = enabled;
     this.updatePrefState();
   },
 
--- a/browser/base/content/browser-fxaccounts.js
+++ b/browser/base/content/browser-fxaccounts.js
@@ -272,33 +272,40 @@ let gFxAccounts = {
       }
 
       // Reset the button to its original state.
       this.panelUILabel.setAttribute("label", defaultLabel);
       this.panelUIStatus.removeAttribute("tooltiptext");
       this.panelUIFooter.removeAttribute("fxastatus");
       this.panelUIFooter.removeAttribute("fxaprofileimage");
       this.panelUIAvatar.style.removeProperty("list-style-image");
+      let showErrorBadge = false;
 
       if (!this._inCustomizationMode && userData) {
         // At this point we consider the user as logged-in (but still can be in an error state)
         if (this.loginFailed) {
           let tooltipDescription = this.strings.formatStringFromName("reconnectDescription", [userData.email], 1);
           this.panelUIFooter.setAttribute("fxastatus", "error");
           this.panelUILabel.setAttribute("label", errorLabel);
           this.panelUIStatus.setAttribute("tooltiptext", tooltipDescription);
+          showErrorBadge = true;
         } else {
           this.panelUIFooter.setAttribute("fxastatus", "signedin");
           this.panelUILabel.setAttribute("label", userData.email);
           this.panelUIStatus.setAttribute("tooltiptext", signedInTooltiptext);
         }
         if (profileInfoEnabled) {
           this.panelUIFooter.setAttribute("fxaprofileimage", "enabled");
         }
       }
+      if (showErrorBadge) {
+        gMenuButtonBadgeManager.addBadge(gMenuButtonBadgeManager.BADGEID_FXA, "fxa-needs-authentication");
+      } else {
+        gMenuButtonBadgeManager.removeBadge(gMenuButtonBadgeManager.BADGEID_FXA);
+      }
     }
 
     let updateWithProfile = (profile) => {
       if (!this._inCustomizationMode && profileInfoEnabled) {
         if (profile.displayName) {
           this.panelUILabel.setAttribute("label", profile.displayName);
         }
         if (profile.avatar) {
--- a/browser/base/content/browser-syncui.js
+++ b/browser/base/content/browser-syncui.js
@@ -11,17 +11,16 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 let CloudSync = null;
 #endif
 
 // gSyncUI handles updating the tools menu and displaying notifications.
 let gSyncUI = {
   _obs: ["weave:service:sync:start",
          "weave:service:sync:finish",
          "weave:service:sync:error",
-         "weave:service:quota:remaining",
          "weave:service:setup-complete",
          "weave:service:login:start",
          "weave:service:login:finish",
          "weave:service:login:error",
          "weave:service:logout:finish",
          "weave:service:start-over",
          "weave:service:start-over:finish",
          "weave:ui:login:error",
@@ -119,16 +118,19 @@ let gSyncUI = {
     try {
       firstSync = Services.prefs.getCharPref("services.sync.firstSync");
     } catch (e) { }
 
     return Weave.Status.checkSetup() == Weave.CLIENT_NOT_CONFIGURED ||
            firstSync == "notReady";
   },
 
+  // Note that we don't show login errors in a notification bar here, but do
+  // still need to track a login-failed state so the "Tools" menu updates
+  // with the correct state.
   _loginFailed: function () {
     this.log.debug("_loginFailed has sync state=${sync}",
                    { sync: Weave.Status.login});
     return Weave.Status.login == Weave.LOGIN_FAILED_LOGIN_REJECTED;
   },
 
   updateUI: function SUI_updateUI() {
     let needsSetup = this._needsSetup();
@@ -137,18 +139,18 @@ let gSyncUI = {
     // Start off with a clean slate
     document.getElementById("sync-reauth-state").hidden = true;
     document.getElementById("sync-setup-state").hidden = true;
     document.getElementById("sync-syncnow-state").hidden = true;
 
     if (CloudSync && CloudSync.ready && CloudSync().adapters.count) {
       document.getElementById("sync-syncnow-state").hidden = false;
     } else if (loginFailed) {
+      // unhiding this element makes the menubar show the login failure state.
       document.getElementById("sync-reauth-state").hidden = false;
-      this.showLoginError();
     } else if (needsSetup) {
       document.getElementById("sync-setup-state").hidden = false;
     } else {
       document.getElementById("sync-syncnow-state").hidden = false;
     }
 
     if (!gBrowser)
       return;
@@ -162,33 +164,33 @@ let gSyncUI = {
       if (statusButton) {
         statusButton.removeAttribute("tooltiptext");
       }
     }
 
     this._updateLastSyncTime();
   },
 
-
   // Functions called by observers
   onActivityStart() {
     if (!gBrowser)
       return;
 
     this.log.debug("onActivityStart with numActive", this._numActiveSyncTasks);
     if (++this._numActiveSyncTasks == 1) {
       let button = document.getElementById("sync-button");
       if (button) {
         button.setAttribute("status", "active");
       }
       let container = document.getElementById("PanelUI-footer-fxa");
       if (container) {
         container.setAttribute("syncstatus", "active");
       }
     }
+    this.updateUI();
   },
 
   onActivityStop() {
     if (!gBrowser)
       return;
     this.log.debug("onActivityStop with numActive", this._numActiveSyncTasks);
     if (--this._numActiveSyncTasks) {
       if (this._numActiveSyncTasks < 0) {
@@ -204,115 +206,41 @@ let gSyncUI = {
     let syncButton = document.getElementById("sync-button");
     if (syncButton) {
       syncButton.removeAttribute("status");
     }
     let fxaContainer = document.getElementById("PanelUI-footer-fxa");
     if (fxaContainer) {
       fxaContainer.removeAttribute("syncstatus");
     }
-  },
-
-  onLoginFinish: function SUI_onLoginFinish() {
-    // Clear out any login failure notifications
-    let title = this._stringBundle.GetStringFromName("error.login.title");
-    this.clearError(title);
-  },
-
-  onSetupComplete: function SUI_onSetupComplete() {
-    this.onLoginFinish();
+    this.updateUI();
   },
 
   onLoginError: function SUI_onLoginError() {
     this.log.debug("onLoginError: login=${login}, sync=${sync}", Weave.Status);
     Weave.Notifications.removeAll();
 
-    // if we haven't set up the client, don't show errors
-    if (this._needsSetup()) {
-      this.updateUI();
-      return;
-    }
-    // if we are still waiting for the identity manager to initialize, or it's
-    // a network/server error, don't show errors.  If it weren't for the legacy
-    // provider we could just check LOGIN_FAILED_LOGIN_REJECTED, but the legacy
-    // provider has states like LOGIN_FAILED_INVALID_PASSPHRASE which we
-    // probably do want to surface.
-    if (Weave.Status.login == Weave.LOGIN_FAILED_NOT_READY ||
-        Weave.Status.login == Weave.LOGIN_FAILED_NETWORK_ERROR ||
-        Weave.Status.login == Weave.LOGIN_FAILED_SERVER_ERROR) {
-      this.updateUI();
-      return;
-    }
-    this.showLoginError();
+    // We don't show any login errors here; browser-fxaccounts shows them in
+    // the hamburger menu.
     this.updateUI();
   },
 
-  showLoginError() {
-    let title = this._stringBundle.GetStringFromName("error.login.title");
-
-    let description;
-    if (Weave.Status.sync == Weave.PROLONGED_SYNC_FAILURE) {
-      this.log.debug("showLoginError has a prolonged login error");
-      // Convert to days
-      let lastSync =
-        Services.prefs.getIntPref("services.sync.errorhandler.networkFailureReportTimeout") / 86400;
-      description =
-        this._stringBundle.formatStringFromName("error.sync.prolonged_failure", [lastSync], 1);
-    } else {
-      let reason = Weave.Utils.getErrorString(Weave.Status.login);
-      description =
-        this._stringBundle.formatStringFromName("error.sync.description", [reason], 1);
-      this.log.debug("showLoginError has a non-prolonged error", reason);
-    }
-
-    let buttons = [];
-    buttons.push(new Weave.NotificationButton(
-      this._stringBundle.GetStringFromName("error.login.prefs.label"),
-      this._stringBundle.GetStringFromName("error.login.prefs.accesskey"),
-      function() { gSyncUI.openPrefs(); return true; }
-    ));
-
-    let notification = new Weave.Notification(title, description, null,
-                                              Weave.Notifications.PRIORITY_WARNING, buttons);
-    Weave.Notifications.replaceTitle(notification);
-  },
-
   onLogout: function SUI_onLogout() {
     this.updateUI();
   },
 
   onStartOver: function SUI_onStartOver() {
     this.clearError();
   },
 
-  onQuotaNotice: function onQuotaNotice(subject, data) {
-    let title = this._stringBundle.GetStringFromName("warning.sync.quota.label");
-    let description = this._stringBundle.GetStringFromName("warning.sync.quota.description");
-    let buttons = [];
-    buttons.push(new Weave.NotificationButton(
-      this._stringBundle.GetStringFromName("error.sync.viewQuotaButton.label"),
-      this._stringBundle.GetStringFromName("error.sync.viewQuotaButton.accesskey"),
-      function() { gSyncUI.openQuotaDialog(); return true; }
-    ));
-
-    let notification = new Weave.Notification(
-      title, description, null, Weave.Notifications.PRIORITY_WARNING, buttons);
-    Weave.Notifications.replaceTitle(notification);
-  },
-
   _getAppName: function () {
     let brand = new StringBundle("chrome://branding/locale/brand.properties");
     return brand.get("brandShortName");
   },
 
-  openServerStatus: function () {
-    let statusURL = Services.prefs.getCharPref("services.sync.statusURL");
-    window.openUILinkIn(statusURL, "tab");
-  },
-
   // Commands
   doSync: function SUI_doSync() {
     let needsSetup = this._needsSetup();
 
     if (!needsSetup) {
       setTimeout(function () Weave.Service.errorHandler.syncAndReportErrors(), 0);
     }
 
@@ -321,19 +249,16 @@ let gSyncUI = {
 
   handleToolbarButton: function SUI_handleStatusbarButton() {
     if (this._needsSetup())
       this.openSetup();
     else
       this.doSync();
   },
 
-  //XXXzpao should be part of syncCommon.js - which we might want to make a module...
-  //        To be fixed in a followup (bug 583366)
-
   /**
    * Invoke the Sync setup wizard.
    *
    * @param wizardType
    *        Indicates type of wizard to launch:
    *          null    -- regular set up wizard
    *          "pair"  -- pair a device first
    *          "reset" -- reset sync
@@ -379,26 +304,16 @@ let gSyncUI = {
     let win = Services.wm.getMostRecentWindow("Sync:AddDevice");
     if (win)
       win.focus();
     else
       window.openDialog("chrome://browser/content/sync/addDevice.xul",
                         "syncAddDevice", "centerscreen,chrome,resizable=no");
   },
 
-  openQuotaDialog: function SUI_openQuotaDialog() {
-    let win = Services.wm.getMostRecentWindow("Sync:ViewQuota");
-    if (win)
-      win.focus();
-    else
-      Services.ww.activeWindow.openDialog(
-        "chrome://browser/content/sync/quota.xul", "",
-        "centerscreen,chrome,dialog,modal");
-  },
-
   openPrefs: function SUI_openPrefs() {
     openPreferences("paneSync");
   },
 
   openSignInAgainPage: function (entryPoint = "syncbutton") {
     gFxAccounts.openSignInAgainPage(entryPoint);
   },
 
@@ -445,91 +360,16 @@ let gSyncUI = {
 
   onSyncFinish: function SUI_onSyncFinish() {
     let title = this._stringBundle.GetStringFromName("error.sync.title");
 
     // Clear out sync failures on a successful sync
     this.clearError(title);
   },
 
-  onSyncError: function SUI_onSyncError() {
-    this.log.debug("onSyncError: login=${login}, sync=${sync}", Weave.Status);
-    let title = this._stringBundle.GetStringFromName("error.sync.title");
-
-    if (Weave.Status.login != Weave.LOGIN_SUCCEEDED) {
-      this.onLoginError();
-      return;
-    }
-
-    let description;
-    if (Weave.Status.sync == Weave.PROLONGED_SYNC_FAILURE) {
-      // Convert to days
-      let lastSync =
-        Services.prefs.getIntPref("services.sync.errorhandler.networkFailureReportTimeout") / 86400;
-      description =
-        this._stringBundle.formatStringFromName("error.sync.prolonged_failure", [lastSync], 1);
-    } else {
-      let error = Weave.Utils.getErrorString(Weave.Status.sync);
-      description =
-        this._stringBundle.formatStringFromName("error.sync.description", [error], 1);
-    }
-    let priority = Weave.Notifications.PRIORITY_WARNING;
-    let buttons = [];
-
-    // Check if the client is outdated in some way (but note: we've never in the
-    // past, and probably never will, bump the relevent version numbers, so
-    // this is effectively dead code!)
-    let outdated = Weave.Status.sync == Weave.VERSION_OUT_OF_DATE;
-    for (let [engine, reason] in Iterator(Weave.Status.engines))
-      outdated = outdated || reason == Weave.VERSION_OUT_OF_DATE;
-
-    if (outdated) {
-      description = this._stringBundle.GetStringFromName(
-        "error.sync.needUpdate.description");
-      buttons.push(new Weave.NotificationButton(
-        this._stringBundle.GetStringFromName("error.sync.needUpdate.label"),
-        this._stringBundle.GetStringFromName("error.sync.needUpdate.accesskey"),
-        function() { window.openUILinkIn("https://services.mozilla.com/update/", "tab"); return true; }
-      ));
-    }
-    else if (Weave.Status.sync == Weave.OVER_QUOTA) {
-      description = this._stringBundle.GetStringFromName(
-        "error.sync.quota.description");
-      buttons.push(new Weave.NotificationButton(
-        this._stringBundle.GetStringFromName(
-          "error.sync.viewQuotaButton.label"),
-        this._stringBundle.GetStringFromName(
-          "error.sync.viewQuotaButton.accesskey"),
-        function() { gSyncUI.openQuotaDialog(); return true; } )
-      );
-    }
-    else if (Weave.Status.enforceBackoff) {
-      priority = Weave.Notifications.PRIORITY_INFO;
-      buttons.push(new Weave.NotificationButton(
-        this._stringBundle.GetStringFromName("error.sync.serverStatusButton.label"),
-        this._stringBundle.GetStringFromName("error.sync.serverStatusButton.accesskey"),
-        function() { gSyncUI.openServerStatus(); return true; }
-      ));
-    }
-    else {
-      priority = Weave.Notifications.PRIORITY_INFO;
-      buttons.push(new Weave.NotificationButton(
-        this._stringBundle.GetStringFromName("error.sync.tryAgainButton.label"),
-        this._stringBundle.GetStringFromName("error.sync.tryAgainButton.accesskey"),
-        function() { gSyncUI.doSync(); return true; }
-      ));
-    }
-
-    let notification =
-      new Weave.Notification(title, description, null, priority, buttons);
-    Weave.Notifications.replaceTitle(notification);
-
-    this.updateUI();
-  },
-
   observe: function SUI_observe(subject, topic, data) {
     this.log.debug("observed", topic);
     if (this._unloaded) {
       Cu.reportError("SyncUI observer called after unload: " + topic);
       return;
     }
 
     // Unwrap, just like Svc.Obs, but without pulling in that dependency.
@@ -554,26 +394,18 @@ let gSyncUI = {
     }
     // Now non-activity state (eg, enabled, errors, etc)
     // Note that sync uses the ":ui:" notifications for errors because sync.
     switch (topic) {
       case "weave:ui:sync:finish":
         this.onSyncFinish();
         break;
       case "weave:ui:sync:error":
-        this.onSyncError();
-        break;
-      case "weave:service:quota:remaining":
-        this.onQuotaNotice();
-        break;
       case "weave:service:setup-complete":
-        this.onSetupComplete();
-        break;
-      case "weave:service:login:finish":
-        this.onLoginFinish();
+        this.updateUI();
         break;
       case "weave:ui:login:error":
         this.onLoginError();
         break;
       case "weave:service:logout:finish":
         this.onLogout();
         break;
       case "weave:service:start-over":
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1386,16 +1386,18 @@ var gBrowserInit = {
 
     LoopUI.init();
 
     gBrowserThumbnails.init();
 
     // Add Devtools menuitems and listeners
     gDevToolsBrowser.registerBrowserWindow(window);
 
+    gMenuButtonBadgeManager.init();
+
     gMenuButtonUpdateBadge.init();
 
     window.addEventListener("mousemove", MousePosTracker, false);
     window.addEventListener("dragover", MousePosTracker, false);
 
     gNavToolbox.addEventListener("customizationstarting", CustomizationHandler);
     gNavToolbox.addEventListener("customizationchange", CustomizationHandler);
     gNavToolbox.addEventListener("customizationending", CustomizationHandler);
@@ -1541,16 +1543,18 @@ var gBrowserInit = {
     BrowserOnClick.uninit();
 
     DevEdition.uninit();
 
     TrackingProtection.uninit();
 
     gMenuButtonUpdateBadge.uninit();
 
+    gMenuButtonBadgeManager.uninit();
+
     SidebarUI.uninit();
 
     // Now either cancel delayedStartup, or clean up the services initialized from
     // it.
     if (this._boundDelayedStartup) {
       this._cancelDelayedStartup();
     } else {
       if (Win7Features)
@@ -2564,45 +2568,104 @@ function SetPageProxyState(aState)
 }
 
 function PageProxyClickHandler(aEvent)
 {
   if (aEvent.button == 1 && gPrefService.getBoolPref("middlemouse.paste"))
     middleMousePaste(aEvent);
 }
 
+let gMenuButtonBadgeManager = {
+  BADGEID_APPUPDATE: "update",
+  BADGEID_FXA: "fxa",
+
+  fxaBadge: null,
+  appUpdateBadge: null,
+
+  init: function () {
+    PanelUI.panel.addEventListener("popupshowing", this, true);
+  },
+
+  uninit: function () {
+    PanelUI.panel.removeEventListener("popupshowing", this, true);
+  },
+
+  handleEvent: function (e) {
+    if (e.type === "popupshowing") {
+      this.clearBadges();
+    }
+  },
+
+  _showBadge: function () {
+    let badgeToShow = this.appUpdateBadge || this.fxaBadge;
+
+    if (badgeToShow) {
+      PanelUI.menuButton.setAttribute("badge-status", badgeToShow);
+    } else {
+      PanelUI.menuButton.removeAttribute("badge-status");
+    }
+  },
+
+  _changeBadge: function (badgeId, badgeStatus = null) {
+    if (badgeId == this.BADGEID_APPUPDATE) {
+      this.appUpdateBadge = badgeStatus;
+    } else if (badgeId == this.BADGEID_FXA) {
+      this.fxaBadge = badgeStatus;
+    } else {
+      Cu.reportError("This badge ID is unknown!");
+    }
+    this._showBadge();
+  },
+
+  addBadge: function (badgeId, badgeStatus) {
+    if (!badgeStatus) {
+      Cu.reportError("badgeStatus must be defined");
+      return;
+    }
+    this._changeBadge(badgeId, badgeStatus);
+  },
+
+  removeBadge: function (badgeId) {
+    this._changeBadge(badgeId);
+  },
+
+  clearBadges: function () {
+    this.appUpdateBadge = null;
+    this.fxaBadge = null;
+    this._showBadge();
+  }
+};
+
 // Setup the hamburger button badges for updates, if enabled.
 let gMenuButtonUpdateBadge = {
   enabled: false,
   badgeWaitTime: 0,
   timer: null,
 
   init: function () {
     try {
       this.enabled = Services.prefs.getBoolPref("app.update.badge");
     } catch (e) {}
     if (this.enabled) {
       try {
         this.badgeWaitTime = Services.prefs.getIntPref("app.update.badgeWaitTime");
       } catch (e) {
         this.badgeWaitTime = 345600; // 4 days
       }
-      PanelUI.menuButton.classList.add("badged-button");
       Services.obs.addObserver(this, "update-staged", false);
       Services.obs.addObserver(this, "update-downloaded", false);
     }
   },
 
   uninit: function () {
     if (this.timer)
       this.timer.cancel();
     if (this.enabled) {
       Services.obs.removeObserver(this, "update-staged");
       Services.obs.removeObserver(this, "update-downloaded");
-      PanelUI.panel.removeEventListener("popupshowing", this, true);
       this.enabled = false;
     }
   },
 
   onMenuPanelCommand: function(event) {
     if (event.originalTarget.getAttribute("update-status") === "succeeded") {
       // restart the app
       let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"]
@@ -2640,20 +2703,18 @@ let gMenuButtonUpdateBadge = {
     // to non-staged updates, add a badge to the hamburger menu to indicate an
     // update will be applied once the browser restarts.
     this.displayBadge(true);
     this.uninit();
   },
 
   displayBadge: function (succeeded) {
     let status = succeeded ? "succeeded" : "failed";
-    PanelUI.menuButton.setAttribute("update-status", status);
-    if (!succeeded) {
-      PanelUI.menuButton.setAttribute("badge", "!");
-    }
+    let badgeStatus = "update-" + status;
+    gMenuButtonBadgeManager.addBadge(gMenuButtonBadgeManager.BADGEID_APPUPDATE, badgeStatus);
 
     let stringId;
     let updateButtonText;
     if (succeeded) {
       let brandBundle = document.getElementById("bundle_brand");
       let brandShortName = brandBundle.getString("brandShortName");
       stringId = "appmenu.restartNeeded.description";
       updateButtonText = gNavigatorBundle.getFormattedString(stringId,
@@ -2662,25 +2723,16 @@ let gMenuButtonUpdateBadge = {
       stringId = "appmenu.updateFailed.description";
       updateButtonText = gNavigatorBundle.getString(stringId);
     }
 
     let updateButton = document.getElementById("PanelUI-update-status");
     updateButton.setAttribute("label", updateButtonText);
     updateButton.setAttribute("update-status", status);
     updateButton.hidden = false;
-
-    PanelUI.panel.addEventListener("popupshowing", this, true);
-  },
-
-  handleEvent: function(e) {
-    if (e.type === "popupshowing") {
-      PanelUI.menuButton.removeAttribute("badge");
-      PanelUI.panel.removeEventListener("popupshowing", this, true);
-    }
   }
 };
 
 // Values for telemtery bins: see TLS_ERROR_REPORT_UI in Histograms.json
 const TLS_ERROR_REPORT_TELEMETRY_AUTO_CHECKED   = 2;
 const TLS_ERROR_REPORT_TELEMETRY_AUTO_UNCHECKED = 3;
 const TLS_ERROR_REPORT_TELEMETRY_MANUAL_SEND    = 4;
 const TLS_ERROR_REPORT_TELEMETRY_AUTO_SEND      = 5;
@@ -7142,19 +7194,21 @@ var gIdentityHandler = {
     }
   },
 
   handleEvent(event) {
     let elem = document.activeElement;
     let position = elem.compareDocumentPosition(this._identityPopup);
 
     if (!(position & (Node.DOCUMENT_POSITION_CONTAINS |
-                      Node.DOCUMENT_POSITION_CONTAINED_BY))) {
+                      Node.DOCUMENT_POSITION_CONTAINED_BY)) &&
+        !this._identityPopup.hasAttribute("noautohide")) {
       // Hide the panel when focusing an element that is
-      // neither an ancestor nor descendant.
+      // neither an ancestor nor descendant unless the panel has
+      // @noautohide (e.g. for a tour).
       this._identityPopup.hidePopup();
     }
   },
 
   onDragStart: function (event) {
     if (gURLBar.getAttribute("pageproxystate") != "valid")
       return;
 
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -142,20 +142,22 @@
     <!-- for search and content formfill/pw manager -->
     <panel type="autocomplete" id="PopupAutoComplete" noautofocus="true" hidden="true"/>
 
     <!-- for search with one-off buttons -->
     <panel type="autocomplete" id="PopupSearchAutoComplete" noautofocus="true" hidden="true"/>
 
     <!-- for url bar autocomplete -->
     <panel type="autocomplete-richlistbox" id="PopupAutoCompleteRichResult" noautofocus="true" hidden="true">
+#ifdef NIGHTLY_BUILD
       <hbox id="urlbar-search-footer" flex="1" align="stretch" pack="end">
         <button id="urlbar-search-settings" label="&changeSearchSettings.button;"
                 oncommand="BrowserUITelemetry.countSearchSettingsEvent('urlbar'); openPreferences('paneSearch')"/>
       </hbox>
+#endif
     </panel>
 
     <!-- for select dropdowns. The menupopup is what shows the list of options,
          and the popuponly menulist makes things like the menuactive attributes
          work correctly on the menupopup. ContentSelectDropdown expects the
          popuponly menulist to be its immediate parent. -->
     <menulist popuponly="true" id="ContentSelectDropdown" hidden="true">
       <menupopup rolluponmousewheel="true"
@@ -955,17 +957,17 @@
                      class="toolbarbutton-1 chromeclass-toolbar-additional overflow-button"
                      skipintoolbarset="true"
                      tooltiptext="&navbarOverflow.label;"/>
 
       <toolbaritem id="PanelUI-button"
                    class="chromeclass-toolbar-additional"
                    removable="false">
         <toolbarbutton id="PanelUI-menu-button"
-                       class="toolbarbutton-1"
+                       class="toolbarbutton-1 badged-button"
                        consumeanchor="PanelUI-button"
                        label="&brandShortName;"
                        tooltiptext="&appmenu.tooltip;"/>
       </toolbaritem>
 
       <hbox id="window-controls" hidden="true" pack="end" skipintoolbarset="true"
             ordinal="1000">
         <toolbarbutton id="minimize-button"
--- a/browser/base/content/newtab/newTab.css
+++ b/browser/base/content/newtab/newTab.css
@@ -784,16 +784,20 @@ input[type=button] {
   margin: 0 0 30px;
 }
 
 #newtab-intro-text > p > a {
   text-decoration: none;
   color: #4A90E2;
 }
 
+#newtab-intro-text > p > a:hover {
+  color: #000000;
+}
+
 #newtab-intro-text .newtab-control {
   background-size: 18px auto;
   height: 18px;
   width: 18px;
   vertical-align: middle;
   opacity: 1;
   position: inherit;
   pointer-events: none;
deleted file mode 100644
--- a/browser/base/content/sync/quota.js
+++ /dev/null
@@ -1,267 +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/. */
-
-const Ci = Components.interfaces;
-const Cc = Components.classes;
-const Cr = Components.results;
-const Cu = Components.utils;
-
-Cu.import("resource://services-sync/main.js");
-Cu.import("resource://gre/modules/DownloadUtils.jsm");
-
-let gSyncQuota = {
-
-  init: function init() {
-    this.bundle = document.getElementById("quotaStrings");
-    let caption = document.getElementById("treeCaption");
-    caption.firstChild.nodeValue = this.bundle.getString("quota.treeCaption.label");
-
-    gUsageTreeView.init();
-    this.tree = document.getElementById("usageTree");
-    this.tree.view = gUsageTreeView;
-
-    this.loadData();
-  },
-
-  loadData: function loadData() {
-    this._usage_req = Weave.Service.getStorageInfo(Weave.INFO_COLLECTION_USAGE,
-                                                   function (error, usage) {
-      delete gSyncQuota._usage_req;
-      // displayUsageData handles null values, so no need to check 'error'.
-      gUsageTreeView.displayUsageData(usage);
-    });
-
-    let usageLabel = document.getElementById("usageLabel");
-    let bundle = this.bundle;
-
-    this._quota_req = Weave.Service.getStorageInfo(Weave.INFO_QUOTA,
-                                                   function (error, quota) {
-      delete gSyncQuota._quota_req;
-
-      if (error) {
-        usageLabel.value = bundle.getString("quota.usageError.label");
-        return;
-      }
-      let used = gSyncQuota.convertKB(quota[0]);
-      if (!quota[1]) {
-        // No quota on the server.
-        usageLabel.value = bundle.getFormattedString(
-          "quota.usageNoQuota.label", used);
-        return;
-      }
-      let percent = Math.round(100 * quota[0] / quota[1]);
-      let total = gSyncQuota.convertKB(quota[1]);
-      usageLabel.value = bundle.getFormattedString(
-        "quota.usagePercentage.label", [percent].concat(used).concat(total));
-    });
-  },
-
-  onCancel: function onCancel() {
-    if (this._usage_req) {
-      this._usage_req.abort();
-    }
-    if (this._quota_req) {
-      this._quota_req.abort();
-    }
-    return true;
-  },
-
-  onAccept: function onAccept() {
-    let engines = gUsageTreeView.getEnginesToDisable();
-    for each (let engine in engines) {
-      Weave.Service.engineManager.get(engine).enabled = false;
-    }
-    if (engines.length) {
-      // The 'Weave' object will disappear once the window closes.
-      let Service = Weave.Service;
-      Weave.Utils.nextTick(function() { Service.sync(); });
-    }
-    return this.onCancel();
-  },
-
-  convertKB: function convertKB(value) {
-    return DownloadUtils.convertByteUnits(value * 1024);
-  }
-
-};
-
-let gUsageTreeView = {
-
-  _ignored: {keys: true,
-             meta: true,
-             clients: true},
-
-  /*
-   * Internal data structures underlaying the tree.
-   */
-  _collections: [],
-  _byname: {},
-
-  init: function init() {
-    let retrievingLabel = gSyncQuota.bundle.getString("quota.retrieving.label");
-    for each (let engine in Weave.Service.engineManager.getEnabled()) {
-      if (this._ignored[engine.name])
-        continue;
-
-      // Some engines use the same pref, which means they can only be turned on
-      // and off together. We need to combine them here as well.
-      let existing = this._byname[engine.prefName];
-      if (existing) {
-        existing.engines.push(engine.name);
-        continue;
-      }
-
-      let obj = {name: engine.prefName,
-                 title: this._collectionTitle(engine),
-                 engines: [engine.name],
-                 enabled: true,
-                 sizeLabel: retrievingLabel};
-      this._collections.push(obj);
-      this._byname[engine.prefName] = obj;
-    }
-  },
-
-  _collectionTitle: function _collectionTitle(engine) {
-    try {
-      return gSyncQuota.bundle.getString(
-        "collection." + engine.prefName + ".label");
-    } catch (ex) {
-      return engine.Name;
-    }
-  },
-
-  /*
-   * Process the quota information as returned by info/collection_usage.
-   */
-  displayUsageData: function displayUsageData(data) {
-    for each (let coll in this._collections) {
-      coll.size = 0;
-      // If we couldn't retrieve any data, just blank out the label.
-      if (!data) {
-        coll.sizeLabel = "";
-        continue;
-      }
-
-      for each (let engineName in coll.engines)
-        coll.size += data[engineName] || 0;
-      let sizeLabel = "";
-      sizeLabel = gSyncQuota.bundle.getFormattedString(
-        "quota.sizeValueUnit.label", gSyncQuota.convertKB(coll.size));
-      coll.sizeLabel = sizeLabel;
-    }
-    let sizeColumn = this.treeBox.columns.getNamedColumn("size");
-    this.treeBox.invalidateColumn(sizeColumn);
-  },
-
-  /*
-   * Handle click events on the tree.
-   */
-  onTreeClick: function onTreeClick(event) {
-    if (event.button == 2)
-      return;
-
-    let cell = this.treeBox.getCellAt(event.clientX, event.clientY);
-    if (cell.col && cell.col.id == "enabled")
-      this.toggle(cell.row);
-  },
-
-  /*
-   * Toggle enabled state of an engine.
-   */
-  toggle: function toggle(row) {
-    // Update the tree
-    let collection = this._collections[row];
-    collection.enabled = !collection.enabled;
-    this.treeBox.invalidateRow(row);
-
-    // Display which ones will be removed 
-    let freeup = 0;
-    let toremove = [];
-    for each (collection in this._collections) {
-      if (collection.enabled)
-        continue;
-      toremove.push(collection.name);
-      freeup += collection.size;
-    }
-
-    let caption = document.getElementById("treeCaption");
-    if (!toremove.length) {
-      caption.className = "";
-      caption.firstChild.nodeValue = gSyncQuota.bundle.getString(
-        "quota.treeCaption.label");
-      return;
-    }
-
-    toremove = [this._byname[coll].title for each (coll in toremove)];
-    toremove = toremove.join(gSyncQuota.bundle.getString("quota.list.separator"));
-    caption.firstChild.nodeValue = gSyncQuota.bundle.getFormattedString(
-      "quota.removal.label", [toremove]);
-    if (freeup)
-      caption.firstChild.nodeValue += gSyncQuota.bundle.getFormattedString(
-        "quota.freeup.label", gSyncQuota.convertKB(freeup));
-    caption.className = "captionWarning";
-  },
-
-  /*
-   * Return a list of engines (or rather their pref names) that should be
-   * disabled.
-   */
-  getEnginesToDisable: function getEnginesToDisable() {
-    return [coll.name for each (coll in this._collections) if (!coll.enabled)];
-  },
-
-  // nsITreeView
-
-  get rowCount() {
-    return this._collections.length;
-  },
-
-  getRowProperties: function(index) { return ""; },
-  getCellProperties: function(row, col) { return ""; },
-  getColumnProperties: function(col) { return ""; },
-  isContainer: function(index) { return false; },
-  isContainerOpen: function(index) { return false; },
-  isContainerEmpty: function(index) { return false; },
-  isSeparator: function(index) { return false; },
-  isSorted: function() { return false; },
-  canDrop: function(index, orientation, dataTransfer) { return false; },
-  drop: function(row, orientation, dataTransfer) {},
-  getParentIndex: function(rowIndex) {},
-  hasNextSibling: function(rowIndex, afterIndex) { return false; },
-  getLevel: function(index) { return 0; },
-  getImageSrc: function(row, col) {},
-
-  getCellValue: function(row, col) {
-    return this._collections[row].enabled;
-  },
-
-  getCellText: function getCellText(row, col) {
-    let collection = this._collections[row];
-    switch (col.id) {
-      case "collection":
-        return collection.title;
-      case "size":
-        return collection.sizeLabel;
-      default:
-        return "";
-    }
-  },
-
-  setTree: function setTree(tree) {
-    this.treeBox = tree;
-  },
-
-  toggleOpenState: function(index) {},
-  cycleHeader: function(col) {},
-  selectionChanged: function() {},
-  cycleCell: function(row, col) {},
-  isEditable: function(row, col) { return false; },
-  isSelectable: function (row, col) { return false; },
-  setCellValue: function(row, col, value) {},
-  setCellText: function(row, col, value) {},
-  performAction: function(action) {},
-  performActionOnRow: function(action, row) {},
-  performActionOnCell: function(action, row, col) {}
-
-};
deleted file mode 100644
--- a/browser/base/content/sync/quota.xul
+++ /dev/null
@@ -1,65 +0,0 @@
-<?xml version="1.0"?>
-
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
-<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
-<?xml-stylesheet href="chrome://browser/skin/syncQuota.css"?>
-
-<!DOCTYPE dialog [
-<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
-<!ENTITY % syncBrandDTD SYSTEM "chrome://browser/locale/syncBrand.dtd">
-<!ENTITY % syncQuotaDTD SYSTEM "chrome://browser/locale/syncQuota.dtd">
-%brandDTD;
-%syncBrandDTD;
-%syncQuotaDTD;
-]>
-<dialog id="quotaDialog"
-        windowtype="Sync:ViewQuota"
-        persist="screenX screenY width height"
-        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-        xmlns:html="http://www.w3.org/1999/xhtml"
-        onload="gSyncQuota.init()"
-        buttons="accept,cancel"
-        title="&quota.dialogTitle.label;"
-        ondialogcancel="return gSyncQuota.onCancel();"
-        ondialogaccept="return gSyncQuota.onAccept();">
-
-  <script type="application/javascript"
-          src="chrome://browser/content/sync/quota.js"/>
-
-  <stringbundleset id="stringbundleset">
-    <stringbundle id="quotaStrings"
-                  src="chrome://browser/locale/syncQuota.properties"/>
-  </stringbundleset>
-
-  <vbox flex="1">
-    <label id="usageLabel"
-           value="&quota.retrievingInfo.label;"/>
-    <separator/>
-    <tree id="usageTree"
-          seltype="single"
-          hidecolumnpicker="true"
-          onclick="gUsageTreeView.onTreeClick(event);"
-          flex="1">
-      <treecols>
-        <treecol id="enabled"
-                 type="checkbox"
-                 fixed="true"/>
-        <splitter class="tree-splitter"/>
-        <treecol id="collection"
-                 label="&quota.typeColumn.label;"
-                 flex="1"/>
-        <splitter class="tree-splitter"/>
-        <treecol id="size"
-                 label="&quota.sizeColumn.label;"
-                 flex="1"/>
-      </treecols>
-      <treechildren flex="1"/>
-    </tree>
-    <separator/>
-    <description id="treeCaption"> </description>
-  </vbox>
-
-</dialog>
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -277,17 +277,17 @@ skip-if = e10s # Bug 1094510 - test hits
 [browser_clipboard.js]
 [browser_contentAreaClick.js]
 [browser_contextSearchTabPosition.js]
 skip-if = os == "mac" || e10s # bug 967013; e10s: bug 1094761 - test hits the network in e10s, causing next test to crash
 [browser_ctrlTab.js]
 [browser_datareporting_notification.js]
 skip-if = !datareporting
 [browser_datachoices_notification.js]
-skip-if = !datareporting
+skip-if = !datareporting || e10s # bug 1113930
 [browser_devedition.js]
 [browser_devices_get_user_media.js]
 skip-if = buildapp == 'mulet' || (os == "linux" && debug) || e10s # linux: bug 976544; e10s: bug 1071623
 [browser_devices_get_user_media_about_urls.js]
 skip-if = e10s # Bug 1071623
 [browser_devices_get_user_media_in_frame.js]
 skip-if = e10s # Bug 1071623
 [browser_discovery.js]
@@ -504,8 +504,9 @@ support-files =
   readerModeArticle.html
 [browser_readerMode_hidden_nodes.js]
 support-files =
   readerModeArticleHiddenNodes.html
 [browser_bug1124271_readerModePinnedTab.js]
 support-files =
   readerModeArticle.html
 [browser_domFullscreen_fullscreenMode.js]
+[browser_menuButtonBadgeManager.js]
--- a/browser/base/content/test/general/browser_aboutHealthReport.js
+++ b/browser/base/content/test/general/browser_aboutHealthReport.js
@@ -8,26 +8,30 @@ XPCOMUtils.defineLazyModuleGetter(this, 
   "resource://gre/modules/Task.jsm");
 
 const CHROME_BASE = "chrome://mochitests/content/browser/browser/base/content/test/general/";
 const HTTPS_BASE = "https://example.com/browser/browser/base/content/test/general/";
 
 const TELEMETRY_LOG_PREF = "toolkit.telemetry.log.level";
 const telemetryOriginalLogPref = Preferences.get(TELEMETRY_LOG_PREF, null);
 
+const originalReportUrl = Services.prefs.getCharPref("datareporting.healthreport.about.reportUrl");
+const originalReportUrlUnified = Services.prefs.getCharPref("datareporting.healthreport.about.reportUrlUnified");
+
 registerCleanupFunction(function() {
   // Ensure we don't pollute prefs for next tests.
   if (telemetryOriginalLogPref) {
     Preferences.set(TELEMETRY_LOG_PREF, telemetryOriginalLogPref);
   } else {
     Preferences.reset(TELEMETRY_LOG_PREF);
   }
 
   try {
-    Services.prefs.clearUserPref("datareporting.healthreport.about.reportUrl");
+    Services.prefs.setCharPref("datareporting.healthreport.about.reportUrl", originalReportUrl);
+    Services.prefs.setCharPref("datareporting.healthreport.about.reportUrlUnified", originalReportUrlUnified);
     let policy = Cc["@mozilla.org/datareporting/service;1"]
                  .getService(Ci.nsISupports)
                  .wrappedJSObject
                  .policy;
     policy.recordHealthReportUploadEnabled(true,
                                            "Resetting after tests.");
   } catch (ex) {}
 });
@@ -65,16 +69,18 @@ let gTests = [
 {
   desc: "Test the remote commands",
   setup: Task.async(function*()
   {
     Preferences.set(TELEMETRY_LOG_PREF, "Trace");
     yield setupPingArchive();
     Preferences.set("datareporting.healthreport.about.reportUrl",
                     HTTPS_BASE + "healthreport_testRemoteCommands.html");
+    Preferences.set("datareporting.healthreport.about.reportUrlUnified",
+                    HTTPS_BASE + "healthreport_testRemoteCommands.html");
   }),
   run: function (iframe)
   {
     let deferred = Promise.defer();
 
     let policy = Cc["@mozilla.org/datareporting/service;1"]
                  .getService(Ci.nsISupports)
                  .wrappedJSObject
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_menuButtonBadgeManager.js
@@ -0,0 +1,37 @@
+/* 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/. */
+
+let menuButton = document.getElementById("PanelUI-menu-button");
+
+add_task(function* testButtonActivities() {
+  is(menuButton.hasAttribute("badge-status"), false, "Should not have a badge status");
+  is(menuButton.hasAttribute("badge"), false, "Should not have the badge attribute set");
+
+  gMenuButtonBadgeManager.addBadge(gMenuButtonBadgeManager.BADGEID_FXA, "fxa-needs-authentication");
+  is(menuButton.getAttribute("badge-status"), "fxa-needs-authentication", "Should have fxa-needs-authentication badge status");
+
+  gMenuButtonBadgeManager.addBadge(gMenuButtonBadgeManager.BADGEID_APPUPDATE, "update-succeeded");
+  is(menuButton.getAttribute("badge-status"), "update-succeeded", "Should have update-succeeded badge status (update > fxa)");
+
+  gMenuButtonBadgeManager.addBadge(gMenuButtonBadgeManager.BADGEID_APPUPDATE, "update-failed");
+  is(menuButton.getAttribute("badge-status"), "update-failed", "Should have update-failed badge status");
+
+  gMenuButtonBadgeManager.addBadge("unknownbadge", "attr");
+  is(menuButton.getAttribute("badge-status"), "update-failed", "Should not have changed badge status");
+
+  gMenuButtonBadgeManager.removeBadge(gMenuButtonBadgeManager.BADGEID_APPUPDATE);
+  is(menuButton.getAttribute("badge-status"), "fxa-needs-authentication", "Should have fxa-needs-authentication badge status");
+
+  gMenuButtonBadgeManager.removeBadge(gMenuButtonBadgeManager.BADGEID_FXA);
+  is(menuButton.hasAttribute("badge-status"), false, "Should not have a badge status");
+
+  yield PanelUI.show();
+  is(menuButton.hasAttribute("badge-status"), false, "Should not have a badge status (Hamburger menu opened)");
+  PanelUI.hide();
+
+  gMenuButtonBadgeManager.addBadge(gMenuButtonBadgeManager.BADGEID_FXA, "fxa-needs-authentication");
+  gMenuButtonBadgeManager.addBadge(gMenuButtonBadgeManager.BADGEID_UPDATE, "update-succeeded");
+  gMenuButtonBadgeManager.clearBadges();
+  is(menuButton.hasAttribute("badge-status"), false, "Should not have a badge status (clearBadges called)");
+});
--- a/browser/base/content/test/general/browser_syncui.js
+++ b/browser/base/content/test/general/browser_syncui.js
@@ -7,130 +7,76 @@ let {Notifications} = Cu.import("resourc
 
 let stringBundle = Cc["@mozilla.org/intl/stringbundle;1"]
                    .getService(Ci.nsIStringBundleService)
                    .createBundle("chrome://weave/locale/services/sync.properties");
 
 // ensure test output sees log messages.
 Log.repository.getLogger("browserwindow.syncui").addAppender(new Log.DumpAppender());
 
+// Sync manages 3 broadcasters so the menus correctly reflect the Sync state.
+// Only one of these 3 should ever be visible - pass the ID of the broadcaster
+// you expect to be visible and it will check it's the only one that is.
+function checkBroadcasterVisible(broadcasterId) {
+  let all = ["sync-reauth-state", "sync-setup-state", "sync-syncnow-state"];
+  Assert.ok(all.indexOf(broadcasterId) >= 0, "valid id");
+  for (let check of all) {
+    let eltHidden = document.getElementById(check).hidden;
+    Assert.equal(eltHidden, check == broadcasterId ? false : true, check);
+  }
+}
+
 function promiseObserver(topic) {
   return new Promise(resolve => {
     let obs = (subject, topic, data) => {
       Services.obs.removeObserver(obs, topic);
       resolve(subject);
     }
     Services.obs.addObserver(obs, topic, false);
   });
 }
 
 add_task(function* prepare() {
   let xps = Components.classes["@mozilla.org/weave/service;1"]
                               .getService(Components.interfaces.nsISupports)
                               .wrappedJSObject;
   yield xps.whenLoaded();
+  checkBroadcasterVisible("sync-setup-state");
   // mock out the "_needsSetup()" function so we don't short-circuit.
   let oldNeedsSetup = window.gSyncUI._needsSetup;
   window.gSyncUI._needsSetup = () => false;
   registerCleanupFunction(() => {
     window.gSyncUI._needsSetup = oldNeedsSetup;
   });
-});
-
-add_task(function* testProlongedSyncError() {
-  let promiseNotificationAdded = promiseObserver("weave:notification:added");
-  Assert.equal(Notifications.notifications.length, 0, "start with no notifications");
-
-  // Pretend we are in the "prolonged error" state.
-  Weave.Status.sync = Weave.PROLONGED_SYNC_FAILURE;
-  Weave.Status.login = Weave.LOGIN_SUCCEEDED;
-  Services.obs.notifyObservers(null, "weave:ui:sync:error", null);
-
-  let subject = yield promiseNotificationAdded;
-  let notification = subject.wrappedJSObject.object; // sync's observer abstraction is abstract!
-  Assert.equal(notification.title, stringBundle.GetStringFromName("error.sync.title"));
-  Assert.equal(Notifications.notifications.length, 1, "exactly 1 notification");
-
-  // Now pretend we just had a successful sync - the error notification should go away.
-  let promiseNotificationRemoved = promiseObserver("weave:notification:removed");
-  Weave.Status.sync = Weave.STATUS_OK;
-  Services.obs.notifyObservers(null, "weave:ui:sync:finish", null);
-  yield promiseNotificationRemoved;
-  Assert.equal(Notifications.notifications.length, 0, "no notifications left");
+  // and a notification to have the state change away from "needs setup"
+  Services.obs.notifyObservers(null, "weave:ui:clear-error", null);
+  checkBroadcasterVisible("sync-syncnow-state");
 });
 
 add_task(function* testSyncLoginError() {
-  let promiseNotificationAdded = promiseObserver("weave:notification:added");
   Assert.equal(Notifications.notifications.length, 0, "start with no notifications");
+  checkBroadcasterVisible("sync-syncnow-state");
 
-  // Pretend we are in the "prolonged error" state.
+  // Pretend we are in a "login failed" error state
   Weave.Status.sync = Weave.LOGIN_FAILED;
   Weave.Status.login = Weave.LOGIN_FAILED_LOGIN_REJECTED;
   Services.obs.notifyObservers(null, "weave:ui:sync:error", null);
 
-  let subject = yield promiseNotificationAdded;
-  let notification = subject.wrappedJSObject.object; // sync's observer abstraction is abstract!
-  Assert.equal(notification.title, stringBundle.GetStringFromName("error.login.title"));
-  Assert.equal(Notifications.notifications.length, 1, "exactly 1 notification");
+  Assert.equal(Notifications.notifications.length, 0, "no notifications shown on login error");
+  // But the menu *should* reflect the login error.
+  checkBroadcasterVisible("sync-reauth-state");
 
   // Now pretend we just had a successful login - the error notification should go away.
   Weave.Status.sync = Weave.STATUS_OK;
   Weave.Status.login = Weave.LOGIN_SUCCEEDED;
-  let promiseNotificationRemoved = promiseObserver("weave:notification:removed");
   Services.obs.notifyObservers(null, "weave:service:login:start", null);
   Services.obs.notifyObservers(null, "weave:service:login:finish", null);
-  yield promiseNotificationRemoved;
   Assert.equal(Notifications.notifications.length, 0, "no notifications left");
-});
-
-add_task(function* testSyncLoginNetworkError() {
-  Assert.equal(Notifications.notifications.length, 0, "start with no notifications");
-
-  // This test relies on the fact that observers are synchronous, and that error
-  // notifications synchronously create the error notification, which itself
-  // fires an observer notification.
-  // ie, we should see the error notification *during* the notifyObservers call.
-
-  // To prove that, we cause a notification that *does* show an error and make
-  // sure we see the error notification during that call. We then cause a
-  // notification that *should not* show an error, and the lack of the
-  // notification during the call implies the error was ignored.
-
-  // IOW, if this first part of the test fails in the future, it means the
-  // above is no longer true and we need a different strategy to check for
-  // ignored errors.
-
-  let sawNotificationAdded = false;
-  let obs = (subject, topic, data) => {
-    sawNotificationAdded = true;
-  }
-  Services.obs.addObserver(obs, "weave:notification:added", false);
-  try {
-    // notify of a display-able error - we should synchronously see our flag set.
-    Weave.Status.sync = Weave.LOGIN_FAILED;
-    Weave.Status.login = Weave.LOGIN_FAILED_LOGIN_REJECTED;
-    Services.obs.notifyObservers(null, "weave:ui:login:error", null);
-    Assert.ok(sawNotificationAdded);
-
-    // reset the flag and test what should *not* show an error.
-    sawNotificationAdded = false;
-    Weave.Status.sync = Weave.LOGIN_FAILED;
-    Weave.Status.login = Weave.LOGIN_FAILED_NETWORK_ERROR;
-    Services.obs.notifyObservers(null, "weave:ui:login:error", null);
-    Assert.ok(!sawNotificationAdded);
-
-    // ditto for LOGIN_FAILED_SERVER_ERROR
-    Weave.Status.sync = Weave.LOGIN_FAILED;
-    Weave.Status.login = Weave.LOGIN_FAILED_SERVER_ERROR;
-    Services.obs.notifyObservers(null, "weave:ui:login:error", null);
-    Assert.ok(!sawNotificationAdded);
-    // we are done.
-  } finally {
-    Services.obs.removeObserver(obs, "weave:notification:added");
-  }
+  // The menus should be back to "all good"
+  checkBroadcasterVisible("sync-syncnow-state");
 });
 
 function checkButtonsStatus(shouldBeActive) {
   let button = document.getElementById("sync-button");
   let fxaContainer = document.getElementById("PanelUI-footer-fxa");
   if (shouldBeActive) {
     Assert.equal(button.getAttribute("status"), "active");
     Assert.equal(fxaContainer.getAttribute("syncstatus"), "active");
--- a/browser/base/content/test/general/browser_urlbar_searchsettings.js
+++ b/browser/base/content/test/general/browser_urlbar_searchsettings.js
@@ -1,24 +1,30 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 add_task(function*() {
+  let button = document.getElementById("urlbar-search-settings");
+  if (!button) {
+    ok("Skipping test");
+    return;
+  }
+
   yield BrowserTestUtils.withNewTab({ gBrowser, url: "about:blank" }, function* () {
     let popupopened = BrowserTestUtils.waitForEvent(gURLBar.popup, "popupshown");
 
     gURLBar.focus();
     EventUtils.synthesizeKey("a", {});
     yield popupopened;
 
     // Since the current tab is blank the preferences pane will load there
     let loaded = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
     let popupclosed = BrowserTestUtils.waitForEvent(gURLBar.popup, "popuphidden");
-    EventUtils.synthesizeMouseAtCenter(document.getElementById("urlbar-search-settings"), {});
+    EventUtils.synthesizeMouseAtCenter(button, {});
     yield loaded;
     yield popupclosed;
 
     is(gBrowser.selectedBrowser.currentURI.spec, "about:preferences#search",
        "Should have loaded the right page");
   });
 });
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -125,18 +125,16 @@ browser.jar:
         content/browser/sync/setup.xul                (content/sync/setup.xul)
         content/browser/sync/addDevice.js             (content/sync/addDevice.js)
         content/browser/sync/addDevice.xul            (content/sync/addDevice.xul)
         content/browser/sync/setup.js                 (content/sync/setup.js)
         content/browser/sync/genericChange.xul        (content/sync/genericChange.xul)
         content/browser/sync/genericChange.js         (content/sync/genericChange.js)
         content/browser/sync/key.xhtml                (content/sync/key.xhtml)
         content/browser/sync/notification.xml         (content/sync/notification.xml)
-        content/browser/sync/quota.xul                (content/sync/quota.xul)
-        content/browser/sync/quota.js                 (content/sync/quota.js)
         content/browser/sync/utils.js                 (content/sync/utils.js)
 *       content/browser/sync/customize.xul            (content/sync/customize.xul)
         content/browser/sync/customize.js             (content/sync/customize.js)
         content/browser/sync/customize.css            (content/sync/customize.css)
 #endif
         content/browser/safeMode.css                  (content/safeMode.css)
         content/browser/safeMode.js                   (content/safeMode.js)
         content/browser/safeMode.xul                  (content/safeMode.xul)
--- a/browser/base/moz.build
+++ b/browser/base/moz.build
@@ -22,16 +22,18 @@ BROWSER_CHROME_MANIFESTS += [
     'content/test/popupNotifications/browser.ini',
     'content/test/referrer/browser.ini',
     'content/test/social/browser.ini',
 ]
 
 DEFINES['MOZ_APP_VERSION'] = CONFIG['MOZ_APP_VERSION']
 DEFINES['MOZ_APP_VERSION_DISPLAY'] = CONFIG['MOZ_APP_VERSION_DISPLAY']
 
+DEFINES['NIGHTLY_BUILD'] = CONFIG['NIGHTLY_BUILD']
+
 DEFINES['APP_LICENSE_BLOCK'] = '%s/content/overrides/app-license.html' % SRCDIR
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'gtk2', 'gtk3', 'cocoa'):
     DEFINES['HAVE_SHELL_SERVICE'] = 1
     DEFINES['CONTEXT_COPY_IMAGE_CONTENTS'] = 1
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'cocoa'):
     DEFINES['CAN_DRAW_IN_TITLEBAR'] = 1
--- a/browser/components/loop/content/css/panel.css
+++ b/browser/components/loop/content/css/panel.css
@@ -66,68 +66,130 @@ body {
 
 .tab-view-container {
   background-image: url("../shared/img/beta-ribbon.svg#beta-ribbon");
   background-size: 36px 36px;
   background-repeat: no-repeat;
 }
 
 .tab-view {
-  display: flex;
-  flex-direction: row;
-  padding: 10px 0;
-  border-bottom: 1px solid #ccc;
-  color: #000;
-  border-top-right-radius: 2px;
-  border-top-left-radius: 2px;
+  position: relative;
+  width: 100%;
+  height: 4rem;
+  line-height: 3.7rem;
+  color: #4A4A4A;
   list-style: none;
+  border-bottom: 2px solid #ccc;
 }
 
 .tab-view > li {
-  flex: 1;
+  display: inline-block;
+  vertical-align: middle;
+  line-height: 1.2rem;
   text-align: center;
-  color: #ccc;
-  -moz-border-end: 1px solid #ccc;
-  padding: 0 10px;
-  height: 16px;
+  padding: 0;
   cursor: pointer;
-  background-repeat: no-repeat;
-  background-size: 16px 16px;
-  background-position: center;
+}
+
+.tab-view > .slide-bar {
+  position: absolute;
+  bottom: -2px;
+  left: 0;
+  height: .2em;
+  width: 50%;
+  background: #00A9DC;
+  border: none;
+  transition: margin .3s ease-in-out;
+}
+
+.tab-view li:nth-child(1).selected ~ .slide-bar {
+  margin-left: 0;
 }
 
-.tab-view > li:last-child {
-  -moz-border-end-style: none;
+.tab-view li:nth-child(2).selected ~ .slide-bar {
+  margin-left: 50%;
+}
+
+html[dir="rtl"] .tab-view li:nth-child(1).selected ~ .slide-bar {
+  margin-left: 50%;
+}
+
+html[dir="rtl"] .tab-view li:nth-child(2).selected ~ .slide-bar {
+  margin-left: 0;
 }
 
-.tab-view > li[data-tab-name="call"],
-.tab-view > li[data-tab-name="rooms"] {
-  background-image: url("../shared/img/icons-16x16.svg#precall");
+.tab-view > li > div {
+  font-size: 1.2rem;
+  pointer-events: none;
+  display: inline;
+}
+
+.tab-view > li:before {
+  content: "";
+  pointer-events: none;
+  display: inline-block;
+  -moz-margin-end: .5rem;
+  vertical-align: middle;
+  height: 1.4rem;
+  width: 1.4rem;
+  transition-property: background-image;
 }
 
-.tab-view > li[data-tab-name="call"]:hover,
-.tab-view > li[data-tab-name="rooms"]:hover {
-  background-image: url("../shared/img/icons-16x16.svg#precall-hover");
+.tab-view > li.selected {
+  transition-delay: .3s;
+}
+
+.tab-view > li[data-tab-name="rooms"]:before {
+  background-image: url("../shared/img/icons-14x14.svg#hello");
 }
 
-.tab-view > li[data-tab-name="call"].selected,
-.tab-view > li[data-tab-name="rooms"].selected {
-  background-image: url("../shared/img/icons-16x16.svg#precall-active");
+.tab-view > li[data-tab-name="rooms"]:hover:before {
+  background-image: url("../shared/img/icons-14x14.svg#hello-hover");
+}
+
+.tab-view > li[data-tab-name="rooms"].selected:before {
+  background-image: url("../shared/img/icons-14x14.svg#hello-active");
+}
+
+.tab-view > li[data-tab-name="contacts"]:before {
+  background-image: url("../shared/img/icons-14x14.svg#contacts");
+}
+
+.tab-view > li[data-tab-name="contacts"]:hover:before {
+  background-image: url("../shared/img/icons-14x14.svg#contacts-hover");
 }
 
-.tab-view > li[data-tab-name="contacts"] {
-  background-image: url("../shared/img/icons-16x16.svg#contacts");
+.tab-view > li[data-tab-name="contacts"].selected:before {
+  background-image: url("../shared/img/icons-14x14.svg#contacts-active");
+}
+
+/* Styling for one tab */
+.tab-view li:first-child:nth-last-child(2) {
+  width: 100%;
+}
+
+.tab-view li:first-child:nth-last-child(2) > span {
+  display: none;
 }
 
-.tab-view > li[data-tab-name="contacts"]:hover {
-  background-image: url("../shared/img/icons-16x16.svg#contacts-hover");
+.tab-view li:first-child:nth-last-child(2) > span {
+  display: none;
+}
+
+.tab-view li:first-child:nth-last-child(2):before {
+  background-image: url("../shared/img/icons-14x14.svg#hello-hover");
 }
 
-.tab-view > li[data-tab-name="contacts"].selected {
-  background-image: url("../shared/img/icons-16x16.svg#contacts-active");
+.tab-view li:first-child:nth-last-child(2) ~ div {
+  display: none;
+}
+
+.tab-view li:first-child:nth-last-child(3),
+.tab-view li:first-child:nth-last-child(3) ~ li {
+  width: 50%;
 }
 
 .tab {
   display: none;
 }
 
 .tab.selected {
   display: block;
--- a/browser/components/loop/content/js/panel.js
+++ b/browser/components/loop/content/js/panel.js
@@ -76,33 +76,38 @@ loop.panel = (function(_, mozL10n) {
           return;
         }
         var tabName = tab.props.name;
         if (this.props.buttonsHidden.indexOf(tabName) > -1) {
           return;
         }
         var isSelected = (this.state.selectedTab == tabName);
         if (!tab.props.hidden) {
+          var label = mozL10n.get(tabName + "_tab_button");
           tabButtons.push(
             React.createElement("li", {className: cx({selected: isSelected}), 
                 "data-tab-name": tabName, 
                 key: i, 
-                onClick: this.handleSelectTab, 
-                title: mozL10n.get(tabName + "_tab_button_tooltip")})
+                onClick: this.handleSelectTab}, 
+              React.createElement("div", null, label)
+            )
           );
         }
         tabs.push(
           React.createElement("div", {className: cx({tab: true, selected: isSelected}), key: i}, 
             tab.props.children
           )
         );
       }, this);
       return (
         React.createElement("div", {className: "tab-view-container"}, 
-          React.createElement("ul", {className: "tab-view"}, tabButtons), 
+          React.createElement("ul", {className: "tab-view"}, 
+            tabButtons, 
+            React.createElement("div", {className: "slide-bar"})
+          ), 
           tabs
         )
       );
     }
   });
 
   var Tab = React.createClass({displayName: "Tab",
     render: function() {
--- a/browser/components/loop/content/js/panel.jsx
+++ b/browser/components/loop/content/js/panel.jsx
@@ -76,33 +76,38 @@ loop.panel = (function(_, mozL10n) {
           return;
         }
         var tabName = tab.props.name;
         if (this.props.buttonsHidden.indexOf(tabName) > -1) {
           return;
         }
         var isSelected = (this.state.selectedTab == tabName);
         if (!tab.props.hidden) {
+          var label = mozL10n.get(tabName + "_tab_button");
           tabButtons.push(
             <li className={cx({selected: isSelected})}
                 data-tab-name={tabName}
                 key={i}
-                onClick={this.handleSelectTab}
-                title={mozL10n.get(tabName + "_tab_button_tooltip")} />
+                onClick={this.handleSelectTab}>
+              <div>{label}</div>
+            </li>
           );
         }
         tabs.push(
           <div className={cx({tab: true, selected: isSelected})} key={i}>
             {tab.props.children}
           </div>
         );
       }, this);
       return (
         <div className="tab-view-container">
-          <ul className="tab-view">{tabButtons}</ul>
+          <ul className="tab-view">
+            {tabButtons}
+            <div className="slide-bar" />
+          </ul>
           {tabs}
         </div>
       );
     }
   });
 
   var Tab = React.createClass({
     render: function() {
--- a/browser/components/loop/content/shared/css/conversation.css
+++ b/browser/components/loop/content/shared/css/conversation.css
@@ -1463,17 +1463,17 @@ html[dir="rtl"] .room-context-btn-close 
   overflow: auto;
 }
 
 .text-chat-entry {
   display: flex;
   flex-direction: row;
   margin-right: .2em;
   margin-bottom: .5em;
-  text-align: end;
+  text-align: start;
   flex-wrap: nowrap;
   justify-content: flex-start;
   align-content: stretch;
   align-items: flex-start;
 }
 
 html[dir="rtl"] .text-chat-entry {
   margin-right: auto;
--- a/browser/components/loop/content/shared/img/icons-14x14.svg
+++ b/browser/components/loop/content/shared/img/icons-14x14.svg
@@ -35,26 +35,34 @@
       <path fill-rule="evenodd" d="M9.741,7.857c0,1.42-1.151,2.572-2.572,2.572 c-0.625,0-1.199-0.223-1.645-0.595l-0.605,0.605c0.395,0.344,0.87,0.599,1.393,0.734v0.97H5.884c-0.56,0-1.034,0.359-1.212,0.858 h4.994c-0.178-0.499-0.652-0.858-1.212-0.858H8.026v-0.97c1.478-0.38,2.572-1.718,2.572-3.316V6.142H9.741V7.857z"/>
     </g>
     <path id="pause-shape" fill-rule="evenodd" d="M4.75,1h-1.5C2.836,1,2.5,1.336,2.5,1.75v10.5 C2.5,12.664,2.836,13,3.25,13h1.5c0.414,0,0.75-0.336,0.75-0.75V1.75C5.5,1.336,5.164,1,4.75,1z M10.75,1h-1.5 C8.836,1,8.5,1.336,8.5,1.75v10.5C8.5,12.664,8.836,13,9.25,13h1.5c0.414,0,0.75-0.336,0.75-0.75V1.75C11.5,1.336,11.164,1,10.75,1 z"/>
     <path id="video-shape" fill-rule="evenodd" d="M12.175,3.347L9.568,5.651V3.905c0-0.657-0.497-1.19-1.111-1.19 H2.111C1.498,2.714,1,3.247,1,3.905v6.191c0,0.658,0.498,1.19,1.111,1.19h6.345c0.614,0,1.111-0.533,1.111-1.19V8.322l2.607,2.305 C12.4,10.867,12.71,10.938,13,10.874V3.099C12.71,3.035,12.4,3.106,12.175,3.347z"/>
     <g id="volume-shape">
       <path fill-rule="evenodd" d="M3.513,4.404H1.896c-0.417,0-0.756,0.338-0.756,0.755v3.679 c0,0.417,0.338,0.755,0.756,0.755H3.51l2.575,2.575c0.261,0.261,0.596,0.4,0.938,0.422V1.409C6.682,1.431,6.346,1.57,6.085,1.831 L3.513,4.404z M8.555,5.995C8.619,6.32,8.653,6.656,8.653,7c0,0.344-0.034,0.679-0.098,1.004l0.218,0.142 C8.852,7.777,8.895,7.393,8.895,7c0-0.394-0.043-0.777-0.123-1.147L8.555,5.995z M12.224,3.6l-0.475,0.31 c0.359,0.962,0.557,2.003,0.557,3.09c0,1.087-0.198,2.128-0.557,3.09l0.475,0.31c0.41-1.054,0.635-2.201,0.635-3.4 C12.859,5.8,12.634,4.654,12.224,3.6z M10.061,5.012C10.25,5.642,10.353,6.308,10.353,7c0,0.691-0.103,1.358-0.293,1.987 l0.351,0.229C10.634,8.517,10.756,7.772,10.756,7c0-0.773-0.121-1.517-0.345-2.216L10.061,5.012z"/>
       <path d="M7.164,12.74l-0.15-0.009c-0.389-0.024-0.754-0.189-1.028-0.463L3.452,9.735H1.896 C1.402,9.735,1,9.333,1,8.838V5.16c0-0.494,0.402-0.896,0.896-0.896h1.558l2.531-2.531C6.26,1.458,6.625,1.293,7.014,1.269 l0.15-0.009V12.74z M1.896,4.545c-0.339,0-0.615,0.276-0.615,0.615v3.679c0,0.339,0.276,0.615,0.615,0.615h1.672l2.616,2.616 c0.19,0.19,0.434,0.316,0.697,0.363V1.568C6.619,1.615,6.375,1.741,6.185,1.931L3.571,4.545H1.896z M12.292,10.612l-0.714-0.467 l0.039-0.105C11.981,9.067,12.165,8.044,12.165,7c0-1.044-0.184-2.067-0.548-3.041l-0.039-0.105l0.714-0.467l0.063,0.162 C12.783,4.649,13,5.81,13,7s-0.217,2.351-0.645,3.451L12.292,10.612z M11.92,10.033l0.234,0.153 c0.374-1.019,0.564-2.09,0.564-3.186s-0.19-2.167-0.564-3.186L11.92,3.966C12.27,4.94,12.447,5.96,12.447,7 C12.447,8.04,12.27,9.059,11.92,10.033z M10.489,9.435L9.895,9.047l0.031-0.101C10.116,8.315,10.212,7.66,10.212,7 c0-0.661-0.096-1.316-0.287-1.947L9.895,4.952l0.594-0.388l0.056,0.176C10.779,5.471,10.897,6.231,10.897,7 c0,0.769-0.118,1.529-0.351,2.259L10.489,9.435z M10.225,8.926l0.106,0.069C10.52,8.348,10.615,7.677,10.615,7 c0-0.677-0.095-1.348-0.284-1.996l-0.106,0.07C10.403,5.699,10.494,6.347,10.494,7C10.494,7.652,10.403,8.3,10.225,8.926z M8.867,8.376L8.398,8.07l0.018-0.093C8.48,7.654,8.512,7.325,8.512,7S8.48,6.345,8.417,6.022L8.398,5.929l0.469-0.306l0.043,0.2 C8.994,6.211,9.036,6.607,9.036,7c0,0.393-0.042,0.789-0.126,1.176L8.867,8.376z"/>
     </g>
+    <path id="contacts-shape" fill-rule="evenodd" transform="translate(-79.000000, -59.000000)" d="M91.5000066,69.9765672 C91.5000066,68.2109401 91.0859436,65.4999994 88.7968783,65.4999994 C88.5546906,65.4999994 87.5312518,66.5859382 86,66.5859382 C84.4687482,66.5859382 83.4453095,65.4999994 83.2031217,65.4999994 C80.9140564,65.4999994 80.4999935,68.2109401 80.4999935,69.9765672 C80.4999935,71.2421938 81.3437445,72.0000072 82.5859334,72.0000072 L89.4140666,72.0000072 C90.6562555,72.0000072 91.5000066,71.2421938 91.5000066,69.9765672 L91.5000066,69.9765672 L91.5000066,69.9765672 Z M89.0000036,62.9999964 C89.0000036,61.3437444 87.656252,59.9999928 86,59.9999928 C84.343748,59.9999928 82.9999964,61.3437444 82.9999964,62.9999964 C82.9999964,64.6562484 84.343748,66 86,66 C87.656252,66 89.0000036,64.6562484 89.0000036,62.9999964 L89.0000036,62.9999964 L89.0000036,62.9999964 Z" />
+    <path id="hello-shape" fill-rule="evenodd" transform="translate(-261.000000, -59.000000)" d="M268.273778,60 C264.809073,60 262,62.4730749 262,65.523237 C262,67.0417726 262.697086,68.4174001 263.822897,69.4155754 C263.627626,70.1061164 263.240356,71.0442922 262.474542,71.959559 C262.605451,72.1919211 264.761073,71.3737446 266.2807,70.7617485 C266.907968,70.946111 267.577782,71.046474 268.274868,71.046474 C271.740664,71.046474 274.549737,68.5733991 274.549737,65.523237 C274.549737,62.4730749 271.739573,60 268.274868,60 L268.273778,60 Z M270.15122,63.3119786 C270.609399,63.3119786 270.980306,63.6850671 270.980306,64.1432459 C270.980306,64.6036066 270.609399,64.9756042 270.15122,64.9756042 C269.693041,64.9756042 269.321044,64.6036066 269.321044,64.1432459 C269.321044,63.6850671 269.693041,63.3119786 270.15122,63.3119786 L270.15122,63.3119786 Z M266.36579,63.3119786 C266.823969,63.3119786 267.195966,63.6850671 267.195966,64.1432459 C267.195966,64.6036066 266.823969,64.9756042 266.36579,64.9756042 C265.907611,64.9756042 265.535613,64.6036066 265.535613,64.1432459 C265.535613,63.6850671 265.907611,63.3119786 266.36579,63.3119786 L266.36579,63.3119786 Z M268.283596,69.3675757 L268.258505,69.3664848 L268.233414,69.3675757 C266.557789,69.3675757 264.685801,68.2777646 264.254894,66.4428674 C265.38616,66.9675913 266.967968,67.1966807 268.258505,67.1966807 C269.549042,67.1966807 271.13085,66.9675913 272.262115,66.4428674 C271.8323,68.2777646 269.959221,69.3675757 268.283596,69.3675757 L268.283596,69.3675757 Z" />
   </defs>
   <use id="audio" xlink:href="#audio-shape"/>
   <use id="audio-active" xlink:href="#audio-shape"/>
   <use id="audio-disabled" xlink:href="#audio-shape"/>
+  <use id="contacts" xlink:href="#contacts-shape"/>
+  <use id="contacts-hover" xlink:href="#contacts-shape"/>
+  <use id="contacts-active" xlink:href="#contacts-shape"/>
   <use id="facemute" xlink:href="#facemute-shape"/>
   <use id="facemute-active" xlink:href="#facemute-shape"/>
   <use id="facemute-disabled" xlink:href="#facemute-shape"/>
   <use id="hangup" xlink:href="#hangup-shape"/>
   <use id="hangup-active" xlink:href="#hangup-shape"/>
   <use id="hangup-disabled" xlink:href="#hangup-shape"/>
+  <use id="hello" xlink:href="#hello-shape"/>
+  <use id="hello-hover" xlink:href="#hello-shape"/>
+  <use id="hello-active" xlink:href="#hello-shape"/>
   <use id="incoming" xlink:href="#incoming-shape"/>
   <use id="incoming-active" xlink:href="#incoming-shape"/>
   <use id="incoming-disabled" xlink:href="#incoming-shape"/>
   <use id="link" xlink:href="#link-shape"/>
   <use id="link-active" xlink:href="#link-shape"/>
   <use id="link-disabled" xlink:href="#link-shape"/>
   <use id="mute" xlink:href="#mute-shape"/>
   <use id="mute-active" xlink:href="#mute-shape"/>
--- a/browser/components/loop/content/shared/img/icons-16x16.svg
+++ b/browser/components/loop/content/shared/img/icons-16x16.svg
@@ -35,17 +35,16 @@
     <path id="block-shape" fill-rule="evenodd" d="M8,0C3.582,0,0,3.582,0,8c0,4.418,3.582,8,8,8 c4.418,0,8-3.582,8-8C16,3.582,12.418,0,8,0z M8,2.442c1.073,0,2.075,0.301,2.926,0.821l-7.673,7.673 C2.718,10.085,2.408,9.079,2.408,8C2.408,4.931,4.911,2.442,8,2.442z M8,13.557c-1.073,0-2.075-0.301-2.926-0.821l7.673-7.673 C13.282,5.915,13.592,6.921,13.592,8C13.592,11.069,11.089,13.557,8,13.557z"/>
     <path id="contacts-shape" fill-rule="evenodd" d="M8,6.526c1.802,0,3.263-1.461,3.263-3.263 C11.263,1.461,9.802,0,8,0C6.198,0,4.737,1.461,4.737,3.263C4.737,5.066,6.198,6.526,8,6.526z M14.067,11.421c0,0,0-0.001,0-0.001 c0-1.676-1.397-3.119-3.419-3.807L8.001,10.26L5.354,7.613C3.331,8.3,1.933,9.744,1.933,11.42v0.001H1.93 c0,1.679,0.328,3.246,0.896,4.579h10.348c0.568-1.333,0.896-2.9,0.896-4.579H14.067z"/>
     <g id="google-shape">
       <path fill-rule="evenodd" d="M8.001,9.278c-0.9,0.03-1.989,0.454-2.144,1.274 c-0.292,1.54,1.284,2.004,2.455,1.932c1.097-0.067,1.737-0.593,1.813-1.26c0.063-0.554-0.184-1.153-0.959-1.644 c-0.142-0.09-0.28-0.185-0.413-0.282C8.504,9.291,8.25,9.27,8.001,9.278z"/>
       <path fill-rule="evenodd" d="M7.381,3.409C6.638,3.64,6.32,4.405,6.627,5.61 C6.908,6.708,7.78,7.322,8.569,7.104c0.77-0.213,0.987-1.021,0.847-1.873C9.201,3.929,8.261,3.136,7.381,3.409z"/>
       <path fill-rule="evenodd" d="M8,0C3.582,0,0,3.582,0,8s3.582,8,8,8c4.418,0,8-3.582,8-8 S12.418,0,8,0z M10.544,4.471c0.17,0.453,0.194,0.954,0.021,1.416c-0.163,0.436-0.495,0.811-0.982,1.096 C9.307,7.146,9.167,7.351,9.151,7.548c-0.045,0.575,0.658,0.993,1.064,1.297c0.889,0.666,1.236,1.758,0.648,2.813 c-0.562,1.007-1.901,1.457-3.322,1.462c-1.766-0.008-2.88-0.817-2.938-1.918C4.527,9.779,5.987,9.101,7.307,8.947 c0.369-0.043,0.7-0.036,1.01-0.014C7.85,8.625,7.675,7.998,7.914,7.58c0.062-0.109,0.023-0.072-0.095-0.054 C6.739,7.689,5.628,6.985,5.367,5.92c-0.132-0.54-0.05-1.105,0.156-1.547C5.97,3.413,6.964,2.88,8.067,2.88 c1.147,0,2.209,0,3.334,0.009L10.612,3.4H9.714C10.093,3.665,10.384,4.046,10.544,4.471z"/>
     </g>
     <path id="history-shape" fill-rule="evenodd" d="M8,16c-4.418,0-8-3.582-8-8c0-4.418,3.582-8,8-8 c4.418,0,8,3.582,8,8C16,12.418,12.418,16,8,16z M8,2.442C4.911,2.442,2.408,4.931,2.408,8c0,3.069,2.504,5.557,5.592,5.557 S13.592,11.069,13.592,8C13.592,4.931,11.089,2.442,8,2.442z M7.649,9.048C7.206,8.899,6.882,8.493,6.882,8V4.645 c0-0.618,0.501-1.119,1.118-1.119c0.618,0,1.119,0.501,1.119,1.119v3.078c1.176,1.22,2.237,3.633,2.237,3.633 S8.844,10.252,7.649,9.048z"/>
-    <path id="precall-shape" fill-rule="evenodd" d="M8.014,0.003c-4.411,0-7.987,3.576-7.987,7.986 c0,1.642,0.496,3.168,1.346,4.437L0,15.997l3.568-1.372c1.271,0.853,2.8,1.352,4.446,1.352c4.411,0,7.986-3.576,7.986-7.987 C16,3.579,12.424,0.003,8.014,0.003z"/>
     <path id="settings-shape" fill-rule="evenodd" d="M14.77,8c0,0.804,0.262,1.548,0.634,1.678L16,9.887 c-0.205,0.874-0.553,1.692-1.011,2.434l-0.567-0.272c-0.355-0.171-1.066,0.17-1.635,0.738c-0.569,0.569-0.909,1.279-0.738,1.635 l0.273,0.568c-0.741,0.46-1.566,0.79-2.438,0.998l-0.205-0.584c-0.13-0.372-0.874-0.634-1.678-0.634s-1.548,0.262-1.678,0.634 l-0.209,0.596c-0.874-0.205-1.692-0.553-2.434-1.011l0.272-0.567c0.171-0.355-0.17-1.066-0.739-1.635 c-0.568-0.568-1.279-0.909-1.635-0.738l-0.568,0.273c-0.46-0.741-0.79-1.566-0.998-2.439l0.584-0.205 C0.969,9.547,1.231,8.804,1.231,8c0-0.804-0.262-1.548-0.634-1.678L0,6.112c0.206-0.874,0.565-1.685,1.025-2.427l0.554,0.266 c0.355,0.171,1.066-0.17,1.635-0.738c0.569-0.568,0.909-1.28,0.739-1.635L3.686,1.025c0.742-0.46,1.553-0.818,2.427-1.024 l0.209,0.596C6.453,0.969,7.197,1.23,8.001,1.23s1.548-0.262,1.678-0.634l0.209-0.596c0.874,0.205,1.692,0.553,2.434,1.011 l-0.272,0.567c-0.171,0.355,0.17,1.066,0.738,1.635c0.569,0.568,1.279,0.909,1.635,0.738l0.568-0.273 c0.46,0.741,0.79,1.566,0.998,2.438l-0.584,0.205C15.032,6.452,14.77,7.196,14.77,8z M8.001,3.661C5.604,3.661,3.661,5.603,3.661,8 c0,2.397,1.943,4.34,4.339,4.34c2.397,0,4.339-1.943,4.339-4.34C12.34,5.603,10.397,3.661,8.001,3.661z"/>
     <g id="share-shape">
       <path fill-rule="evenodd" d="M8.999,10.654L8.69,10.6L8.999,16l2.56-3.754L8.999,10.654z M8.658,10.041l0.341-0.043l6,2.898V0L1,10.998l4.55-0.569L8.999,16l-1.892-5.68l-0.283-0.05l0.256-0.032L7,9.998l6.999-8.003 L8.656,9.998L8.658,10.041z"/>
     </g>
     <path id="tag-shape" fill-rule="evenodd" d="M15.578,7.317L9.659,1.398 C9.374,1.033,8.955,0.777,8.471,0.761L2.556,0C1.72-0.027-0.027,1.72,0,2.556l0.761,5.916c0.016,0.484,0.272,0.902,0.637,1.188 l5.919,5.919c0.591,0.591,1.584,0.557,2.218-0.076l5.966-5.966C16.135,8.902,16.169,7.909,15.578,7.317z M4.222,4.163 c-0.511,0.511-1.339,0.511-1.85,0c-0.511-0.511-0.511-1.339,0-1.85c0.511-0.511,1.339-0.511,1.85,0 C4.733,2.823,4.733,3.652,4.222,4.163z"/>
     <path id="unblock-shape" fill-rule="evenodd" d="M8,16c-4.418,0-8-3.582-8-8c0-4.418,3.582-8,8-8 c4.418,0,8,3.582,8,8C16,12.418,12.418,16,8,16z M8,2.442C4.911,2.442,2.408,4.931,2.408,8c0,3.069,2.504,5.557,5.592,5.557 S13.592,11.069,13.592,8C13.592,4.931,11.089,2.442,8,2.442z"/>
     <path id="video-shape" fill-rule="evenodd" d="M14.9,3.129l-3.476,3.073V3.873c0-0.877-0.663-1.587-1.482-1.587 H1.482C0.663,2.286,0,2.996,0,3.873v8.254c0,0.877,0.663,1.587,1.482,1.587h8.461c0.818,0,1.482-0.711,1.482-1.587V9.762 l3.476,3.073c0.3,0.321,0.714,0.416,1.1,0.331V2.798C15.614,2.713,15.2,2.808,14.9,3.129z"/>
     <g id="copy-shape">
@@ -108,19 +107,16 @@
   <use id="globe" xlink:href="#globe-shape"/>
   <use id="google" xlink:href="#google-shape"/>
   <use id="google-hover" xlink:href="#google-shape"/>
   <use id="google-active" xlink:href="#google-shape"/>
   <use id="history" xlink:href="#history-shape"/>
   <use id="history-hover" xlink:href="#history-shape"/>
   <use id="history-active" xlink:href="#history-shape"/>
   <use id="leave" xlink:href="#leave-shape"/>
-  <use id="precall" xlink:href="#precall-shape"/>
-  <use id="precall-hover" xlink:href="#precall-shape"/>
-  <use id="precall-active" xlink:href="#precall-shape"/>
   <use id="settings" xlink:href="#settings-shape"/>
   <use id="settings-hover" xlink:href="#settings-shape"/>
   <use id="settings-active" xlink:href="#settings-shape"/>
   <use id="share-darkgrey" xlink:href="#share-shape"/>
   <use id="tag" xlink:href="#tag-shape"/>
   <use id="tag-hover" xlink:href="#tag-shape"/>
   <use id="tag-active" xlink:href="#tag-shape"/>
   <use id="trash" xlink:href="#trash-shape"/>
--- a/browser/components/loop/ui/ui-showcase.js
+++ b/browser/components/loop/ui/ui-showcase.js
@@ -503,31 +503,31 @@
       "10x10": ["close", "close-active", "close-disabled", "dropdown",
         "dropdown-white", "dropdown-active", "dropdown-disabled", "edit",
         "edit-active", "edit-disabled", "edit-white", "expand", "expand-active",
         "expand-disabled", "minimize", "minimize-active", "minimize-disabled",
         "settings-cog"
       ],
       "14x14": ["audio", "audio-active", "audio-disabled", "facemute",
         "facemute-active", "facemute-disabled", "hangup", "hangup-active",
-        "hangup-disabled", "incoming", "incoming-active", "incoming-disabled",
+        "hangup-disabled", "hello", "hello-hover", "hello-active",
+        "incoming", "incoming-active", "incoming-disabled",
         "link", "link-active", "link-disabled", "mute", "mute-active",
         "mute-disabled", "pause", "pause-active", "pause-disabled", "video",
         "video-white", "video-active", "video-disabled", "volume", "volume-active",
         "volume-disabled"
       ],
       "16x16": ["add", "add-hover", "add-active", "audio", "audio-hover", "audio-active",
         "block", "block-red", "block-hover", "block-active", "contacts", "contacts-hover",
         "contacts-active", "copy", "checkmark", "delete", "globe", "google", "google-hover",
         "google-active", "history", "history-hover", "history-active", "leave",
-        "precall", "precall-hover", "precall-active", "screen-white", "screenmute-white",
-        "settings", "settings-hover", "settings-active", "share-darkgrey", "tag",
-        "tag-hover", "tag-active", "trash", "unblock", "unblock-hover", "unblock-active",
-        "video", "video-hover", "video-active", "tour", "status-available",
-        "status-unavailable"
+        "screen-white", "screenmute-white", "settings", "settings-hover", "settings-active",
+        "share-darkgrey", "tag", "tag-hover", "tag-active", "trash", "unblock",
+        "unblock-hover", "unblock-active", "video", "video-hover", "video-active", "tour",
+        "status-available", "status-unavailable"
       ]
     },
 
     render: function() {
       var icons = this.shapes[this.props.size].map(function(shapeId, i) {
         return (
           React.createElement("li", {className: "svg-icon-entry", key: this.props.size + "-" + i}, 
             React.createElement("p", null, React.createElement(SVGIcon, {shapeId: shapeId, size: this.props.size})), 
--- a/browser/components/loop/ui/ui-showcase.jsx
+++ b/browser/components/loop/ui/ui-showcase.jsx
@@ -503,31 +503,31 @@
       "10x10": ["close", "close-active", "close-disabled", "dropdown",
         "dropdown-white", "dropdown-active", "dropdown-disabled", "edit",
         "edit-active", "edit-disabled", "edit-white", "expand", "expand-active",
         "expand-disabled", "minimize", "minimize-active", "minimize-disabled",
         "settings-cog"
       ],
       "14x14": ["audio", "audio-active", "audio-disabled", "facemute",
         "facemute-active", "facemute-disabled", "hangup", "hangup-active",
-        "hangup-disabled", "incoming", "incoming-active", "incoming-disabled",
+        "hangup-disabled", "hello", "hello-hover", "hello-active",
+        "incoming", "incoming-active", "incoming-disabled",
         "link", "link-active", "link-disabled", "mute", "mute-active",
         "mute-disabled", "pause", "pause-active", "pause-disabled", "video",
         "video-white", "video-active", "video-disabled", "volume", "volume-active",
         "volume-disabled"
       ],
       "16x16": ["add", "add-hover", "add-active", "audio", "audio-hover", "audio-active",
         "block", "block-red", "block-hover", "block-active", "contacts", "contacts-hover",
         "contacts-active", "copy", "checkmark", "delete", "globe", "google", "google-hover",
         "google-active", "history", "history-hover", "history-active", "leave",
-        "precall", "precall-hover", "precall-active", "screen-white", "screenmute-white",
-        "settings", "settings-hover", "settings-active", "share-darkgrey", "tag",
-        "tag-hover", "tag-active", "trash", "unblock", "unblock-hover", "unblock-active",
-        "video", "video-hover", "video-active", "tour", "status-available",
-        "status-unavailable"
+        "screen-white", "screenmute-white", "settings", "settings-hover", "settings-active",
+        "share-darkgrey", "tag", "tag-hover", "tag-active", "trash", "unblock",
+        "unblock-hover", "unblock-active", "video", "video-hover", "video-active", "tour",
+        "status-available", "status-unavailable"
       ]
     },
 
     render: function() {
       var icons = this.shapes[this.props.size].map(function(shapeId, i) {
         return (
           <li className="svg-icon-entry" key={this.props.size + "-" + i}>
             <p><SVGIcon shapeId={shapeId} size={this.props.size} /></p>
--- a/browser/components/migration/BrowserProfileMigrators.manifest
+++ b/browser/components/migration/BrowserProfileMigrators.manifest
@@ -1,18 +1,28 @@
 component {6F8BB968-C14F-4D6F-9733-6C6737B35DCE} ProfileMigrator.js
 contract @mozilla.org/toolkit/profile-migrator;1 {6F8BB968-C14F-4D6F-9733-6C6737B35DCE}
+
+#if defined(XP_WIN) || defined(XP_MACOSX)
+component {4bf85aa5-4e21-46ca-825f-f9c51a5e8c76} ChromeProfileMigrator.js
+contract @mozilla.org/profile/migrator;1?app=browser&type=canary {4bf85aa5-4e21-46ca-825f-f9c51a5e8c76}
+#endif
 component {4cec1de4-1671-4fc3-a53e-6c539dc77a26} ChromeProfileMigrator.js
 contract @mozilla.org/profile/migrator;1?app=browser&type=chrome {4cec1de4-1671-4fc3-a53e-6c539dc77a26}
+component {8cece922-9720-42de-b7db-7cef88cb07ca} ChromeProfileMigrator.js
+contract @mozilla.org/profile/migrator;1?app=browser&type=chromium {8cece922-9720-42de-b7db-7cef88cb07ca}
+
 component {91185366-ba97-4438-acba-48deaca63386} FirefoxProfileMigrator.js
 contract @mozilla.org/profile/migrator;1?app=browser&type=firefox {91185366-ba97-4438-acba-48deaca63386}
+
 #ifdef HAS_IE_MIGRATOR
 component {3d2532e3-4932-4774-b7ba-968f5899d3a4} IEProfileMigrator.js
 contract @mozilla.org/profile/migrator;1?app=browser&type=ie {3d2532e3-4932-4774-b7ba-968f5899d3a4}
 #endif
+
 #ifdef HAS_SAFARI_MIGRATOR
 component {4b609ecf-60b2-4655-9df4-dc149e474da1} SafariProfileMigrator.js
 contract @mozilla.org/profile/migrator;1?app=browser&type=safari {4b609ecf-60b2-4655-9df4-dc149e474da1}
 #endif
 #ifdef HAS_360SE_MIGRATOR
 component {d0037b95-296a-4a4e-94b2-c3d075d20ab1} 360seProfileMigrator.js
 contract @mozilla.org/profile/migrator;1?app=browser&type=360se {d0037b95-296a-4a4e-94b2-c3d075d20ab1}
 #endif
--- a/browser/components/migration/ChromeProfileMigrator.js
+++ b/browser/components/migration/ChromeProfileMigrator.js
@@ -456,9 +456,56 @@ function GetWindowsPasswordsResource(aPr
     }
   };
 }
 
 ChromeProfileMigrator.prototype.classDescription = "Chrome Profile Migrator";
 ChromeProfileMigrator.prototype.contractID = "@mozilla.org/profile/migrator;1?app=browser&type=chrome";
 ChromeProfileMigrator.prototype.classID = Components.ID("{4cec1de4-1671-4fc3-a53e-6c539dc77a26}");
 
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ChromeProfileMigrator]);
+
+/**
+ *  Chromium migration
+ **/
+function ChromiumProfileMigrator() {
+  let chromiumUserDataFolder = FileUtils.getDir(
+#ifdef XP_WIN
+    "LocalAppData", ["Chromium", "User Data"]
+#elifdef XP_MACOSX
+    "ULibDir", ["Application Support", "Chromium"]
+#else
+    "Home", [".config", "chromium"]
+#endif
+    , false);
+  this._chromeUserDataFolder = chromiumUserDataFolder.exists() ? chromiumUserDataFolder : null;
+}
+
+ChromiumProfileMigrator.prototype = Object.create(ChromeProfileMigrator.prototype);
+ChromiumProfileMigrator.prototype.classDescription = "Chromium Profile Migrator";
+ChromiumProfileMigrator.prototype.contractID = "@mozilla.org/profile/migrator;1?app=browser&type=chromium";
+ChromiumProfileMigrator.prototype.classID = Components.ID("{8cece922-9720-42de-b7db-7cef88cb07ca}");
+
+let componentsArray = [ChromeProfileMigrator, ChromiumProfileMigrator];
+
+#if defined(XP_WIN) || defined(XP_MACOSX)
+/**
+ * Chrome Canary
+ * Not available on Linux
+ **/
+function CanaryProfileMigrator() {
+  let chromeUserDataFolder = FileUtils.getDir(
+#ifdef XP_WIN
+    "LocalAppData", ["Google", "Chrome SxS", "User Data"]
+#elifdef XP_MACOSX
+    "ULibDir", ["Application Support", "Google", "Chrome Canary"]
+#endif
+    , false);
+  this._chromeUserDataFolder = chromeUserDataFolder.exists() ? chromeUserDataFolder : null;
+}
+CanaryProfileMigrator.prototype = Object.create(ChromeProfileMigrator.prototype);
+CanaryProfileMigrator.prototype.classDescription = "Chrome Canary Profile Migrator";
+CanaryProfileMigrator.prototype.contractID = "@mozilla.org/profile/migrator;1?app=browser&type=canary";
+CanaryProfileMigrator.prototype.classID = Components.ID("{4bf85aa5-4e21-46ca-825f-f9c51a5e8c76}");
+
+componentsArray.push(CanaryProfileMigrator);
+#endif
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory(componentsArray);
--- a/browser/components/migration/MigrationUtils.jsm
+++ b/browser/components/migration/MigrationUtils.jsm
@@ -1,20 +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";
 
 this.EXPORTED_SYMBOLS = ["MigrationUtils", "MigratorPrototype"];
 
-const Cu = Components.utils;
-const Ci = Components.interfaces;
-const Cc = Components.classes;
-
+const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
 const TOPIC_WILL_IMPORT_BOOKMARKS = "initial-migration-will-import-default-bookmarks";
 const TOPIC_DID_IMPORT_BOOKMARKS = "initial-migration-did-import-default-bookmarks";
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
@@ -32,27 +29,30 @@ function getMigrationBundle() {
      "chrome://browser/locale/migration/migration.properties");
   }
   return gMigrationBundle;
 }
 
 /**
  * Figure out what is the default browser, and if there is a migrator
  * for it, return that migrator's internal name.
- * For the time being, the "internal name" of a migraotr is its contract-id
+ * For the time being, the "internal name" of a migrator is its contract-id
  * trailer (e.g. ie for @mozilla.org/profile/migrator;1?app=browser&type=ie),
  * but it will soon be exposed properly.
  */
 function getMigratorKeyForDefaultBrowser() {
+  // Canary uses the same description as Chrome so we can't distinguish them.
   const APP_DESC_TO_KEY = {
     "Internet Explorer":                 "ie",
     "Safari":                            "safari",
     "Firefox":                           "firefox",
     "Google Chrome":                     "chrome",  // Windows, Linux
     "Chrome":                            "chrome",  // OS X
+    "Chromium":                          "chromium", // Windows, OS X
+    "Chromium Web Browser":              "chromium", // Linux
     "360\u5b89\u5168\u6d4f\u89c8\u5668": "360se",
   };
 
   let browserDesc = "";
   try {
     let browserDesc =
       Cc["@mozilla.org/uriloader/external-protocol-service;1"].
       getService(Ci.nsIExternalProtocolService).
@@ -394,23 +394,25 @@ this.MigrationUtils = Object.freeze({
    * nsIStringBundle.GetStringFromName, if aReplacements isn't passed, or for
    * nsIStringBundle.formatStringFromName if it is.
    *
    * This method also takes care of "bumped" keys (See bug 737381 comment 8 for
    * details).
    *
    * @param aKey
    *        The key of the string to retrieve.
-   * @param aReplacemts
+   * @param aReplacements
    *        [optioanl] Array of replacements to run on the retrieved string.
    * @return the retrieved string.
    *
    * @see nsIStringBundle
    */
   getLocalizedString: function MU_getLocalizedString(aKey, aReplacements) {
+    aKey = aKey.replace(/_(canary|chromium)$/, "_chrome");
+
     const OVERRIDES = {
       "4_firefox": "4_firefox_history_and_bookmarks",
       "64_firefox": "64_firefox_other"
     };
     aKey = OVERRIDES[aKey] || aKey;
 
     if (aReplacements === undefined)
       return getMigrationBundle().GetStringFromName(aKey);
@@ -445,17 +447,19 @@ this.MigrationUtils = Object.freeze({
 
   /*
    * Returns the migrator for the given source, if any data is available
    * for this source, or null otherwise.
    *
    * @param aKey internal name of the migration source.
    *             Supported values: ie (windows),
    *                               safari (mac/windows),
+   *                               canary (mac/windows),
    *                               chrome (mac/windows/linux),
+   *                               chromium (mac/windows/linux),
    *                               360se (windows),
    *                               firefox.
    *
    * If null is returned,  either no data can be imported
    * for the given migrator, or aMigratorKey is invalid  (e.g. ie on mac,
    * or mosaic everywhere).  This method should be used rather than direct
    * getService for future compatibility (see bug 718280).
    *
@@ -479,21 +483,21 @@ this.MigrationUtils = Object.freeze({
     return migrator && migrator.sourceExists ? migrator : null;
   },
 
   // Iterates the available migrators, in the most suitable
   // order for the running platform.
   get migrators() {
     let migratorKeysOrdered = [
 #ifdef XP_WIN
-      "firefox", "ie", "chrome", "safari", "360se"
+      "firefox", "ie", "chrome", "chromium", "safari", "360se", "canary"
 #elifdef XP_MACOSX
-      "firefox", "safari", "chrome"
+      "firefox", "safari", "chrome", "chromium", "canary"
 #elifdef XP_UNIX
-      "firefox", "chrome"
+      "firefox", "chrome", "chromium"
 #endif
     ];
 
     // If a supported default browser is found check it first
     // so that the wizard defaults to import from that browser.
     let defaultBrowserKey = getMigratorKeyForDefaultBrowser();
     if (defaultBrowserKey)
       migratorKeysOrdered.sort(function (a, b) b == defaultBrowserKey ? 1 : 0);
--- a/browser/components/migration/content/migration.js
+++ b/browser/components/migration/content/migration.js
@@ -281,19 +281,25 @@ var MigrationWizard = {
     var source = null;
     switch (this._source) {
       case "ie":
         source = "sourceNameIE";
         break;
       case "safari":
         source = "sourceNameSafari";
         break;
+      case "canary":
+        source = "sourceNameCanary";
+        break;
       case "chrome":
         source = "sourceNameChrome";
         break;
+      case "chromium":
+        source = "sourceNameChromium";
+        break;
       case "firefox":
         source = "sourceNameFirefox";
         break;
       case "360se":
         source = "sourceName360se";
         break;
     }
 
--- a/browser/components/migration/content/migration.xul
+++ b/browser/components/migration/content/migration.xul
@@ -31,23 +31,28 @@
 #endif
     <description id="importBookmarks" control="importSourceGroup" hidden="true">&importFromBookmarks.label;</description>
 
     <radiogroup id="importSourceGroup" align="start">
       <radio id="firefox"   label="&importFromFirefox.label;"   accesskey="&importFromFirefox.accesskey;"/>
 #ifdef XP_WIN
       <radio id="ie"        label="&importFromIE.label;"        accesskey="&importFromIE.accesskey;"/>
       <radio id="chrome"    label="&importFromChrome.label;"    accesskey="&importFromChrome.accesskey;"/>
+      <radio id="chromium"  label="&importFromChromium.label;"  accesskey="&importFromChromium.accesskey;"/>
       <radio id="safari"    label="&importFromSafari.label;"    accesskey="&importFromSafari.accesskey;"/>
+      <radio id="canary"    label="&importFromCanary.label;"    accesskey="&importFromCanary.accesskey;"/>
       <radio id="360se"     label="&importFrom360se.label;"     accesskey="&importFrom360se.accesskey;"/>
 #elifdef XP_MACOSX
       <radio id="safari"    label="&importFromSafari.label;"    accesskey="&importFromSafari.accesskey;"/>
       <radio id="chrome"    label="&importFromChrome.label;"    accesskey="&importFromChrome.accesskey;"/>
+      <radio id="chromium"  label="&importFromChromium.label;"  accesskey="&importFromChromium.accesskey;"/>
+      <radio id="canary"    label="&importFromCanary.label;"    accesskey="&importFromCanary.accesskey;"/>
 #elifdef XP_UNIX
       <radio id="chrome"    label="&importFromChrome.label;"    accesskey="&importFromChrome.accesskey;"/>
+      <radio id="chromium"  label="&importFromChromium.label;"  accesskey="&importFromChromium.accesskey;"/>
 #endif
       <radio id="nothing"   label="&importFromNothing.label;"   accesskey="&importFromNothing.accesskey;" hidden="true"/>
     </radiogroup>
     <label id="noSources" hidden="true">&noMigrationSources.label;</label>
   </wizardpage>
 
   <wizardpage id="selectProfile" pageid="selectProfile" label="&selectProfile.title;"
               next="importItems"
--- a/browser/components/preferences/in-content/privacy.js
+++ b/browser/components/preferences/in-content/privacy.js
@@ -112,17 +112,18 @@ var gPrivacyPane = {
                      gPrivacyPane.updateAutostart);
     setEventListener("cookieExceptions", "command",
                      gPrivacyPane.showCookieExceptions);
     setEventListener("showCookiesButton", "command",
                      gPrivacyPane.showCookies);
     setEventListener("clearDataSettings", "command",
                      gPrivacyPane.showClearPrivateDataSettings);
 
-    document.getElementById("searchesSuggestion").hidden = !AppConstants.NIGHTLY_BUILD;
+    document.getElementById("searchesSuggestion").hidden = 
+      !Services.prefs.getBoolPref("browser.urlbar.unifiedcomplete");
   },
 
   // HISTORY MODE
 
   /**
    * The list of preferences which affect the initial history mode settings.
    * If the auto start private browsing mode pref is active, the initial
    * history mode would be set to "Don't remember anything".
--- a/browser/components/preferences/in-content/sync.js
+++ b/browser/components/preferences/in-content/sync.js
@@ -188,17 +188,16 @@ let gSyncPane = {
     setEventListener("noAccountSetup", "click", function (aEvent) {
       aEvent.stopPropagation();
       gSyncPane.openSetup(null);
     });
     setEventListener("noAccountPair", "click", function (aEvent) {
       aEvent.stopPropagation();
       gSyncPane.openSetup('pair');
     });
-    setEventListener("syncViewQuota", "command", gSyncPane.openQuotaDialog);
     setEventListener("syncChangePassword", "command",
       () => gSyncUtils.changePassword());
     setEventListener("syncResetPassphrase", "command",
       () => gSyncUtils.resetPassphrase());
     setEventListener("syncReset", "command", gSyncPane.resetSync);
     setEventListener("syncAddDeviceLabel", "click", function () {
       gSyncPane.openAddDevice();
       return false;
@@ -709,25 +708,16 @@ let gSyncPane = {
         return;
       }
     }
     fxAccounts.signOut().then(() => {
       this.updateWeavePrefs();
     });
   },
 
-  openQuotaDialog: function () {
-    let win = Services.wm.getMostRecentWindow("Sync:ViewQuota");
-    if (win)
-      win.focus();
-    else 
-      window.openDialog("chrome://browser/content/sync/quota.xul", "",
-                        "centerscreen,chrome,dialog,modal");
-  },
-
   openAddDevice: function () {
     if (!Weave.Utils.ensureMPUnlocked())
       return;
     
     let win = Services.wm.getMostRecentWindow("Sync:AddDevice");
     if (win)
       win.focus();
     else 
--- a/browser/components/preferences/in-content/sync.xul
+++ b/browser/components/preferences/in-content/sync.xul
@@ -92,18 +92,16 @@
         <label id="accountName"/>
       </caption>
 
       <hbox>
         <button type="menu"
                 label="&manageAccount.label;"
                 accesskey="&manageAccount.accesskey;">
           <menupopup>
-            <menuitem id="syncViewQuota" label="&viewQuota.label;"/>
-            <menuseparator/>
             <menuitem id="syncChangePassword" label="&changePassword2.label;"/>
             <menuitem id="syncResetPassphrase" label="&myRecoveryKey.label;"/>
             <menuseparator/>
             <menuitem id="syncReset" label="&resetSync2.label;"/>
           </menupopup>
         </button>
       </hbox>
 
--- a/browser/components/preferences/in-content/tests/browser_privacypane_5.js
+++ b/browser/components/preferences/in-content/tests/browser_privacypane_5.js
@@ -15,13 +15,13 @@ function test() {
   let tests = [
     test_locbar_suggestion_retention("history", true),
     test_locbar_suggestion_retention("bookmark", true),
     test_locbar_suggestion_retention("openpage", false),
     test_locbar_suggestion_retention("history", true),
     test_locbar_suggestion_retention("history", false),
   ];
 
-  if (AppConstants.NIGHTLY_BUILD)
+  if (Services.prefs.getBoolPref("browser.urlbar.unifiedcomplete"))
     tests.push(test_locbar_suggestion_retention("searches", true));
 
   run_test_subset(tests);
 }
--- a/browser/components/preferences/privacy.js
+++ b/browser/components/preferences/privacy.js
@@ -66,17 +66,18 @@ var gPrivacyPane = {
     this.updateHistoryModePane();
     this.updatePrivacyMicroControls();
     this.initAutoStartPrivateBrowsingReverter();
 #ifdef NIGHTLY_BUILD
     this._initTrackingProtection();
 #endif
     this._initAutocomplete();
 
-    document.getElementById("searchesSuggestion").hidden = !AppConstants.NIGHTLY_BUILD;
+    document.getElementById("searchesSuggestion").hidden =
+      !Services.prefs.getBoolPref("browser.urlbar.unifiedcomplete");
   },
 
   // HISTORY MODE
 
   /**
    * The list of preferences which affect the initial history mode settings.
    * If the auto start private browsing mode pref is active, the initial
    * history mode would be set to "Don't remember anything".
--- a/browser/components/preferences/sync.js
+++ b/browser/components/preferences/sync.js
@@ -449,26 +449,16 @@ let gSyncPane = {
         return;
       }
     }
     fxAccounts.signOut().then(() => {
       this.updateWeavePrefs();
     });
   },
 
-  openQuotaDialog: function () {
-    let win = Services.wm.getMostRecentWindow("Sync:ViewQuota");
-    if (win) {
-      win.focus();
-    } else {
-      window.openDialog("chrome://browser/content/sync/quota.xul", "",
-                        "centerscreen,chrome,dialog,modal");
-    }
-  },
-
   openAddDevice: function () {
     if (!Weave.Utils.ensureMPUnlocked()) {
       return;
     }
 
     let win = Services.wm.getMostRecentWindow("Sync:AddDevice");
     if (win) {
       win.focus();
--- a/browser/components/preferences/sync.xul
+++ b/browser/components/preferences/sync.xul
@@ -90,19 +90,16 @@
               <label id="accountName" value=""/>
             </caption>
 
             <hbox>
               <button type="menu"
                       label="&manageAccount.label;"
                       accesskey="&manageAccount.accesskey;">
                 <menupopup>
-                  <menuitem label="&viewQuota.label;"
-                            oncommand="gSyncPane.openQuotaDialog();"/>
-                  <menuseparator/>
                   <menuitem label="&changePassword2.label;"
                             oncommand="gSyncUtils.changePassword();"/>
                   <menuitem label="&myRecoveryKey.label;"
                             oncommand="gSyncUtils.resetPassphrase();"/>
                   <menuseparator/>
                   <menuitem label="&resetSync2.label;"
                             oncommand="gSyncPane.resetSync();"/>
                 </menupopup>
--- a/browser/components/preferences/tests/browser_privacypane_5.js
+++ b/browser/components/preferences/tests/browser_privacypane_5.js
@@ -15,13 +15,13 @@ function test() {
   let tests = [
     test_locbar_suggestion_retention("history", true),
     test_locbar_suggestion_retention("bookmark", true),
     test_locbar_suggestion_retention("openpage", false),
     test_locbar_suggestion_retention("history", true),
     test_locbar_suggestion_retention("history", false),
   ];
 
-  if (AppConstants.NIGHTLY_BUILD)
+  if (Services.prefs.getBoolPref("browser.urlbar.unifiedcomplete"))
     tests.push(test_locbar_suggestion_retention("searches", true));
 
   run_test_subset(tests);
 }
--- a/browser/components/uitour/test/browser_showMenu_controlCenter.js
+++ b/browser/components/uitour/test/browser_showMenu_controlCenter.js
@@ -12,16 +12,20 @@ function test() {
   UITourTest();
 }
 
 let tests = [
   taskify(function* test_showMenu() {
     is_element_hidden(CONTROL_CENTER_PANEL, "Panel should initially be hidden");
     yield showMenuPromise(CONTROL_CENTER_MENU_NAME);
     is_element_visible(CONTROL_CENTER_PANEL, "Panel should be visible after showMenu");
+
+    yield gURLBar.focus();
+    is_element_visible(CONTROL_CENTER_PANEL, "Panel should remain visible after focus outside");
+
     yield showMenuPromise(CONTROL_CENTER_MENU_NAME);
     is_element_visible(CONTROL_CENTER_PANEL,
                        "Panel should remain visible and callback called after a 2nd showMenu");
 
     yield BrowserTestUtils.withNewTab({
       gBrowser,
       url: "about:blank"
     }, function*() {
--- a/browser/devtools/animationinspector/animation-controller.js
+++ b/browser/devtools/animationinspector/animation-controller.js
@@ -4,27 +4,25 @@
  * 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, results: Cr } = Components;
 
 Cu.import("resource://gre/modules/Task.jsm");
-Cu.import("resource://gre/modules/devtools/Loader.jsm");
+let { devtools: loader, require } = Cu.import("resource://gre/modules/devtools/Loader.jsm");
 Cu.import("resource://gre/modules/devtools/Console.jsm");
 Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
 
-devtools.lazyRequireGetter(this, "promise");
-devtools.lazyRequireGetter(this, "EventEmitter",
-                                 "devtools/toolkit/event-emitter");
-devtools.lazyRequireGetter(this, "AnimationsFront",
-                                 "devtools/server/actors/animation", true);
-
-const require = devtools.require;
+loader.lazyRequireGetter(this, "promise");
+loader.lazyRequireGetter(this, "EventEmitter",
+                               "devtools/toolkit/event-emitter");
+loader.lazyRequireGetter(this, "AnimationsFront",
+                               "devtools/server/actors/animation", true);
 
 const STRINGS_URI = "chrome://browser/locale/devtools/animationinspector.properties";
 const L10N = new ViewHelpers.L10N(STRINGS_URI);
 
 // Global toolbox/inspector, set when startup is called.
 let gToolbox, gInspector;
 
 /**
--- a/browser/devtools/animationinspector/test/head.js
+++ b/browser/devtools/animationinspector/test/head.js
@@ -1,23 +1,22 @@
 /* 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";
 
 const Cu = Components.utils;
 const {gDevTools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
-const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
-const {require} = devtools;
+const {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
-const TargetFactory = devtools.TargetFactory;
+const {TargetFactory} = require("devtools/framework/target");
 const {console} = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
 const {ViewHelpers} = Cu.import("resource:///modules/devtools/ViewHelpers.jsm", {});
-const DevToolsUtils = devtools.require("devtools/toolkit/DevToolsUtils");
+const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
 
 // All tests are asynchronous
 waitForExplicitFinish();
 
 const TEST_URL_ROOT = "http://example.com/browser/browser/devtools/animationinspector/test/";
 const ROOT_TEST_DIR = getRootDirectory(gTestPath);
 const FRAME_SCRIPT_URL = ROOT_TEST_DIR + "doc_frame_script.js";
 const COMMON_FRAME_SCRIPT_URL = "chrome://browser/content/devtools/frame-script-utils.js";
--- a/browser/devtools/animationinspector/test/unit/test_findOptimalTimeInterval.js
+++ b/browser/devtools/animationinspector/test/unit/test_findOptimalTimeInterval.js
@@ -2,19 +2,17 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 /* eslint no-eval:0 */
 
 "use strict";
 
 const Cu = Components.utils;
-const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
-const {require} = devtools;
-
+const {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 const {findOptimalTimeInterval} = require("devtools/animationinspector/utils");
 
 // This test array contains objects that are used to test the
 // findOptimalTimeInterval function. Each object should have the following
 // properties:
 // - desc: an optional string that will be printed out
 // - timeScale: a number that represents how many pixels is 1ms
 // - minSpacing: an optional number that represents the minim space between 2
--- a/browser/devtools/animationinspector/test/unit/test_timeScale.js
+++ b/browser/devtools/animationinspector/test/unit/test_timeScale.js
@@ -1,19 +1,17 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 const Cu = Components.utils;
-const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
-const {require} = devtools;
-
+const {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 const {TimeScale} = require("devtools/animationinspector/components");
 
 const TEST_ANIMATIONS = [{
   startTime: 500,
   delay: 0,
   duration: 1000,
   iterationCount: 1
 }, {
--- a/browser/devtools/app-manager/content/connection-footer.js
+++ b/browser/devtools/app-manager/content/connection-footer.js
@@ -4,18 +4,17 @@
 
 const Cu = Components.utils;
 const Ci = Components.interfaces;
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource:///modules/devtools/gDevTools.jsm");
 
 const {Simulator} = Cu.import("resource://gre/modules/devtools/Simulator.jsm")
 const {Devices} = Cu.import("resource://gre/modules/devtools/Devices.jsm");
-const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
-const {require} = devtools;
+const {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 
 const {ConnectionManager, Connection} = require("devtools/client/connection-manager");
 const {getDeviceFront} = require("devtools/server/actors/device");
 const ConnectionStore = require("devtools/app-manager/connection-store");
 const DeviceStore = require("devtools/app-manager/device-store");
 const simulatorsStore = require("devtools/app-manager/simulators-store");
 const adbStore = require("devtools/app-manager/builtin-adb-store");
 
--- a/browser/devtools/app-manager/content/device.js
+++ b/browser/devtools/app-manager/content/device.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/. */
 
 const Cu = Components.utils;
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
 const {gDevTools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
 
-const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
-const {require} = devtools;
+const {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+const {TargetFactory} = require("devtools/framework/target");
 
 const {ConnectionManager, Connection}
   = require("devtools/client/connection-manager");
 const {getDeviceFront} = require("devtools/server/actors/device");
 const {getTargetForApp, launchApp, closeApp}
   = require("devtools/app-actor-front");
 const DeviceStore = require("devtools/app-manager/device-store");
 const WebappsStore = require("devtools/app-manager/webapps-store");
@@ -162,17 +162,17 @@ let UI = {
       return;
     }
 
     let options = {
       form: this.listTabsResponse,
       client: this.connection.client,
       chrome: true
     };
-    devtools.TargetFactory.forRemoteTab(options).then((target) => {
+    TargetFactory.forRemoteTab(options).then((target) => {
       top.UI.openAndShowToolboxForTarget(target, "Main process", null);
     });
   },
 
   openToolboxForApp: function(manifest) {
     if (!this.connected) {
       return;
     }
@@ -188,17 +188,17 @@ let UI = {
 
   _getTargetForTab: function (form) {
       let options = {
         form: form,
         client: this.connection.client,
         chrome: false
       };
       let deferred = promise.defer();
-      return devtools.TargetFactory.forRemoteTab(options);
+      return TargetFactory.forRemoteTab(options);
   },
 
   openToolboxForTab: function (aNode) {
     let index = Array.prototype.indexOf.apply(
       aNode.parentNode.parentNode.parentNode.children,
       [aNode.parentNode.parentNode]);
     this.connection.client.listTabs(
       response => {
--- a/browser/devtools/app-manager/content/index.js
+++ b/browser/devtools/app-manager/content/index.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 {utils: Cu, interfaces: Ci} = Components;
 Cu.import("resource:///modules/devtools/gDevTools.jsm");
-const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
-const {require} = devtools;
+const {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+const {Toolbox} = require("devtools/framework/toolbox");
 const {ConnectionManager, Connection} = require("devtools/client/connection-manager");
 const promise = require("devtools/toolkit/deprecated-sync-thenables");
 const prefs = require("sdk/preferences/service");
 const Services = require("Services");
 const Strings = Services.strings.createBundle("chrome://browser/locale/devtools/app-manager.properties");
 
 let UI = {
   _toolboxTabCursor: 0,
@@ -159,17 +159,17 @@ let UI = {
       this.selectTab(lastTab);
     }
 
     buttonToDestroy.remove();
     panelToDestroy.remove();
   },
 
   openAndShowToolboxForTarget: function(target, name, icon) {
-    let host = devtools.Toolbox.HostType.CUSTOM;
+    let host = Toolbox.HostType.CUSTOM;
     let toolbox = gDevTools.getToolbox(target);
     if (!toolbox) {
       let uid = "uid" + this._toolboxTabCursor++;
       let iframe = this.createToolboxTab(name, icon, uid);
       let options = { customIframe: iframe , uid: uid };
       this._handledTargets.set(target, null);
       return gDevTools.showToolbox(target, null, host, options).then(toolbox => {
         this._handledTargets.set(target, toolbox);
--- a/browser/devtools/app-manager/content/projects.js
+++ b/browser/devtools/app-manager/content/projects.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/. */
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 const Cr = Components.results;
 Cu.import("resource:///modules/devtools/gDevTools.jsm");
-const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
-const {require} = devtools;
+const {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 const {ConnectionManager, Connection} = require("devtools/client/connection-manager");
 const {AppProjects} = require("devtools/app-manager/app-projects");
 const {AppValidator} = require("devtools/app-manager/app-validator");
 const {Services} = Cu.import("resource://gre/modules/Services.jsm");
 const {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm");
 const {installHosted, installPackaged, getTargetForApp,
        reloadApp, launchApp, closeApp} = require("devtools/app-actor-front");
 const {EventEmitter} = Cu.import("resource://gre/modules/devtools/event-emitter.js");
--- a/browser/devtools/app-manager/content/utils.js
+++ b/browser/devtools/app-manager/content/utils.js
@@ -9,18 +9,17 @@
  *  . mergeStores: merge several store into one.
  *  . l10n: resolves strings from app-manager.properties.
  *
  */
 
 let Utils = (function() {
   const Cu = Components.utils;
   const {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
-  const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
-  const {require} = devtools;
+  const {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
   const EventEmitter = require("devtools/toolkit/event-emitter");
 
 
   function _createSetEventForwarder(key, finalStore) {
     return function(event, path, value) {
       finalStore.emit("set", [key].concat(path), value);
     };
   }
--- a/browser/devtools/app-manager/test/head.js
+++ b/browser/devtools/app-manager/test/head.js
@@ -1,19 +1,18 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
 const {utils: Cu, classes: Cc, interfaces: Ci} = Components;
 
 const {Promise: promise} =
   Cu.import("resource://gre/modules/devtools/deprecated-sync-thenables.js", {});
-const {devtools} =
+const {require} =
   Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
-const {require} = devtools;
 
 const {AppProjects} = require("devtools/app-manager/app-projects");
 const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
 
 const APP_MANAGER_URL = "about:app-manager";
 const TEST_BASE =
   "chrome://mochitests/content/browser/browser/devtools/app-manager/test/";
 const HOSTED_APP_MANIFEST = TEST_BASE + "hosted_app.manifest";
--- a/browser/devtools/app-manager/test/test_app_validator.html
+++ b/browser/devtools/app-manager/test/test_app_validator.html
@@ -13,18 +13,17 @@
 
   <body>
 
     <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 {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
-    const {require} = devtools;
+    const {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 
     const {AppValidator} = require("devtools/app-manager/app-validator");
     const {Services} = Cu.import("resource://gre/modules/Services.jsm");
     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://browser/locale/devtools/app-manager.properties");
--- a/browser/devtools/app-manager/test/test_connection_store.html
+++ b/browser/devtools/app-manager/test/test_connection_store.html
@@ -33,18 +33,17 @@ Bug 901519 - [app manager] data store fo
       }
 
       window.onload = function() {
         SimpleTest.waitForExplicitFinish();
 
         Cu.import("resource://gre/modules/Services.jsm");
         Cu.import("resource:///modules/devtools/gDevTools.jsm");
 
-        const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
-        const {require} = devtools;
+        const {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 
         const {ConnectionManager} = require("devtools/client/connection-manager");
         const ConnectionStore = require("devtools/app-manager/connection-store");
 
         let connection = ConnectionManager.createConnection();
         let store = new ConnectionStore(connection);
 
         let root = document.querySelector("#root");
--- a/browser/devtools/app-manager/test/test_device_store.html
+++ b/browser/devtools/app-manager/test/test_device_store.html
@@ -32,23 +32,22 @@ Bug 901520 - [app manager] data store fo
 
       window.onload = function() {
         SimpleTest.waitForExplicitFinish();
 
         Cu.import("resource://gre/modules/Services.jsm");
         Cu.import("resource:///modules/devtools/gDevTools.jsm");
 
 
-        const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
-        const {require} = devtools;
+        const {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 
         const {ConnectionManager} = require("devtools/client/connection-manager");
         const DeviceStore = require("devtools/app-manager/device-store");
 
-        let {getDeviceFront} = devtools.require("devtools/server/actors/device");
+        let {getDeviceFront} = require("devtools/server/actors/device");
 
         let connection = ConnectionManager.createConnection();
         let store = new DeviceStore(connection);
 
         connection.once("connected", function() {
           store.on("set", function check(event, path, value) {
             if (path.join(".") != "description") return;
             store.off("set", check);
--- a/browser/devtools/app-manager/test/test_projects_store.html
+++ b/browser/devtools/app-manager/test/test_projects_store.html
@@ -17,18 +17,17 @@ Bug 907206 - data store for local apps
   <body>
 
     <script type="application/javascript;version=1.8">
       const Cu = Components.utils;
 
       window.onload = function() {
         SimpleTest.waitForExplicitFinish();
 
-        const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
-        const {require} = devtools;
+        const { require } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 
         const { AppProjects } = require("devtools/app-manager/app-projects");
 
         function testHosted(projects) {
           let manifestURL = document.location.href.replace("test_projects_store.html", "hosted_app/webapp.manifest");
           AppProjects.addHosted(manifestURL)
             .then(function (app) {
               is(projects.length, 1,
--- a/browser/devtools/app-manager/test/test_remain_connected.html
+++ b/browser/devtools/app-manager/test/test_remain_connected.html
@@ -26,19 +26,20 @@ Bug 912646 - Closing app toolbox causes 
         DebuggerServer.addBrowserActors();
       }
 
       window.onload = function() {
         SimpleTest.waitForExplicitFinish();
 
         Cu.import("resource:///modules/devtools/gDevTools.jsm");
 
-        const {devtools} =
+        const {require} =
           Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
-        const {require} = devtools;
+        const {TargetFactory} = require("devtools/framework/target");
+        const {Toolbox} = require("devtools/framework/toolbox");
 
         const {Connection, ConnectionManager} =
           require("devtools/client/connection-manager");
         const ConnectionStore =
           require("devtools/app-manager/connection-store");
 
         let connection = ConnectionManager.createConnection();
 
@@ -71,18 +72,18 @@ Bug 912646 - Closing app toolbox causes 
 
         function cycleToolbox() {
           connection.client.listTabs(response => {
             let options = {
               form: response.tabs[0],
               client: connection.client,
               chrome: true
             };
-            devtools.TargetFactory.forRemoteTab(options).then(target => {
-              let hostType = devtools.Toolbox.HostType.WINDOW;
+            TargetFactory.forRemoteTab(options).then(target => {
+              let hostType = Toolbox.HostType.WINDOW;
               gDevTools.showToolbox(target,
                                     null,
                                     hostType).then(toolbox => {
                 SimpleTest.executeSoon(() => {
                   toolbox.once("destroyed", onDestroyToolbox);
                   toolbox.destroy();
                 });
               });
--- a/browser/devtools/app-manager/test/test_template.html
+++ b/browser/devtools/app-manager/test/test_template.html
@@ -154,18 +154,17 @@
   </template>
 
   <script type="application/javascript;version=1.8" src="chrome://browser/content/devtools/app-manager/template.js"></script>
   <script type="application/javascript;version=1.8">
     SimpleTest.waitForExplicitFinish();
 
     const Cu = Components.utils;
     Cu.import("resource:///modules/devtools/gDevTools.jsm");
-    const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
-    const {require} = devtools;
+    const {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
     const ObservableObject = require("devtools/shared/observable-object");
 
     let data = {
       title: "ttt",
       mop: {
         name: "meh",
       },
       foo1: {
--- a/browser/devtools/canvasdebugger/canvasdebugger.js
+++ b/browser/devtools/canvasdebugger/canvasdebugger.js
@@ -6,18 +6,17 @@
 const { 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:///modules/devtools/SideMenuWidget.jsm");
 Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
 Cu.import("resource://gre/modules/devtools/Console.jsm");
 
-const devtools = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
-const { require } = devtools;
+const { require } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 const promise = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;
 const EventEmitter = require("devtools/toolkit/event-emitter");
 const { CallWatcherFront } = require("devtools/server/actors/call-watcher");
 const { CanvasFront } = require("devtools/server/actors/canvas");
 const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
 
 const Telemetry = require("devtools/shared/telemetry");
 const telemetry = new Telemetry();
--- a/browser/devtools/canvasdebugger/test/head.js
+++ b/browser/devtools/canvasdebugger/test/head.js
@@ -10,26 +10,26 @@ let { Services } = Cu.import("resource:/
 // be affected by this pref.
 let gEnableLogging = Services.prefs.getBoolPref("devtools.debugger.log");
 Services.prefs.setBoolPref("devtools.debugger.log", false);
 
 let { generateUUID } = Cc['@mozilla.org/uuid-generator;1'].getService(Ci.nsIUUIDGenerator);
 let { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
 let { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
 let { gDevTools } = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
-let { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+let { require } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 let { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
 let { DebuggerClient } = Cu.import("resource://gre/modules/devtools/dbg-client.jsm", {});
-let { CallWatcherFront } = devtools.require("devtools/server/actors/call-watcher");
-let { CanvasFront } = devtools.require("devtools/server/actors/canvas");
-let { setTimeout } = devtools.require("sdk/timers");
-let DevToolsUtils = devtools.require("devtools/toolkit/DevToolsUtils");
-let TiltGL = devtools.require("devtools/tilt/tilt-gl");
-let TargetFactory = devtools.TargetFactory;
-let Toolbox = devtools.Toolbox;
+let { CallWatcherFront } = require("devtools/server/actors/call-watcher");
+let { CanvasFront } = require("devtools/server/actors/canvas");
+let { setTimeout } = require("sdk/timers");
+let DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
+let TiltGL = require("devtools/tilt/tilt-gl");
+let { TargetFactory } = require("devtools/framework/target");
+let { Toolbox } = require("devtools/framework/toolbox");
 let mm = null
 
 const FRAME_SCRIPT_UTILS_URL = "chrome://browser/content/devtools/frame-script-utils.js";
 const EXAMPLE_URL = "http://example.com/browser/browser/devtools/canvasdebugger/test/";
 const SET_TIMEOUT_URL = EXAMPLE_URL + "doc_settimeout.html";
 const NO_CANVAS_URL = EXAMPLE_URL + "doc_no-canvas.html";
 const RAF_NO_CANVAS_URL = EXAMPLE_URL + "doc_raf-no-canvas.html";
 const SIMPLE_CANVAS_URL = EXAMPLE_URL + "doc_simple-canvas.html";
--- a/browser/devtools/commandline/test/head.js
+++ b/browser/devtools/commandline/test/head.js
@@ -1,18 +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/. */
 
 const TEST_BASE_HTTP = "http://example.com/browser/browser/devtools/commandline/test/";
 const TEST_BASE_HTTPS = "https://example.com/browser/browser/devtools/commandline/test/";
 
-let { require } =
-  Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
-let console = require("resource://gre/modules/devtools/Console.jsm").console;
+let { require } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+let { console } = require("resource://gre/modules/devtools/Console.jsm");
 let DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
 
 // Import the GCLI test helper
 let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
 Services.scriptloader.loadSubScript(testDir + "/helpers.js", this);
 Services.scriptloader.loadSubScript(testDir + "/mockCommands.js", this);
 
 DevToolsUtils.testing = true;
--- a/browser/devtools/commandline/test/helpers.js
+++ b/browser/devtools/commandline/test/helpers.js
@@ -17,18 +17,18 @@
 'use strict';
 
 // A copy of this code exists in firefox mochitests. They should be kept
 // in sync. Hence the exports synonym for non AMD contexts.
 var { helpers, assert } = (function() {
 
 var helpers = {};
 
-var TargetFactory = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.TargetFactory;
-var require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
+var { require } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+var { TargetFactory } = require("devtools/framework/target");
 
 var assert = { ok: ok, is: is, log: info };
 var util = require('gcli/util/util');
 var Promise = require('gcli/util/promise').Promise;
 var cli = require('gcli/cli');
 var KeyEvent = require('gcli/util/util').KeyEvent;
 
 const { GcliFront } = require("devtools/server/actors/gcli");
--- a/browser/devtools/debugger/debugger-controller.js
+++ b/browser/devtools/debugger/debugger-controller.js
@@ -97,19 +97,20 @@ Cu.import("resource://gre/modules/Servic
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/devtools/event-emitter.js");
 Cu.import("resource:///modules/devtools/SimpleListWidget.jsm");
 Cu.import("resource:///modules/devtools/BreadcrumbsWidget.jsm");
 Cu.import("resource:///modules/devtools/SideMenuWidget.jsm");
 Cu.import("resource:///modules/devtools/VariablesView.jsm");
 Cu.import("resource:///modules/devtools/VariablesViewController.jsm");
 Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
-const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 
-const require = devtools.require;
+const {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+const {TargetFactory} = require("devtools/framework/target");
+const {Toolbox} = require("devtools/framework/toolbox")
 const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
 const promise = require("devtools/toolkit/deprecated-sync-thenables");
 const Editor = require("devtools/sourceeditor/editor");
 const DebuggerEditor = require("devtools/sourceeditor/debugger.js");
 const {Tooltip} = require("devtools/shared/widgets/Tooltip");
 const FastListWidget = require("devtools/shared/widgets/FastListWidget");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Task",
@@ -121,17 +122,17 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "ShortcutUtils",
   "resource://gre/modules/ShortcutUtils.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "clipboardHelper",
   "@mozilla.org/widget/clipboardhelper;1", "nsIClipboardHelper");
 
 Object.defineProperty(this, "NetworkHelper", {
   get: function() {
-    return devtools.require("devtools/toolkit/webconsole/network-helper");
+    return require("devtools/toolkit/webconsole/network-helper");
   },
   configurable: true,
   enumerable: true
 });
 
 /**
  * Object defining the debugger controller components.
  */
@@ -486,19 +487,19 @@ Workers.prototype = {
 
   _onWorkerThaw: function (type, packet) {
     let workerClient = this._workerClients.get(packet.from);
     DebuggerView.Workers.addWorker(packet.from, workerClient.url);
   },
 
   _onWorkerSelect: function (workerActor) {
     let workerClient = this._workerClients.get(workerActor);
-    gDevTools.showToolbox(devtools.TargetFactory.forWorker(workerClient),
+    gDevTools.showToolbox(TargetFactory.forWorker(workerClient),
                           "jsdebugger",
-                          devtools.Toolbox.HostType.WINDOW);
+                          Toolbox.HostType.WINDOW);
   }
 };
 
 /**
  * ThreadState keeps the UI up to date with the state of the
  * thread (paused/attached/etc.).
  */
 function ThreadState() {
--- a/browser/devtools/debugger/test/browser_dbg_hide-toolbar-buttons.js
+++ b/browser/devtools/debugger/test/browser_dbg_hide-toolbar-buttons.js
@@ -3,18 +3,17 @@
 
 /**
  * Bug 1093349: Test that the pretty-printing and blackboxing buttons
  * are hidden if the server doesn't support them
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_auto-pretty-print-01.html";
 
-let devtools = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
-let { RootActor } = devtools.require("devtools/server/actors/root");
+let { RootActor } = require("devtools/server/actors/root");
 
 function test() {
   let gTab, gDebuggee, gPanel, gDebugger;
   let gEditor, gSources, gBreakpoints, gBreakpointsAdded, gBreakpointsRemoving;
 
   RootActor.prototype.traits.noBlackBoxing = true;
   RootActor.prototype.traits.noPrettyPrinting = true;
 
--- a/browser/devtools/debugger/test/browser_dbg_listtabs-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_listtabs-02.js
@@ -1,17 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Make sure the root actor's live tab list implementation works as specified.
  */
 
-let devtools = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
-let { BrowserTabList } = devtools.require("devtools/server/actors/webbrowser");
+let { BrowserTabList } = require("devtools/server/actors/webbrowser");
 
 let gTestPage = "data:text/html;charset=utf-8," + encodeURIComponent(
   "<title>JS Debugger BrowserTabList test page</title><body>Yo.</body>");
 
 // The tablist object whose behavior we observe.
 let gTabList;
 let gFirstActor, gActorA;
 let gTabA, gTabB, gTabC;
--- a/browser/devtools/debugger/test/browser_dbg_on-pause-raise.js
+++ b/browser/devtools/debugger/test/browser_dbg_on-pause-raise.js
@@ -56,25 +56,25 @@ function onFocus() {
 
 function testPause() {
   is(gDebugger.gThreadClient.paused, false,
     "Should be running after starting the test.");
 
   is(gFocusedWindow, window,
     "Main window is the top level window before pause.");
 
-  if (gToolbox.hostType == devtools.Toolbox.HostType.WINDOW) {
+  if (gToolbox.hostType == Toolbox.HostType.WINDOW) {
     gToolbox._host._window.addEventListener("focus", function onFocus() {
       gToolbox._host._window.removeEventListener("focus", onFocus, true);
       gFocusedWindow = gToolbox._host._window;
     }, true);
   }
 
   gDebugger.gThreadClient.addOneTimeListener("paused", () => {
-    if (gToolbox.hostType == devtools.Toolbox.HostType.WINDOW) {
+    if (gToolbox.hostType == Toolbox.HostType.WINDOW) {
       is(gFocusedWindow, gToolbox._host._window,
          "Toolbox window is the top level window on pause.");
     } else {
       is(gBrowser.selectedTab, gTab,
         "Debugger's tab got selected.");
     }
     gToolbox.selectTool("webconsole").then(() => {
       ok(gToolboxTab.hasAttribute("highlighted") &&
@@ -111,29 +111,29 @@ function testResume() {
   });
 
   EventUtils.sendMouseEvent({ type: "mousedown" },
     gDebugger.document.getElementById("resume"),
     gDebugger);
 }
 
 function maybeEndTest() {
-  if (gToolbox.hostType == devtools.Toolbox.HostType.BOTTOM) {
+  if (gToolbox.hostType == Toolbox.HostType.BOTTOM) {
     info("Switching to a toolbox window host.");
-    gToolbox.switchHost(devtools.Toolbox.HostType.WINDOW).then(focusMainWindow);
+    gToolbox.switchHost(Toolbox.HostType.WINDOW).then(focusMainWindow);
   } else {
     info("Switching to main window host.");
-    gToolbox.switchHost(devtools.Toolbox.HostType.BOTTOM).then(() => closeDebuggerAndFinish(gPanel));
+    gToolbox.switchHost(Toolbox.HostType.BOTTOM).then(() => closeDebuggerAndFinish(gPanel));
   }
 }
 
 registerCleanupFunction(function() {
   // Revert to the default toolbox host, so that the following tests proceed
   // normally and not inside a non-default host.
-  Services.prefs.setCharPref("devtools.toolbox.host", devtools.Toolbox.HostType.BOTTOM);
+  Services.prefs.setCharPref("devtools.toolbox.host", Toolbox.HostType.BOTTOM);
 
   gTab = null;
   gPanel = null;
   gDebugger = null;
 
   gFocusedWindow = null;
   gToolbox = null;
   gToolboxTab = null;
--- a/browser/devtools/debugger/test/browser_dbg_promises-allocation-stack.js
+++ b/browser/devtools/debugger/test/browser_dbg_promises-allocation-stack.js
@@ -3,18 +3,18 @@
 
 /**
  * Test that we can get a stack to a promise's allocation point.
  */
 
 "use strict";
 
 const TAB_URL = EXAMPLE_URL + "doc_promise-get-allocation-stack.html";
-const { PromisesFront } = devtools.require("devtools/server/actors/promises");
-let events = devtools.require("sdk/event/core");
+const { PromisesFront } = require("devtools/server/actors/promises");
+let events = require("sdk/event/core");
 
 function test() {
   Task.spawn(function* () {
     DebuggerServer.init();
     DebuggerServer.addBrowserActors();
 
     const [ tab,, panel ] = yield initDebugger(TAB_URL);
 
--- a/browser/devtools/debugger/test/browser_dbg_promises-chrome-allocation-stack.js
+++ b/browser/devtools/debugger/test/browser_dbg_promises-chrome-allocation-stack.js
@@ -4,18 +4,18 @@
 /**
  * Test that we can get a stack to a promise's allocation point in the chrome
  * process.
  */
 
 "use strict";
 
 const SOURCE_URL = "browser_dbg_promises-chrome-allocation-stack.js";
-const { PromisesFront } = devtools.require("devtools/server/actors/promises");
-let events = devtools.require("sdk/event/core");
+const { PromisesFront } = require("devtools/server/actors/promises");
+let events = require("sdk/event/core");
 
 const STACK_DATA = [
   { functionDisplayName: "test/</<" },
   { functionDisplayName: "testGetAllocationStack" },
 ];
 
 function test() {
   Task.spawn(function* () {
--- a/browser/devtools/debugger/test/code_ugly-4.js
+++ b/browser/devtools/debugger/test/code_ugly-4.js
@@ -1,15 +1,16 @@
 function a(){b()}function b(){debugger}
 //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWJjLmpzIiwic291cmNlcyI6WyJkYXRhOnRleHQvamF2YXNjcmlwdCxmdW5jdGlvbiBhKCl7YigpfSIsImRhdGE6dGV4dC9qYXZhc2NyaXB0LGZ1bmN0aW9uIGIoKXtkZWJ1Z2dlcn0iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsaUJDQUEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMifQ==
 
 // Generate this file by evaluating the following in a browser-environment
 // scratchpad:
 //
-//    Components.utils.import('resource://gre/modules/devtools/SourceMap.jsm');
+//    let { require } = Components.utils.import('resource://gre/modules/devtools/Loader.jsm', {});
+//    let { SourceNode } = require("source-map");
 //
 //    let dataUrl = s => "data:text/javascript," + s;
 //
 //    let A = "function a(){b()}";
 //    let A_URL = dataUrl(A);
 //    let B = "function b(){debugger}";
 //    let B_URL = dataUrl(B);
 //
--- a/browser/devtools/debugger/test/head.js
+++ b/browser/devtools/debugger/test/head.js
@@ -10,28 +10,27 @@ let { Services } = Cu.import("resource:/
 // debug a test in your try runs. Both the debugger server and frontend will
 // be affected by this pref.
 let gEnableLogging = Services.prefs.getBoolPref("devtools.debugger.log");
 Services.prefs.setBoolPref("devtools.debugger.log", false);
 
 let { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
 let { Promise: promise } = Cu.import("resource://gre/modules/devtools/deprecated-sync-thenables.js", {});
 let { gDevTools } = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
-let { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
-let { require } = devtools;
+let { require } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 let DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
 let { BrowserToolboxProcess } = Cu.import("resource:///modules/devtools/ToolboxProcess.jsm", {});
 let { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
 let { DebuggerClient, ObjectClient } =
   Cu.import("resource://gre/modules/devtools/dbg-client.jsm", {});
 let { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm", {});
 let EventEmitter = require("devtools/toolkit/event-emitter");
 const { promiseInvoke } = require("devtools/async-utils");
-let TargetFactory = devtools.TargetFactory;
-let Toolbox = devtools.Toolbox;
+let { TargetFactory } = require("devtools/framework/target");
+let { Toolbox } = require("devtools/framework/toolbox")
 
 const EXAMPLE_URL = "http://example.com/browser/browser/devtools/debugger/test/";
 const FRAME_SCRIPT_URL = getRootDirectory(gTestPath) + "code_frame-script.js";
 
 DevToolsUtils.testing = true;
 SimpleTest.registerCleanupFunction(() => {
   DevToolsUtils.testing = false;
 });
@@ -596,18 +595,18 @@ AddonDebugger.prototype = {
       chrome: true,
       isTabActor: false
     };
 
     let toolboxOptions = {
       customIframe: this.frame
     };
 
-    this.target = devtools.TargetFactory.forTab(targetOptions);
-    let toolbox = yield gDevTools.showToolbox(this.target, "jsdebugger", devtools.Toolbox.HostType.CUSTOM, toolboxOptions);
+    this.target = TargetFactory.forTab(targetOptions);
+    let toolbox = yield gDevTools.showToolbox(this.target, "jsdebugger", Toolbox.HostType.CUSTOM, toolboxOptions);
 
     info("Addon debugger panel shown successfully.");
 
     this.debuggerPanel = toolbox.getCurrentPanel();
 
     // Wait for the initial resume...
     yield waitForClientEvents(this.debuggerPanel, "resumed");
     yield prepareDebugger(this.debuggerPanel);
--- a/browser/devtools/definitions.js
+++ b/browser/devtools/definitions.js
@@ -89,17 +89,17 @@ Tools.inspector = {
   label: l10n("inspector.label", inspectorStrings),
   panelLabel: l10n("inspector.panelLabel", inspectorStrings),
   get tooltip() {
     return l10n("inspector.tooltip2", inspectorStrings,
     ( osString == "Darwin" ? "Cmd+Opt+" : "Ctrl+Shift+" ) + this.key);
   },
   inMenu: true,
   commands: [
-    "devtools/resize-commands",
+    "devtools/responsivedesign/resize-commands",
     "devtools/inspector/inspector-commands",
     "devtools/eyedropper/commands.js"
   ],
 
   preventClosingOnKey: true,
   onkey: function(panel) {
     panel.toolbox.highlighterUtils.togglePicker();
   },
--- a/browser/devtools/devtools-clhandler.js
+++ b/browser/devtools/devtools-clhandler.js
@@ -34,21 +34,21 @@ devtoolsCommandlineHandler.prototype = {
     if (debuggerServerFlag) {
       this.handleDebuggerServerFlag(cmdLine, debuggerServerFlag);
     }
   },
 
   handleConsoleFlag: function(cmdLine) {
     let window = Services.wm.getMostRecentWindow("devtools:webconsole");
     if (!window) {
-      let devtools = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
+      let { require } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
       // Load the browser devtools main module as the loader's main module.
       Cu.import("resource:///modules/devtools/gDevTools.jsm");
-      let hudservice = devtools.require("devtools/webconsole/hudservice");
-      let console = Cu.import("resource://gre/modules/devtools/Console.jsm", {}).console;
+      let hudservice = require("devtools/webconsole/hudservice");
+      let { console } = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
       hudservice.toggleBrowserConsole().then(null, console.error);
     } else {
       window.focus(); // the Browser Console was already open
     }
 
     if (cmdLine.state == Ci.nsICommandLine.STATE_REMOTE_AUTO) {
       cmdLine.preventDefault = true;
     }
--- a/browser/devtools/eyedropper/test/head.js
+++ b/browser/devtools/eyedropper/test/head.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // shared-head.js handles imports, constants, and utility functions
 Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/browser/devtools/framework/test/shared-head.js", this);
 Services.scriptloader.loadSubScript(TEST_DIR + "../../../commandline/test/helpers.js", this);
 
-const { Eyedropper, EyedropperManager } = devtools.require("devtools/eyedropper/eyedropper");
+const { Eyedropper, EyedropperManager } = require("devtools/eyedropper/eyedropper");
 
 function waitForClipboard(setup, expected) {
   let deferred = promise.defer();
   SimpleTest.waitForClipboard(expected, setup, deferred.resolve, deferred.reject);
   return deferred.promise;
 }
 
 function dropperStarted(dropper) {
--- a/browser/devtools/fontinspector/test/browser_fontinspector_theme-change.js
+++ b/browser/devtools/fontinspector/test/browser_fontinspector_theme-change.js
@@ -1,16 +1,16 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
 // Test that the preview images are updated when the theme changes.
 
-const { getTheme, setTheme } = devtools.require("devtools/shared/theme");
+const { getTheme, setTheme } = require("devtools/shared/theme");
 
 const TEST_URI = BASE_URI + "browser_fontinspector.html";
 const originalTheme = getTheme();
 
 registerCleanupFunction(() => {
   info(`Restoring theme to '${originalTheme}.`);
   setTheme(originalTheme);
 });
--- a/browser/devtools/framework/ToolboxProcess.jsm
+++ b/browser/devtools/framework/ToolboxProcess.jsm
@@ -7,27 +7,23 @@
 
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 const DBG_XUL = "chrome://browser/content/devtools/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")
-
-XPCOMUtils.defineLazyModuleGetter(this, "DevToolsLoader",
-  "resource://gre/modules/devtools/Loader.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "devtools",
-  "resource://gre/modules/devtools/Loader.jsm");
+const { require, DevToolsLoader } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 
 XPCOMUtils.defineLazyGetter(this, "Telemetry", function () {
-  return devtools.require("devtools/shared/telemetry");
+  return require("devtools/shared/telemetry");
 });
 XPCOMUtils.defineLazyGetter(this, "EventEmitter", function () {
-  return devtools.require("devtools/toolkit/event-emitter");
+  return require("devtools/toolkit/event-emitter");
 });
 const { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
 
 this.EXPORTED_SYMBOLS = ["BrowserToolboxProcess"];
 
 let processes = new Set();
 
 /**
--- a/browser/devtools/framework/connect/connect.js
+++ b/browser/devtools/framework/connect/connect.js
@@ -7,17 +7,19 @@
 "use strict";
 
 const Cu = Components.utils;
 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
 let {gDevTools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
-let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+let {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+let {TargetFactory} = require("devtools/framework/target");
+let {Toolbox} = require("devtools/framework/toolbox")
 let {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
 
 let gClient;
 let gConnectionTimeout;
 
 XPCOMUtils.defineLazyGetter(window, 'l10n', function () {
   return Services.strings.createBundle('chrome://browser/locale/devtools/connection-screen.properties');
 });
@@ -231,18 +233,18 @@ function handleConnectionTimeout() {
  */
 function openToolbox(form, chrome=false, tool="webconsole", isTabActor) {
   let options = {
     form: form,
     client: gClient,
     chrome: chrome,
     isTabActor: isTabActor
   };
-  devtools.TargetFactory.forRemoteTab(options).then((target) => {
-    let hostType = devtools.Toolbox.HostType.WINDOW;
+  TargetFactory.forRemoteTab(options).then((target) => {
+    let hostType = Toolbox.HostType.WINDOW;
     gDevTools.showToolbox(target, tool, hostType).then((toolbox) => {
       toolbox.once("destroyed", function() {
         gClient.close();
       });
     }, console.error.bind(console));
     window.close();
   }, console.error.bind(console));
 }
--- a/browser/devtools/framework/gDevTools.jsm
+++ b/browser/devtools/framework/gDevTools.jsm
@@ -5,31 +5,35 @@
 "use strict";
 
 this.EXPORTED_SYMBOLS = [ "gDevTools", "DevTools", "gDevToolsBrowser" ];
 
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/devtools/Loader.jsm");
+const {require, devtools: loader} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+// Load target and toolbox lazily as they need gDevTools to be fully initialized
+loader.lazyRequireGetter(this, "TargetFactory", "devtools/framework/target", true);
+loader.lazyRequireGetter(this, "Toolbox", "devtools/framework/toolbox", true);
 
 XPCOMUtils.defineLazyModuleGetter(this, "promise",
                                   "resource://gre/modules/Promise.jsm", "Promise");
 XPCOMUtils.defineLazyModuleGetter(this, "console",
                                   "resource://gre/modules/devtools/Console.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
                                   "resource:///modules/CustomizableUI.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "DebuggerServer",
                                   "resource://gre/modules/devtools/dbg-server.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "DebuggerClient",
                                   "resource://gre/modules/devtools/dbg-client.jsm");
 
-const EventEmitter = devtools.require("devtools/toolkit/event-emitter");
-const Telemetry = devtools.require("devtools/shared/telemetry");
+const DefaultTools = require("definitions").defaultTools;
+const EventEmitter = require("devtools/toolkit/event-emitter");
+const Telemetry = require("devtools/shared/telemetry");
 
 const TABS_OPEN_PEAK_HISTOGRAM = "DEVTOOLS_TABS_OPEN_PEAK_LINEAR";
 const TABS_OPEN_AVG_HISTOGRAM = "DEVTOOLS_TABS_OPEN_AVERAGE_LINEAR";
 const TABS_PINNED_PEAK_HISTOGRAM = "DEVTOOLS_TABS_PINNED_PEAK_LINEAR";
 const TABS_PINNED_AVG_HISTOGRAM = "DEVTOOLS_TABS_PINNED_AVERAGE_LINEAR";
 
 const FORBIDDEN_IDS = new Set(["toolbox", ""]);
 const MAX_ORDINAL = 99;
@@ -91,17 +95,17 @@ DevTools.prototype = {
 
     if (!toolId || FORBIDDEN_IDS.has(toolId)) {
       throw new Error("Invalid definition.id");
     }
 
     // Make sure that additional tools will always be able to be hidden.
     // When being called from main.js, defaultTools has not yet been exported.
     // But, we can assume that in this case, it is a default tool.
-    if (devtools.defaultTools && devtools.defaultTools.indexOf(toolDefinition) == -1) {
+    if (DefaultTools && DefaultTools.indexOf(toolDefinition) == -1) {
       toolDefinition.visibilityswitch = "devtools." + toolId + ".enabled";
     }
 
     this._tools.set(toolId, toolDefinition);
 
     this.emit("tool-registered", toolId);
   },
 
@@ -137,23 +141,23 @@ DevTools.prototype = {
    */
   ordinalSort: function DT_ordinalSort(d1, d2) {
     let o1 = (typeof d1.ordinal == "number") ? d1.ordinal : MAX_ORDINAL;
     let o2 = (typeof d2.ordinal == "number") ? d2.ordinal : MAX_ORDINAL;
     return o1 - o2;
   },
 
   getDefaultTools: function DT_getDefaultTools() {
-    return devtools.defaultTools.sort(this.ordinalSort);
+    return DefaultTools.sort(this.ordinalSort);
   },
 
   getAdditionalTools: function DT_getAdditionalTools() {
     let tools = [];
     for (let [key, value] of this._tools) {
-      if (devtools.defaultTools.indexOf(value) == -1) {
+      if (DefaultTools.indexOf(value) == -1) {
         tools.push(value);
       }
     }
     return tools.sort(this.ordinalSort);
   },
 
   /**
    * Get a tool definition if it exists and is enabled.
@@ -389,17 +393,17 @@ DevTools.prototype = {
 
       return hostPromise.then(function() {
         toolbox.raise();
         return toolbox;
       });
     }
     else {
       // No toolbox for target, create one
-      toolbox = new devtools.Toolbox(target, toolId, hostType, hostOptions);
+      toolbox = new Toolbox(target, toolId, hostType, hostOptions);
 
       this.emit("toolbox-created", toolbox);
 
       this._toolboxes.set(target, toolbox);
 
       toolbox.once("destroy", () => {
         this.emit("toolbox-destroy", target);
       });
@@ -532,28 +536,28 @@ let gDevToolsBrowser = {
   },
 
   /**
    * This function is for the benefit of Tools:DevToolbox in
    * browser/base/content/browser-sets.inc and should not be used outside
    * of there
    */
   toggleToolboxCommand: function(gBrowser) {
-    let target = devtools.TargetFactory.forTab(gBrowser.selectedTab);
+    let target = TargetFactory.forTab(gBrowser.selectedTab);
     let toolbox = gDevTools.getToolbox(target);
 
     // If a toolbox exists, using toggle from the Main window :
     // - should close a docked toolbox
     // - should focus a windowed toolbox
-    let isDocked = toolbox && toolbox.hostType != devtools.Toolbox.HostType.WINDOW;
+    let isDocked = toolbox && toolbox.hostType != Toolbox.HostType.WINDOW;
     isDocked ? toolbox.destroy() : gDevTools.showToolbox(target);
   },
 
   toggleBrowserToolboxCommand: function(gBrowser) {
-    let target = devtools.TargetFactory.forWindow(gBrowser.ownerDocument.defaultView);
+    let target = TargetFactory.forWindow(gBrowser.ownerDocument.defaultView);
     let toolbox = gDevTools.getToolbox(target);
 
     toolbox ? toolbox.destroy()
      : gDevTools.showToolbox(target, "inspector", Toolbox.HostType.WINDOW);
   },
 
   /**
    * This function ensures the right commands are enabled in a window,
@@ -645,35 +649,35 @@ let gDevToolsBrowser = {
    * - if the toolbox is open, and the targeted tool is not selected,
    *   we select it
    * - if the toolbox is open, and the targeted tool is selected,
    *   and the host is NOT a window, we close the toolbox
    * - if the toolbox is open, and the targeted tool is selected,
    *   and the host is a window, we raise the toolbox window
    */
   selectToolCommand: function(gBrowser, toolId) {
-    let target = devtools.TargetFactory.forTab(gBrowser.selectedTab);
+    let target = TargetFactory.forTab(gBrowser.selectedTab);
     let toolbox = gDevTools.getToolbox(target);
     let toolDefinition = gDevTools.getToolDefinition(toolId);
 
     if (toolbox &&
         (toolbox.currentToolId == toolId ||
           (toolId == "webconsole" && toolbox.splitConsole)))
     {
       toolbox.fireCustomKey(toolId);
 
-      if (toolDefinition.preventClosingOnKey || toolbox.hostType == devtools.Toolbox.HostType.WINDOW) {
+      if (toolDefinition.preventClosingOnKey || toolbox.hostType == Toolbox.HostType.WINDOW) {
         toolbox.raise();
       } else {
         toolbox.destroy();
       }
       gDevTools.emit("select-tool-command", toolId);
     } else {
       gDevTools.showToolbox(target, toolId).then(() => {
-        let target = devtools.TargetFactory.forTab(gBrowser.selectedTab);
+        let target = TargetFactory.forTab(gBrowser.selectedTab);
         let toolbox = gDevTools.getToolbox(target);
 
         toolbox.fireCustomKey(toolId);
         gDevTools.emit("select-tool-command", toolId);
       });
     }
   },
 
@@ -729,17 +733,17 @@ let gDevToolsBrowser = {
         client.getProcess(contentProcesses[0].id)
               .then(response => {
                 let options = {
                   form: response.form,
                   client: client,
                   chrome: true,
                   isTabActor: false
                 };
-                return devtools.TargetFactory.forRemoteTab(options);
+                return TargetFactory.forRemoteTab(options);
               })
               .then(target => {
                 // Ensure closing the connection in order to cleanup
                 // the debugger client and also the server created in the
                 // content process
                 target.on("close", () => {
                   client.close();
                 });
@@ -751,17 +755,17 @@ let gDevToolsBrowser = {
     return deferred.promise;
   },
 
   openContentProcessToolbox: function () {
     this._getContentProcessTarget()
         .then(target => {
           // Display a new toolbox, in a new window, with debugger by default
           return gDevTools.showToolbox(target, "jsdebugger",
-                                       devtools.Toolbox.HostType.WINDOW);
+                                       Toolbox.HostType.WINDOW);
         });
   },
 
   /**
    * Install WebIDE widget
    */
   installWebIDEWidget: function() {
     if (this.isWebIDEWidgetInstalled()) {
@@ -867,17 +871,17 @@ let gDevToolsBrowser = {
    * dialog.
    */
   setSlowScriptDebugHandler: function DT_setSlowScriptDebugHandler() {
     let debugService = Cc["@mozilla.org/dom/slow-script-debug;1"]
                          .getService(Ci.nsISlowScriptDebug);
     let tm = Cc["@mozilla.org/thread-manager;1"].getService(Ci.nsIThreadManager);
 
     function slowScriptDebugHandler(aTab, aCallback) {
-      let target = devtools.TargetFactory.forTab(aTab);
+      let target = TargetFactory.forTab(aTab);
 
       gDevTools.showToolbox(target, "jsdebugger").then(toolbox => {
         let threadClient = toolbox.getCurrentPanel().panelWin.gThreadClient;
 
         // Break in place, which means resuming the debuggee thread and pausing
         // right before the next step happens.
         switch (threadClient.state) {
           case "paused":
@@ -1169,18 +1173,18 @@ let gDevToolsBrowser = {
   /**
    * Update the "Toggle Tools" checkbox in the developer tools menu. This is
    * called when a toolbox is created or destroyed.
    */
   _updateMenuCheckbox: function DT_updateMenuCheckbox() {
     for (let win of gDevToolsBrowser._trackedBrowserWindows) {
 
       let hasToolbox = false;
-      if (devtools.TargetFactory.isKnownTab(win.gBrowser.selectedTab)) {
-        let target = devtools.TargetFactory.forTab(win.gBrowser.selectedTab);
+      if (TargetFactory.isKnownTab(win.gBrowser.selectedTab)) {
+        let target = TargetFactory.forTab(win.gBrowser.selectedTab);
         if (gDevTools._toolboxes.has(target)) {
           hasToolbox = true;
         }
       }
 
       let broadcaster = win.document.getElementById("devtoolsMenuBroadcaster_DevToolbox");
       if (hasToolbox) {
         broadcaster.setAttribute("checked", "true");
@@ -1318,9 +1322,9 @@ gDevTools.on("tool-unregistered", functi
 });
 
 gDevTools.on("toolbox-ready", gDevToolsBrowser._updateMenuCheckbox);
 gDevTools.on("toolbox-destroyed", gDevToolsBrowser._updateMenuCheckbox);
 
 Services.obs.addObserver(gDevToolsBrowser.destroy, "quit-application", false);
 
 // Load the browser devtools main module as the loader's main module.
-devtools.main("main");
+loader.main("main");
--- a/browser/devtools/framework/test/browser_keybindings_02.js
+++ b/browser/devtools/framework/test/browser_keybindings_02.js
@@ -2,23 +2,25 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Test that the toolbox keybindings still work after the host is changed.
 
 const URL = "data:text/html;charset=utf8,test page";
 
+let {Toolbox} = require("devtools/framework/toolbox");
+
 add_task(function*() {
   info("Create a test tab and open the toolbox");
   let tab = yield addTab(URL);
   let target = TargetFactory.forTab(tab);
   let toolbox = yield gDevTools.showToolbox(target, "webconsole");
 
-  let {SIDE, BOTTOM} = devtools.Toolbox.HostType;
+  let {SIDE, BOTTOM} = Toolbox.HostType;
   for (let type of [SIDE, BOTTOM, SIDE]) {
     info("Switch to host type " + type);
     yield toolbox.switchHost(type);
 
     info("Try to use the toolbox shortcuts");
     yield checkKeyBindings(toolbox);
   }
 
--- a/browser/devtools/framework/test/browser_keybindings_03.js
+++ b/browser/devtools/framework/test/browser_keybindings_03.js
@@ -3,25 +3,27 @@
 
 "use strict";
 
 // Test that the toolbox 'switch to previous host' feature works.
 // Pressing ctrl/cmd+shift+d should switch to the last used host.
 
 const URL = "data:text/html;charset=utf8,test page for toolbox switching";
 
+let {Toolbox} = require("devtools/framework/toolbox");
+
 add_task(function*() {
   info("Create a test tab and open the toolbox");
   let tab = yield addTab(URL);
   let target = TargetFactory.forTab(tab);
   let toolbox = yield gDevTools.showToolbox(target, "webconsole");
 
   let keyElement = toolbox.doc.getElementById("toolbox-toggle-host-key");
 
-  let {SIDE, BOTTOM, WINDOW} = devtools.Toolbox.HostType;
+  let {SIDE, BOTTOM, WINDOW} = Toolbox.HostType;
   checkHostType(toolbox, BOTTOM, SIDE);
 
   info ("Switching from bottom to side");
   synthesizeKeyElement(keyElement);
   yield toolbox.once("host-changed");
   checkHostType(toolbox, SIDE, BOTTOM);
 
   info ("Switching from side to bottom");
--- a/browser/devtools/framework/test/browser_target_remote.js
+++ b/browser/devtools/framework/test/browser_target_remote.js
@@ -1,26 +1,23 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
-let { devtools } =
-  Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
-
 // Ensure target is closed if client is closed directly
 function test() {
   waitForExplicitFinish();
 
   getChromeActors((client, response) => {
     let options = {
       form: response,
       client: client,
       chrome: true
     };
 
-    devtools.TargetFactory.forRemoteTab(options).then(target => {
+    TargetFactory.forRemoteTab(options).then(target => {
       target.on("close", () => {
         ok(true, "Target was closed");
         finish();
       });
       client.close();
     });
   });
 }
--- a/browser/devtools/framework/test/browser_target_support.js
+++ b/browser/devtools/framework/test/browser_target_support.js
@@ -1,20 +1,18 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Test support methods on Target, such as `hasActor`, `getActorDescription`,
 // `actorHasMethod` and `getTrait`.
 
-let { devtools } =
-  Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 let { Task } =
   Cu.import("resource://gre/modules/Task.jsm", {});
 let { WebAudioFront } =
-  devtools.require("devtools/server/actors/webaudio");
+  require("devtools/server/actors/webaudio");
 
 function* testTarget (client, target) {
   yield target.makeRemote();
 
   is(target.hasActor("timeline"), true, "target.hasActor() true when actor exists.");
   is(target.hasActor("webaudio"), true, "target.hasActor() true when actor exists.");
   is(target.hasActor("notreal"), false, "target.hasActor() false when actor does not exist.");
   // Create a front to ensure the actor is loaded
@@ -58,17 +56,17 @@ function test() {
 
   getChromeActors((client, response) => {
     let options = {
       form: response,
       client: client,
       chrome: true
     };
 
-    devtools.TargetFactory.forRemoteTab(options).then(Task.async(testTarget).bind(null, client));
+    TargetFactory.forRemoteTab(options).then(Task.async(testTarget).bind(null, client));
   });
 }
 
 function close (target, client) {
   target.on("close", () => {
     ok(true, "Target was closed");
     finish();
   });
--- a/browser/devtools/framework/test/browser_toolbox_custom_host.js
+++ b/browser/devtools/framework/test/browser_toolbox_custom_host.js
@@ -1,16 +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/ */
 
 function test() {
-  let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
-
-  let Toolbox = devtools.Toolbox;
+  let {Toolbox} = require("devtools/framework/toolbox");
 
   let toolbox, iframe, target, tab;
 
   gBrowser.selectedTab = gBrowser.addTab();
   target = TargetFactory.forTab(gBrowser.selectedTab);
 
   window.addEventListener("message", onMessage);
 
--- a/browser/devtools/framework/test/browser_toolbox_highlight.js
+++ b/browser/devtools/framework/test/browser_toolbox_highlight.js
@@ -1,16 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
-let Toolbox = devtools.Toolbox;
-let temp = {};
-Cu.import("resource://gre/modules/Services.jsm", temp);
-let Services = temp.Services;
-temp = null;
+let {Toolbox} = require("devtools/framework/toolbox");
+
 let toolbox = null;
 
 function test() {
   const URL = "data:text/plain;charset=UTF-8,Nothing to see here, move along";
 
   const TOOL_ID_1 = "jsdebugger";
   const TOOL_ID_2 = "webconsole";
 
--- a/browser/devtools/framework/test/browser_toolbox_hosts.js
+++ b/browser/devtools/framework/test/browser_toolbox_hosts.js
@@ -1,15 +1,16 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-let {SIDE, BOTTOM, WINDOW} = devtools.Toolbox.HostType;
+let {Toolbox} = require("devtools/framework/toolbox");
+let {SIDE, BOTTOM, WINDOW} = Toolbox.HostType;
 let toolbox, target;
 
 const URL = "data:text/html;charset=utf8,test for opening toolbox in different hosts";
 
 add_task(function* runTest() {
   info("Create a test tab and open the toolbox");
   let tab = yield addTab(URL);
   target = TargetFactory.forTab(tab);
--- a/browser/devtools/framework/test/browser_toolbox_hosts_size.js
+++ b/browser/devtools/framework/test/browser_toolbox_hosts_size.js
@@ -5,16 +5,18 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that getPanelWhenReady returns the correct panel in promise
 // resolutions regardless of whether it has opened first.
 
 const URL = "data:text/html;charset=utf8,test for host sizes";
 
+let {Toolbox} = require("devtools/framework/toolbox");
+
 add_task(function*() {
   // Set size prefs to make the hosts way too big, so that the size has
   // to be clamped to fit into the browser window.
   Services.prefs.setIntPref("devtools.toolbox.footer.height", 10000);
   Services.prefs.setIntPref("devtools.toolbox.sidebar.width", 10000);
 
   let tab = yield addTab(URL);
   let nbox = gBrowser.getNotificationBox();
@@ -22,17 +24,17 @@ add_task(function*() {
   let toolbox = yield gDevTools.showToolbox(TargetFactory.forTab(tab));
 
   is (nbox.clientHeight, nboxHeight, "Opening the toolbox hasn't changed the height of the nbox");
   is (nbox.clientWidth, nboxWidth, "Opening the toolbox hasn't changed the width of the nbox");
 
   let iframe = document.getAnonymousElementByAttribute(nbox, "class", "devtools-toolbox-bottom-iframe");
   is (iframe.clientHeight, nboxHeight - 25, "The iframe fits within the available space");
 
-  yield toolbox.switchHost(devtools.Toolbox.HostType.SIDE);
+  yield toolbox.switchHost(Toolbox.HostType.SIDE);
   iframe = document.getAnonymousElementByAttribute(nbox, "class", "devtools-toolbox-side-iframe");
   iframe.style.minWidth = "1px"; // Disable the min width set in css
   is (iframe.clientWidth, nboxWidth - 25, "The iframe fits within the available space");
 
   yield cleanup(toolbox);
 });
 
 add_task(function*() {
@@ -47,17 +49,17 @@ add_task(function*() {
   let toolbox = yield gDevTools.showToolbox(TargetFactory.forTab(tab));
 
   is (nbox.clientHeight, nboxHeight, "Opening the toolbox hasn't changed the height of the nbox");
   is (nbox.clientWidth, nboxWidth, "Opening the toolbox hasn't changed the width of the nbox");
 
   let iframe = document.getAnonymousElementByAttribute(nbox, "class", "devtools-toolbox-bottom-iframe");
   is (iframe.clientHeight, 100, "The iframe is resized properly");
 
-  yield toolbox.switchHost(devtools.Toolbox.HostType.SIDE);
+  yield toolbox.switchHost(Toolbox.HostType.SIDE);
   iframe = document.getAnonymousElementByAttribute(nbox, "class", "devtools-toolbox-side-iframe");
   iframe.style.minWidth = "1px"; // Disable the min width set in css
   is (iframe.clientWidth, 100, "The iframe is resized properly");
 
   yield cleanup(toolbox);
 });
 
 function* cleanup(toolbox) {
--- a/browser/devtools/framework/test/browser_toolbox_minimize.js
+++ b/browser/devtools/framework/test/browser_toolbox_minimize.js
@@ -5,16 +5,17 @@
 
 // Test that when the toolbox is displayed in a bottom host, that host can be
 // minimized to just the tabbar height, and maximized again.
 // Also test that while minimized, switching to a tool, clicking on the
 // settings, or clicking on the selected tool's tab maximizes the toolbox again.
 // Finally test that the minimize button doesn't exist in other host types.
 
 const URL = "data:text/html;charset=utf8,test page";
+const {Toolbox} = require("devtools/framework/toolbox");
 
 add_task(function*() {
   info("Create a test tab and open the toolbox");
   let tab = yield addTab(URL);
   let target = TargetFactory.forTab(tab);
   let toolbox = yield gDevTools.showToolbox(target, "webconsole");
 
   let button = toolbox.doc.querySelector("#toolbox-dock-bottom-minimize");
@@ -56,17 +57,17 @@ add_task(function*() {
   info("Minimize again and click on the settings tab");
   yield minimize(toolbox);
   onMaximized = toolbox._host.once("maximized");
   let settingsButton = toolbox.doc.querySelector("#toolbox-tab-options");
   EventUtils.synthesizeMouseAtCenter(settingsButton, {}, toolbox.doc.defaultView);
   yield onMaximized;
 
   info("Switch to a different host");
-  yield toolbox.switchHost(devtools.Toolbox.HostType.SIDE);
+  yield toolbox.switchHost(Toolbox.HostType.SIDE);
   button = toolbox.doc.querySelector("#toolbox-dock-bottom-minimize");
   ok(!button, "The minimize button doesn't exist in the side host");
 
   Services.prefs.clearUserPref("devtools.toolbox.host");
   yield toolbox.destroy();
   gBrowser.removeCurrentTab();
 });
 
--- a/browser/devtools/framework/test/browser_toolbox_raise.js
+++ b/browser/devtools/framework/test/browser_toolbox_raise.js
@@ -1,21 +1,13 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
-Cu.import("resource://gre/modules/Services.jsm");
-let temp = {}
-Cu.import("resource:///modules/devtools/gDevTools.jsm", temp);
-let DevTools = temp.DevTools;
-
-Cu.import("resource://gre/modules/devtools/Loader.jsm", temp);
-let devtools = temp.devtools;
-
-let Toolbox = devtools.Toolbox;
+let {Toolbox} = require("devtools/framework/toolbox");
 
 let toolbox, target, tab1, tab2;
 
 function test() {
   gBrowser.selectedTab = tab1 = gBrowser.addTab();
   tab2 = gBrowser.addTab();
   target = TargetFactory.forTab(gBrowser.selectedTab);
 
@@ -75,14 +67,14 @@ function onFocus() {
   // Now raise toolbox.
   toolbox.raise();
 }
 
 function cleanup() {
   Services.prefs.setCharPref("devtools.toolbox.host", Toolbox.HostType.BOTTOM);
 
   toolbox.destroy().then(function() {
-    DevTools = Toolbox = toolbox = target = null;
+    toolbox = target = null;
     gBrowser.removeCurrentTab();
     gBrowser.removeCurrentTab();
     finish();
   });
 }
--- a/browser/devtools/framework/test/browser_toolbox_sidebar.js
+++ b/browser/devtools/framework/test/browser_toolbox_sidebar.js
@@ -1,14 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 function test() {
   const Cu = Components.utils;
-  let {ToolSidebar} = devtools.require("devtools/framework/sidebar");
+  let {ToolSidebar} = require("devtools/framework/sidebar");
 
   const toolURL = "data:text/xml;charset=utf8,<?xml version='1.0'?>" +
                   "<?xml-stylesheet href='chrome://browser/skin/devtools/common.css' type='text/css'?>" +
                   "<window xmlns='http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'>" +
                   "<hbox flex='1'><description flex='1'>foo</description><splitter class='devtools-side-splitter'/>" +
                   "<tabbox flex='1' id='sidebar' class='devtools-sidebar-tabs'><tabs/><tabpanels flex='1'/></tabbox>" +
                   "</hbox>" +
                   "</window>";
--- a/browser/devtools/framework/test/browser_toolbox_sidebar_events.js
+++ b/browser/devtools/framework/test/browser_toolbox_sidebar_events.js
@@ -1,14 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 function test() {
   const Cu = Components.utils;
-  const { ToolSidebar } = devtools.require("devtools/framework/sidebar");
+  const { ToolSidebar } = require("devtools/framework/sidebar");
 
   const toolURL = "data:text/xml;charset=utf8,<?xml version='1.0'?>" +
                   "<?xml-stylesheet href='chrome://browser/skin/devtools/common.css' type='text/css'?>" +
                   "<window xmlns='http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'>" +
                   "<hbox flex='1'><description flex='1'>foo</description><splitter class='devtools-side-splitter'/>" +
                   "<tabbox flex='1' id='sidebar' class='devtools-sidebar-tabs'><tabs/><tabpanels flex='1'/></tabbox>" +
                   "</hbox>" +
                   "</window>";
--- a/browser/devtools/framework/test/browser_toolbox_sidebar_existing_tabs.js
+++ b/browser/devtools/framework/test/browser_toolbox_sidebar_existing_tabs.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
 // Test that the sidebar widget auto-registers existing tabs.
 
-const {ToolSidebar} = devtools.require("devtools/framework/sidebar");
+const {ToolSidebar} = require("devtools/framework/sidebar");
 
 const testToolURL = "data:text/xml;charset=utf8,<?xml version='1.0'?>" +
                 "<?xml-stylesheet href='chrome://browser/skin/devtools/common.css' type='text/css'?>" +
                 "<window xmlns='http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'>" +
                 "<hbox flex='1'><description flex='1'>test tool</description>" +
                 "<splitter class='devtools-side-splitter'/>" +
                 "<tabbox flex='1' id='sidebar' class='devtools-sidebar-tabs'>" +
                 "<tabs><tab id='tab1' label='tab 1'></tab><tab id='tab2' label='tab 2'></tab></tabs>" +
--- a/browser/devtools/framework/test/browser_toolbox_sidebar_overflow_menu.js
+++ b/browser/devtools/framework/test/browser_toolbox_sidebar_overflow_menu.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
 // Test that the sidebar widget correctly displays the "all tabs..." button
 // when the tabs overflow.
 
-const {ToolSidebar} = devtools.require("devtools/framework/sidebar");
+const {ToolSidebar} = require("devtools/framework/sidebar");
 
 const testToolDefinition = {
   id: "testTool",
   url: CHROME_URL_ROOT + "browser_toolbox_sidebar_tool.xul",
   label: "Test Tool",
   isTargetSupported: () => true,
   build: (iframeWindow, toolbox) => {
     return {
--- a/browser/devtools/framework/test/browser_toolbox_tabsswitch_shortcuts.js
+++ b/browser/devtools/framework/test/browser_toolbox_tabsswitch_shortcuts.js
@@ -1,12 +1,12 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
-let Toolbox = devtools.Toolbox;
+let {Toolbox} = require("devtools/framework/toolbox");
 
 let toolbox, toolIDs, idIndex, secondTime = false,
     reverse = false, nextKey = null, prevKey = null;
 
 function test() {
   addTab("about:blank").then(function() {
     let target = TargetFactory.forTab(gBrowser.selectedTab);
     idIndex = 0;
--- a/browser/devtools/framework/test/browser_toolbox_toggle.js
+++ b/browser/devtools/framework/test/browser_toolbox_toggle.js
@@ -3,16 +3,18 @@
 
 "use strict";
 
 // Test toggling the toolbox with ACCEL+SHIFT+I / ACCEL+ALT+I and F12 in docked
 // and detached (window) modes.
 
 const URL = "data:text/html;charset=utf-8,Toggling devtools using shortcuts";
 
+let {Toolbox} = require("devtools/framework/toolbox");
+
 add_task(function*() {
   // Test with ACCEL+SHIFT+I / ACCEL+ALT+I (MacOSX) ; modifiers should match :
   // - toolbox-key-toggle in browser/devtools/framework/toolbox-window.xul
   // - key_devToolboxMenuItem in browser/base/content/browser.xul
   info("Test toggle using CTRL+SHIFT+I/CMD+ALT+I");
   yield testToggle("I", {
     accelKey: true,
     shiftKey: !navigator.userAgent.match(/Mac/),
@@ -32,17 +34,17 @@ function* testToggle(key, modifiers) {
   yield testToggleDetachedToolbox(tab, key, modifiers);
 
   yield cleanup();
 }
 
 function* testToggleDockedToolbox (tab, key, modifiers) {
   let toolbox = getToolboxForTab(tab);
 
-  isnot(toolbox.hostType, devtools.Toolbox.HostType.WINDOW,
+  isnot(toolbox.hostType, Toolbox.HostType.WINDOW,
     "Toolbox is docked in the main window");
 
   info("verify docked toolbox is destroyed when using toggle key");
   let onToolboxDestroyed = once(gDevTools, "toolbox-destroyed");
   EventUtils.synthesizeKey(key, modifiers);
   yield onToolboxDestroyed;
   ok(true, "Docked toolbox is destroyed when using a toggle key");
 
@@ -53,18 +55,18 @@ function* testToggleDockedToolbox (tab, 
   ok(true, "Toolbox is created by using when toggle key");
 }
 
 function* testToggleDetachedToolbox (tab, key, modifiers) {
   let toolbox = getToolboxForTab(tab);
 
   info("change the toolbox hostType to WINDOW");
 
-  yield toolbox.switchHost(devtools.Toolbox.HostType.WINDOW);
-  is(toolbox.hostType, devtools.Toolbox.HostType.WINDOW,
+  yield toolbox.switchHost(Toolbox.HostType.WINDOW);
+  is(toolbox.hostType, Toolbox.HostType.WINDOW,
     "Toolbox opened on separate window");
 
   info("Wait for focus on the toolbox window");
   yield new Promise(res => waitForFocus(res, toolbox.frame.contentWindow));
 
   info("Focus main window to put the toolbox window in the background");
 
   let onMainWindowFocus = once(window, "focus");
@@ -90,11 +92,11 @@ function* testToggleDetachedToolbox (tab
 }
 
 function getToolboxForTab(tab) {
   return gDevTools.getToolbox(TargetFactory.forTab(tab));
 }
 
 function* cleanup() {
   Services.prefs.setCharPref("devtools.toolbox.host",
-    devtools.Toolbox.HostType.BOTTOM);
+    Toolbox.HostType.BOTTOM);
   gBrowser.removeCurrentTab();
 }
--- a/browser/devtools/framework/test/browser_toolbox_transport_events.js
+++ b/browser/devtools/framework/test/browser_toolbox_transport_events.js
@@ -1,12 +1,12 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
-const { on, off } = devtools.require("sdk/event/core");
+const { on, off } = require("sdk/event/core");
 const { DebuggerClient } = Cu.import("resource://gre/modules/devtools/dbg-client.jsm", {});
 
 function test() {
   gDevTools.on("toolbox-created", onToolboxCreated);
   on(DebuggerClient, "connect", onDebuggerClientConnect);
 
   addTab("about:blank").then(function() {
     let target = TargetFactory.forTab(gBrowser.selectedTab);
--- a/browser/devtools/framework/test/browser_toolbox_window_reload_target.js
+++ b/browser/devtools/framework/test/browser_toolbox_window_reload_target.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const TEST_URL = "data:text/html;charset=utf-8,"+
                  "<html><head><title>Test reload</title></head>"+
                  "<body><h1>Testing reload from devtools</h1></body></html>";
 
-let Toolbox = devtools.Toolbox;
+let {Toolbox} = require("devtools/framework/toolbox");
 
 let target, toolbox, description, reloadsSent, toolIDs;
 
 function test() {
   addTab(TEST_URL).then(() => {
     target = TargetFactory.forTab(gBrowser.selectedTab);
 
     target.makeRemote().then(() => {
--- a/browser/devtools/framework/test/browser_toolbox_window_shortcuts.js
+++ b/browser/devtools/framework/test/browser_toolbox_window_shortcuts.js
@@ -1,12 +1,12 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
-let Toolbox = devtools.Toolbox;
+let {Toolbox} = require("devtools/framework/toolbox");
 
 let toolbox, toolIDs, idIndex, modifiedPrefs = [];
 
 function test() {
   addTab("about:blank").then(function() {
     toolIDs = [];
     for (let [id, definition] of gDevTools._tools) {
       if (definition.key) {
--- a/browser/devtools/framework/test/browser_toolbox_window_title_changes.js
+++ b/browser/devtools/framework/test/browser_toolbox_window_title_changes.js
@@ -1,16 +1,12 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
-let Toolbox = devtools.Toolbox;
-let temp = {};
-Cu.import("resource://gre/modules/Services.jsm", temp);
-let Services = temp.Services;
-temp = null;
+let {Toolbox} = require("devtools/framework/toolbox");
 
 function test() {
   const URL_1 = "data:text/plain;charset=UTF-8,abcde";
   const URL_2 = "data:text/plain;charset=UTF-8,12345";
 
   const TOOL_ID_1 = "webconsole";
   const TOOL_ID_2 = "jsdebugger";
 
--- a/browser/devtools/framework/test/browser_two_tabs.js
+++ b/browser/devtools/framework/test/browser_two_tabs.js
@@ -4,18 +4,16 @@
 /**
  * Check regression when opening two tabs
  */
 
 let { DebuggerServer } =
   Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
 let { DebuggerClient } =
   Cu.import("resource://gre/modules/devtools/dbg-client.jsm", {});
-let { devtools } =
-  Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 
 const TAB_URL_1 = "data:text/html;charset=utf-8,foo";
 const TAB_URL_2 = "data:text/html;charset=utf-8,bar";
 
 let gClient;
 let gTab1, gTab2;
 let gTabActor1, gTabActor2;
 
--- a/browser/devtools/framework/test/head.js
+++ b/browser/devtools/framework/test/head.js
@@ -122,17 +122,17 @@ function synthesizeKeyElement(el) {
   info(`Synthesizing: key=${key}, mod=${JSON.stringify(mod)}`);
   EventUtils.synthesizeKey(key, mod, el.ownerDocument.defaultView);
 }
 
 /* Check the toolbox host type and prefs to make sure they match the
  * expected values
  * @param {Toolbox}
  * @param {HostType} hostType
- *        One of {SIDE, BOTTOM, WINDOW} from devtools.Toolbox.HostType
+ *        One of {SIDE, BOTTOM, WINDOW} from Toolbox.HostType
  * @param {HostType} Optional previousHostType
  *        The host that will be switched to when calling switchToPreviousHost
  */
 function checkHostType(toolbox, hostType, previousHostType) {
   is(toolbox.hostType, hostType, "host type is " + hostType);
 
   let pref = Services.prefs.getCharPref("devtools.toolbox.host");
   is(pref, hostType, "host pref is " + hostType);
--- a/browser/devtools/framework/test/shared-head.js
+++ b/browser/devtools/framework/test/shared-head.js
@@ -3,19 +3,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // This shared-head.js file is used for multiple directories in devtools.
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 const {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
 const {gDevTools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
 const {console} = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
 const {ScratchpadManager} = Cu.import("resource:///modules/devtools/scratchpad-manager.jsm", {});
-const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
-const {require} = devtools;
-const {TargetFactory} = devtools;
+const {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+const {TargetFactory} = require("devtools/framework/target");
 const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
 const promise = require("promise");
 
 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/");
 
 // All test are asynchronous
--- a/browser/devtools/framework/toolbox-process-window.js
+++ b/browser/devtools/framework/toolbox-process-window.js
@@ -1,17 +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 { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 let { gDevTools } = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
-let { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+let { require } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+let { TargetFactory } = require("devtools/framework/target");
+let { Toolbox } = require("devtools/framework/toolbox");
 let { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
 let { DebuggerClient } =
   Cu.import("resource://gre/modules/devtools/dbg-client.jsm", {});
 let { ViewHelpers } =
   Cu.import("resource:///modules/devtools/ViewHelpers.jsm", {});
 let { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
 
 /**
@@ -75,34 +77,34 @@ function onCloseCommand(event) {
 
 function openToolbox({ form, chrome, isTabActor }) {
   let options = {
     form: form,
     client: gClient,
     chrome: chrome,
     isTabActor: isTabActor
   };
-  devtools.TargetFactory.forRemoteTab(options).then(target => {
+  TargetFactory.forRemoteTab(options).then(target => {
     let frame = document.getElementById("toolbox-iframe");
     let selectedTool = "jsdebugger";
 
     try {
       // Remember the last panel that was used inside of this profile.
       selectedTool = Services.prefs.getCharPref("devtools.toolbox.selectedTool");
     } catch(e) {}
 
     try {
       // But if we are testing, then it should always open the debugger panel.
       selectedTool = Services.prefs.getCharPref("devtools.browsertoolbox.panel");
     } catch(e) {}
 
     let options = { customIframe: frame };
     gDevTools.showToolbox(target,
                           selectedTool,
-                          devtools.Toolbox.HostType.CUSTOM,
+                          Toolbox.HostType.CUSTOM,
                           options)
              .then(onNewToolbox);
   });
 }
 
 function onNewToolbox(toolbox) {
    gToolbox = toolbox;
    bindToolboxHandlers();
--- a/browser/devtools/inspector/test/head.js
+++ b/browser/devtools/inspector/test/head.js
@@ -19,17 +19,18 @@ const Cc = Components.classes;
 const TEST_URL_ROOT = "http://example.com/browser/browser/devtools/inspector/test/";
 const ROOT_TEST_DIR = getRootDirectory(gTestPath);
 const FRAME_SCRIPT_URL = ROOT_TEST_DIR + "doc_frame_script.js";
 const { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
 
 // All test are asynchronous
 waitForExplicitFinish();
 
-let {TargetFactory, require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
+let {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+let {TargetFactory} = require("devtools/framework/target");
 let {console} = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
 let DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
 
 // Import the GCLI test helper
 let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
 Services.scriptloader.loadSubScript(testDir + "../../../commandline/test/helpers.js", this);
 
 DevToolsUtils.testing = true;
--- a/browser/devtools/layoutview/test/head.js
+++ b/browser/devtools/layoutview/test/head.js
@@ -1,21 +1,21 @@
 /* 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";
 
 const Cu = Components.utils;
 let {gDevTools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
-let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
-let TargetFactory = devtools.TargetFactory;
+let {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+let {TargetFactory} = require("devtools/framework/target");
 let {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
 let {console} = Components.utils.import("resource://gre/modules/devtools/Console.jsm", {});
-let DevToolsUtils = devtools.require("devtools/toolkit/DevToolsUtils");
+let DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
 
 // All test are asynchronous
 waitForExplicitFinish();
 
 const TEST_URL_ROOT = "http://example.com/browser/browser/devtools/layoutview/test/";
 
 // Uncomment this pref to dump all devtools emitted events to the console.
 // Services.prefs.setBoolPref("devtools.dump.emit", true);
--- a/browser/devtools/layoutview/view.js
+++ b/browser/devtools/layoutview/view.js
@@ -5,21 +5,20 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 /* globals ViewHelpers, window, document */
 
 "use strict";
 
 const {utils: Cu, interfaces: Ci, classes: Cc} = Components;
 
 Cu.import("resource://gre/modules/Task.jsm");
-Cu.import("resource://gre/modules/devtools/Loader.jsm");
+const {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 Cu.import("resource://gre/modules/devtools/Console.jsm");
 Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
 
-const {require} = devtools;
 const {InplaceEditor, editableItem} = require("devtools/shared/inplace-editor");
 const {ReflowFront} = require("devtools/server/actors/layout");
 
 const STRINGS_URI = "chrome://browser/locale/devtools/shared.properties";
 const SHARED_L10N = new ViewHelpers.L10N(STRINGS_URI);
 const NUMERIC = /^-?[\d\.]+$/;
 const LONG_TEXT_ROTATE_LIMIT = 3;
 
--- a/browser/devtools/markupview/test/actor_events_form.js
+++ b/browser/devtools/markupview/test/actor_events_form.js
@@ -7,18 +7,17 @@
 // on NodeActor. Custom form property is set when 'form' event is sent
 // by NodeActor actor (see 'onNodeActorForm' method).
 
 const Events = require("sdk/event/core");
 const {ActorClass, Actor, FrontClass, Front, method} =
   require("devtools/server/protocol");
 
 const {Cu} = require("chrome");
-const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
-const {NodeActor} = devtools.require("devtools/server/actors/inspector");
+const {NodeActor} = require("devtools/server/actors/inspector");
 
 const EventsFormActor = ActorClass({
   typeName: "eventsFormActor",
 
   initialize: function() {
     Actor.prototype.initialize.apply(this, arguments);
   },
 
--- a/browser/devtools/markupview/test/browser_markupview_events_form.js
+++ b/browser/devtools/markupview/test/browser_markupview_events_form.js
@@ -7,18 +7,18 @@
 // Testing the feature whereby custom registered actors can listen to
 // 'form' events sent by the NodeActor to hook custom data to it.
 // The test registers one backend actor providing custom form data
 // and checks that the value is properly sent to the client (NodeFront).
 
 const TEST_PAGE_URL = TEST_URL_ROOT + "doc_markup_events_form.html";
 const TEST_ACTOR_URL = CHROME_BASE + "actor_events_form.js";
 
-let {ActorRegistryFront} = devtools.require("devtools/server/actors/actor-registry");
-let {EventsFormFront} = devtools.require(TEST_ACTOR_URL);
+let {ActorRegistryFront} = require("devtools/server/actors/actor-registry");
+let {EventsFormFront} = require(TEST_ACTOR_URL);
 
 add_task(function*() {
   info("Opening the Toolbox");
   let {tab} = yield addTab(TEST_PAGE_URL);
   let {toolbox} = yield openToolbox("webconsole");
 
   info("Registering test actor");
   let {registrar, front} = yield registerTestActor(toolbox);
--- a/browser/devtools/markupview/test/head.js
+++ b/browser/devtools/markupview/test/head.js
@@ -1,22 +1,22 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const Cu = Components.utils;
-let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
-let TargetFactory = devtools.TargetFactory;
+let {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+let {TargetFactory} = require("devtools/framework/target");
 let {console} = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
-let promise = devtools.require("resource://gre/modules/Promise.jsm").Promise;
-let {getInplaceEditorForSpan: inplaceEditor} = devtools.require("devtools/shared/inplace-editor");
-let clipboard = devtools.require("sdk/clipboard");
-let {setTimeout, clearTimeout} = devtools.require("sdk/timers");
-let {promiseInvoke} = devtools.require("devtools/async-utils");
-let DevToolsUtils = devtools.require("devtools/toolkit/DevToolsUtils");
+let promise = require("resource://gre/modules/Promise.jsm").Promise;
+let {getInplaceEditorForSpan: inplaceEditor} = require("devtools/shared/inplace-editor");
+let clipboard = require("sdk/clipboard");
+let {setTimeout, clearTimeout} = require("sdk/timers");
+let {promiseInvoke} = require("devtools/async-utils");
+let DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
 
 // All test are asynchronous
 waitForExplicitFinish();
 
 // If a test times out we want to see the complete log and not just the last few
 // lines.
 SimpleTest.requestCompleteLog();
 
--- a/browser/devtools/netmonitor/har/har-automation.js
+++ b/browser/devtools/netmonitor/har/har-automation.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, Ci, Cc } = require("chrome");
 const { Class } = require("sdk/core/heritage");
-const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 const { defer, resolve } = require("sdk/core/promise");
 const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
 
 Cu.import("resource://gre/modules/Task.jsm");
 
 loader.lazyRequireGetter(this, "HarCollector", "devtools/netmonitor/har/har-collector", true);
 loader.lazyRequireGetter(this, "HarExporter", "devtools/netmonitor/har/har-exporter", true);
 loader.lazyRequireGetter(this, "HarUtils", "devtools/netmonitor/har/har-utils", true);
--- a/browser/devtools/netmonitor/har/har-builder.js
+++ b/browser/devtools/netmonitor/har/har-builder.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 { Cu, Ci, Cc } = require("chrome");
 const { defer, all, resolve } = require("sdk/core/promise");
 const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
-const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 
 loader.lazyImporter(this, "ViewHelpers", "resource:///modules/devtools/ViewHelpers.jsm");
 loader.lazyRequireGetter(this, "NetworkHelper", "devtools/toolkit/webconsole/network-helper");
 
 loader.lazyGetter(this, "appInfo", () => {
   return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo);
 });
 
@@ -162,17 +161,17 @@ HarBuilder.prototype = {
 
   buildRequest: function(file) {
     let request = {
       bodySize: 0
     };
 
     request.method = file.method;
     request.url = file.url;
-    request.httpVersion = file.httpVersion;
+    request.httpVersion = file.httpVersion || "";
 
     request.headers = this.buildHeaders(file.requestHeaders);
     request.cookies = this.buildCookies(file.requestCookies);
 
     request.queryString = NetworkHelper.parseQueryString(
       NetworkHelper.nsIURL(file.url).query) || [];
 
     request.postData = this.buildPostData(file);
@@ -280,48 +279,59 @@ HarBuilder.prototype = {
       status: 0
     };
 
     // Arbitrary value if it's aborted to make sure status has a number
     if (file.status) {
       response.status = parseInt(file.status);
     }
 
+    let responseHeaders = file.responseHeaders;
+
     response.statusText = file.statusText || "";
-    response.httpVersion = file.httpVersion;
+    response.httpVersion = file.httpVersion || "";
 
-    response.headers = this.buildHeaders(file.responseHeaders);
+    response.headers = this.buildHeaders(responseHeaders);
     response.cookies = this.buildCookies(file.responseCookies);
+    response.content = this.buildContent(file);
 
-    response.content = this.buildContent(file);
-    response.redirectURL = findValue(file.responseHeaders.headers, "Location");
-    response.headersSize = file.responseHeaders.headersSize;
+    let headers = responseHeaders ? responseHeaders.headers : null;
+    let headersSize = responseHeaders ? responseHeaders.headersSize : -1;
+
+    response.redirectURL = findValue(headers, "Location");
+    response.headersSize = headersSize;
     response.bodySize = file.transferredSize || -1;
 
     return response;
   },
 
   buildContent: function(file) {
     let content = {
       mimeType: file.mimeType,
       size: -1
     };
 
-    if (file.responseContent && file.responseContent.content) {
-      content.size = file.responseContent.content.size;
+    let responseContent = file.responseContent;
+    if (responseContent && responseContent.content) {
+      content.size = responseContent.content.size;
     }
 
-    if (!this._options.includeResponseBodies ||
-        file.responseContent.contentDiscarded) {
+    let includeBodies = this._options.includeResponseBodies;
+    let contentDiscarded = responseContent ?
+      responseContent.contentDiscarded : false;
+
+    // The comment is appended only if the response content
+    // is explicitly discarded.
+    if (!includeBodies || contentDiscarded) {
       content.comment = L10N.getStr("har.responseBodyNotIncluded");
       return content;
     }
 
-    if (file.responseContent) {
-      let text = file.responseContent.content.text;
+    if (responseContent) {
+      let text = responseContent.content.text;
       let promise = this.fetchData(text).then(value => {
         content.text = value;
       });
     }
 
     return content;
   },
 
@@ -410,16 +420,20 @@ function isURLEncodedFile(file, text) {
   return false;
 }
 
 /**
  * Find specified value within an array of name-value pairs
  * (used for headers, cookies and cache entries)
  */
 function findValue(arr, name) {
+  if (!arr) {
+    return "";
+  }
+
   name = name.toLowerCase();
   let result = arr.find(entry => entry.name.toLowerCase() == name);
   return result ? result.value : "";
 }
 
 /**
  * Generate HAR representation of a date.
  * (YYYY-MM-DDThh:mm:ss.sTZD, e.g. 2009-07-24T19:20:30.45+01:00)
--- a/browser/devtools/netmonitor/har/toolbox-overlay.js
+++ b/browser/devtools/netmonitor/har/toolbox-overlay.js
@@ -1,15 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const { Cu, Ci } = require("chrome");
-const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 
 loader.lazyRequireGetter(this, "HarAutomation", "devtools/netmonitor/har/har-automation", true);
 
 // Map of all created overlays. There is always one instance of
 // an overlay per Toolbox instance (i.e. one per browser tab).
 const overlays = new WeakMap();
 
 /**
--- a/browser/devtools/netmonitor/netmonitor-controller.js
+++ b/browser/devtools/netmonitor/netmonitor-controller.js
@@ -112,17 +112,17 @@ const ACTIVITY_TYPE = {
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource:///modules/devtools/SideMenuWidget.jsm");
 Cu.import("resource:///modules/devtools/VariablesView.jsm");
 Cu.import("resource:///modules/devtools/VariablesViewController.jsm");
 Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
 
-const require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
+const {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 const promise = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;
 const EventEmitter = require("devtools/toolkit/event-emitter");
 const Editor = require("devtools/sourceeditor/editor");
 const {Tooltip} = require("devtools/shared/widgets/Tooltip");
 const {ToolSidebar} = require("devtools/framework/sidebar");
 const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Chart",
--- a/browser/devtools/netmonitor/netmonitor-view.js
+++ b/browser/devtools/netmonitor/netmonitor-view.js
@@ -1,23 +1,21 @@
 /* -*- 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/. */
 "use strict";
 
-const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
-
 XPCOMUtils.defineLazyGetter(this, "HarExporter", function() {
-  return devtools.require("devtools/netmonitor/har/har-exporter.js").HarExporter;
+  return require("devtools/netmonitor/har/har-exporter.js").HarExporter;
 });
 
 XPCOMUtils.defineLazyGetter(this, "NetworkHelper", function() {
-  return devtools.require("devtools/toolkit/webconsole/network-helper");
+  return require("devtools/toolkit/webconsole/network-helper");
 });
 
 const HTML_NS = "http://www.w3.org/1999/xhtml";
 const EPSILON = 0.001;
 const SOURCE_SYNTAX_HIGHLIGHT_MAX_FILE_SIZE = 102400; // 100 KB in bytes
 const RESIZE_REFRESH_RATE = 50; // ms
 const REQUESTS_REFRESH_RATE = 50; // ms
 const REQUESTS_HEADERS_SAFE_BOUNDS = 30; // px
--- a/browser/devtools/netmonitor/test/head.js
+++ b/browser/devtools/netmonitor/test/head.js
@@ -3,22 +3,22 @@
 "use strict";
 
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 let { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
 let { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
 let { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
 let { gDevTools } = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
-let { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+let { require } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 let { CurlUtils } = Cu.import("resource:///modules/devtools/Curl.jsm", {});
-let NetworkHelper = devtools.require("devtools/toolkit/webconsole/network-helper");
-let DevToolsUtils = devtools.require("devtools/toolkit/DevToolsUtils");
-let TargetFactory = devtools.TargetFactory;
-let Toolbox = devtools.Toolbox;
+let NetworkHelper = require("devtools/toolkit/webconsole/network-helper");
+let DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
+let { TargetFactory } = require("devtools/framework/target");
+let { Toolbox } = require("devtools/framework/toolbox");
 
 const EXAMPLE_URL = "http://example.com/browser/browser/devtools/netmonitor/test/";
 
 const API_CALLS_URL = EXAMPLE_URL + "html_api-calls-test-page.html";
 const SIMPLE_URL = EXAMPLE_URL + "html_simple-test-page.html";
 const NAVIGATE_URL = EXAMPLE_URL + "html_navigate-test-page.html";
 const CONTENT_TYPE_URL = EXAMPLE_URL + "html_content-type-test-page.html";
 const CONTENT_TYPE_WITHOUT_CACHE_URL = EXAMPLE_URL + "html_content-type-without-cache-test-page.html";
--- a/browser/devtools/performance/performance-controller.js
+++ b/browser/devtools/performance/performance-controller.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 { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
-const { devtools: loader } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
-const require = loader.require;
+const { devtools: loader, require } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 
 const { Task } = require("resource://gre/modules/Task.jsm");
 const { Heritage, ViewHelpers, WidgetMethods } = require("resource:///modules/devtools/ViewHelpers.jsm");
 
 loader.lazyRequireGetter(this, "Services");
 loader.lazyRequireGetter(this, "promise");
 loader.lazyRequireGetter(this, "EventEmitter",
   "devtools/toolkit/event-emitter");
--- a/browser/devtools/performance/test/browser_perf-allocations-to-samples.js
+++ b/browser/devtools/performance/test/browser_perf-allocations-to-samples.js
@@ -3,17 +3,17 @@
 
 /**
  * Tests if allocations data received from the memory actor is properly
  * converted to something that follows the same structure as the samples data
  * received from the profiler.
  */
 
 function test() {
-  let RecordingUtils = devtools.require("devtools/performance/recording-utils");
+  let RecordingUtils = require("devtools/performance/recording-utils");
 
   let output = RecordingUtils.getProfileThreadFromAllocations(TEST_DATA);
   is(output.toSource(), EXPECTED_OUTPUT.toSource(), "The output is correct.");
 
   finish();
 }
 
 let TEST_DATA = {
--- a/browser/devtools/performance/test/browser_perf-console-record-01.js
+++ b/browser/devtools/performance/test/browser_perf-console-record-01.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests if the profiler is populated by console recordings that have finished
  * before it was opened.
  */
 
-let { getPerformanceFront } = devtools.require("devtools/performance/front");
+let { getPerformanceFront } = require("devtools/performance/front");
 let WAIT_TIME = 10;
 
 function* spawnTest() {
   let profilerConnected = waitForProfilerConnection();
   let { target, toolbox, console } = yield initConsole(SIMPLE_URL);
   yield profilerConnected;
   let front = getPerformanceFront(target);
 
--- a/browser/devtools/performance/test/browser_perf-console-record-02.js
+++ b/browser/devtools/performance/test/browser_perf-console-record-02.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests if the profiler is populated by in-progress console recordings
  * when it is opened.
  */
 
-let { getPerformanceFront } = devtools.require("devtools/performance/front");
+let { getPerformanceFront } = require("devtools/performance/front");
 let WAIT_TIME = 10;
 
 function* spawnTest() {
   let profilerConnected = waitForProfilerConnection();
   let { target, toolbox, console } = yield initConsole(SIMPLE_URL);
   yield profilerConnected;
   let front = getPerformanceFront(target);
 
--- a/browser/devtools/performance/test/browser_perf-console-record-03.js
+++ b/browser/devtools/performance/test/browser_perf-console-record-03.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests if the profiler is populated by in-progress console recordings, and
  * also console recordings that have finished before it was opened.
  */
 
-let { getPerformanceFront } = devtools.require("devtools/performance/front");
+let { getPerformanceFront } = require("devtools/performance/front");
 let WAIT_TIME = 10;
 
 function* spawnTest() {
   let profilerConnected = waitForProfilerConnection();
   let { target, toolbox, console } = yield initConsole(SIMPLE_URL);
   yield profilerConnected;
   let front = getPerformanceFront(target);
 
--- a/browser/devtools/performance/test/browser_perf-events-calltree.js
+++ b/browser/devtools/performance/test/browser_perf-events-calltree.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that the call tree up/down events work for js calltree and memory calltree.
  */
-const { ThreadNode } = devtools.require("devtools/performance/tree-model");
-const RecordingUtils = devtools.require("devtools/performance/recording-utils")
+const { ThreadNode } = require("devtools/performance/tree-model");
+const RecordingUtils = require("devtools/performance/recording-utils")
 
 function* spawnTest() {
   let focus = 0;
   let focusEvent = () => focus++;
 
   Services.prefs.setBoolPref(MEMORY_PREF, true);
 
   let { panel } = yield initPerformance(SIMPLE_URL);
--- a/browser/devtools/performance/test/browser_perf-highlighted.js
+++ b/browser/devtools/performance/test/browser_perf-highlighted.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that the toolbox tab for performance is highlighted when recording,
  * whether already loaded, or via console.profile with an unloaded performance tools.
  */
 
-let { getPerformanceFront } = devtools.require("devtools/performance/front");
+let { getPerformanceFront } = require("devtools/performance/front");
 
 function* spawnTest() {
   let profilerConnected = waitForProfilerConnection();
   let { target, toolbox, console } = yield initConsole(SIMPLE_URL);
   yield profilerConnected;
   let front = getPerformanceFront(target);
   let tab = toolbox.doc.getElementById("toolbox-tab-performance");
 
--- a/browser/devtools/performance/test/browser_perf-jit-view-01.js
+++ b/browser/devtools/performance/test/browser_perf-jit-view-01.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that the JIT Optimizations view renders optimization data
  * if on, and displays selected frames on focus.
  */
 
-const RecordingUtils = devtools.require("devtools/performance/recording-utils");
+const RecordingUtils = require("devtools/performance/recording-utils");
 
 Services.prefs.setBoolPref(INVERT_PREF, false);
 
 function* spawnTest() {
   let { panel } = yield initPerformance(SIMPLE_URL);
   let { EVENTS, $, $$, window, PerformanceController } = panel.panelWin;
   let { OverviewView, DetailsView, JITOptimizationsView, JsCallTreeView, RecordingsView } = panel.panelWin;
 
--- a/browser/devtools/performance/test/browser_perf-jit-view-02.js
+++ b/browser/devtools/performance/test/browser_perf-jit-view-02.js
@@ -1,18 +1,18 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that the JIT Optimizations view does not display information
  * for meta nodes when viewing "content only".
  */
 
-const { CATEGORY_MASK } = devtools.require("devtools/performance/global");
-const RecordingUtils = devtools.require("devtools/performance/recording-utils");
+const { CATEGORY_MASK } = require("devtools/performance/global");
+const RecordingUtils = require("devtools/performance/recording-utils");
 
 Services.prefs.setBoolPref(INVERT_PREF, false);
 Services.prefs.setBoolPref(PLATFORM_DATA_PREF, false);
 
 function* spawnTest() {
   let { panel } = yield initPerformance(SIMPLE_URL);
   let { EVENTS, $, $$, window, PerformanceController } = panel.panelWin;
   let { OverviewView, DetailsView, JITOptimizationsView, JsCallTreeView, RecordingsView } = panel.panelWin;
--- a/browser/devtools/performance/test/browser_perf-theme-toggle-01.js
+++ b/browser/devtools/performance/test/browser_perf-theme-toggle-01.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests if the markers and memory overviews render with the correct
  * theme on load, and rerenders when changed.
  */
 
-const { setTheme } = devtools.require("devtools/shared/theme");
+const { setTheme } = require("devtools/shared/theme");
 
 const LIGHT_BG = "#fcfcfc";
 const DARK_BG = "#14171a";
 
 setTheme("dark");
 Services.prefs.setBoolPref(MEMORY_PREF, false);
 
 function* spawnTest() {
--- a/browser/devtools/performance/test/browser_perf_recordings-io-04.js
+++ b/browser/devtools/performance/test/browser_perf_recordings-io-04.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests if the performance tool can import profiler data from the
  * original profiler tool (Performance Recording v1, and Profiler data v2) and the correct views and graphs are loaded.
  */
-let RecordingUtils = devtools.require("devtools/performance/recording-utils");
+let RecordingUtils = require("devtools/performance/recording-utils");
 
 let TICKS_DATA = (function () {
   let ticks = [];
   for (let i = 0; i < 100; i++) {
     ticks.push(i * 10);
   }
   return ticks;
 })();
--- a/browser/devtools/performance/test/browser_perf_recordings-io-06.js
+++ b/browser/devtools/performance/test/browser_perf_recordings-io-06.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests if the performance tool can import profiler data when Profiler is v2
  * and requires deflating, and has an extra thread that's a string. Not sure
  * what causes this.
  */
-let RecordingUtils = devtools.require("devtools/performance/recording-utils");
+let RecordingUtils = require("devtools/performance/recording-utils");
 
 let STRINGED_THREAD = (function () {
   let thread = {};
 
   thread.libs = [{
     start: 123,
     end: 456,
     offset: 0,
--- a/browser/devtools/performance/test/browser_profiler_tree-view-01.js
+++ b/browser/devtools/performance/test/browser_profiler_tree-view-01.js
@@ -2,18 +2,18 @@
    http://foo/bar/creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests if the profiler's tree view implementation works properly and
  * creates the correct column structure.
  */
 
 function test() {
-  let { ThreadNode } = devtools.require("devtools/performance/tree-model");
-  let { CallView } = devtools.require("devtools/performance/tree-view");
+  let { ThreadNode } = require("devtools/performance/tree-model");
+  let { CallView } = require("devtools/performance/tree-view");
 
   let threadNode = new ThreadNode(gThread, { startTime: 0, endTime: 20 });
   // Don't display the synthesized (root) and the real (root) node twice.
   threadNode.calls = threadNode.calls[0].calls;
   let treeRoot = new CallView({ frame: threadNode });
 
   let container = document.createElement("vbox");
   treeRoot.autoExpandDepth = 0;
--- a/browser/devtools/performance/test/browser_profiler_tree-view-02.js
+++ b/browser/devtools/performance/test/browser_profiler_tree-view-02.js
@@ -1,21 +1,21 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests if the profiler's tree view implementation works properly and
  * creates the correct column structure after expanding some of the nodes.
  */
 
-let { CATEGORY_MASK } = devtools.require("devtools/performance/global");
+let { CATEGORY_MASK } = require("devtools/performance/global");
 
 function test() {
-  let { ThreadNode } = devtools.require("devtools/performance/tree-model");
-  let { CallView } = devtools.require("devtools/performance/tree-view");
+  let { ThreadNode } = require("devtools/performance/tree-model");
+  let { CallView } = require("devtools/performance/tree-view");
 
   let threadNode = new ThreadNode(gThread, { startTime: 0, endTime: 20 });
   // Don't display the synthesized (root) and the real (root) node twice.
   threadNode.calls = threadNode.calls[0].calls;
   let treeRoot = new CallView({ frame: threadNode });
 
   let container = document.createElement("vbox");
   treeRoot.autoExpandDepth = 0;
--- a/browser/devtools/performance/test/browser_profiler_tree-view-03.js
+++ b/browser/devtools/performance/test/browser_profiler_tree-view-03.js
@@ -2,18 +2,18 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests if the profiler's tree view implementation works properly and
  * creates the correct column structure and can auto-expand all nodes.
  */
 
 function test() {
-  let { ThreadNode } = devtools.require("devtools/performance/tree-model");
-  let { CallView } = devtools.require("devtools/performance/tree-view");
+  let { ThreadNode } = require("devtools/performance/tree-model");
+  let { CallView } = require("devtools/performance/tree-view");
 
   let threadNode = new ThreadNode(gThread, { startTime: 0, endTime: 20 });
   // Don't display the synthesized (root) and the real (root) node twice.
   threadNode.calls = threadNode.calls[0].calls;
   let treeRoot = new CallView({ frame: threadNode });
 
   let container = document.createElement("vbox");
   treeRoot.attachTo(container);
--- a/browser/devtools/performance/test/browser_profiler_tree-view-04.js
+++ b/browser/devtools/performance/test/browser_profiler_tree-view-04.js
@@ -1,21 +1,21 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests if the profiler's tree view implementation works properly and
  * creates the correct DOM nodes in the correct order.
  */
 
-let { CATEGORY_MASK } = devtools.require("devtools/performance/global");
+let { CATEGORY_MASK } = require("devtools/performance/global");
 
 function test() {
-  let { ThreadNode } = devtools.require("devtools/performance/tree-model");
-  let { CallView } = devtools.require("devtools/performance/tree-view");
+  let { ThreadNode } = require("devtools/performance/tree-model");
+  let { CallView } = require("devtools/performance/tree-view");
 
   let threadNode = new ThreadNode(gThread, { startTime: 0, endTime: 20 });
   // Don't display the synthesized (root) and the real (root) node twice.
   threadNode.calls = threadNode.calls[0].calls;
   let treeRoot = new CallView({ frame: threadNode });
 
   let container = document.createElement("vbox");
   treeRoot.attachTo(container);
--- a/browser/devtools/performance/test/browser_profiler_tree-view-05.js
+++ b/browser/devtools/performance/test/browser_profiler_tree-view-05.js
@@ -2,18 +2,18 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests if the profiler's tree view implementation works properly and
  * can toggle categories hidden or visible.
  */
 
 function test() {
-  let { ThreadNode } = devtools.require("devtools/performance/tree-model");
-  let { CallView } = devtools.require("devtools/performance/tree-view");
+  let { ThreadNode } = require("devtools/performance/tree-model");
+  let { CallView } = require("devtools/performance/tree-view");
 
   let threadNode = new ThreadNode(gThread, { startTime: 0, endTime: 20 });
   // Don't display the synthesized (root) and the real (root) node twice.
   threadNode.calls = threadNode.calls[0].calls;
   let treeRoot = new CallView({ frame: threadNode });
 
   let container = document.createElement("vbox");
   treeRoot.attachTo(container);
--- a/browser/devtools/performance/test/browser_profiler_tree-view-06.js
+++ b/browser/devtools/performance/test/browser_profiler_tree-view-06.js
@@ -2,18 +2,18 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests if the profiler's tree view implementation works properly and
  * correctly emits events when certain DOM nodes are clicked.
  */
 
 function* spawnTest() {
-  let { ThreadNode } = devtools.require("devtools/performance/tree-model");
-  let { CallView } = devtools.require("devtools/performance/tree-view");
+  let { ThreadNode } = require("devtools/performance/tree-model");
+  let { CallView } = require("devtools/performance/tree-view");
 
   let threadNode = new ThreadNode(gThread, { startTime: 0, endTime: 20 });
   // Don't display the synthesized (root) and the real (root) node twice.
   threadNode.calls = threadNode.calls[0].calls;
   let treeRoot = new CallView({ frame: threadNode });
 
   let container = document.createElement("vbox");
   treeRoot.attachTo(container);
--- a/browser/devtools/performance/test/browser_profiler_tree-view-07.js
+++ b/browser/devtools/performance/test/browser_profiler_tree-view-07.js
@@ -2,18 +2,18 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests if the profiler's tree view implementation works properly and
  * has the correct 'root', 'parent', 'level' etc. accessors on child nodes.
  */
 
 function* spawnTest() {
-  let { ThreadNode } = devtools.require("devtools/performance/tree-model");
-  let { CallView } = devtools.require("devtools/performance/tree-view");
+  let { ThreadNode } = require("devtools/performance/tree-model");
+  let { CallView } = require("devtools/performance/tree-view");
 
   let threadNode = new ThreadNode(gThread, { startTime: 0, endTime: 20 });
   // Don't display the synthesized (root) and the real (root) node twice.
   threadNode.calls = threadNode.calls[0].calls;
   let treeRoot = new CallView({ frame: threadNode });
 
   let container = document.createElement("vbox");
   container.id = "call-tree-container";
--- a/browser/devtools/performance/test/browser_profiler_tree-view-08.js
+++ b/browser/devtools/performance/test/browser_profiler_tree-view-08.js
@@ -1,21 +1,21 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that the profiler's tree view renders generalized platform data
  * when `contentOnly` is on correctly.
  */
 
-let { CATEGORY_MASK } = devtools.require("devtools/performance/global");
+let { CATEGORY_MASK } = require("devtools/performance/global");
 
 function test() {
-  let { ThreadNode } = devtools.require("devtools/performance/tree-model");
-  let { CallView } = devtools.require("devtools/performance/tree-view");
+  let { ThreadNode } = require("devtools/performance/tree-model");
+  let { CallView } = require("devtools/performance/tree-view");
 
   /*
    * should have a tree like:
    * root
    *   - A
    *     - B
    *       - C
    *       - D
--- a/browser/devtools/performance/test/browser_profiler_tree-view-09.js
+++ b/browser/devtools/performance/test/browser_profiler_tree-view-09.js
@@ -1,21 +1,21 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that the profiler's tree view sorts inverted call trees by
  * "self cost" and not "total cost".
  */
 
-let { CATEGORY_MASK } = devtools.require("devtools/performance/global");
+let { CATEGORY_MASK } = require("devtools/performance/global");
 
 let test = Task.async(function*() {
-  let { ThreadNode } = devtools.require("devtools/performance/tree-model");
-  let { CallView } = devtools.require("devtools/performance/tree-view");
+  let { ThreadNode } = require("devtools/performance/tree-model");
+  let { CallView } = require("devtools/performance/tree-view");
 
   let threadNode = new ThreadNode(gSamples, { invertTree: true, startTime: 0, endTime: 10 });
   let treeRoot = new CallView({ frame: threadNode, inverted: true, autoExpandDepth: 1 });
 
   let container = document.createElement("vbox");
   treeRoot.attachTo(container);
 
   is(treeRoot.getChild(0).frame.location, "B",
--- a/browser/devtools/performance/test/browser_profiler_tree-view-10.js
+++ b/browser/devtools/performance/test/browser_profiler_tree-view-10.js
@@ -2,18 +2,18 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests if the profiler's tree view, when inverted, displays the self and
  * total costs correctly.
  */
 
 function test() {
-  let { ThreadNode } = devtools.require("devtools/performance/tree-model");
-  let { CallView } = devtools.require("devtools/performance/tree-view");
+  let { ThreadNode } = require("devtools/performance/tree-model");
+  let { CallView } = require("devtools/performance/tree-view");
 
   let threadNode = new ThreadNode(gThread, { invertTree: true, startTime: 0, endTime: 50 });
   let treeRoot = new CallView({ frame: threadNode, inverted: true, hidden: true });
 
   let container = document.createElement("vbox");
   treeRoot.attachTo(container);
 
   // Add 1 to each index to skip the hidden root node
--- a/browser/devtools/performance/test/browser_profiler_tree-view-11.js
+++ b/browser/devtools/performance/test/browser_profiler_tree-view-11.js
@@ -1,18 +1,18 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that if a recording set `withJITOptimizations` on, then an
  * icon is next to the frame with optimizations
  */
 
-const RecordingUtils = devtools.require("devtools/performance/recording-utils");
-const { CATEGORY_MASK } = devtools.require("devtools/performance/global");
+const RecordingUtils = require("devtools/performance/recording-utils");
+const { CATEGORY_MASK } = require("devtools/performance/global");
 
 function* spawnTest() {
   let { panel } = yield initPerformance(SIMPLE_URL);
   let { EVENTS, $, $$, window, PerformanceController } = panel.panelWin;
   let { OverviewView, DetailsView, JITOptimizationsView, JsCallTreeView, RecordingsView } = panel.panelWin;
 
   let profilerData = { threads: [gThread] };
 
--- a/browser/devtools/performance/test/browser_timeline-filters-01.js
+++ b/browser/devtools/performance/test/browser_timeline-filters-01.js
@@ -5,17 +5,17 @@
  * Tests markers filtering mechanism.
  */
 
 const EPSILON = 0.00000001;
 
 function* spawnTest() {
   let { panel } = yield initPerformance(SIMPLE_URL);
   let { $, $$, EVENTS, PerformanceController, OverviewView, WaterfallView } = panel.panelWin;
-  let { TimelineGraph } = devtools.require("devtools/performance/graphs");
+  let { TimelineGraph } = require("devtools/performance/graphs");
   let { rowHeight: MARKERS_GRAPH_ROW_HEIGHT } = TimelineGraph.prototype;
 
   yield startRecording(panel);
   ok(true, "Recording has started.");
 
   yield waitUntil(() => {
     // Wait until we get 3 different markers.
     let markers = PerformanceController.getCurrentRecording().getMarkers();
--- a/browser/devtools/performance/test/browser_timeline-waterfall-background.js
+++ b/browser/devtools/performance/test/browser_timeline-waterfall-background.js
@@ -4,17 +4,17 @@
 /**
  * Tests if the waterfall background is a 1px high canvas stretching across
  * the container bounds.
  */
 
 function* spawnTest() {
   let { target, panel } = yield initPerformance(SIMPLE_URL);
   let { $, EVENTS, PerformanceController, OverviewView, DetailsView, WaterfallView } = panel.panelWin;
-  let { WATERFALL_MARKER_SIDEBAR_SAFE_BOUNDS } = devtools.require("devtools/performance/marker-view");
+  let { WATERFALL_MARKER_SIDEBAR_SAFE_BOUNDS } = require("devtools/performance/marker-view");
 
   yield startRecording(panel);
   ok(true, "Recording has started.");
 
   let updated = 0;
   OverviewView.on(EVENTS.OVERVIEW_RENDERED, () => updated++);
 
   ok((yield waitUntil(() => updated > 0)),
--- a/browser/devtools/performance/test/browser_timeline-waterfall-generic.js
+++ b/browser/devtools/performance/test/browser_timeline-waterfall-generic.js
@@ -3,17 +3,17 @@
 
 /**
  * Tests if the waterfall is properly built after finishing a recording.
  */
 
 function* spawnTest() {
   let { target, panel } = yield initPerformance(SIMPLE_URL);
   let { $, $$, EVENTS, PerformanceController, OverviewView, WaterfallView, DetailsView } = panel.panelWin;
-  let { WATERFALL_MARKER_SIDEBAR_SAFE_BOUNDS } = devtools.require("devtools/performance/marker-view");
+  let { WATERFALL_MARKER_SIDEBAR_SAFE_BOUNDS } = require("devtools/performance/marker-view");
 
   yield startRecording(panel);
   ok(true, "Recording has started.");
 
   let updated = 0;
   OverviewView.on(EVENTS.OVERVIEW_RENDERED, () => updated++);
 
   ok((yield waitUntil(() => updated > 0)),
--- a/browser/devtools/performance/test/browser_timeline-waterfall-sidebar.js
+++ b/browser/devtools/performance/test/browser_timeline-waterfall-sidebar.js
@@ -3,18 +3,18 @@
 
 /**
  * Tests if the sidebar is properly updated when a marker is selected.
  */
 
 function* spawnTest() {
   let { target, panel } = yield initPerformance(SIMPLE_URL);
   let { $, $$, PerformanceController, WaterfallView } = panel.panelWin;
-  let { L10N } = devtools.require("devtools/performance/global");
-  let { getMarkerLabel } = devtools.require("devtools/performance/marker-utils");
+  let { L10N } = require("devtools/performance/global");
+  let { getMarkerLabel } = require("devtools/performance/marker-utils");
 
   // Hijack the markers massaging part of creating the waterfall view,
   // to prevent collapsing markers and allowing this test to verify
   // everything individually. A better solution would be to just expand
   // all markers first and then skip the meta nodes, but I'm lazy.
   WaterfallView._prepareWaterfallTree = markers => {
     return { submarkers: markers };
   };
--- a/browser/devtools/performance/test/head.js
+++ b/browser/devtools/performance/test/head.js
@@ -3,25 +3,25 @@
 "use strict";
 
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 let { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
 let { Preferences } = Cu.import("resource://gre/modules/Preferences.jsm", {});
 let { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
 let { Promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
-let { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+let { require } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+let { TargetFactory } = require("devtools/framework/target");
 let { gDevTools } = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
-const DevToolsUtils = devtools.require("devtools/toolkit/DevToolsUtils");
+const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
 let { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
-let { console } = devtools.require("resource://gre/modules/devtools/Console.jsm");
-let { merge } = devtools.require("sdk/util/object");
+let { console } = require("resource://gre/modules/devtools/Console.jsm");
+let { merge } = require("sdk/util/object");
 let { generateUUID } = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
-let { getPerformanceFront, PerformanceFront } = devtools.require("devtools/performance/front");
-let TargetFactory = devtools.TargetFactory;
+let { getPerformanceFront, PerformanceFront } = require("devtools/performance/front");
 
 let mm = null;
 
 const FRAME_SCRIPT_UTILS_URL = "chrome://browser/content/devtools/frame-script-utils.js"
 const EXAMPLE_URL = "http://example.com/browser/browser/devtools/performance/test/";
 const SIMPLE_URL = EXAMPLE_URL + "doc_simple-test.html";
 const MARKERS_URL = EXAMPLE_URL + "doc_markers.html";
 const ALLOCS_URL = EXAMPLE_URL + "doc_allocs.html";
@@ -541,17 +541,17 @@ function getInflatedStackLocations(threa
   // The profiler tree is inverted, so reverse the array.
   return locations.reverse();
 }
 
 /**
  * Synthesize a profile for testing.
  */
 function synthesizeProfileForTest(samples) {
-  const RecordingUtils = devtools.require("devtools/performance/recording-utils");
+  const RecordingUtils = require("devtools/performance/recording-utils");
 
   samples.unshift({
     time: 0,
     frames: [
       { location: "(root)" }
     ]
   });
 
--- a/browser/devtools/performance/test/unit/head.js
+++ b/browser/devtools/performance/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";
 
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
-let { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+let { require } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 let { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
-let { console } = devtools.require("resource://gre/modules/devtools/Console.jsm");
-const RecordingUtils = devtools.require("devtools/performance/recording-utils");
+let { console } = require("resource://gre/modules/devtools/Console.jsm");
+const RecordingUtils = require("devtools/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) {
   let calls = root.calls;
--- a/browser/devtools/performance/test/unit/test_frame-utils-01.js
+++ b/browser/devtools/performance/test/unit/test_frame-utils-01.js
@@ -35,17 +35,17 @@ const CHROME_LOCATIONS = [
   "EnterJIT",
 ].map(argify);
 
 function run_test() {
   run_next_test();
 }
 
 add_task(function () {
-  const { computeIsContentAndCategory, parseLocation } = devtools.require("devtools/performance/frame-utils");
+  const { computeIsContentAndCategory, parseLocation } = require("devtools/performance/frame-utils");
   let isContent = (frame) => {
     computeIsContentAndCategory(frame);
     return frame.isContent;
   };
 
 
   for (let frame of CONTENT_LOCATIONS) {
     ok(isContent.apply(null, frameify(frame)), `${frame[0]} should be considered a content frame.`);
--- a/browser/devtools/performance/test/unit/test_frame-utils-02.js
+++ b/browser/devtools/performance/test/unit/test_frame-utils-02.js
@@ -6,17 +6,17 @@
  * works properly.
  */
 
 function run_test() {
   run_next_test();
 }
 
 add_task(function () {
-  let FrameUtils = devtools.require("devtools/performance/frame-utils");
+  let FrameUtils = require("devtools/performance/frame-utils");
 
   let isContent = (frame) => {
     FrameUtils.computeIsContentAndCategory(frame);
     return frame.isContent;
   };
 
   ok(isContent({ location: "http://foo" }),
     "Verifying content/chrome frames is working properly.");
--- a/browser/devtools/performance/test/unit/test_jit-graph-data.js
+++ b/browser/devtools/performance/test/unit/test_jit-graph-data.js
@@ -14,18 +14,18 @@ const RESOLUTION = 50;
 const TIME_PER_SAMPLE = 5;
 
 // Offset needed since ThreadNode requires the first sample to be strictly
 // greater than its start time. This lets us still have pretty numbers
 // in this test to keep it (more) simple, which it sorely needs.
 const TIME_OFFSET = 5;
 
 add_task(function test() {
-  let { ThreadNode } = devtools.require("devtools/performance/tree-model");
-  let { createTierGraphDataFromFrameNode } = devtools.require("devtools/performance/jit");
+  let { ThreadNode } = require("devtools/performance/tree-model");
+  let { createTierGraphDataFromFrameNode } = require("devtools/performance/jit");
 
   // Select the second half of the set of samples
   let startTime = (SAMPLE_COUNT / 2 * TIME_PER_SAMPLE) - TIME_OFFSET;
   let endTime = (SAMPLE_COUNT * TIME_PER_SAMPLE) - TIME_OFFSET;
   let invertTree = true;
 
   let root = new ThreadNode(gThread, { invertTree, startTime, endTime });
 
--- a/browser/devtools/performance/test/unit/test_jit-model-01.js
+++ b/browser/devtools/performance/test/unit/test_jit-model-01.js
@@ -7,17 +7,17 @@
  * FrameNode, and the returning of that data is as expected.
  */
 
 function run_test() {
   run_next_test();
 }
 
 add_task(function test() {
-  let { JITOptimizations } = devtools.require("devtools/performance/jit");
+  let { JITOptimizations } = require("devtools/performance/jit");
 
   let rawSites = [];
   rawSites.push(gRawSite2);
   rawSites.push(gRawSite2);
   rawSites.push(gRawSite1);
   rawSites.push(gRawSite1);
   rawSites.push(gRawSite2);
   rawSites.push(gRawSite3);
--- a/browser/devtools/performance/test/unit/test_jit-model-02.js
+++ b/browser/devtools/performance/test/unit/test_jit-model-02.js
@@ -6,17 +6,17 @@
  * OptimizationSites methods work as expected.
  */
 
 function run_test() {
   run_next_test();
 }
 
 add_task(function test() {
-  let { JITOptimizations, OptimizationSite } = devtools.require("devtools/performance/jit");
+  let { JITOptimizations, OptimizationSite } = require("devtools/performance/jit");
 
   let rawSites = [];
   rawSites.push(gRawSite2);
   rawSites.push(gRawSite2);
   rawSites.push(gRawSite1);
   rawSites.push(gRawSite1);
   rawSites.push(gRawSite2);
   rawSites.push(gRawSite3);
--- a/browser/devtools/performance/test/unit/test_marker-blueprint.js
+++ b/browser/devtools/performance/test/unit/test_marker-blueprint.js
@@ -5,17 +5,17 @@
  * Tests if the timeline blueprint has a correct structure.
  */
 
 function run_test() {
   run_next_test();
 }
 
 add_task(function () {
-  let { TIMELINE_BLUEPRINT } = devtools.require("devtools/performance/markers");
+  let { TIMELINE_BLUEPRINT } = require("devtools/performance/markers");
 
   ok(TIMELINE_BLUEPRINT,
     "A timeline blueprint should be available.");
 
   ok(Object.keys(TIMELINE_BLUEPRINT).length,
     "The timeline blueprint has at least one entry.");
 
   for (let [key, value] of Iterator(TIMELINE_BLUEPRINT)) {
--- a/browser/devtools/performance/test/unit/test_marker-utils.js
+++ b/browser/devtools/performance/test/unit/test_marker-utils.js
@@ -5,18 +5,18 @@
  * Tests the marker utils methods.
  */
 
 function run_test() {
   run_next_test();
 }
 
 add_task(function () {
-  let { TIMELINE_BLUEPRINT } = devtools.require("devtools/performance/markers");
-  let Utils = devtools.require("devtools/performance/marker-utils");
+  let { TIMELINE_BLUEPRINT } = require("devtools/performance/markers");
+  let Utils = require("devtools/performance/marker-utils");
 
   Services.prefs.setBoolPref(PLATFORM_DATA_PREF, false);
 
   equal(Utils.getMarkerLabel({ name: "DOMEvent" }), "DOM Event",
     "getMarkerLabel() returns a simple label");
   equal(Utils.getMarkerLabel({ name: "Javascript", causeName: "setTimeout handler" }), "setTimeout",
     "getMarkerLabel() returns a label defined via function");
 
--- a/browser/devtools/performance/test/unit/test_profiler-categories.js
+++ b/browser/devtools/performance/test/unit/test_profiler-categories.js
@@ -5,17 +5,17 @@
  * Tests if the profiler categories are mapped correctly.
  */
 
 function run_test() {
   run_next_test();
 }
 
 add_task(function () {
-  let global = devtools.require("devtools/performance/global");
+  let global = require("devtools/performance/global");
   let l10n = global.L10N;
   let categories = global.CATEGORIES;
   let mappings = global.CATEGORY_MAPPINGS;
   let count = categories.length;
 
   ok(count,
     "Should have a non-empty list of categories available.");
 
--- a/browser/devtools/performance/test/unit/test_tree-model-01.js
+++ b/browser/devtools/performance/test/unit/test_tree-model-01.js
@@ -5,17 +5,17 @@
  * Tests if a call tree model can be correctly computed from a samples array.
  */
 
 function run_test() {
   run_next_test();
 }
 
 add_task(function test() {
-  const { ThreadNode } = devtools.require("devtools/performance/tree-model");
+  const { ThreadNode } = require("devtools/performance/tree-model");
 
   // Create a root node from a given samples array.
 
   let threadNode = new ThreadNode(gThread, { startTime: 0, endTime: 20 });
   let root = getFrameNodePath(threadNode, "(root)");
 
   // Test the root node.
 
--- a/browser/devtools/performance/test/unit/test_tree-model-02.js
+++ b/browser/devtools/performance/test/unit/test_tree-model-02.js
@@ -5,17 +5,17 @@
  * Tests if a call tree model ignores samples with no timing information.
  */
 
 function run_test() {
   run_next_test();
 }
 
 add_task(function test() {
-  let { ThreadNode } = devtools.require("devtools/performance/tree-model");
+  let { ThreadNode } = require("devtools/performance/tree-model");
 
   // Create a root node from a given samples array.
 
   let thread = new ThreadNode(gThread, { startTime: 0, endTime: 10 });
   let root = getFrameNodePath(thread, "(root)");
 
   // Test the ThreadNode, only node with a duration.
   equal(thread.duration, 10,
--- a/browser/devtools/performance/test/unit/test_tree-model-03.js
+++ b/browser/devtools/performance/test/unit/test_tree-model-03.js
@@ -6,17 +6,17 @@
  * while at the same time filtering by duration.
  */
 
 function run_test() {
   run_next_test();
 }
 
 add_task(function test() {
-  let { ThreadNode } = devtools.require("devtools/performance/tree-model");
+  let { ThreadNode } = require("devtools/performance/tree-model");
 
   // Create a root node from a given samples array, filtering by time.
   //
   // Filtering from 5 to 18 includes the 2nd and 3rd samples. The 2nd sample
   // starts exactly on 5 and ends at 11. The 3rd sample starts at 11 and ends
   // exactly at 18.
   let startTime = 5;
   let endTime = 18;
--- a/browser/devtools/performance/test/unit/test_tree-model-04.js
+++ b/browser/devtools/performance/test/unit/test_tree-model-04.js
@@ -6,17 +6,17 @@
  * while at the same time filtering by duration and content-only frames.
  */
 
 function run_test() {
   run_next_test();
 }
 
 add_task(function test() {
-  let { ThreadNode } = devtools.require("devtools/performance/tree-model");
+  let { ThreadNode } = require("devtools/performance/tree-model");
 
   // Create a root node from a given samples array, filtering by time.
 
   let startTime = 5;
   let endTime = 18;
   let thread = new ThreadNode(gThread, { startTime, endTime, contentOnly: true });
   let root = getFrameNodePath(thread, "(root)");
 
--- a/browser/devtools/performance/test/unit/test_tree-model-05.js
+++ b/browser/devtools/performance/test/unit/test_tree-model-05.js
@@ -42,17 +42,17 @@ let gThread = synthesizeProfileForTest([
   ]
 }]);
 
 function run_test() {
   run_next_test();
 }
 
 add_task(function test() {
-  let { ThreadNode } = devtools.require("devtools/performance/tree-model");
+  let { ThreadNode } = require("devtools/performance/tree-model");
 
   let root = new ThreadNode(gThread, { invertTree: true, startTime: 0, endTime: 4 });
 
   equal(root.calls.length, 2,
      "Should get the 2 youngest frames, not the 1 oldest frame");
 
   let C = getFrameNodePath(root, "C");
   ok(C, "Should have C as a child of the root.");
--- a/browser/devtools/performance/test/unit/test_tree-model-06.js
+++ b/browser/devtools/performance/test/unit/test_tree-model-06.js
@@ -7,17 +7,17 @@
  * and only youngest frames capture optimization data.
  */
 
 function run_test() {
   run_next_test();
 }
 
 add_task(function test() {
-  let { ThreadNode } = devtools.require("devtools/performance/tree-model");
+  let { ThreadNode } = require("devtools/performance/tree-model");
   let root = getFrameNodePath(new ThreadNode(gThread, { startTime: 0, endTime: 30 }), "(root)");
 
   let A = getFrameNodePath(root, "A");
   let B = getFrameNodePath(A, "B");
   let C = getFrameNodePath(B, "C");
   let Aopts = A.getOptimizations();
   let Bopts = B.getOptimizations();
   let Copts = C.getOptimizations();
--- a/browser/devtools/performance/test/unit/test_tree-model-07.js
+++ b/browser/devtools/performance/test/unit/test_tree-model-07.js
@@ -1,23 +1,23 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that when displaying only content nodes, platform nodes are generalized.
  */
 
-let { CATEGORY_MASK } = devtools.require("devtools/performance/global");
+let { CATEGORY_MASK } = require("devtools/performance/global");
 
 function run_test() {
   run_next_test();
 }
 
 add_task(function test() {
-  let { ThreadNode } = devtools.require("devtools/performance/tree-model");
+  let { ThreadNode } = require("devtools/performance/tree-model");
   let url = (n) => `http://content/${n}`;
 
   // Create a root node from a given samples array.
 
   let root = getFrameNodePath(new ThreadNode(gThread, { startTime: 5, endTime: 30, contentOnly: true }), "(root)");
 
   /*
    * should have a tree like:
--- a/browser/devtools/performance/test/unit/test_tree-model-08.js
+++ b/browser/devtools/performance/test/unit/test_tree-model-08.js
@@ -5,19 +5,19 @@
  * Verifies if FrameNodes retain and parse their data appropriately.
  */
 
 function run_test() {
   run_next_test();
 }
 
 add_task(function test() {
-  let FrameUtils = devtools.require("devtools/performance/frame-utils");
-  let { FrameNode } = devtools.require("devtools/performance/tree-model");
-  let { CATEGORY_OTHER } = devtools.require("devtools/performance/global");
+  let FrameUtils = require("devtools/performance/frame-utils");
+  let { FrameNode } = require("devtools/performance/tree-model");
+  let { CATEGORY_OTHER } = require("devtools/performance/global");
   let compute = frame => {
     FrameUtils.computeIsContentAndCategory(frame);
     return frame;
   };
 
   let frames = [
     new FrameNode("hello/<.world (http://foo/bar.js:123:987)", compute({
       location: "hello/<.world (http://foo/bar.js:123:987)",
--- a/browser/devtools/performance/test/unit/test_tree-model-09.js
+++ b/browser/devtools/performance/test/unit/test_tree-model-09.js
@@ -1,23 +1,23 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that when displaying only content nodes, platform nodes are generalized.
  */
 
-let { CATEGORY_MASK } = devtools.require("devtools/performance/global");
+let { CATEGORY_MASK } = require("devtools/performance/global");
 
 function run_test() {
   run_next_test();
 }
 
 add_task(function test() {
-  let { ThreadNode } = devtools.require("devtools/performance/tree-model");
+  let { ThreadNode } = require("devtools/performance/tree-model");
   let url = (n) => `http://content/${n}`;
 
   // Create a root node from a given samples array.
 
   let root = getFrameNodePath(new ThreadNode(gThread, { startTime: 5, endTime: 25, contentOnly: true }), "(root)");
 
   /*
    * should have a tree like:
--- a/browser/devtools/performance/test/unit/test_tree-model-10.js
+++ b/browser/devtools/performance/test/unit/test_tree-model-10.js
@@ -6,17 +6,17 @@
  * frame nodes. The model-only version of browser_profiler-tree-view-10.js
  */
 
 function run_test() {
   run_next_test();
 }
 
 add_task(function () {
-  let { ThreadNode } = devtools.require("devtools/performance/tree-model");
+  let { ThreadNode } = require("devtools/performance/tree-model");
   let thread = new ThreadNode(gThread, { invertTree: true, startTime: 0, endTime: 50 });
 
   /**
    * Samples
    *
    * A->C
    * A->B
    * A->B->C x4
--- a/browser/devtools/performance/test/unit/test_tree-model-11.js
+++ b/browser/devtools/performance/test/unit/test_tree-model-11.js
@@ -6,17 +6,17 @@
  * samples.
  */
 
 function run_test() {
   run_next_test();
 }
 
 add_task(function () {
-  let { ThreadNode } = devtools.require("devtools/performance/tree-model");
+  let { ThreadNode } = require("devtools/performance/tree-model");
   let thread = new ThreadNode(gThread, { startTime: 0, endTime: 50, flattenRecursion: true });
 
   /**
    * Samples
    *
    * A->B->C
    * A->B->B->B->C
    * A->B
--- a/browser/devtools/performance/test/unit/test_tree-model-12.js
+++ b/browser/devtools/performance/test/unit/test_tree-model-12.js
@@ -4,17 +4,17 @@
 // Test that uninverting the call tree works correctly when there are stacks
 // in the profile that prefixes of other stacks.
 
 function run_test() {
   run_next_test();
 }
 
 add_task(function () {
-  let { ThreadNode } = devtools.require("devtools/performance/tree-model");
+  let { ThreadNode } = require("devtools/performance/tree-model");
   let thread = new ThreadNode(gThread, { startTime: 0, endTime: 50 });
   let root = getFrameNodePath(thread, "(root)");
 
   /**
    * Samples
    *
    * A->B
    * C->B
--- a/browser/devtools/performance/test/unit/test_tree-model-13.js
+++ b/browser/devtools/performance/test/unit/test_tree-model-13.js
@@ -3,17 +3,17 @@
 
 // Like test_tree-model-12, but inverted.
 
 function run_test() {
   run_next_test();
 }
 
 add_task(function () {
-  let { ThreadNode } = devtools.require("devtools/performance/tree-model");
+  let { ThreadNode } = require("devtools/performance/tree-model");
   let root = new ThreadNode(gThread, { invertTree: true, startTime: 0, endTime: 50 });
 
   /**
    * Samples
    *
    * A->B
    * C->B
    * B
--- a/browser/devtools/performance/test/unit/test_waterfall-utils-collapse-01.js
+++ b/browser/devtools/performance/test/unit/test_waterfall-utils-collapse-01.js
@@ -5,17 +5,17 @@
  * Tests if the waterfall collapsing logic works properly.
  */
 
 function run_test() {
   run_next_test();
 }
 
 add_task(function test() {
-  const WaterfallUtils = devtools.require("devtools/performance/waterfall-utils");
+  const WaterfallUtils = require("devtools/performance/waterfall-utils");
 
   let rootMarkerNode = WaterfallUtils.createParentNode({ name: "(root)" });
 
   WaterfallUtils.collapseMarkersIntoNode({
     rootNode: rootMarkerNode,
     markersList: gTestMarkers
   });
 
--- a/browser/devtools/performance/test/unit/test_waterfall-utils-collapse-02.js
+++ b/browser/devtools/performance/test/unit/test_waterfall-utils-collapse-02.js
@@ -6,17 +6,17 @@
  * markers, as they should ignore any sort of collapsing.
  */
 
 function run_test() {
   run_next_test();
 }
 
 add_task(function test() {
-  const WaterfallUtils = devtools.require("devtools/performance/waterfall-utils");
+  const WaterfallUtils = require("devtools/performance/waterfall-utils");
 
   let rootMarkerNode = WaterfallUtils.createParentNode({ name: "(root)" });
 
   WaterfallUtils.collapseMarkersIntoNode({
     rootNode: rootMarkerNode,
     markersList: gTestMarkers
   });
 
--- a/browser/devtools/performance/test/unit/test_waterfall-utils-collapse-03.js
+++ b/browser/devtools/performance/test/unit/test_waterfall-utils-collapse-03.js
@@ -6,17 +6,17 @@
  * collapsible markers downward, and the following marker is outside of both ranges.
  */
 
 function run_test() {
   run_next_test();
 }
 
 add_task(function test() {
-  const WaterfallUtils = devtools.require("devtools/performance/waterfall-utils");
+  const WaterfallUtils = require("devtools/performance/waterfall-utils");
 
   let rootMarkerNode = WaterfallUtils.createParentNode({ name: "(root)" });
 
   WaterfallUtils.collapseMarkersIntoNode({
     rootNode: rootMarkerNode,
     markersList: gTestMarkers
   });
 
--- a/browser/devtools/performance/test/unit/test_waterfall-utils-collapse-04.js
+++ b/browser/devtools/performance/test/unit/test_waterfall-utils-collapse-04.js
@@ -6,17 +6,17 @@
  * when filtering parents and children.
  */
 
 function run_test() {
   run_next_test();
 }
 
 add_task(function test() {
-  const WaterfallUtils = devtools.require("devtools/performance/waterfall-utils");
+  const WaterfallUtils = require("devtools/performance/waterfall-utils");
 
   [
     [["DOMEvent"], gExpectedOutputNoDOMEvent],
     [["Javascript"], gExpectedOutputNoJS],
     [["DOMEvent", "Javascript"], gExpectedOutputNoDOMEventOrJS],
   ].forEach(([filter, expected]) => {
     let rootMarkerNode = WaterfallUtils.createParentNode({ name: "(root)" });
 
--- a/browser/devtools/projecteditor/chrome/content/projecteditor-loader.js
+++ b/browser/devtools/projecteditor/chrome/content/projecteditor-loader.js
@@ -1,13 +1,12 @@
 const Cu = Components.utils;
-const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+const {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 const {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm", {});
 const {NetUtil} = Cu.import("resource://gre/modules/NetUtil.jsm", {});
-const require = devtools.require;
 const promise = require("projecteditor/helpers/promise");
 const ProjectEditor = require("projecteditor/projecteditor");
 
 const SAMPLE_PATH = buildTempDirectoryStructure();
 const SAMPLE_NAME = "DevTools Content Application Name";
 const SAMPLE_PROJECT_URL = "data:text/html;charset=utf-8,<body><h1>Project Overview</h1></body>";
 const SAMPLE_ICON = "chrome://browser/skin/devtools/tool-debugger.svg";
 
--- a/browser/devtools/projecteditor/test/head.js
+++ b/browser/devtools/projecteditor/test/head.js
@@ -1,21 +1,21 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const Cu = Components.utils;
-const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
-const TargetFactory = devtools.TargetFactory;
+const {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+const {TargetFactory} = require("devtools/framework/target");
 const {console} = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
-const promise = devtools.require("sdk/core/promise");
+const promise = require("sdk/core/promise");
 const {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm", {});
 const {NetUtil} = Cu.import("resource://gre/modules/NetUtil.jsm", {});
-const ProjectEditor = devtools.require("projecteditor/projecteditor");
-const DevToolsUtils = devtools.require("devtools/toolkit/DevToolsUtils");
+const ProjectEditor = require("projecteditor/projecteditor");
+const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
 
 const TEST_URL_ROOT = "http://mochi.test:8888/browser/browser/devtools/projecteditor/test/";
 const SAMPLE_WEBAPP_URL = TEST_URL_ROOT + "/helper_homepage.html";
 let TEMP_PATH;
 let TEMP_FOLDER_NAME = "ProjectEditor" + (new Date().getTime());
 
 // All test are asynchronous
 waitForExplicitFinish();
--- a/browser/devtools/responsivedesign/moz.build
+++ b/browser/devtools/responsivedesign/moz.build
@@ -1,11 +1,14 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXTRA_JS_MODULES.devtools += [
-    'resize-commands.js',
     'responsivedesign-child.js',
     'responsivedesign.jsm'
 ]
+EXTRA_JS_MODULES.devtools.responsivedesign += [
+    'resize-commands.js'
+]
+
 
 BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
--- a/browser/devtools/responsivedesign/responsivedesign.jsm
+++ b/browser/devtools/responsivedesign/responsivedesign.jsm
@@ -11,17 +11,17 @@ Cu.import("resource://gre/modules/Servic
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource:///modules/devtools/gDevTools.jsm");
 Cu.import("resource://gre/modules/devtools/event-emitter.js");
 Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
 let { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
 XPCOMUtils.defineLazyModuleGetter(this, "SystemAppProxy",
                                   "resource://gre/modules/SystemAppProxy.jsm");
 
-var require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
+let { require } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 let Telemetry = require("devtools/shared/telemetry");
 let { showDoorhanger } = require("devtools/shared/doorhanger");
 let { TouchEventSimulator } = require("devtools/toolkit/touch/simulator");
 let { Task } = require("resource://gre/modules/Task.jsm");
 
 this.EXPORTED_SYMBOLS = ["ResponsiveUIManager"];
 
 const MIN_WIDTH = 50;
--- a/browser/devtools/responsivedesign/test/head.js
+++ b/browser/devtools/responsivedesign/test/head.js
@@ -1,18 +1,18 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 const { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
 
-let {devtools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
-let TargetFactory = devtools.TargetFactory;
-let DevToolsUtils = devtools.require("devtools/toolkit/DevToolsUtils");
+let {require} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
+let {TargetFactory} = require("devtools/framework/target");
+let DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
 
 // Import the GCLI test helper
 let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
 Services.scriptloader.loadSubScript(testDir + "../../../commandline/test/helpers.js", this);
 
 DevToolsUtils.testing = true;
 registerCleanupFunction(() => {
   DevToolsUtils.testing = false;
--- a/browser/devtools/scratchpad/scratchpad.js
+++ b/browser/devtools/scratchpad/scratchpad.js
@@ -38,17 +38,17 @@ const WRAP_TEXT = "devtools.scratchpad.w
 const SHOW_TRAILING_SPACE = "devtools.scratchpad.showTrailingSpace";
 const EDITOR_FONT_SIZE = "devtools.scratchpad.editorFontSize";
 const ENABLE_AUTOCOMPLETION = "devtools.scratchpad.enableAutocompletion";
 const TAB_SIZE = "devtools.editor.tabsize";
 const FALLBACK_CHARSET_LIST = "intl.fallbackCharsetList.ISO-8859-1";
 
 const VARIABLES_VIEW_URL = "chrome://browser/content/devtools/widgets/VariablesView.xul";
 
-const require   = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
+const {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 
 const Telemetry = require("devtools/shared/telemetry");
 const Editor    = require("devtools/sourceeditor/editor");
 const TargetFactory = require("devtools/framework/target").TargetFactory;
 const EventEmitter = require("devtools/toolkit/event-emitter");
 const {DevToolsWorker} = require("devtools/toolkit/shared/worker");
 const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
 
--- a/browser/devtools/scratchpad/test/browser_scratchpad_close_toolbox.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_close_toolbox.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Test that closing the toolbox after having opened a scratchpad leaves the
 // latter in a functioning state.
 
 let {Task} = Cu.import("resource://gre/modules/Task.jsm", {});
-let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+let {TargetFactory} = require("devtools/framework/target");
 
 function test() {
   const options = {
     tabContent: "test closing toolbox and then reusing scratchpad"
   };
   openTabAndScratchpad(options)
     .then(Task.async(runTests))
     .then(finish, console.error);
@@ -19,17 +19,17 @@ function test() {
 function* runTests([win, sp]) {
   // Use the scratchpad before opening the toolbox.
   const source = "window.foobar = 7;";
   sp.setText(source);
   let [,,result] = yield sp.display();
   is(result, 7, "Display produced the expected output.");
 
   // Now open the toolbox and close it again.
-  let target = devtools.TargetFactory.forTab(gBrowser.selectedTab);
+  let target = TargetFactory.forTab(gBrowser.selectedTab);
   let toolbox = yield gDevTools.showToolbox(target, "webconsole");
   ok(toolbox, "Toolbox was opened.");
   let closed = yield gDevTools.closeToolbox(target);
   is(closed, true, "Toolbox was closed.");
 
   // Now see if using the scratcphad works as expected.
   sp.setText(source);
   let [,,result2] = yield sp.display();
--- a/browser/devtools/scratchpad/test/browser_scratchpad_inspect_primitives.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_inspect_primitives.js
@@ -1,16 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Test that inspecting primitive values uses the object inspector, not an
 // inline comment.
 
 let {Task} = Cu.import("resource://gre/modules/Task.jsm", {});
-let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 
 function test() {
   const options = {
     tabContent: "test inspecting primitive values"
   };
   openTabAndScratchpad(options)
     .then(Task.async(runTests))
     .then(finish, console.error);
--- a/browser/devtools/scratchpad/test/browser_scratchpad_wrong_window_focus.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_wrong_window_focus.js
@@ -21,20 +21,21 @@ function test()
   // is currently active (it should be the older one).
 
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
     gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
 
     openScratchpad(function () {
       let sw = gScratchpadWindow;
-      let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+      let {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+      let {TargetFactory} = require("devtools/framework/target");
 
       openScratchpad(function () {
-        let target = devtools.TargetFactory.forTab(gBrowser.selectedTab);
+        let target = TargetFactory.forTab(gBrowser.selectedTab);
         gDevTools.showToolbox(target, "webconsole").then((toolbox) => {
           let hud = toolbox.getCurrentPanel().hud;
           hud.jsterm.clearOutput(true);
           testFocus(sw, hud);
         });
       });
     });
   }, true);
--- a/browser/devtools/scratchpad/test/head.js
+++ b/browser/devtools/scratchpad/test/head.js
@@ -3,17 +3,17 @@
    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 {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
 const {console} = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
-const {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
+const {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
 const {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
 
 
 let gScratchpadWindow; // Reference to the Scratchpad chrome window object
 
 DevToolsUtils.testing = true;
 SimpleTest.registerCleanupFunction(() => {
--- a/browser/devtools/shadereditor/shadereditor.js
+++ b/browser/devtools/shadereditor/shadereditor.js
@@ -7,17 +7,17 @@ const { classes: Cc, interfaces: Ci, uti
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource:///modules/devtools/SideMenuWidget.jsm");
 Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
 Cu.import("resource://gre/modules/devtools/Console.jsm");
 
-const require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
+const {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 const promise = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;
 const EventEmitter = require("devtools/toolkit/event-emitter");
 const {Tooltip} = require("devtools/shared/widgets/Tooltip");
 const Editor = require("devtools/sourceeditor/editor");
 const Telemetry = require("devtools/shared/telemetry");
 const telemetry = new Telemetry();
 
 // The panel's window global is an EventEmitter firing the following events:
--- a/browser/devtools/shadereditor/test/head.js
+++ b/browser/devtools/shadereditor/test/head.js
@@ -8,25 +8,25 @@ let { Services } = Cu.import("resource:/
 
 let 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);
 
 let { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
 let { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
 let { gDevTools } = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
-let { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+let { require } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 let { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
 let { DebuggerClient } = Cu.import("resource://gre/modules/devtools/dbg-client.jsm", {});
 
-let { WebGLFront } = devtools.require("devtools/server/actors/webgl");
-let DevToolsUtils = devtools.require("devtools/toolkit/DevToolsUtils");
-let TiltGL = devtools.require("devtools/tilt/tilt-gl");
-let TargetFactory = devtools.TargetFactory;
-let Toolbox = devtools.Toolbox;
+let { WebGLFront } = require("devtools/server/actors/webgl");
+let DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
+let TiltGL = require("devtools/tilt/tilt-gl");
+let {TargetFactory} = require("devtools/framework/target");
+let {Toolbox} = require("devtools/framework/toolbox");
 let mm = null;
 
 const FRAME_SCRIPT_UTILS_URL = "chrome://browser/content/devtools/frame-script-utils.js"
 const EXAMPLE_URL = "http://example.com/browser/browser/devtools/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";
--- a/browser/devtools/shared/DeveloperToolbar.jsm
+++ b/browser/devtools/shared/DeveloperToolbar.jsm
@@ -8,17 +8,18 @@ this.EXPORTED_SYMBOLS = [ "DeveloperTool
 
 const NS_XHTML = "http://www.w3.org/1999/xhtml";
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
-const { require, TargetFactory } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
+const { require } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+const { TargetFactory } = require("devtools/framework/target");
 
 const Node = Ci.nsIDOMNode;
 
 XPCOMUtils.defineLazyModuleGetter(this, "console",
                                   "resource://gre/modules/devtools/Console.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
                                   "resource://gre/modules/PluralForm.jsm");
--- a/browser/devtools/shared/Jsbeautify.jsm
+++ b/browser/devtools/shared/Jsbeautify.jsm
@@ -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";
 
 /*
- * JS Beautifier. Please use devtools.require("devtools/jsbeautify") instead of
+ * JS Beautifier. Please use require("devtools/jsbeautify") instead of
  * this JSM.
  */
 
 this.EXPORTED_SYMBOLS = [ "jsBeautify" ];
 
-const { devtools } = Components.utils.import("resource://gre/modules/devtools/Loader.jsm", {});
-const { beautify } = devtools.require("devtools/jsbeautify");
+const { require } = Components.utils.import("resource://gre/modules/devtools/Loader.jsm", {});
+const { beautify } = require("devtools/jsbeautify");
 const jsBeautify = beautify.js;
--- a/browser/devtools/shared/Parser.jsm
+++ b/browser/devtools/shared/Parser.jsm
@@ -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 Ci = Components.interfaces;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
-const DevToolsUtils = devtools.require("devtools/toolkit/DevToolsUtils");
+const { require } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
 
 XPCOMUtils.defineLazyModuleGetter(this,
   "Reflect", "resource://gre/modules/reflect.jsm");
 
 this.EXPORTED_SYMBOLS = ["Parser", "ParserHelpers", "SyntaxTreeVisitor"];
 
 /**
  * A JS parser using the reflection API.
new file mode 100644
--- /dev/null
+++ b/browser/devtools/shared/content/react-dev.js
@@ -0,0 +1,21642 @@
+/**
+ * React (with addons) v0.13.3
+ */
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.React = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
+/**
+ * Copyright 2013-2015, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * @providesModule ReactWithAddons
+ */
+
+/**
+ * This module exists purely in the open source project, and is meant as a way
+ * to create a separate standalone build of React. This build has "addons", or
+ * functionality we've built and think might be useful but doesn't have a good
+ * place to live inside React core.
+ */
+
+'use strict';
+
+var LinkedStateMixin = _dereq_(25);
+var React = _dereq_(31);
+var ReactComponentWithPureRenderMixin =
+  _dereq_(42);
+var ReactCSSTransitionGroup = _dereq_(34);
+var ReactFragment = _dereq_(69);
+var ReactTransitionGroup = _dereq_(98);
+var ReactUpdates = _dereq_(100);
+
+var cx = _dereq_(127);
+var cloneWithProps = _dereq_(122);
+var update = _dereq_(170);
+
+React.addons = {
+  CSSTransitionGroup: ReactCSSTransitionGroup,
+  LinkedStateMixin: LinkedStateMixin,
+  PureRenderMixin: ReactComponentWithPureRenderMixin,
+  TransitionGroup: ReactTransitionGroup,
+
+  batchedUpdates: ReactUpdates.batchedUpdates,
+  classSet: cx,
+  cloneWithProps: cloneWithProps,
+  createFragment: ReactFragment.create,
+  update: update
+};
+
+if ("production" !== "development") {
+  React.addons.Perf = _dereq_(61);
+  React.addons.TestUtils = _dereq_(95);
+}
+
+module.exports = React;
+
+},{"100":100,"122":122,"127":127,"170":170,"25":25,"31":31,"34":34,"42":42,"61":61,"69":69,"95":95,"98":98}],2:[function(_dereq_,module,exports){
+/**
+ * Copyright 2013-2015, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * @providesModule AutoFocusMixin
+ * @typechecks static-only
+ */
+
+'use strict';
+
+var focusNode = _dereq_(134);
+
+var AutoFocusMixin = {
+  componentDidMount: function() {
+    if (this.props.autoFocus) {
+      focusNode(this.getDOMNode());
+    }
+  }
+};
+
+module.exports = AutoFocusMixin;
+
+},{"134":134}],3:[function(_dereq_,module,exports){
+/**
+ * Copyright 2013-2015 Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * @providesModule BeforeInputEventPlugin
+ * @typechecks static-only
+ */
+
+'use strict';
+
+var EventConstants = _dereq_(16);
+var EventPropagators = _dereq_(21);
+var ExecutionEnvironment = _dereq_(22);
+var FallbackCompositionState = _dereq_(23);
+var SyntheticCompositionEvent = _dereq_(106);
+var SyntheticInputEvent = _dereq_(110);
+
+var keyOf = _dereq_(157);
+
+var END_KEYCODES = [9, 13, 27, 32]; // Tab, Return, Esc, Space
+var START_KEYCODE = 229;
+
+var canUseCompositionEvent = (
+  ExecutionEnvironment.canUseDOM &&
+  'CompositionEvent' in window
+);
+
+var documentMode = null;
+if (ExecutionEnvironment.canUseDOM && 'documentMode' in document) {
+  documentMode = document.documentMode;
+}
+
+// Webkit offers a very useful `textInput` event that can be used to
+// directly represent `beforeInput`. The IE `textinput` event is not as
+// useful, so we don't use it.
+var canUseTextInputEvent = (
+  ExecutionEnvironment.canUseDOM &&
+  'TextEvent' in window &&
+  !documentMode &&
+  !isPresto()
+);
+
+// In IE9+, we have access to composition events, but the data supplied
+// by the native compositionend event may be incorrect. Japanese ideographic
+// spaces, for instance (\u3000) are not recorded correctly.
+var useFallbackCompositionData = (
+  ExecutionEnvironment.canUseDOM &&
+  (
+    (!canUseCompositionEvent || documentMode && documentMode > 8 && documentMode <= 11)
+  )
+);
+
+/**
+ * Opera <= 12 includes TextEvent in window, but does not fire
+ * text input events. Rely on keypress instead.
+ */
+function isPresto() {
+  var opera = window.opera;
+  return (
+    typeof opera === 'object' &&
+    typeof opera.version === 'function' &&
+    parseInt(opera.version(), 10) <= 12
+  );
+}
+
+var SPACEBAR_CODE = 32;
+var SPACEBAR_CHAR = String.fromCharCode(SPACEBAR_CODE);
+
+var topLevelTypes = EventConstants.topLevelTypes;
+
+// Events and their corresponding property names.
+var eventTypes = {
+  beforeInput: {
+    phasedRegistrationNames: {
+      bubbled: keyOf({onBeforeInput: null}),
+      captured: keyOf({onBeforeInputCapture: null})
+    },
+    dependencies: [
+      topLevelTypes.topCompositionEnd,
+      topLevelTypes.topKeyPress,
+      topLevelTypes.topTextInput,
+      topLevelTypes.topPaste
+    ]
+  },
+  compositionEnd: {
+    phasedRegistrationNames: {
+      bubbled: keyOf({onCompositionEnd: null}),
+      captured: keyOf({onCompositionEndCapture: null})
+    },
+    dependencies: [
+      topLevelTypes.topBlur,
+      topLevelTypes.topCompositionEnd,
+      topLevelTypes.topKeyDown,
+      topLevelTypes.topKeyPress,
+      topLevelTypes.topKeyUp,
+      topLevelTypes.topMouseDown
+    ]
+  },
+  compositionStart: {
+    phasedRegistrationNames: {
+      bubbled: keyOf({onCompositionStart: null}),
+      captured: keyOf({onCompositionStartCapture: null})
+    },
+    dependencies: [
+      topLevelTypes.topBlur,
+      topLevelTypes.topCompositionStart,
+      topLevelTypes.topKeyDown,
+      topLevelTypes.topKeyPress,
+      topLevelTypes.topKeyUp,
+      topLevelTypes.topMouseDown
+    ]
+  },
+  compositionUpdate: {
+    phasedRegistrationNames: {
+      bubbled: keyOf({onCompositionUpdate: null}),
+      captured: keyOf({onCompositionUpdateCapture: null})
+    },
+    dependencies: [
+      topLevelTypes.topBlur,
+      topLevelTypes.topCompositionUpdate,
+      topLevelTypes.topKeyDown,
+      topLevelTypes.topKeyPress,
+      topLevelTypes.topKeyUp,
+      topLevelTypes.topMouseDown
+    ]
+  }
+};
+
+// Track whether we've ever handled a keypress on the space key.
+var hasSpaceKeypress = false;
+
+/**
+ * Return whether a native keypress event is assumed to be a command.
+ * This is required because Firefox fires `keypress` events for key commands
+ * (cut, copy, select-all, etc.) even though no character is inserted.
+ */
+function isKeypressCommand(nativeEvent) {
+  return (
+    (nativeEvent.ctrlKey || nativeEvent.altKey || nativeEvent.metaKey) &&
+    // ctrlKey && altKey is equivalent to AltGr, and is not a command.
+    !(nativeEvent.ctrlKey && nativeEvent.altKey)
+  );
+}
+
+
+/**
+ * Translate native top level events into event types.
+ *
+ * @param {string} topLevelType
+ * @return {object}
+ */
+function getCompositionEventType(topLevelType) {
+  switch (topLevelType) {
+    case topLevelTypes.topCompositionStart:
+      return eventTypes.compositionStart;
+    case topLevelTypes.topCompositionEnd:
+      return eventTypes.compositionEnd;
+    case topLevelTypes.topCompositionUpdate:
+      return eventTypes.compositionUpdate;
+  }
+}
+
+/**
+ * Does our fallback best-guess model think this event signifies that
+ * composition has begun?
+ *
+ * @param {string} topLevelType
+ * @param {object} nativeEvent
+ * @return {boolean}
+ */
+function isFallbackCompositionStart(topLevelType, nativeEvent) {
+  return (
+    topLevelType === topLevelTypes.topKeyDown &&
+    nativeEvent.keyCode === START_KEYCODE
+  );
+}
+
+/**
+ * Does our fallback mode think that this event is the end of composition?
+ *
+ * @param {string} topLevelType
+ * @param {object} nativeEvent
+ * @return {boolean}
+ */
+function isFallbackCompositionEnd(topLevelType, nativeEvent) {
+  switch (topLevelType) {
+    case topLevelTypes.topKeyUp:
+      // Command keys insert or clear IME input.
+      return (END_KEYCODES.indexOf(nativeEvent.keyCode) !== -1);
+    case topLevelTypes.topKeyDown:
+      // Expect IME keyCode on each keydown. If we get any other
+      // code we must have exited earlier.
+      return (nativeEvent.keyCode !== START_KEYCODE);
+    case topLevelTypes.topKeyPress:
+    case topLevelTypes.topMouseDown:
+    case topLevelTypes.topBlur:
+      // Events are not possible without cancelling IME.
+      return true;
+    default:
+      return false;
+  }
+}
+
+/**
+ * Google Input Tools provides composition data via a CustomEvent,
+ * with the `data` property populated in the `detail` object. If this
+ * is available on the event object, use it. If not, this is a plain
+ * composition event and we have nothing special to extract.
+ *
+ * @param {object} nativeEvent
+ * @return {?string}
+ */
+function getDataFromCustomEvent(nativeEvent) {
+  var detail = nativeEvent.detail;
+  if (typeof detail === 'object' && 'data' in detail) {
+    return detail.data;
+  }
+  return null;
+}
+
+// Track the current IME composition fallback object, if any.
+var currentComposition = null;
+
+/**
+ * @param {string} topLevelType Record from `EventConstants`.
+ * @param {DOMEventTarget} topLevelTarget The listening component root node.
+ * @param {string} topLevelTargetID ID of `topLevelTarget`.
+ * @param {object} nativeEvent Native browser event.
+ * @return {?object} A SyntheticCompositionEvent.
+ */
+function extractCompositionEvent(
+  topLevelType,
+  topLevelTarget,
+  topLevelTargetID,
+  nativeEvent
+) {
+  var eventType;
+  var fallbackData;
+
+  if (canUseCompositionEvent) {
+    eventType = getCompositionEventType(topLevelType);
+  } else if (!currentComposition) {
+    if (isFallbackCompositionStart(topLevelType, nativeEvent)) {
+      eventType = eventTypes.compositionStart;
+    }
+  } else if (isFallbackCompositionEnd(topLevelType, nativeEvent)) {
+    eventType = eventTypes.compositionEnd;
+  }
+
+  if (!eventType) {
+    return null;
+  }
+
+  if (useFallbackCompositionData) {
+    // The current composition is stored statically and must not be
+    // overwritten while composition continues.
+    if (!currentComposition && eventType === eventTypes.compositionStart) {
+      currentComposition = FallbackCompositionState.getPooled(topLevelTarget);
+    } else if (eventType === eventTypes.compositionEnd) {
+      if (currentComposition) {
+        fallbackData = currentComposition.getData();
+      }
+    }
+  }
+
+  var event = SyntheticCompositionEvent.getPooled(
+    eventType,
+    topLevelTargetID,
+    nativeEvent
+  );
+
+  if (fallbackData) {
+    // Inject data generated from fallback path into the synthetic event.
+    // This matches the property of native CompositionEventInterface.
+    event.data = fallbackData;
+  } else {
+    var customData = getDataFromCustomEvent(nativeEvent);
+    if (customData !== null) {
+      event.data = customData;
+    }
+  }
+
+  EventPropagators.accumulateTwoPhaseDispatches(event);
+  return event;
+}
+
+/**
+ * @param {string} topLevelType Record from `EventConstants`.
+ * @param {object} nativeEvent Native browser event.
+ * @return {?string} The string corresponding to this `beforeInput` event.
+ */
+function getNativeBeforeInputChars(topLevelType, nativeEvent) {
+  switch (topLevelType) {
+    case topLevelTypes.topCompositionEnd:
+      return getDataFromCustomEvent(nativeEvent);
+    case topLevelTypes.topKeyPress:
+      /**
+       * If native `textInput` events are available, our goal is to make
+       * use of them. However, there is a special case: the spacebar key.
+       * In Webkit, preventing default on a spacebar `textInput` event
+       * cancels character insertion, but it *also* causes the browser
+       * to fall back to its default spacebar behavior of scrolling the
+       * page.
+       *
+       * Tracking at:
+       * https://code.google.com/p/chromium/issues/detail?id=355103
+       *
+       * To avoid this issue, use the keypress event as if no `textInput`
+       * event is available.
+       */
+      var which = nativeEvent.which;
+      if (which !== SPACEBAR_CODE) {
+        return null;
+      }
+
+      hasSpaceKeypress = true;
+      return SPACEBAR_CHAR;
+
+    case topLevelTypes.topTextInput:
+      // Record the characters to be added to the DOM.
+      var chars = nativeEvent.data;
+
+      // If it's a spacebar character, assume that we have already handled
+      // it at the keypress level and bail immediately. Android Chrome
+      // doesn't give us keycodes, so we need to blacklist it.
+      if (chars === SPACEBAR_CHAR && hasSpaceKeypress) {
+        return null;
+      }
+
+      return chars;
+
+    default:
+      // For other native event types, do nothing.
+      return null;
+  }
+}
+
+/**
+ * For browsers that do not provide the `textInput` event, extract the
+ * appropriate string to use for SyntheticInputEvent.
+ *
+ * @param {string} topLevelType Record from `EventConstants`.
+ * @param {object} nativeEvent Native browser event.
+ * @return {?string} The fallback string for this `beforeInput` event.
+ */
+function getFallbackBeforeInputChars(topLevelType, nativeEvent) {
+  // If we are currently composing (IME) and using a fallback to do so,
+  // try to extract the composed characters from the fallback object.
+  if (currentComposition) {
+    if (
+      topLevelType === topLevelTypes.topCompositionEnd ||
+      isFallbackCompositionEnd(topLevelType, nativeEvent)
+    ) {
+      var chars = currentComposition.getData();
+      FallbackCompositionState.release(currentComposition);
+      currentComposition = null;
+      return chars;
+    }
+    return null;
+  }
+
+  switch (topLevelType) {
+    case topLevelTypes.topPaste:
+      // If a paste event occurs after a keypress, throw out the input
+      // chars. Paste events should not lead to BeforeInput events.
+      return null;
+    case topLevelTypes.topKeyPress:
+      /**
+       * As of v27, Firefox may fire keypress events even when no character
+       * will be inserted. A few possibilities:
+       *
+       * - `which` is `0`. Arrow keys, Esc key, etc.
+       *
+       * - `which` is the pressed key code, but no char is available.
+       *   Ex: 'AltGr + d` in Polish. There is no modified character for
+       *   this key combination and no character is inserted into the
+       *   document, but FF fires the keypress for char code `100` anyway.
+       *   No `input` event will occur.
+       *
+       * - `which` is the pressed key code, but a command combination is
+       *   being used. Ex: `Cmd+C`. No character is inserted, and no
+       *   `input` event will occur.
+       */
+      if (nativeEvent.which && !isKeypressCommand(nativeEvent)) {
+        return String.fromCharCode(nativeEvent.which);
+      }
+      return null;
+    case topLevelTypes.topCompositionEnd:
+      return useFallbackCompositionData ? null : nativeEvent.data;
+    default:
+      return null;
+  }
+}
+
+/**
+ * Extract a SyntheticInputEvent for `beforeInput`, based on either native
+ * `textInput` or fallback behavior.
+ *
+ * @param {string} topLevelType Record from `EventConstants`.
+ * @param {DOMEventTarget} topLevelTarget The listening component root node.
+ * @param {string} topLevelTargetID ID of `topLevelTarget`.
+ * @param {object} nativeEvent Native browser event.
+ * @return {?object} A SyntheticInputEvent.
+ */
+function extractBeforeInputEvent(
+  topLevelType,
+  topLevelTarget,
+  topLevelTargetID,
+  nativeEvent
+) {
+  var chars;
+
+  if (canUseTextInputEvent) {
+    chars = getNativeBeforeInputChars(topLevelType, nativeEvent);
+  } else {
+    chars = getFallbackBeforeInputChars(topLevelType, nativeEvent);
+  }
+
+  // If no characters are being inserted, no BeforeInput event should
+  // be fired.
+  if (!chars) {
+    return null;
+  }
+
+  var event = SyntheticInputEvent.getPooled(
+    eventTypes.beforeInput,
+    topLevelTargetID,
+    nativeEvent
+  );
+
+  event.data = chars;
+  EventPropagators.accumulateTwoPhaseDispatches(event);
+  return event;
+}
+
+/**
+ * Create an `onBeforeInput` event to match
+ * http://www.w3.org/TR/2013/WD-DOM-Level-3-Events-20131105/#events-inputevents.
+ *
+ * This event plugin is based on the native `textInput` event
+ * available in Chrome, Safari, Opera, and IE. This event fires after
+ * `onKeyPress` and `onCompositionEnd`, but before `onInput`.
+ *
+ * `beforeInput` is spec'd but not implemented in any browsers, and
+ * the `input` event does not provide any useful information about what has
+ * actually been added, contrary to the spec. Thus, `textInput` is the best
+ * available event to identify the characters that have actually been inserted
+ * into the target node.
+ *
+ * This plugin is also responsible for emitting `composition` events, thus
+ * allowing us to share composition fallback code for both `beforeInput` and
+ * `composition` event types.
+ */
+var BeforeInputEventPlugin = {
+
+  eventTypes: eventTypes,
+
+  /**
+   * @param {string} topLevelType Record from `EventConstants`.
+   * @param {DOMEventTarget} topLevelTarget The listening component root node.
+   * @param {string} topLevelTargetID ID of `topLevelTarget`.
+   * @param {object} nativeEvent Native browser event.
+   * @return {*} An accumulation of synthetic events.
+   * @see {EventPluginHub.extractEvents}
+   */
+  extractEvents: function(
+    topLevelType,
+    topLevelTarget,
+    topLevelTargetID,
+    nativeEvent
+  ) {
+    return [
+      extractCompositionEvent(
+        topLevelType,
+        topLevelTarget,
+        topLevelTargetID,
+        nativeEvent
+      ),
+      extractBeforeInputEvent(
+        topLevelType,
+        topLevelTarget,
+        topLevelTargetID,
+        nativeEvent
+      )
+    ];
+  }
+};
+
+module.exports = BeforeInputEventPlugin;
+
+},{"106":106,"110":110,"157":157,"16":16,"21":21,"22":22,"23":23}],4:[function(_dereq_,module,exports){
+/**
+ * Copyright 2013-2015, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * @providesModule CSSCore
+ * @typechecks
+ */
+
+var invariant = _dereq_(150);
+
+/**
+ * The CSSCore module specifies the API (and implements most of the methods)
+ * that should be used when dealing with the display of elements (via their
+ * CSS classes and visibility on screen. It is an API focused on mutating the
+ * display and not reading it as no logical state should be encoded in the
+ * display of elements.
+ */
+
+var CSSCore = {
+
+  /**
+   * Adds the class passed in to the element if it doesn't already have it.
+   *
+   * @param {DOMElement} element the element to set the class on
+   * @param {string} className the CSS className
+   * @return {DOMElement} the element passed in
+   */
+  addClass: function(element, className) {
+    ("production" !== "development" ? invariant(
+      !/\s/.test(className),
+      'CSSCore.addClass takes only a single class name. "%s" contains ' +
+      'multiple classes.', className
+    ) : invariant(!/\s/.test(className)));
+
+    if (className) {
+      if (element.classList) {
+        element.classList.add(className);
+      } else if (!CSSCore.hasClass(element, className)) {
+        element.className = element.className + ' ' + className;
+      }
+    }
+    return element;
+  },
+
+  /**
+   * Removes the class passed in from the element
+   *
+   * @param {DOMElement} element the element to set the class on
+   * @param {string} className the CSS className
+   * @return {DOMElement} the element passed in
+   */
+  removeClass: function(element, className) {
+    ("production" !== "development" ? invariant(
+      !/\s/.test(className),
+      'CSSCore.removeClass takes only a single class name. "%s" contains ' +
+      'multiple classes.', className
+    ) : invariant(!/\s/.test(className)));
+
+    if (className) {
+      if (element.classList) {
+        element.classList.remove(className);
+      } else if (CSSCore.hasClass(element, className)) {
+        element.className = element.className
+          .replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)', 'g'), '$1')
+          .replace(/\s+/g, ' ') // multiple spaces to one
+          .replace(/^\s*|\s*$/g, ''); // trim the ends
+      }
+    }
+    return element;
+  },
+
+  /**
+   * Helper to add or remove a class from an element based on a condition.
+   *
+   * @param {DOMElement} element the element to set the class on
+   * @param {string} className the CSS className
+   * @param {*} bool condition to whether to add or remove the class
+   * @return {DOMElement} the element passed in
+   */
+  conditionClass: function(element, className, bool) {
+    return (bool ? CSSCore.addClass : CSSCore.removeClass)(element, className);
+  },
+
+  /**
+   * Tests whether the element has the class specified.
+   *
+   * @param {DOMNode|DOMWindow} element the element to set the class on
+   * @param {string} className the CSS className
+   * @return {boolean} true if the element has the class, false if not
+   */
+  hasClass: function(element, className) {
+    ("production" !== "development" ? invariant(
+      !/\s/.test(className),
+      'CSS.hasClass takes only a single class name.'
+    ) : invariant(!/\s/.test(className)));
+    if (element.classList) {
+      return !!className && element.classList.contains(className);
+    }
+    return (' ' + element.className + ' ').indexOf(' ' + className + ' ') > -1;
+  }
+
+};
+
+module.exports = CSSCore;
+
+},{"150":150}],5:[function(_dereq_,module,exports){
+/**
+ * Copyright 2013-2015, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * @providesModule CSSProperty
+ */
+
+'use strict';
+
+/**
+ * CSS properties which accept numbers but are not in units of "px".
+ */
+var isUnitlessNumber = {
+  boxFlex: true,
+  boxFlexGroup: true,
+  columnCount: true,
+  flex: true,
+  flexGrow: true,
+  flexPositive: true,
+  flexShrink: true,
+  flexNegative: true,
+  fontWeight: true,
+  lineClamp: true,
+  lineHeight: true,
+  opacity: true,
+  order: true,
+  orphans: true,
+  widows: true,
+  zIndex: true,
+  zoom: true,
+
+  // SVG-related properties
+  fillOpacity: true,
+  strokeDashoffset: true,
+  strokeOpacity: true,
+  strokeWidth: true
+};
+
+/**
+ * @param {string} prefix vendor-specific prefix, eg: Webkit
+ * @param {string} key style name, eg: transitionDuration
+ * @return {string} style name prefixed with `prefix`, properly camelCased, eg:
+ * WebkitTransitionDuration
+ */
+function prefixKey(prefix, key) {
+  return prefix + key.charAt(0).toUpperCase() + key.substring(1);
+}
+
+/**
+ * Support style names that may come passed in prefixed by adding permutations
+ * of vendor prefixes.
+ */
+var prefixes = ['Webkit', 'ms', 'Moz', 'O'];
+
+// Using Object.keys here, or else the vanilla for-in loop makes IE8 go into an
+// infinite loop, because it iterates over the newly added props too.
+Object.keys(isUnitlessNumber).forEach(function(prop) {
+  prefixes.forEach(function(prefix) {
+    isUnitlessNumber[prefixKey(prefix, prop)] = isUnitlessNumber[prop];
+  });
+});
+
+/**
+ * Most style properties can be unset by doing .style[prop] = '' but IE8
+ * doesn't like doing that with shorthand properties so for the properties that
+ * IE8 breaks on, which are listed here, we instead unset each of the
+ * individual properties. See http://bugs.jquery.com/ticket/12385.
+ * The 4-value 'clock' properties like margin, padding, border-width seem to
+ * behave without any problems. Curiously, list-style works too without any
+ * special prodding.
+ */
+var shorthandPropertyExpansions = {
+  background: {
+    backgroundImage: true,
+    backgroundPosition: true,
+    backgroundRepeat: true,
+    backgroundColor: true
+  },
+  border: {
+    borderWidth: true,
+    borderStyle: true,
+    borderColor: true
+  },
+  borderBottom: {
+    borderBottomWidth: true,
+    borderBottomStyle: true,
+    borderBottomColor: true
+  },
+  borderLeft: {
+    borderLeftWidth: true,
+    borderLeftStyle: true,
+    borderLeftColor: true
+  },
+  borderRight: {
+    borderRightWidth: true,
+    borderRightStyle: true,
+    borderRightColor: true
+  },
+  borderTop: {
+    borderTopWidth: true,
+    borderTopStyle: true,
+    borderTopColor: true
+  },
+  font: {
+    fontStyle: true,
+    fontVariant: true,
+    fontWeight: true,
+    fontSize: true,
+    lineHeight: true,
+    fontFamily: true
+  }
+};
+
+var CSSProperty = {
+  isUnitlessNumber: isUnitlessNumber,
+  shorthandPropertyExpansions: shorthandPropertyExpansions
+};
+
+module.exports = CSSProperty;
+
+},{}],6:[function(_dereq_,module,exports){
+/**
+ * Copyright 2013-2015, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * @providesModule CSSPropertyOperations
+ * @typechecks static-only
+ */
+
+'use strict';
+
+var CSSProperty = _dereq_(5);
+var ExecutionEnvironment = _dereq_(22);
+
+var camelizeStyleName = _dereq_(121);
+var dangerousStyleValue = _dereq_(128);
+var hyphenateStyleName = _dereq_(148);
+var memoizeStringOnly = _dereq_(159);
+var warning = _dereq_(171);
+
+var processStyleName = memoizeStringOnly(function(styleName) {
+  return hyphenateStyleName(styleName);
+});
+
+var styleFloatAccessor = 'cssFloat';
+if (ExecutionEnvironment.canUseDOM) {
+  // IE8 only supports accessing cssFloat (standard) as styleFloat
+  if (document.documentElement.style.cssFloat === undefined) {
+    styleFloatAccessor = 'styleFloat';
+  }
+}
+
+if ("production" !== "development") {
+  // 'msTransform' is correct, but the other prefixes should be capitalized
+  var badVendoredStyleNamePattern = /^(?:webkit|moz|o)[A-Z]/;
+
+  // style values shouldn't contain a semicolon
+  var badStyleValueWithSemicolonPattern = /;\s*$/;
+
+  var warnedStyleNames = {};
+  var warnedStyleValues = {};
+
+  var warnHyphenatedStyleName = function(name) {
+    if (warnedStyleNames.hasOwnProperty(name) && warnedStyleNames[name]) {
+      return;
+    }
+
+    warnedStyleNames[name] = true;
+    ("production" !== "development" ? warning(
+      false,
+      'Unsupported style property %s. Did you mean %s?',
+      name,
+      camelizeStyleName(name)
+    ) : null);
+  };
+
+  var warnBadVendoredStyleName = function(name) {
+    if (warnedStyleNames.hasOwnProperty(name) && warnedStyleNames[name]) {
+      return;
+    }
+
+    warnedStyleNames[name] = true;
+    ("production" !== "development" ? warning(
+      false,
+      'Unsupported vendor-prefixed style property %s. Did you mean %s?',
+      name,
+      name.charAt(0).toUpperCase() + name.slice(1)
+    ) : null);
+  };
+
+  var warnStyleValueWithSemicolon = function(name, value) {
+    if (warnedStyleValues.hasOwnProperty(value) && warnedStyleValues[value]) {
+      return;
+    }
+
+    warnedStyleValues[value] = true;
+    ("production" !== "development" ? warning(
+      false,
+      'Style property values shouldn\'t contain a semicolon. ' +
+      'Try "%s: %s" instead.',
+      name,
+      value.replace(badStyleValueWithSemicolonPattern, '')
+    ) : null);
+  };
+
+  /**
+   * @param {string} name
+   * @param {*} value
+   */
+  var warnValidStyle = function(name, value) {
+    if (name.indexOf('-') > -1) {
+      warnHyphenatedStyleName(name);
+    } else if (badVendoredStyleNamePattern.test(name)) {
+      warnBadVendoredStyleName(name);
+    } else if (badStyleValueWithSemicolonPattern.test(value)) {
+      warnStyleValueWithSemicolon(name, value);
+    }
+  };
+}
+
+/**
+ * Operations for dealing with CSS properties.
+ */
+var CSSPropertyOperations = {
+
+  /**
+   * Serializes a mapping of style properties for use as inline styles:
+   *
+   *   > createMarkupForStyles({width: '200px', height: 0})
+   *   "width:200px;height:0;"
+   *
+   * Undefined values are ignored so that declarative programming is easier.
+   * The result should be HTML-escaped before insertion into the DOM.
+   *
+   * @param {object} styles
+   * @return {?string}
+   */
+  createMarkupForStyles: function(styles) {
+    var serialized = '';
+    for (var styleName in styles) {
+      if (!styles.hasOwnProperty(styleName)) {
+        continue;
+      }
+      var styleValue = styles[styleName];
+      if ("production" !== "development") {
+        warnValidStyle(styleName, styleValue);
+      }
+      if (styleValue != null) {
+        serialized += processStyleName(styleName) + ':';
+        serialized += dangerousStyleValue(styleName, styleValue) + ';';
+      }
+    }
+    return serialized || null;
+  },
+
+  /**
+   * Sets the value for multiple styles on a node.  If a value is specified as
+   * '' (empty string), the corresponding style property will be unset.
+   *
+   * @param {DOMElement} node
+   * @param {object} styles
+   */
+  setValueForStyles: function(node, styles) {
+    var style = node.style;
+    for (var styleName in styles) {
+      if (!styles.hasOwnProperty(styleName)) {
+        continue;
+      }
+      if ("production" !== "development") {
+        warnValidStyle(styleName, styles[styleName]);
+      }
+      var styleValue = dangerousStyleValue(styleName, styles[styleName]);
+      if (styleName === 'float') {
+        styleName = styleFloatAccessor;
+      }
+      if (styleValue) {
+        style[styleName] = styleValue;
+      } else {
+        var expansion = CSSProperty.shorthandPropertyExpansions[styleName];
+        if (expansion) {
+          // Shorthand property that IE8 won't like unsetting, so unset each
+          // component to placate it
+          for (var individualStyleName in expansion) {
+            style[individualStyleName] = '';
+          }
+        } else {
+          style[styleName] = '';
+        }
+      }
+    }
+  }
+
+};
+
+module.exports = CSSPropertyOperations;
+
+},{"121":121,"128":128,"148":148,"159":159,"171":171,"22":22,"5":5}],7:[function(_dereq_,module,exports){
+/**
+ * Copyright 2013-2015, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * @providesModule CallbackQueue
+ */
+
+'use strict';
+
+var PooledClass = _dereq_(30);
+
+var assign = _dereq_(29);
+var invariant = _dereq_(150);
+
+/**
+ * A specialized pseudo-event module to help keep track of components waiting to
+ * be notified when their DOM representations are available for use.
+ *
+ * This implements `PooledClass`, so you should never need to instantiate this.
+ * Instead, use `CallbackQueue.getPooled()`.
+ *
+ * @class ReactMountReady
+ * @implements PooledClass
+ * @internal
+ */
+function CallbackQueue() {
+  this._callbacks = null;
+  this._contexts = null;
+}
+
+assign(CallbackQueue.prototype, {
+
+  /**
+   * Enqueues a callback to be invoked when `notifyAll` is invoked.
+   *
+   * @param {function} callback Invoked when `notifyAll` is invoked.
+   * @param {?object} context Context to call `callback` with.
+   * @internal
+   */
+  enqueue: function(callback, context) {
+    this._callbacks = this._callbacks || [];
+    this._contexts = this._contexts || [];
+    this._callbacks.push(callback);
+    this._contexts.push(context);
+  },
+
+  /**
+   * Invokes all enqueued callbacks and clears the queue. This is invoked after
+   * the DOM representation of a component has been created or updated.
+   *
+   * @internal
+   */
+  notifyAll: function() {
+    var callbacks = this._callbacks;
+    var contexts = this._contexts;
+    if (callbacks) {
+      ("production" !== "development" ? invariant(
+        callbacks.length === contexts.length,
+        'Mismatched list of contexts in callback queue'
+      ) : invariant(callbacks.length === contexts.length));
+      this._callbacks = null;
+      this._contexts = null;
+      for (var i = 0, l = callbacks.length; i < l; i++) {
+        callbacks[i].call(contexts[i]);
+      }
+      callbacks.length = 0;
+      contexts.length = 0;
+    }
+  },
+
+  /**
+   * Resets the internal queue.
+   *
+   * @internal
+   */
+  reset: function() {
+    this._callbacks = null;
+    this._contexts = null;
+  },
+
+  /**
+   * `PooledClass` looks for this.
+   */
+  destructor: function() {
+    this.reset();
+  }
+
+});
+
+PooledClass.addPoolingTo(CallbackQueue);
+
+module.exports = CallbackQueue;
+
+},{"150":150,"29":29,"30":30}],8:[function(_dereq_,module,exports){
+/**
+ * Copyright 2013-2015, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * @providesModule ChangeEventPlugin
+ */
+
+'use strict';
+
+var EventConstants = _dereq_(16);
+var EventPluginHub = _dereq_(18);
+var EventPropagators = _dereq_(21);
+var ExecutionEnvironment = _dereq_(22);
+var ReactUpdates = _dereq_(100);
+var SyntheticEvent = _dereq_(108);
+
+var isEventSupported = _dereq_(151);
+var isTextInputElement = _dereq_(153);
+var keyOf = _dereq_(157);
+
+var topLevelTypes = EventConstants.topLevelTypes;
+
+var eventTypes = {
+  change: {
+    phasedRegistrationNames: {
+      bubbled: keyOf({onChange: null}),
+      captured: keyOf({onChangeCapture: null})
+    },
+    dependencies: [
+      topLevelTypes.topBlur,
+      topLevelTypes.topChange,
+      topLevelTypes.topClick,
+      topLevelTypes.topFocus,
+      topLevelTypes.topInput,
+      topLevelTypes.topKeyDown,
+      topLevelTypes.topKeyUp,
+      topLevelTypes.topSelectionChange
+    ]
+  }
+};
+
+/**
+ * For IE shims
+ */
+var activeElement = null;
+var activeElementID = null;
+var activeElementValue = null;
+var activeElementValueProp = null;
+
+/**
+ * SECTION: handle `change` event
+ */
+function shouldUseChangeEvent(elem) {
+  return (
+    elem.nodeName === 'SELECT' ||
+    (elem.nodeName === 'INPUT' && elem.type === 'file')
+  );
+}
+
+var doesChangeEventBubble = false;
+if (ExecutionEnvironment.canUseDOM) {
+  // See `handleChange` comment below
+  doesChangeEventBubble = isEventSupported('change') && (
+    (!('documentMode' in document) || document.documentMode > 8)
+  );
+}
+
+function manualDispatchChangeEvent(nativeEvent) {
+  var event = SyntheticEvent.getPooled(
+    eventTypes.change,
+    activeElementID,
+    nativeEvent
+  );
+  EventPropagators.accumulateTwoPhaseDispatches(event);
+
+  // If change and propertychange bubbled, we'd just bind to it like all the
+  // other events and have it go through ReactBrowserEventEmitter. Since it
+  // doesn't, we manually listen for the events and so we have to enqueue and
+  // process the abstract event manually.
+  //
+  // Batching is necessary here in order to ensure that all event handlers run
+  // before the next rerender (including event handlers attached to ancestor
+  // elements instead of directly on the input). Without this, controlled
+  // components don't work properly in conjunction with event bubbling because
+  // the component is rerendered and the value reverted before all the event
+  // handlers can run. See https://github.com/facebook/react/issues/708.
+  ReactUpdates.batchedUpdates(runEventInBatch, event);
+}
+
+function runEventInBatch(event) {
+  EventPluginHub.enqueueEvents(event);
+  EventPluginHub.processEventQueue();
+}
+
+function startWatchingForChangeEventIE8(target, targetID) {
+  activeElement = target;
+  activeElementID = targetID;
+  activeElement.attachEvent('onchange', manualDispatchChangeEvent);
+}
+
+function stopWatchingForChangeEventIE8() {
+  if (!activeElement) {
+    return;
+  }
+  activeElement.detachEvent('onchange', manualDispatchChangeEvent);
+  activeElement = null;
+  activeElementID = null;
+}
+
+function getTargetIDForChangeEvent(
+    topLevelType,
+    topLevelTarget,
+    topLevelTargetID) {
+  if (topLevelType === topLevelTypes.topChange) {
+    return topLevelTargetID;
+  }
+}
+function handleEventsForChangeEventIE8(
+    topLevelType,
+    topLevelTarget,
+    topLevelTargetID) {
+  if (topLevelType === topLevelTypes.topFocus) {
+    // stopWatching() should be a noop here but we call it just in case we
+    // missed a blur event somehow.
+    stopWatchingForChangeEventIE8();
+    startWatchingForChangeEventIE8(topLevelTarget, topLevelTargetID);
+  } else if (topLevelType === topLevelTypes.topBlur) {
+    stopWatchingForChangeEventIE8();
+  }
+}
+
+
+/**
+ * SECTION: handle `input` event
+ */
+var isInputEventSupported = false;
+if (ExecutionEnvironment.canUseDOM) {
+  // IE9 claims to support the input event but fails to trigger it when
+  // deleting text, so we ignore its input events
+  isInputEventSupported = isEventSupported('input') && (
+    (!('documentMode' in document) || document.documentMode > 9)
+  );
+}
+
+/**
+ * (For old IE.) Replacement getter/setter for the `value` property that gets
+ * set on the active element.
+ */
+var newValueProp =  {
+  get: function() {
+    return activeElementValueProp.get.call(this);
+  },
+  set: function(val) {
+    // Cast to a string so we can do equality checks.
+    activeElementValue = '' + val;
+    activeElementValueProp.set.call(this, val);
+  }
+};
+
+/**
+ * (For old IE.) Starts tracking propertychange events on the passed-in element
+ * and override the value property so that we can distinguish user events from
+ * value changes in JS.
+ */
+function startWatchingForValueChange(target, targetID) {
+  activeElement = target;
+  activeElementID = targetID;
+  activeElementValue = target.value;
+  activeElementValueProp = Object.getOwnPropertyDescriptor(
+    target.constructor.prototype,
+    'value'
+  );
+
+  Object.defineProperty(activeElement, 'value', newValueProp);
+  activeElement.attachEvent('onpropertychange', handlePropertyChange);
+}
+
+/**
+ * (For old IE.) Removes the event listeners from the currently-tracked element,
+ * if any exists.
+ */
+function stopWatchingForValueChange() {
+  if (!activeElement) {
+    return;
+  }
+
+  // delete restores the original property definition
+  delete activeElement.value;
+  activeElement.detachEvent('onpropertychange', handlePropertyChange);
+
+  activeElement = null;
+  activeElementID = null;
+  activeElementValue = null;
+  activeElementValueProp = null;
+}
+
+/**
+ * (For old IE.) Handles a propertychange event, sending a `change` event if
+ * the value of the active element has changed.
+ */
+function handlePropertyChange(nativeEvent) {
+  if (nativeEvent.propertyName !== 'value') {
+    return;
+  }
+  var value = nativeEvent.srcElement.value;
+  if (value === activeElementValue) {
+    return;
+  }
+  activeElementValue = value;
+
+  manualDispatchChangeEvent(nativeEvent);
+}
+
+/**
+ * If a `change` event should be fired, returns the target's ID.
+ */
+function getTargetIDForInputEvent(
+    topLevelType,
+    topLevelTarget,
+    topLevelTargetID) {
+  if (topLevelType === topLevelTypes.topInput) {
+    // In modern browsers (i.e., not IE8 or IE9), the input event is exactly
+    // what we want so fall through here and trigger an abstract event
+    return topLevelTargetID;
+  }
+}
+
+// For IE8 and IE9.
+function handleEventsForInputEventIE(
+    topLevelType,
+    topLevelTarget,
+    topLevelTargetID) {
+  if (topLevelType === topLevelTypes.topFocus) {
+    // In IE8, we can capture almost all .value changes by adding a
+    // propertychange handler and looking for events with propertyName
+    // equal to 'value'
+    // In IE9, propertychange fires for most input events but is buggy and
+    // doesn't fire when text is deleted, but conveniently, selectionchange
+    // appears to fire in all of the remaining cases so we catch those and
+    // forward the event if the value has changed
+    // In either case, we don't want to call the event handler if the value
+    // is changed from JS so we redefine a setter for `.value` that updates
+    // our activeElementValue variable, allowing us to ignore those changes
+    //
+    // stopWatching() should be a noop here but we call it just in case we
+    // missed a blur event somehow.
+    stopWatchingForValueChange();
+    startWatchingForValueChange(topLevelTarget, topLevelTargetID);
+  } else if (topLevelType === topLevelTypes.topBlur) {
+    stopWatchingForValueChange();
+  }
+}
+
+// For IE8 and IE9.
+function getTargetIDForInputEventIE(
+    topLevelType,
+    topLevelTarget,
+    topLevelTargetID) {
+  if (topLevelType === topLevelTypes.topSelectionChange ||
+      topLevelType === topLevelTypes.topKeyUp ||
+      topLevelType === topLevelTypes.topKeyDown) {
+    // On the selectionchange event, the target is just document which isn't
+    // helpful for us so just check activeElement instead.
+    //
+    // 99% of the time, keydown and keyup aren't necessary. IE8 fails to fire
+    // propertychange on the first input event after setting `value` from a
+    // script and fires only keydown, keypress, keyup. Catching keyup usually
+    // gets it and catching keydown lets us fire an event for the first
+    // keystroke if user does a key repeat (it'll be a little delayed: right
+    // before the second keystroke). Other input methods (e.g., paste) seem to
+    // fire selectionchange normally.
+    if (activeElement && activeElement.value !== activeElementValue) {
+      activeElementValue = activeElement.value;
+      return activeElementID;
+    }
+  }
+}
+
+
+/**
+ * SECTION: handle `click` event
+ */
+function shouldUseClickEvent(elem) {
+  // Use the `click` event to detect changes to checkbox and radio inputs.
+  // This approach works across all browsers, whereas `change` does not fire
+  // until `blur` in IE8.
+  return (
+    elem.nodeName === 'INPUT' &&
+    (elem.type === 'checkbox' || elem.type === 'radio')
+  );
+}
+
+function getTargetIDForClickEvent(
+    topLevelType,
+    topLevelTarget,
+    topLevelTargetID) {
+  if (topLevelType === topLevelTypes.topClick) {
+    return topLevelTargetID;
+  }
+}
+
+/**
+ * This plugin creates an `onChange` event that normalizes change events
+ * across form elements. This event fires at a time when it's possible to
+ * change the element's value without seeing a flicker.
+ *
+ * Supported elements are:
+ * - input (see `isTextInputElement`)
+ * - textarea
+ * - select
+ */
+var ChangeEventPlugin = {
+
+  eventTypes: eventTypes,
+
+  /**
+   * @param {string} topLevelType Record from `EventConstants`.
+   * @param {DOMEventTarget} topLevelTarget The listening component root node.
+   * @param {string} topLevelTargetID ID of `topLevelTarget`.
+   * @param {object} nativeEvent Native browser event.
+   * @return {*} An accumulation of synthetic events.
+   * @see {EventPluginHub.extractEvents}
+   */
+  extractEvents: function(
+      topLevelType,
+      topLevelTarget,
+      topLevelTargetID,
+      nativeEvent) {
+
+    var getTargetIDFunc, handleEventFunc;
+    if (shouldUseChangeEvent(topLevelTarget)) {
+      if (doesChangeEventBubble) {
+        getTargetIDFunc = getTargetIDForChangeEvent;
+      } else {
+        handleEventFunc = handleEventsForChangeEventIE8;
+      }
+    } else if (isTextInputElement(topLevelTarget)) {
+      if (isInputEventSupported) {
+        getTargetIDFunc = getTargetIDForInputEvent;
+      } else {
+        getTargetIDFunc = getTargetIDForInputEventIE;
+        handleEventFunc = handleEventsForInputEventIE;
+      }
+    } else if (shouldUseClickEvent(topLevelTarget)) {
+      getTargetIDFunc = getTargetIDForClickEvent;
+    }
+
+    if (getTargetIDFunc) {
+      var targetID = getTargetIDFunc(
+        topLevelType,
+        topLevelTarget,
+        topLevelTargetID
+      );
+      if (targetID) {
+        var event = SyntheticEvent.getPooled(
+          eventTypes.change,
+          targetID,
+          nativeEvent
+        );
+        EventPropagators.accumulateTwoPhaseDispatches(event);
+        return event;
+      }
+    }
+
+    if (handleEventFunc) {
+      handleEventFunc(
+        topLevelType,
+        topLevelTarget,
+        topLevelTargetID
+      );
+    }
+  }
+
+};
+
+module.exports = ChangeEventPlugin;
+
+},{"100":100,"108":108,"151":151,"153":153,"157":157,"16":16,"18":18,"21":21,"22":22}],9:[function(_dereq_,module,exports){
+/**
+ * Copyright 2013-2015, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * @providesModule ClientReactRootIndex
+ * @typechecks
+ */
+
+'use strict';
+
+var nextReactRootIndex = 0;
+
+var ClientReactRootIndex = {
+  createReactRootIndex: function() {
+    return nextReactRootIndex++;
+  }
+};
+
+module.exports = ClientReactRootIndex;
+
+},{}],10:[function(_dereq_,module,exports){
+/**
+ * Copyright 2013-2015, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * @providesModule DOMChildrenOperations
+ * @typechecks static-only
+ */
+
+'use strict';
+
+var Danger = _dereq_(13);
+var ReactMultiChildUpdateTypes = _dereq_(79);
+
+var setTextContent = _dereq_(165);
+var invariant = _dereq_(150);
+
+/**
+ * Inserts `childNode` as a child of `parentNode` at the `index`.
+ *
+ * @param {DOMElement} parentNode Parent node in which to insert.
+ * @param {DOMElement} childNode Child node to insert.
+ * @param {number} index Index at which to insert the child.
+ * @internal
+ */
+function insertChildAt(parentNode, childNode, index) {
+  // By exploiting arrays returning `undefined` for an undefined index, we can
+  // rely exclusively on `insertBefore(node, null)` instead of also using
+  // `appendChild(node)`. However, using `undefined` is not allowed by all
+  // browsers so we must replace it with `null`.
+  parentNode.insertBefore(
+    childNode,
+    parentNode.childNodes[index] || null
+  );
+}
+
+/**
+ * Operations for updating with DOM children.
+ */
+var DOMChildrenOperations = {
+
+  dangerouslyReplaceNodeWithMarkup: Danger.dangerouslyReplaceNodeWithMarkup,
+
+  updateTextContent: setTextContent,
+
+  /**
+   * Updates a component's children by processing a series of updates. The
+   * update configurations are each expected to have a `parentNode` property.
+   *
+   * @param {array<object>} updates List of update configurations.
+   * @param {array<string>} markupList List of markup strings.
+   * @internal
+   */
+  processUpdates: function(updates, markupList) {
+    var update;
+    // Mapping from parent IDs to initial child orderings.
+    var initialChildren = null;
+    // List of children that will be moved or removed.
+    var updatedChildren = null;
+
+    for (var i = 0; i < updates.length; i++) {
+      update = updates[i];
+      if (update.type === ReactMultiChildUpdateTypes.MOVE_EXISTING ||
+          update.type === ReactMultiChildUpdateTypes.REMOVE_NODE) {
+        var updatedIndex = update.fromIndex;
+        var updatedChild = update.parentNode.childNodes[updatedIndex];
+        var parentID = update.parentID;
+
+        ("production" !== "development" ? invariant(
+          updatedChild,
+          'processUpdates(): Unable to find child %s of element. This ' +
+          'probably means the DOM was unexpectedly mutated (e.g., by the ' +
+          'browser), usually due to forgetting a <tbody> when using tables, ' +
+          'nesting tags like <form>, <p>, or <a>, or using non-SVG elements ' +
+          'in an <svg> parent. Try inspecting the child nodes of the element ' +
+          'with React ID `%s`.',
+          updatedIndex,
+          parentID
+        ) : invariant(updatedChild));
+
+        initialChildren = initialChildren || {};
+        initialChildren[parentID] = initialChildren[parentID] || [];
+        initialChildren[parentID][updatedIndex] = updatedChild;
+
+        updatedChildren = updatedChildren || [];
+        updatedChildren.push(updatedChild);
+      }
+    }
+
+    var renderedMarkup = Danger.dangerouslyRenderMarkup(markupList);
+
+    // Remove updated children first so that `toIndex` is consistent.
+    if (updatedChildren) {
+      for (var j = 0; j < updatedChildren.length; j++) {
+        updatedChildren[j].parentNode.removeChild(updatedChildren[j]);
+      }
+    }
+
+    for (var k = 0; k < updates.length; k++) {
+      update = updates[k];
+      switch (update.type) {
+        case ReactMultiChildUpdateTypes.INSERT_MARKUP:
+          insertChildAt(
+            update.parentNode,
+            renderedMarkup[update.markupIndex],
+            update.toIndex
+          );
+          break;
+        case ReactMultiChildUpdateTypes.MOVE_EXISTING:
+          insertChildAt(
+            update.parentNode,
+            initialChildren[update.parentID][update.fromIndex],
+            update.toIndex
+          );
+          break;
+        case ReactMultiChildUpdateTypes.TEXT_CONTENT:
+          setTextContent(
+            update.parentNode,
+            update.textContent
+          );
+          break;
+        case ReactMultiChildUpdateTypes.REMOVE_NODE:
+          // Already removed by the for-loop above.
+          break;
+      }
+    }
+  }
+
+};
+
+module.exports = DOMChildrenOperations;
+
+},{"13":13,"150":150,"165":165,"79":79}],11:[function(_dereq_,module,exports){
+/**
+ * Copyright 2013-2015, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * @providesModule DOMProperty
+ * @typechecks static-only
+ */
+
+/*jslint bitwise: true */
+
+'use strict';
+
+var invariant = _dereq_(150);
+
+function checkMask(value, bitmask) {
+  return (value & bitmask) === bitmask;
+}
+
+var DOMPropertyInjection = {
+  /**
+   * Mapping from normalized, camelcased property names to a configuration that
+   * specifies how the associated DOM property should be accessed or rendered.
+   */
+  MUST_USE_ATTRIBUTE: 0x1,
+  MUST_USE_PROPERTY: 0x2,
+  HAS_SIDE_EFFECTS: 0x4,
+  HAS_BOOLEAN_VALUE: 0x8,
+  HAS_NUMERIC_VALUE: 0x10,
+  HAS_POSITIVE_NUMERIC_VALUE: 0x20 | 0x10,
+  HAS_OVERLOADED_BOOLEAN_VALUE: 0x40,
+
+  /**
+   * Inject some specialized knowledge about the DOM. This takes a config object
+   * with the following properties:
+   *
+   * isCustomAttribute: function that given an attribute name will return true
+   * if it can be inserted into the DOM verbatim. Useful for data-* or aria-*
+   * attributes where it's impossible to enumerate all of the possible
+   * attribute names,
+   *
+   * Properties: object mapping DOM property name to one of the
+   * DOMPropertyInjection constants or null. If your attribute isn't in here,
+   * it won't get written to the DOM.
+   *
+   * DOMAttributeNames: object mapping React attribute name to the DOM
+   * attribute name. Attribute names not specified use the **lowercase**
+   * normalized name.
+   *
+   * DOMPropertyNames: similar to DOMAttributeNames but for DOM properties.
+   * Property names not specified use the normalized name.
+   *
+   * DOMMutationMethods: Properties that require special mutation methods. If
+   * `value` is undefined, the mutation method should unset the property.
+   *
+   * @param {object} domPropertyConfig the config as described above.
+   */
+  injectDOMPropertyConfig: function(domPropertyConfig) {
+    var Properties = domPropertyConfig.Properties || {};
+    var DOMAttributeNames = domPropertyConfig.DOMAttributeNames || {};
+    var DOMPropertyNames = domPropertyConfig.DOMPropertyNames || {};
+    var DOMMutationMethods = domPropertyConfig.DOMMutationMethods || {};
+
+    if (domPropertyConfig.isCustomAttribute) {
+      DOMProperty._isCustomAttributeFunctions.push(
+        domPropertyConfig.isCustomAttribute
+      );
+    }
+
+    for (var propName in Properties) {
+      ("production" !== "development" ? invariant(
+        !DOMProperty.isStandardName.hasOwnProperty(propName),
+        'injectDOMPropertyConfig(...): You\'re trying to inject DOM property ' +
+        '\'%s\' which has already been injected. You may be accidentally ' +
+        'injecting the same DOM property config twice, or you may be ' +
+        'injecting two configs that have conflicting property names.',
+        propName
+      ) : invariant(!DOMProperty.isStandardName.hasOwnProperty(propName)));
+
+      DOMProperty.isStandardName[propName] = true;
+
+      var lowerCased = propName.toLowerCase();
+      DOMProperty.getPossibleStandardName[lowerCased] = propName;
+
+      if (DOMAttributeNames.hasOwnProperty(propName)) {
+        var attributeName = DOMAttributeNames[propName];
+        DOMProperty.getPossibleStandardName[attributeName] = propName;
+        DOMProperty.getAttributeName[propName] = attributeName;
+      } else {
+        DOMProperty.getAttributeName[propName] = lowerCased;
+      }
+
+      DOMProperty.getPropertyName[propName] =
+        DOMPropertyNames.hasOwnProperty(propName) ?
+          DOMPropertyNames[propName] :
+          propName;
+
+      if (DOMMutationMethods.hasOwnProperty(propName)) {
+        DOMProperty.getMutationMethod[propName] = DOMMutationMethods[propName];
+      } else {
+        DOMProperty.getMutationMethod[propName] = null;
+      }
+
+      var propConfig = Properties[propName];
+      DOMProperty.mustUseAttribute[propName] =
+        checkMask(propConfig, DOMPropertyInjection.MUST_USE_ATTRIBUTE);
+      DOMProperty.mustUseProperty[propName] =
+        checkMask(propConfig, DOMPropertyInjection.MUST_USE_PROPERTY);
+      DOMProperty.hasSideEffects[propName] =
+        checkMask(propConfig, DOMPropertyInjection.HAS_SIDE_EFFECTS);
+      DOMProperty.hasBooleanValue[propName] =
+        checkMask(propConfig, DOMPropertyInjection.HAS_BOOLEAN_VALUE);
+      DOMProperty.hasNumericValue[propName] =
+        checkMask(propConfig, DOMPropertyInjection.HAS_NUMERIC_VALUE);
+      DOMProperty.hasPositiveNumericValue[propName] =
+        checkMask(propConfig, DOMPropertyInjection.HAS_POSITIVE_NUMERIC_VALUE);
+      DOMProperty.hasOverloadedBooleanValue[propName] =
+        checkMask(propConfig, DOMPropertyInjection.HAS_OVERLOADED_BOOLEAN_VALUE);
+
+      ("production" !== "development" ? invariant(
+        !DOMProperty.mustUseAttribute[propName] ||
+          !DOMProperty.mustUseProperty[propName],
+        'DOMProperty: Cannot require using both attribute and property: %s',
+        propName
+      ) : invariant(!DOMProperty.mustUseAttribute[propName] ||
+        !DOMProperty.mustUseProperty[propName]));
+      ("production" !== "development" ? invariant(
+        DOMProperty.mustUseProperty[propName] ||
+          !DOMProperty.hasSideEffects[propName],
+        'DOMProperty: Properties that have side effects must use property: %s',
+        propName
+      ) : invariant(DOMProperty.mustUseProperty[propName] ||
+        !DOMProperty.hasSideEffects[propName]));
+      ("production" !== "development" ? invariant(
+        !!DOMProperty.hasBooleanValue[propName] +
+          !!DOMProperty.hasNumericValue[propName] +
+          !!DOMProperty.hasOverloadedBooleanValue[propName] <= 1,
+        'DOMProperty: Value can be one of boolean, overloaded boolean, or ' +
+        'numeric value, but not a combination: %s',
+        propName
+      ) : invariant(!!DOMProperty.hasBooleanValue[propName] +
+        !!DOMProperty.hasNumericValue[propName] +
+        !!DOMProperty.hasOverloadedBooleanValue[propName] <= 1));
+    }
+  }
+};
+var defaultValueCache = {};
+
+/**
+ * DOMProperty exports lookup objects that can be used like functions:
+ *
+ *   > DOMProperty.isValid['id']
+ *   true
+ *   > DOMProperty.isValid['foobar']
+ *   undefined
+ *
+ * Although this may be confusing, it performs better in general.
+ *
+ * @see http://jsperf.com/key-exists
+ * @see http://jsperf.com/key-missing
+ */
+var DOMProperty = {
+
+  ID_ATTRIBUTE_NAME: 'data-reactid',
+
+  /**
+   * Checks whether a property name is a standard property.
+   * @type {Object}
+   */
+  isStandardName: {},
+
+  /**
+   * Mapping from lowercase property names to the properly cased version, used
+   * to warn in the case of missing properties.
+   * @type {Object}
+   */
+  getPossibleStandardName: {},
+
+  /**
+   * Mapping from normalized names to attribute names that differ. Attribute
+   * names are used when rendering markup or with `*Attribute()`.
+   * @type {Object}
+   */
+  getAttributeName: {},
+
+  /**
+   * Mapping from normalized names to properties on DOM node instances.
+   * (This includes properties that mutate due to external factors.)
+   * @type {Object}
+   */
+  getPropertyName: {},
+
+  /**
+   * Mapping from normalized names to mutation methods. This will only exist if
+   * mutation cannot be set simply by the property or `setAttribute()`.
+   * @type {Object}
+   */
+  getMutationMethod: {},
+
+  /**
+   * Whether the property must be accessed and mutated as an object property.
+   * @type {Object}
+   */
+  mustUseAttribute: {},
+
+  /**
+   * Whether the property must be accessed and mutated using `*Attribute()`.
+   * (This includes anything that fails `<propName> in <element>`.)
+   * @type {Object}
+   */
+  mustUseProperty: {},
+
+  /**
+   * Whether or not setting a value causes side effects such as triggering
+   * resources to be loaded or text selection changes. We must ensure that
+   * the value is only set if it has changed.
+   * @type {Object}
+   */
+  hasSideEffects: {},
+
+  /**
+   * Whether the property should be removed when set to a falsey value.
+   * @type {Object}
+   */
+  hasBooleanValue: {},
+
+  /**
+   * Whether the property must be numeric or parse as a
+   * numeric and should be removed when set to a falsey value.
+   * @type {Object}
+   */
+  hasNumericValue: {},
+
+  /**
+   * Whether the property must be positive numeric or parse as a positive
+   * numeric and should be removed when set to a falsey value.
+   * @type {Object}
+   */
+  hasPositiveNumericValue: {},
+
+  /**
+   * Whether the property can be used as a flag as well as with a value. Removed
+   * when strictly equal to false; present without a value when strictly equal
+   * to true; present with a value otherwise.
+   * @type {Object}
+   */
+  hasOverloadedBooleanValue: {},
+
+  /**
+   * All of the isCustomAttribute() functions that have been injected.
+   */
+  _isCustomAttributeFunctions: [],
+
+  /**
+   * Checks whether a property name is a custom attribute.
+   * @method
+   */
+  isCustomAttribute: function(attributeName) {
+    for (var i = 0; i < DOMProperty._isCustomAttributeFunctions.length; i++) {
+      var isCustomAttributeFn = DOMProperty._isCustomAttributeFunctions[i];
+      if (isCustomAttributeFn(attributeName)) {
+        return true;
+      }
+    }
+    return false;
+  },
+
+  /**
+   * Returns the default property value for a DOM property (i.e., not an
+   * attribute). Most default values are '' or false, but not all. Worse yet,
+   * some (in particular, `type`) vary depending on the type of element.
+   *
+   * TODO: Is it better to grab all the possible properties when creating an
+   * element to avoid having to create the same element twice?
+   */
+  getDefaultValueForProperty: function(nodeName, prop) {
+    var nodeDefaults = defaultValueCache[nodeName];
+    var testElement;
+    if (!nodeDefaults) {
+      defaultValueCache[nodeName] = nodeDefaults = {};
+    }
+    if (!(prop in nodeDefaults)) {
+      testElement = document.createElement(nodeName);
+      nodeDefaults[prop] = testElement[prop];
+    }
+    return nodeDefaults[prop];
+  },
+
+  injection: DOMPropertyInjection
+};
+
+module.exports = DOMProperty;
+
+},{"150":150}],12:[function(_dereq_,module,exports){
+/**
+ * Copyright 2013-2015, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * @providesModule DOMPropertyOperations
+ * @typechecks static-only
+ */
+
+'use strict';
+
+var DOMProperty = _dereq_(11);
+
+var quoteAttributeValueForBrowser = _dereq_(163);
+var warning = _dereq_(171);
+
+function shouldIgnoreValue(name, value) {
+  return value == null ||
+    (DOMProperty.hasBooleanValue[name] && !value) ||
+    (DOMProperty.hasNumericValue[name] && isNaN(value)) ||
+    (DOMProperty.hasPositiveNumericValue[name] && (value < 1)) ||
+    (DOMProperty.hasOverloadedBooleanValue[name] && value === false);
+}
+
+if ("production" !== "development") {
+  var reactProps = {
+    children: true,
+    dangerouslySetInnerHTML: true,
+    key: true,
+    ref: true
+  };
+  var warnedProperties = {};
+
+  var warnUnknownProperty = function(name) {
+    if (reactProps.hasOwnProperty(name) && reactProps[name] ||
+        warnedProperties.hasOwnProperty(name) && warnedProperties[name]) {
+      return;
+    }
+
+    warnedProperties[name] = true;
+    var lowerCasedName = name.toLowerCase();
+
+    // data-* attributes should be lowercase; suggest the lowercase version
+    var standardName = (
+      DOMProperty.isCustomAttribute(lowerCasedName) ?
+        lowerCasedName :
+      DOMProperty.getPossibleStandardName.hasOwnProperty(lowerCasedName) ?
+        DOMProperty.getPossibleStandardName[lowerCasedName] :
+        null
+    );
+
+    // For now, only warn when we have a suggested correction. This prevents
+    // logging too much when using transferPropsTo.
+    ("production" !== "development" ? warning(
+      standardName == null,
+      'Unknown DOM property %s. Did you mean %s?',
+      name,
+      standardName
+    ) : null);
+
+  };
+}
+
+/**
+ * Operations for dealing with DOM properties.
+ */
+var DOMPropertyOperations = {
+
+  /**
+   * Creates markup for the ID property.
+   *
+   * @param {string} id Unescaped ID.
+   * @return {string} Markup string.
+   */
+  createMarkupForID: function(id) {
+    return DOMProperty.ID_ATTRIBUTE_NAME + '=' +
+      quoteAttributeValueForBrowser(id);
+  },
+
+  /**
+   * Creates markup for a property.
+   *
+   * @param {string} name
+   * @param {*} value
+   * @return {?string} Markup string, or null if the property was invalid.
+   */
+  createMarkupForProperty: function(name, value) {
+    if (DOMProperty.isStandardName.hasOwnProperty(name) &&
+        DOMProperty.isStandardName[name]) {
+      if (shouldIgnoreValue(name, value)) {
+        return '';
+      }
+      var attributeName = DOMProperty.getAttributeName[name];
+      if (DOMProperty.hasBooleanValue[name] ||
+          (DOMProperty.hasOverloadedBooleanValue[name] && value === true)) {
+        return attributeName;
+      }
+      return attributeName + '=' + quoteAttributeValueForBrowser(value);
+    } else if (DOMProperty.isCustomAttribute(name)) {
+      if (value == null) {
+        return '';
+      }
+      return name + '=' + quoteAttributeValueForBrowser(value);
+    } else if ("production" !== "development") {
+      warnUnknownProperty(name);
+    }
+    return null;
+  },
+
+  /**
+   * Sets the value for a property on a node.
+   *
+   * @param {DOMElement} node
+   * @param {string} name
+   * @param {*} value
+   */
+  setValueForProperty: function(node, name, value) {
+    if (DOMProperty.isStandardName.hasOwnProperty(name) &&
+        DOMProperty.isStandardName[name]) {
+      var mutationMethod = DOMProperty.getMutationMethod[name];
+      if (mutationMethod) {
+        mutationMethod(node, value);
+      } else if (shouldIgnoreValue(name, value)) {
+        this.deleteValueForProperty(node, name);
+      } else if (DOMProperty.mustUseAttribute[name]) {
+        // `setAttribute` with objects becomes only `[object]` in IE8/9,
+        // ('' + value) makes it output the correct toString()-value.
+        node.setAttribute(DOMProperty.getAttributeName[name], '' + value);
+      } else {
+        var propName = DOMProperty.getPropertyName[name];
+        // Must explicitly cast values for HAS_SIDE_EFFECTS-properties to the
+        // property type before comparing; only `value` does and is string.
+        if (!DOMProperty.hasSideEffects[name] ||
+            ('' + node[propName]) !== ('' + value)) {
+          // Contrary to `setAttribute`, object properties are properly
+          // `toString`ed by IE8/9.
+          node[propName] = value;
+        }
+      }
+    } else if (DOMProperty.isCustomAttribute(name)) {
+      if (value == null) {
+        node.removeAttribute(name);
+      } else {
+        node.setAttribute(name, '' + value);
+      }
+    } else if ("production" !== "development") {
+      warnUnknownProperty(name);
+    }
+  },
+
+  /**
+   * Deletes the value for a property on a node.
+   *
+   * @param {DOMElement} node
+   * @param {string} name
+   */
+  deleteValueForProperty: function(node, name) {
+    if (DOMProperty.isStandardName.hasOwnProperty(name) &&
+        DOMProperty.isStandardName[name]) {
+      var mutationMethod = DOMProperty.getMutationMethod[name];
+      if (mutationMethod) {
+        mutationMethod(node, undefined);
+      } else if (DOMProperty.mustUseAttribute[name]) {
+        node.removeAttribute(DOMProperty.getAttributeName[name]);
+      } else {
+        var propName = DOMProperty.getPropertyName[name];
+        var defaultValue = DOMProperty.getDefaultValueForProperty(
+          node.nodeName,
+          propName
+        );
+        if (!DOMProperty.hasSideEffects[name] ||
+            ('' + node[propName]) !== defaultValue) {
+          node[propName] = defaultValue;
+        }
+      }
+    } else if (DOMProperty.isCustomAttribute(name)) {
+      node.removeAttribute(name);
+    } else if ("production" !== "development") {
+      warnUnknownProperty(name);
+    }
+  }
+
+};
+
+module.exports = DOMPropertyOperations;
+
+},{"11":11,"163":163,"171":171}],13:[function(_dereq_,module,exports){
+/**
+ * Copyright 2013-2015, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * @providesModule Danger
+ * @typechecks static-only
+ */
+
+/*jslint evil: true, sub: true */
+
+'use strict';
+
+var ExecutionEnvironment = _dereq_(22);
+
+var createNodesFromMarkup = _dereq_(126);
+var emptyFunction = _dereq_(129);
+var getMarkupWrap = _dereq_(142);
+var invariant = _dereq_(150);
+
+var OPEN_TAG_NAME_EXP = /^(<[^ \/>]+)/;
+var RESULT_INDEX_ATTR = 'data-danger-index';
+
+/**
+ * Extracts the `nodeName` from a string of markup.
+ *
+ * NOTE: Extracting the `nodeName` does not require a regular expression match
+ * because we make assumptions about React-generated markup (i.e. there are no
+ * spaces surrounding the opening tag and there is at least one attribute).
+ *
+ * @param {string} markup String of markup.
+ * @return {string} Node name of the supplied markup.
+ * @see http://jsperf.com/extract-nodename
+ */
+function getNodeName(markup) {
+  return markup.substring(1, markup.indexOf(' '));
+}
+
+var Danger = {
+
+  /**
+   * Renders markup into an array of nodes. The markup is expected to render
+   * into a list of root nodes. Also, the length of `resultList` and
+   * `markupList` should be the same.
+   *
+   * @param {array<string>} markupList List of markup strings to render.
+   * @return {array<DOMElement>} List of rendered nodes.
+   * @internal
+   */
+  dangerouslyRenderMarkup: function(markupList) {
+    ("production" !== "development" ? invariant(
+      ExecutionEnvironment.canUseDOM,
+      'dangerouslyRenderMarkup(...): Cannot render markup in a worker ' +
+      'thread. Make sure `window` and `document` are available globally ' +
+      'before requiring React when unit testing or use ' +
+      'React.renderToString for server rendering.'
+    ) : invariant(ExecutionEnvironment.canUseDOM));
+    var nodeName;
+    var markupByNodeName = {};
+    // Group markup by `nodeName` if a wrap is necessary, else by '*'.
+    for (var i = 0; i < markupList.length; i++) {
+      ("production" !== "development" ? invariant(
+        markupList[i],
+        'dangerouslyRenderMarkup(...): Missing markup.'
+      ) : invariant(markupList[i]));
+      nodeName = getNodeName(markupList[i]);
+      nodeName = getMarkupWrap(nodeName) ? nodeName : '*';
+      markupByNodeName[nodeName] = markupByNodeName[nodeName] || [];
+      markupByNodeName[nodeName][i] = markupList[i];
+    }
+    var resultList = [];
+    var resultListAssignmentCount = 0;
+    for (nodeName in markupByNodeName) {
+      if (!markupByNodeName.hasOwnProperty(nodeName)) {
+        continue;
+      }
+      var markupListByNodeName = markupByNodeName[nodeName];
+
+      // This for-in loop skips the holes of the sparse array. The order of
+      // iteration should follow the order of assignment, which happens to match
+      // numerical index order, but we don't rely on that.
+      var resultIndex;
+      for (resultIndex in markupListByNodeName) {
+        if (markupListByNodeName.hasOwnProperty(resultIndex)) {
+          var markup = markupListByNodeName[resultIndex];
+
+          // Push the requested markup with an additional RESULT_INDEX_ATTR
+          // attribute.  If the markup does not start with a < character, it
+          // will be discarded below (with an appropriate console.error).
+          markupListByNodeName[resultIndex] = markup.replace(
+            OPEN_TAG_NAME_EXP,
+            // This index will be parsed back out below.
+            '$1 ' + RESULT_INDEX_ATTR + '="' + resultIndex + '" '
+          );
+        }
+      }
+
+      // Render each group of markup with similar wrapping `nodeName`.
+      var renderNodes = createNodesFromMarkup(
+        markupListByNodeName.join(''),
+        emptyFunction // Do nothing special with <script> tags.
+      );
+
+      for (var j = 0; j < renderNodes.length; ++j) {
+        var renderNode = renderNodes[j];
+        if (renderNode.hasAttribute &&
+            renderNode.hasAttribute(RESULT_INDEX_ATTR)) {
+
+          resultIndex = +renderNode.getAttribute(RESULT_INDEX_ATTR);
+          renderNode.removeAttribute(RESULT_INDEX_ATTR);
+
+          ("production" !== "development" ? invariant(
+            !resultList.hasOwnProperty(resultIndex),
+            'Danger: Assigning to an already-occupied result index.'
+          ) : invariant(!resultList.hasOwnProperty(resultIndex)));
+
+          resultList[resultIndex] = renderNode;
+
+          // This should match resultList.length and markupList.length when
+          // we're done.
+          resultListAssignmentCount += 1;
+
+        } else if ("production" !== "development") {
+          console.error(
+            'Danger: Discarding unexpected node:',
+            renderNode
+          );
+        }
+      }
+    }
+
+    // Although resultList was populated out of order, it should now be a dense
+    // array.
+    ("production" !== "development" ? invariant(
+      resultListAssignmentCount === resultList.length,
+      'Danger: Did not assign to every index of resultList.'
+    ) : invariant(resultListAssignmentCount === resultList.length));
+
+    ("production" !== "development" ? invariant(
+      resultList.length === markupList.length,
+      'Danger: Expected markup to render %s nodes, but rendered %s.',
+      markupList.length,
+      resultList.length
+    ) : invariant(resultList.length === markupList.length));
+
+    return resultList;
+  },
+
+  /**
+   * Replaces a node with a string of markup at its current position within its
+   * parent. The markup must render into a single root node.
+   *
+   * @param {DOMElement} oldChild Child node to replace.
+   * @param {string} markup Markup to render in place of the child node.
+   * @internal
+   */
+  dangerouslyReplaceNodeWithMarkup: function(oldChild, markup) {
+    ("production" !== "development" ? invariant(
+      ExecutionEnvironment.canUseDOM,
+      'dangerouslyReplaceNodeWithMarkup(...): Cannot render markup in a ' +
+      'worker thread. Make sure `window` and `document` are available ' +
+      'globally before requiring React when unit testing or use ' +
+      'React.renderToString for server rendering.'
+    ) : invariant(ExecutionEnvironment.canUseDOM));
+    ("production" !== "development" ? invariant(markup, 'dangerouslyReplaceNodeWithMarkup(...): Missing markup.') : invariant(markup));
+    ("production" !== "development" ? invariant(
+      oldChild.tagName.toLowerCase() !== 'html',
+      'dangerouslyReplaceNodeWithMarkup(...): Cannot replace markup of the ' +
+      '<html> node. This is because browser quirks make this unreliable ' +
+      'and/or slow. If you want to render to the root you must use ' +
+      'server rendering. See React.renderToString().'
+    ) : invariant(oldChild.tagName.toLowerCase() !== 'html'));
+
+    var newChild = createNodesFromMarkup(markup, emptyFunction)[0];
+    oldChild.parentNode.replaceChild(newChild, oldChild);
+  }
+
+};
+
+module.exports = Danger;
+
+},{"126":126,"129":129,"142":142,"150":150,"22":22}],14:[function(_dereq_,module,exports){
+/**
+ * Copyright 2013-2015, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * @providesModule DefaultEventPluginOrder
+ */
+
+'use strict';
+
+var keyOf = _dereq_(157);
+
+/**
+ * Module that is injectable into `EventPluginHub`, that specifies a
+ * deterministic ordering of `EventPlugin`s. A convenient way to reason about
+ * plugins, without having to package every one of them. This is better than
+ * having plugins be ordered in the same order that they are injected because
+ * that ordering would be influenced by the packaging order.
+ * `ResponderEventPlugin` must occur before `SimpleEventPlugin` so that
+ * preventing default on events is convenient in `SimpleEventPlugin` handlers.
+ */
+var DefaultEventPluginOrder = [
+  keyOf({ResponderEventPlugin: null}),
+  keyOf({SimpleEventPlugin: null}),
+  keyOf({TapEventPlugin: null}),
+  keyOf({EnterLeaveEventPlugin: null}),
+  keyOf({ChangeEventPlugin: null}),
+  keyOf({SelectEventPlugin: null}),
+  keyOf({BeforeInputEventPlugin: null}),
+  keyOf({AnalyticsEventPlugin: null}),
+  keyOf({MobileSafariClickEventPlugin: null})
+];
+
+module.exports = DefaultEventPluginOrder;
+
+},{"157":157}],15:[function(_dereq_,module,exports){
+/**
+ * Copyright 2013-2015, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * @providesModule EnterLeaveEventPlugin
+ * @typechecks static-only
+ */
+
+'use strict';
+
+var EventConstants = _dereq_(16);
+var EventPropagators = _dereq_(21);
+var SyntheticMouseEvent = _dereq_(112);
+
+var ReactMount = _dereq_(77);
+var keyOf = _dereq_(157);
+
+var topLevelTypes = EventConstants.topLevelTypes;
+var getFirstReactDOM = ReactMount.getFirstReactDOM;
+
+var eventTypes = {
+  mouseEnter: {
+    registrationName: keyOf({onMouseEnter: null}),
+    dependencies: [
+      topLevelTypes.topMouseOut,
+      topLevelTypes.topMouseOver
+    ]
+  },
+  mouseLeave: {
+    registrationName: keyOf({onMouseLeave: null}),
+    dependencies: [
+      topLevelTypes.topMouseOut,
+      topLevelTypes.topMouseOver
+    ]
+  }
+};
+
+var extractedEvents = [null, null];
+
+var EnterLeaveEventPlugin = {
+
+  eventTypes: eventTypes,
+
+  /**
+   * For almost every interaction we care about, there will be both a top-level
+   * `mouseover` and `mouseout` event that occurs. Only use `mouseout` so that
+   * we do not extract duplicate events. However, moving the mouse into the
+   * browser from outside will not fire a `mouseout` event. In this case, we use
+   * the `mouseover` top-level event.
+   *
+   * @param {string} topLevelType Record from `EventConstants`.
+   * @param {DOMEventTarget} topLevelTarget The listening component root node.
+   * @param {string} topLevelTargetID ID of `topLevelTarget`.
+   * @param {object} nativeEvent Native browser event.
+   * @return {*} An accumulation of synthetic events.
+   * @see {EventPluginHub.extractEvents}
+   */
+  extractEvents: function(
+      topLevelType,
+      topLevelTarget,
+      topLevelTargetID,
+      nativeEvent) {
+    if (topLevelType === topLevelTypes.topMouseOver &&
+        (nativeEvent.relatedTarget || nativeEvent.fromElement)) {
+      return null;
+    }
+    if (topLevelType !== topLevelTypes.topMouseOut &&
+        topLevelType !== topLevelTypes.topMouseOver) {
+      // Must not be a mouse in or mouse out - ignoring.
+      return null;
+    }
+
+    var win;
+    if (topLevelTarget.window === topLevelTarget) {
+      // `topLevelTarget` is probably a window object.
+      win = topLevelTarget;
+    } else {
+      // TODO: Figure out why `ownerDocument` is sometimes undefined in IE8.
+      var doc = topLevelTarget.ownerDocument;
+      if (doc) {
+        win = doc.defaultView || doc.parentWindow;
+      } else {
+        win = window;
+      }
+    }
+
+    var from, to;
+    if (topLevelType === topLevelTypes.topMouseOut) {
+      from = topLevelTarget;
+      to =
+        getFirstReactDOM(nativeEvent.relatedTarget || nativeEvent.toElement) ||
+        win;
+    } else {
+      from = win;
+      to = topLevelTarget;
+    }
+
+    if (from === to) {
+      // Nothing pertains to our managed components.
+      return null;
+    }
+
+    var fromID = from ? ReactMount.getID(from) : '';
+    var toID = to ? ReactMount.getID(to) : '';
+
+    var leave = SyntheticMouseEvent.getPooled(
+      eventTypes.mouseLeave,
+      fromID,
+      nativeEvent
+    );
+    leave.type = 'mouseleave';
+    leave.target = from;
+    leave.relatedTarget = to;
+
+    var enter = SyntheticMouseEvent.getPooled(
+      eventTypes.mouseEnter,
+      toID,
+      nativeEvent
+    );
+    enter.type = 'mouseenter';
+    enter.target = to;
+    enter.relatedTarget = from;
+
+    EventPropagators.accumulateEnterLeaveDispatches(leave, enter, fromID, toID);
+
+    extractedEvents[0] = leave;
+    extractedEvents[1] = enter;
+
+    return extractedEvents;
+  }
+
+};
+
+module.exports = EnterLeaveEventPlugin;
+
+},{"112":112,"157":157,"16":16,"21":21,"77":77}],16:[function(_dereq_,module,exports){
+/**
+ * Copyright 2013-2015, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * @providesModule EventConstants
+ */
+
+'use strict';
+
+var keyMirror = _dereq_(156);
+
+var PropagationPhases = keyMirror({bubbled: null, captured: null});
+
+/**
+ * Types of raw signals from the browser caught at the top level.
+ */
+var topLevelTypes = keyMirror({
+  topBlur: null,
+  topChange: null,
+  topClick: null,
+  topCompositionEnd: null,
+  topCompositionStart: null,
+  topCompositionUpdate: null,
+  topContextMenu: null,
+  topCopy: null,
+  topCut: null,
+  topDoubleClick: null,
+  topDrag: null,
+  topDragEnd: null,
+  topDragEnter: null,
+  topDragExit: null,
+  topDragLeave: null,
+  topDragOver: null,
+  topDragStart: null,
+  topDrop: null,
+  topError: null,
+  topFocus: null,
+  topInput: null,
+  topKeyDown: null,
+  topKeyPress: null,
+  topKeyUp: null,
+  topLoad: null,
+  topMouseDown: null,
+  topMouseMove: null,
+  topMouseOut: null,
+  topMouseOver: null,
+  topMouseUp: null,
+  topPaste: null,
+  topReset: null,
+  topScroll: null,
+  topSelectionChange: null,
+  topSubmit: null,
+  topTextInput: null,
+  topTouchCancel: null,
+  topTouchEnd: null,
+  topTouchMove: null,
+  topTouchStart: null,
+  topWheel: null
+});
+
+var EventConstants = {
+  topLevelTypes: topLevelTypes,
+  PropagationPhases: PropagationPhases
+};
+
+module.exports = EventConstants;
+
+},{"156":156}],17:[function(_dereq_,module,exports){
+/**
+ * Copyright 2013-2015, Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * 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.
+ *
+ * @providesModule EventListener
+ * @typechecks
+ */
+
+var emptyFunction = _dereq_(129);
+
+/**
+ * Upstream version of event listener. Does not take into account specific
+ * nature of platform.
+ */
+var EventListener = {
+  /**
+   * Listen to DOM events during the bubble phase.
+   *
+   * @param {DOMEventTarget} target DOM element to register listener on.
+   * @param {string} eventType Event type, e.g. 'click' or 'mouseover'.
+   * @param {function} callback Callback function.
+   * @return {object} Object with a `remove` method.
+   */
+  listen: function(target, eventType, callback) {
+    if (target.addEventListener) {
+      target.addEventListener(eventType, callback, false);
+      return {
+        remove: function() {
+          target.removeEventListener(eventType, callback, false);
+        }
+      };
+    } else if (target.attachEvent) {
+      target.attachEvent('on' + eventType, callback);
+      return {
+        remove: function() {
+          target.detachEvent('on' + eventType, callback);
+        }
+      };
+    }
+  },
+
+  /**
+   * Listen to DOM events during the capture phase.
+   *
+   * @param {DOMEventTarget} target DOM element to register listener on.
+   * @param {string} eventType Event type, e.g. 'click' or 'mouseover'.
+   * @param {function} callback Callback function.
+   * @return {object} Object with a `remove` method.
+   */
+  capture: function(target, eventType, callback) {
+    if (!target.addEventListener) {
+      if ("production" !== "development") {
+        console.error(
+          'Attempted to listen to events during the capture phase on a ' +
+          'browser that does not support the capture phase. Your application ' +
+          'will not receive some events.'
+        );
+      }
+      return {
+        remove: emptyFunction
+      };
+    } else {
+      target.addEventListener(eventType, callback, true);
+      return {
+        remove: function() {
+          target.removeEventListener(eventType, callback, true);
+        }
+      };
+    }
+  },
+
+  registerDefault: function() {}
+};
+
+module.exports = EventListener;
+
+},{"129":129}],18:[function(_dereq_,module,exports){
+/**
+ * Copyright 2013-2015, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * @providesModule EventPluginHub
+ */
+
+'use strict';
+
+var EventPluginRegistry = _dereq_(19);
+var EventPluginUtils = _dereq_(20);
+
+var accumulateInto = _dereq_(118);
+var forEachAccumulated = _dereq_(135);
+var invariant = _dereq_(150);
+
+/**
+ * Internal store for event listeners
+ */
+var listenerBank = {};
+
+/**
+ * Internal queue of events that have accumulated their dispatches and are
+ * waiting to have their dispatches executed.
+ */
+var eventQueue = null;
+
+/**
+ * Dispatches an event and releases it back into the pool, unless persistent.
+ *
+ * @param {?object} event Synthetic event to be dispatched.
+ * @private
+ */
+var executeDispatchesAndRelease = function(event) {
+  if (event) {
+    var executeDispatch = EventPluginUtils.executeDispatch;
+    // Plugins can provide custom behavior when dispatching events.
+    var PluginModule = EventPluginRegistry.getPluginModuleForEvent(event);
+    if (PluginModule && PluginModule.executeDispatch) {
+      executeDispatch = PluginModule.executeDispatch;
+    }
+    EventPluginUtils.executeDispatchesInOrder(event, executeDispatch);
+
+    if (!event.isPersistent()) {
+      event.constructor.release(event);
+    }
+  }
+};
+
+/**
+ * - `InstanceHandle`: [required] Module that performs logical traversals of DOM
+ *   hierarchy given ids of the logical DOM elements involved.
+ */
+var InstanceHandle = null;
+
+function validateInstanceHandle() {
+  var valid =
+    InstanceHandle &&
+    InstanceHandle.traverseTwoPhase &&
+    InstanceHandle.traverseEnterLeave;
+  ("production" !== "development" ? invariant(
+    valid,
+    'InstanceHandle not injected before use!'
+  ) : invariant(valid));
+}
+
+/**
+ * This is a unified interface for event plugins to be installed and configured.
+ *
+ * Event plugins can implement the following properties:
+ *
+ *   `extractEvents` {function(string, DOMEventTarget, string, object): *}
+ *     Required. When a top-level event is fired, this method is expected to
+ *     extract synthetic events that will in turn be queued and dispatched.
+ *
+ *   `eventTypes` {object}
+ *     Optional, plugins that fire events must publish a mapping of registration
+ *     names that are used to register listeners. Values of this mapping must
+ *     be objects that contain `registrationName` or `phasedRegistrationNames`.
+ *
+ *   `executeDispatch` {function(object, function, string)}
+ *     Optional, allows plugins to override how an event gets dispatched. By
+ *     default, the listener is simply invoked.
+ *
+ * Each plugin that is injected into `EventsPluginHub` is immediately operable.
+ *
+ * @public
+ */
+var EventPluginHub = {
+
+  /**
+   * Methods for injecting dependencies.
+   */
+  injection: {
+
+    /**
+     * @param {object} InjectedMount
+     * @public
+     */
+    injectMount: EventPluginUtils.injection.injectMount,
+
+    /**
+     * @param {object} InjectedInstanceHandle
+     * @public
+     */
+    injectInstanceHandle: function(InjectedInstanceHandle) {
+      InstanceHandle = InjectedInstanceHandle;
+      if ("production" !== "development") {
+        validateInstanceHandle();
+      }
+    },
+
+    getInstanceHandle: function() {
+      if ("production" !== "development") {
+        validateInstanceHandle();
+      }
+      return InstanceHandle;
+    },
+
+    /**
+     * @param {array} InjectedEventPluginOrder
+     * @public
+     */
+    injectEventPluginOrder: EventPluginRegistry.injectEventPluginOrder,
+
+    /**
+     * @param {object} injectedNamesToPlugins Map from names to plugin modules.
+     */
+    injectEventPluginsByName: EventPluginRegistry.injectEventPluginsByName
+
+  },
+
+  eventNameDispatchConfigs: EventPluginRegistry.eventNameDispatchConfigs,
+
+  registrationNameModules: EventPluginRegistry.registrationNameModules,
+
+  /**
+   * Stores `listener` at `listenerBank[registrationName][id]`. Is idempotent.
+   *
+   * @param {string} id ID of the DOM element.
+   * @param {string} registrationName Name of listener (e.g. `onClick`).
+   * @param {?function} listener The callback to store.
+   */
+  putListener: function(id, registrationName, listener) {
+    ("production" !== "development" ? invariant(
+      !listener || typeof listener === 'function',
+      'Expected %s listener to be a function, instead got type %s',
+      registrationName, typeof listener
+    ) : invariant(!listener || typeof listener === 'function'));
+
+    var bankForRegistrationName =
+      listenerBank[registrationName] || (listenerBank[registrationName] = {});
+    bankForRegistrationName[id] = listener;
+  },
+
+  /**
+   * @param {string} id ID of the DOM element.
+   * @param {string} registrationName Name of listener (e.g. `onClick`).
+   * @return {?function} The stored callback.
+   */
+  getListener: function(id, registrationName) {
+    var bankForRegistrationName = listenerBank[registrationName];
+    return bankForRegistrationName && bankForRegistrationName[id];
+  },
+
+  /**
+   * Deletes a listener from the registration bank.
+   *
+   * @param {string} id ID of the DOM element.
+   * @param {string} registrationName Name of listener (e.g. `onClick`).
+   */
+  deleteListener: function(id, registrationName) {
+    var bankForRegistrationName = listenerBank[registrationName];
+    if (bankForRegistrationName) {
+      delete bankForRegistrationName[id];
+    }
+  },
+
+  /**
+   * Deletes all listeners for the DOM element with the supplied ID.
+   *
+   * @param {string} id ID of the DOM element.
+   */
+  deleteAllListeners: function(id) {
+    for (var registrationName in listenerBank) {
+      delete listenerBank[registrationName][id];
+    }
+  },
+
+  /**
+   * Allows registered plugins an opportunity to extract events from top-level
+   * native browser events.
+   *
+   * @param {string} topLevelType Record from `EventConstants`.
+   * @param {DOMEventTarget} topLevelTarget The listening component root node.
+   * @param {string} topLevelTargetID ID of `topLevelTarget`.
+   * @param {object} nativeEvent Native browser event.
+   * @return {*} An accumulation of synthetic events.
+   * @internal
+   */
+  extractEvents: function(
+      topLevelType,
+      topLevelTarget,
+      topLevelTargetID,
+      nativeEvent) {
+    var events;
+    var plugins = EventPluginRegistry.plugins;
+    for (var i = 0, l = plugins.length; i < l;