Merge m-c to inbound on a CLOSED TREE.
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 17 Jul 2014 17:36:38 -0400
changeset 216716 286635a5a1e1d01298f1eb45380390b20ee0dcd7
parent 216715 4f2dd4d636ac114d9e211c1779c2cecb5b59f4ae (current diff)
parent 216659 f77a9f8254279d66949179c8fa9ca4de580414eb (diff)
child 216717 c361be2aeb66ec71289f34a5edfcc7a2527afbf8
push id515
push userraliiev@mozilla.com
push dateMon, 06 Oct 2014 12:51:51 +0000
treeherdermozilla-release@267c7a481bef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone33.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 on a CLOSED TREE.
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -999,10 +999,12 @@ pref("selectioncaret.enabled", false);
 
 // Enable sync and mozId with Firefox Accounts.
 pref("services.sync.fxaccounts.enabled", true);
 pref("identity.fxaccounts.enabled", true);
 
 // Mobile Identity API.
 pref("services.mobileid.server.uri", "https://msisdn.services.mozilla.com");
 
-// Enable mapped array buffer
+// Enable mapped array buffer.
+#ifndef XP_WIN
 pref("dom.mapped_arraybuffer.enabled", true);
+#endif
--- a/b2g/chrome/content/settings.js
+++ b/b2g/chrome/content/settings.js
@@ -346,17 +346,17 @@ let AdbController = {
       if (this.DEBUG) {
         this.debug("updateState: Waiting for all vars to be initialized");
       }
       return;
     }
 
     // Check if we have a remote debugging session going on. If so, we won't
     // disable adb even if the screen is locked.
-    let isDebugging = RemoteDebugger.isDebugging;
+    let isDebugging = USBRemoteDebugger.isDebugging;
     if (this.DEBUG) {
       this.debug("isDebugging=" + isDebugging);
     }
 
     // If USB Mass Storage, USB tethering, or a debug session is active,
     // then we don't want to disable adb in an automatic fashion (i.e.
     // when the screen locks or due to timeout).
     let sysUsbConfig = libcutils.property_get("sys.usb.config");
@@ -427,55 +427,88 @@ let AdbController = {
 };
 
 SettingsListener.observe("lockscreen.locked", false,
                          AdbController.setLockscreenState.bind(AdbController));
 SettingsListener.observe("lockscreen.enabled", false,
                          AdbController.setLockscreenEnabled.bind(AdbController));
 #endif
 
-// Keep the old setting to not break people that won't have updated
-// gaia and gecko.
-SettingsListener.observe('devtools.debugger.remote-enabled', false, function(value) {
-  Services.prefs.setBoolPref('devtools.debugger.remote-enabled', value);
-  // This preference is consulted during startup
-  Services.prefs.savePrefFile(null);
-  try {
-    value ? RemoteDebugger.start() : RemoteDebugger.stop();
-  } catch(e) {
-    dump("Error while initializing devtools: " + e + "\n" + e.stack + "\n");
-  }
+(function() {
+  // Track these separately here so we can determine the correct value for the
+  // pref "devtools.debugger.remote-enabled", which is true when either mode of
+  // using DevTools is enabled.
+  let devtoolsUSB = false;
+  let devtoolsWiFi = false;
+
+  // Keep the old setting to not break people that won't have updated
+  // gaia and gecko.
+  SettingsListener.observe('devtools.debugger.remote-enabled', false,
+                           function(value) {
+    devtoolsUSB = value;
+    Services.prefs.setBoolPref('devtools.debugger.remote-enabled',
+                               devtoolsUSB || devtoolsWiFi);
+    // This preference is consulted during startup
+    Services.prefs.savePrefFile(null);
+    try {
+      value ? USBRemoteDebugger.start() : USBRemoteDebugger.stop();
+    } catch(e) {
+      dump("Error while initializing USB devtools: "
+           + e + "\n" + e.stack + "\n");
+    }
 
 #ifdef MOZ_WIDGET_GONK
-  AdbController.setRemoteDebuggerState(value);
+    AdbController.setRemoteDebuggerState(value);
 #endif
-});
+  });
 
-SettingsListener.observe('debugger.remote-mode', false, function(value) {
-  if (['disabled', 'adb-only', 'adb-devtools'].indexOf(value) == -1) {
-    dump('Illegal value for debugger.remote-mode: ' + value + '\n');
-    return;
-  }
+  SettingsListener.observe('debugger.remote-mode', false, function(value) {
+    if (['disabled', 'adb-only', 'adb-devtools'].indexOf(value) == -1) {
+      dump('Illegal value for debugger.remote-mode: ' + value + '\n');
+      return;
+    }
 
-  Services.prefs.setBoolPref('devtools.debugger.remote-enabled',
-                             value == 'adb-devtools');
-  // This preference is consulted during startup
-  Services.prefs.savePrefFile(null);
+    devtoolsUSB = value == 'adb-devtools';
+    Services.prefs.setBoolPref('devtools.debugger.remote-enabled',
+                               devtoolsUSB || devtoolsWiFi);
+    // This preference is consulted during startup
+    Services.prefs.savePrefFile(null);
 
-  try {
-    (value == 'adb-devtools') ? RemoteDebugger.start()
-                              : RemoteDebugger.stop();
-  } catch(e) {
-    dump("Error while initializing devtools: " + e + "\n" + e.stack + "\n");
-  }
+    try {
+      (value == 'adb-devtools') ? USBRemoteDebugger.start()
+                                : USBRemoteDebugger.stop();
+    } catch(e) {
+      dump("Error while initializing USB devtools: "
+           + e + "\n" + e.stack + "\n");
+    }
 
 #ifdef MOZ_WIDGET_GONK
-  AdbController.setRemoteDebuggerState(value != 'disabled');
+    AdbController.setRemoteDebuggerState(value != 'disabled');
 #endif
-});
+  });
+
+  SettingsListener.observe('devtools.remote.wifi.enabled', false,
+                           function(value) {
+    devtoolsWiFi = value;
+    Services.prefs.setBoolPref('devtools.debugger.remote-enabled',
+                               devtoolsUSB || devtoolsWiFi);
+    // Allow remote debugging on non-local interfaces when WiFi debug is enabled
+    // TODO: Bug 1034411: Lock down to WiFi interface, instead of all interfaces
+    Services.prefs.setBoolPref('devtools.debugger.force-local', !value);
+    // This preference is consulted during startup
+    Services.prefs.savePrefFile(null);
+
+    try {
+      value ? WiFiRemoteDebugger.start() : WiFiRemoteDebugger.stop();
+    } catch(e) {
+      dump("Error while initializing WiFi devtools: "
+           + e + "\n" + e.stack + "\n");
+    }
+  });
+})();
 
 // =================== Device Storage ====================
 SettingsListener.observe('device.storage.writable.name', 'sdcard', function(value) {
   if (Services.prefs.getPrefType('device.storage.writable.name') != Ci.nsIPrefBranch.PREF_STRING) {
     // We clear the pref because it used to be erroneously written as a bool
     // and we need to clear it before we can change it to have the correct type.
     Services.prefs.clearUserPref('device.storage.writable.name');
   }
@@ -652,16 +685,19 @@ let settingsToObserve = {
     prefName: 'layers.offmainthreadcomposition.log-animations',
     defaultValue: false
   },
   'debug.paint-flashing.enabled': {
     prefName: 'nglayout.debug.paint_flashing',
     defaultValue: false
   },
   'devtools.eventlooplag.threshold': 100,
+  'devtools.remote.wifi.visible': {
+    resetToPref: true
+  },
   'dom.mozApps.use_reviewer_certs': false,
   'layers.draw-borders': false,
   'layers.draw-tile-borders': false,
   'layers.dump': false,
   'layers.enable-tiles': true,
   'layers.simple-tiles': false,
   'privacy.donottrackheader.enabled': false,
   'ril.radio.disabled': false,
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -51,16 +51,26 @@ XPCOMUtils.defineLazyServiceGetter(Servi
                                    '@mozilla.org/focus-manager;1',
                                    'nsIFocusManager');
 
 XPCOMUtils.defineLazyGetter(this, 'DebuggerServer', function() {
   Cu.import('resource://gre/modules/devtools/dbg-server.jsm');
   return DebuggerServer;
 });
 
+XPCOMUtils.defineLazyGetter(this, 'devtools', function() {
+  const { devtools } =
+    Cu.import('resource://gre/modules/devtools/Loader.jsm', {});
+  return devtools;
+});
+
+XPCOMUtils.defineLazyGetter(this, 'discovery', function() {
+  return devtools.require('devtools/toolkit/discovery/discovery');
+});
+
 XPCOMUtils.defineLazyGetter(this, "ppmm", function() {
   return Cc["@mozilla.org/parentprocessmessagemanager;1"]
          .getService(Ci.nsIMessageListenerManager);
 });
 
 #ifdef MOZ_WIDGET_GONK
 XPCOMUtils.defineLazyGetter(this, "libcutils", function () {
   Cu.import("resource://gre/modules/systemlibs.js");
@@ -202,21 +212,16 @@ var shell = {
           && network.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) {
         shell.submitQueuedCrashes();
 
         Services.obs.removeObserver(observer, topic);
       }
     }, "network-connection-state-changed", false);
   },
 
-  get contentBrowser() {
-    delete this.contentBrowser;
-    return this.contentBrowser = document.getElementById('systemapp');
-  },
-
   get homeURL() {
     try {
       let homeSrc = Services.env.get('B2G_HOMESCREEN');
       if (homeSrc)
         return homeSrc;
     } catch (e) {}
 
     return Services.prefs.getCharPref('b2g.system_startup_url');
@@ -306,17 +311,17 @@ var shell = {
     let container = document.getElementById('container');
 #ifdef MOZ_WIDGET_COCOA
     // See shell.html
     let hotfix = document.getElementById('placeholder');
     if (hotfix) {
       container.removeChild(hotfix);
     }
 #endif
-    container.appendChild(systemAppFrame);
+    this.contentBrowser = container.appendChild(systemAppFrame);
 
     systemAppFrame.contentWindow
                   .QueryInterface(Ci.nsIInterfaceRequestor)
                   .getInterface(Ci.nsIWebNavigation)
                   .sessionHistory = Cc["@mozilla.org/browser/shistory;1"]
                                       .createInstance(Ci.nsISHistory);
 
     // On firefox mulet, shell.html is loaded in a tab
@@ -806,20 +811,21 @@ let IndexedDBPromptHelper = {
 
     setTimeout(function() {
       observer.observe(null, responseTopic,
                        Ci.nsIPermissionManager.DENY_ACTION);
     }, 0);
   }
 }
 
-let RemoteDebugger = {
+function RemoteDebugger() {}
+RemoteDebugger.prototype = {
   _promptDone: false,
   _promptAnswer: false,
-  _running: false,
+  _listener: null,
 
   prompt: function debugger_prompt() {
     this._promptDone = false;
 
     shell.sendChromeEvent({
       "type": "remote-debugger-prompt"
     });
 
@@ -830,118 +836,156 @@ let RemoteDebugger = {
     return this._promptAnswer;
   },
 
   handleEvent: function debugger_handleEvent(detail) {
     this._promptAnswer = detail.value;
     this._promptDone = true;
   },
 
-  get isDebugging() {
-    if (!this._running) {
+  initServer: function() {
+    if (DebuggerServer.initialized) {
+      return;
+    }
+
+    // Ask for remote connections.
+    DebuggerServer.init(this.prompt.bind(this));
+
+    // /!\ Be careful when adding a new actor, especially global actors.
+    // Any new global actor will be exposed and returned by the root actor.
+
+    // Add Firefox-specific actors, but prevent tab actors to be loaded in
+    // the parent process, unless we enable certified apps debugging.
+    let restrictPrivileges = Services.prefs.getBoolPref("devtools.debugger.forbid-certified-apps");
+    DebuggerServer.addBrowserActors("navigator:browser", restrictPrivileges);
+
+    /**
+     * Construct a root actor appropriate for use in a server running in B2G.
+     * The returned root actor respects the factories registered with
+     * DebuggerServer.addGlobalActor only if certified apps debugging is on,
+     * otherwise we used an explicit limited list of global actors
+     *
+     * * @param connection DebuggerServerConnection
+     *        The conection to the client.
+     */
+    DebuggerServer.createRootActor = function createRootActor(connection)
+    {
+      let { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
+      let parameters = {
+        // We do not expose browser tab actors yet,
+        // but we still have to define tabList.getList(),
+        // otherwise, client won't be able to fetch global actors
+        // from listTabs request!
+        tabList: {
+          getList: function() {
+            return promise.resolve([]);
+          }
+        },
+        // Use an explicit global actor list to prevent exposing
+        // unexpected actors
+        globalActorFactories: restrictPrivileges ? {
+          webappsActor: DebuggerServer.globalActorFactories.webappsActor,
+          deviceActor: DebuggerServer.globalActorFactories.deviceActor,
+        } : DebuggerServer.globalActorFactories
+      };
+      let { RootActor } = devtools.require("devtools/server/actors/root");
+      let root = new RootActor(connection, parameters);
+      root.applicationType = "operating-system";
+      return root;
+    };
+
+#ifdef MOZ_WIDGET_GONK
+    DebuggerServer.on("connectionchange", function() {
+      AdbController.updateState();
+    });
+#endif
+  }
+};
+
+let USBRemoteDebugger = new RemoteDebugger();
+
+Object.defineProperty(USBRemoteDebugger, "isDebugging", {
+  get: function() {
+    if (!this._listener) {
       return false;
     }
 
     return DebuggerServer._connections &&
            Object.keys(DebuggerServer._connections).length > 0;
-  },
+  }
+});
 
-  // Start the debugger server.
-  start: function debugger_start() {
-    if (this._running) {
-      return;
-    }
+USBRemoteDebugger.start = function() {
+  if (this._listener) {
+    return;
+  }
+
+  this.initServer();
+
+  let portOrPath =
+    Services.prefs.getCharPref("devtools.debugger.unix-domain-socket") ||
+    "/data/local/debugger-socket";
 
-    if (!DebuggerServer.initialized) {
-      // Ask for remote connections.
-      DebuggerServer.init(this.prompt.bind(this));
+  try {
+    debug("Starting USB debugger on " + portOrPath);
+    this._listener = DebuggerServer.openListener(portOrPath);
+    // Temporary event, until bug 942756 lands and offers a way to know
+    // when the server is up and running.
+    Services.obs.notifyObservers(null, 'debugger-server-started', null);
+  } catch (e) {
+    debug('Unable to start USB debugger server: ' + e);
+  }
+};
 
-      // /!\ Be careful when adding a new actor, especially global actors.
-      // Any new global actor will be exposed and returned by the root actor.
-
-      // Add Firefox-specific actors, but prevent tab actors to be loaded in
-      // the parent process, unless we enable certified apps debugging.
-      let restrictPrivileges = Services.prefs.getBoolPref("devtools.debugger.forbid-certified-apps");
-      DebuggerServer.addBrowserActors("navigator:browser", restrictPrivileges);
+USBRemoteDebugger.stop = function() {
+  if (!this._listener) {
+    return;
+  }
 
-      /**
-       * Construct a root actor appropriate for use in a server running in B2G.
-       * The returned root actor respects the factories registered with
-       * DebuggerServer.addGlobalActor only if certified apps debugging is on,
-       * otherwise we used an explicit limited list of global actors
-       *
-       * * @param connection DebuggerServerConnection
-       *        The conection to the client.
-       */
-      DebuggerServer.createRootActor = function createRootActor(connection)
-      {
-        let { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
-        let parameters = {
-          // We do not expose browser tab actors yet,
-          // but we still have to define tabList.getList(),
-          // otherwise, client won't be able to fetch global actors
-          // from listTabs request!
-          tabList: {
-            getList: function() {
-              return promise.resolve([]);
-            }
-          },
-          // Use an explicit global actor list to prevent exposing
-          // unexpected actors
-          globalActorFactories: restrictPrivileges ? {
-            webappsActor: DebuggerServer.globalActorFactories.webappsActor,
-            deviceActor: DebuggerServer.globalActorFactories.deviceActor,
-          } : DebuggerServer.globalActorFactories
-        };
-        let devtools = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
-        let { RootActor } = devtools.require("devtools/server/actors/root");
-        let root = new RootActor(connection, parameters);
-        root.applicationType = "operating-system";
-        return root;
-      };
+  try {
+    this._listener.close();
+    this._listener = null;
+  } catch (e) {
+    debug('Unable to stop USB debugger server: ' + e);
+  }
+};
+
+let WiFiRemoteDebugger = new RemoteDebugger();
+
+WiFiRemoteDebugger.start = function() {
+  if (this._listener) {
+    return;
+  }
+
+  this.initServer();
 
-#ifdef MOZ_WIDGET_GONK
-      DebuggerServer.on("connectionchange", function() {
-        AdbController.updateState();
-      });
-#endif
-    }
-
-    let path = Services.prefs.getCharPref("devtools.debugger.unix-domain-socket") ||
-               "/data/local/debugger-socket";
-    try {
-      DebuggerServer.openListener(path);
-      // Temporary event, until bug 942756 lands and offers a way to know
-      // when the server is up and running.
-      Services.obs.notifyObservers(null, 'debugger-server-started', null);
-      this._running = true;
-    } catch (e) {
-      dump('Unable to start debugger server: ' + e + '\n');
-    }
-  },
+  try {
+    debug("Starting WiFi debugger");
+    this._listener = DebuggerServer.openListener(-1);
+    let port = this._listener.port;
+    debug("Started WiFi debugger on " + port);
+    discovery.addService("devtools", { port: port });
+  } catch (e) {
+    debug('Unable to start WiFi debugger server: ' + e);
+  }
+};
 
-  stop: function debugger_stop() {
-    if (!this._running) {
-      return;
-    }
+WiFiRemoteDebugger.stop = function() {
+  if (!this._listener) {
+    return;
+  }
 
-    if (!DebuggerServer.initialized) {
-      // Can this really happen if we are running?
-      this._running = false;
-      return;
-    }
-
-    try {
-      DebuggerServer.closeAllListeners();
-    } catch (e) {
-      dump('Unable to stop debugger server: ' + e + '\n');
-    }
-    this._running = false;
+  try {
+    discovery.removeService("devtools");
+    this._listener.close();
+    this._listener = null;
+  } catch (e) {
+    debug('Unable to stop WiFi debugger server: ' + e);
   }
-}
+};
 
 let KeyboardHelper = {
   handleEvent: function keyboard_handleEvent(detail) {
     Keyboard.setLayouts(detail.layouts);
   }
 };
 
 // This is the backend for Gaia's screenshot feature.  Gaia requests a
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,23 +14,23 @@
   <!--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="0d616942c300d9fb142483210f1dda9096c9a9fc">
     <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="8712915c5d36f681308afb475ea387af185e3715"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="7fbcfbacf286c2a8e41a3a96b6d82f1541880617"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
-  <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="bf9aaf39dd5a6491925a022db167c460f8207d34"/>
+  <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="227354333a185180b85471f2cc6abfb029e44718"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="777194d772c831b5dab40cf919523d5665f2a46c"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="d99553937cc53b82965421da1ca950c17f16a324"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
   <project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="425f8b5fadf5889834c5acd27d23c9e0b2129c28"/>
   <project name="device/common" path="device/common" revision="42b808b7e93d0619286ae8e59110b176b7732389"/>
   <project name="device/sample" path="device/sample" revision="237bd668d0f114d801a8d6455ef5e02cc3577587"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
   <project name="platform/external/bluetooth/bluez" path="external/bluetooth/bluez" revision="52a1a862a8bac319652b8f82d9541ba40bfa45ce"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,20 +12,20 @@
   <!--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="cc67f31dc638c0b7edba3cf7e3d87cadf0ed52bf">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8712915c5d36f681308afb475ea387af185e3715"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="7fbcfbacf286c2a8e41a3a96b6d82f1541880617"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="777194d772c831b5dab40cf919523d5665f2a46c"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="d99553937cc53b82965421da1ca950c17f16a324"/>
   <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"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="b89fda71fcd0fa0cf969310e75be3ea33e048b44"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="2e7d5348f35575870b3c7e567a9a9f6d66f8d6c5"/>
@@ -121,17 +121,17 @@
   <project name="platform/system/media" path="system/media" revision="d90b836f66bf1d9627886c96f3a2d9c3007fbb80"/>
   <project name="platform/system/netd" path="system/netd" revision="56112dd7b811301b718d0643a82fd5cac9522073"/>
   <project name="platform/system/security" path="system/security" revision="f48ff68fedbcdc12b570b7699745abb6e7574907"/>
   <project name="platform/system/vold" path="system/vold" revision="8de05d4a52b5a91e7336e6baa4592f945a6ddbea"/>
   <default remote="caf" revision="refs/tags/android-4.3_r2.1" sync-j="4"/>
   <!-- Emulator specific things -->
   <project name="android-development" path="development" remote="b2g" revision="dab55669da8f48b6e57df95d5af9f16b4a87b0b1"/>
   <project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="3a9a17613cc685aa232432566ad6cc607eab4ec1"/>
-  <project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="0e31f35a2a77301e91baa8a237aa9e9fa4076084"/>
+  <project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="197cd9492b9fadaa915c5daf36ff557f8f4a8d1c"/>
   <project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="7d33aaf740bbf6c7c6e9c34a92b371eda311b66b"/>
-  <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="749880f1942620ee1caac7c248f7cfe33f56203d"/>
+  <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="421d09bea0feda3934813f0f411d898f08108770"/>
   <project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="0e56e450367cd802241b27164a2979188242b95f"/>
-  <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="832f4acaf481a19031e479a40b03d9ce5370ddee"/>
+  <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="9f28c4faea3b2f01db227b2467b08aeba96d9bec"/>
   <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="8400a1a850d19f28137880b31582efa3416223c3"/>
   <project name="android-sdk" path="sdk" remote="b2g" revision="8b1365af38c9a653df97349ee53a3f5d64fd590a"/>
   <project name="darwinstreamingserver" path="system/darwinstreamingserver" remote="b2g" revision="cf85968c7f85e0ec36e72c87ceb4837a943b8af6"/>
 </manifest>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,25 +10,25 @@
   <!--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="276ce45e78b09c4a4ee643646f691d22804754c1">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8712915c5d36f681308afb475ea387af185e3715"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="7fbcfbacf286c2a8e41a3a96b6d82f1541880617"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <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="777194d772c831b5dab40cf919523d5665f2a46c"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="d99553937cc53b82965421da1ca950c17f16a324"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="6e47ff2790f5656b5b074407829ceecf3e6188c4"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="1950e4760fa14688b83cdbb5acaa1af9f82ef434"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="ac6eb97a37035c09fb5ede0852f0881e9aadf9ad"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="737f591c5f95477148d26602c7be56cbea0cdeb9"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="51da9b1981be481b92a59a826d4d78dc73d0989a"/>
   <project name="device/common" path="device/common" revision="798a3664597e6041985feab9aef42e98d458bc3d"/>
@@ -120,16 +120,16 @@
   <project name="platform/system/security" path="system/security" revision="583374f69f531ba68fc3dcbff1f74893d2a96406"/>
   <project name="platform/system/vold" path="system/vold" revision="d4455b8cf361f8353e8aebac15ffd64b4aedd2b9"/>
   <project name="platform/external/icu4c" path="external/icu4c" remote="aosp" revision="b4c6379528887dc25ca9991a535a8d92a61ad6b6"/>
   <project name="platform_frameworks_av" path="frameworks/av" remote="b2g" revision="9c6cb3231dd096df10a11b4d76be3727bdeec08d"/>
   <project name="platform_system_core" path="system/core" remote="b2g" revision="9395eb5aa885cf6d305a202de6e9694a58a89717"/>
   <default remote="caf" revision="refs/tags/android-4.4.2_r1" sync-j="4"/>
   <!-- Emulator specific things -->
   <project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="72ffdf71c68a96309212eb13d63560d66db14c9e"/>
-  <project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="cf3f83a8ef13597b62fb6de7aa0cfaf5dc5de2b5"/>
-  <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="870b4f52ba480da34651c172141d62e7fccd113e"/>
+  <project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="c0e0019a6ec1a6199a9c7bc4ace041259f3b8512"/>
+  <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="5f184e4aa6ad784e20b4c5e6be24db4b9a848087"/>
   <project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="694cecf256122d0cb3b6a1a4efb4b5c7401db223"/>
-  <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="effff9b06977ffaf46b45e5737f83a608b2df620"/>
+  <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="97d63c256a047b491565d624aea1dd5f1f8593ea"/>
   <project name="platform/development" path="development" revision="5968ff4e13e0d696ad8d972281fc27ae5a12829b"/>
   <project name="android-sdk" path="sdk" remote="b2g" revision="0951179277915335251c5e11d242e4e1a8c2236f"/>
   <project name="darwinstreamingserver" path="system/darwinstreamingserver" remote="b2g" revision="cf85968c7f85e0ec36e72c87ceb4837a943b8af6"/>
 </manifest>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,23 +14,23 @@
   <!--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="0d616942c300d9fb142483210f1dda9096c9a9fc">
     <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="8712915c5d36f681308afb475ea387af185e3715"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="7fbcfbacf286c2a8e41a3a96b6d82f1541880617"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
-  <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="bf9aaf39dd5a6491925a022db167c460f8207d34"/>
+  <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="227354333a185180b85471f2cc6abfb029e44718"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="777194d772c831b5dab40cf919523d5665f2a46c"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="d99553937cc53b82965421da1ca950c17f16a324"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
   <project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="425f8b5fadf5889834c5acd27d23c9e0b2129c28"/>
   <project name="device/common" path="device/common" revision="42b808b7e93d0619286ae8e59110b176b7732389"/>
   <project name="device/sample" path="device/sample" revision="237bd668d0f114d801a8d6455ef5e02cc3577587"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
   <project name="platform/external/bluetooth/bluez" path="external/bluetooth/bluez" revision="52a1a862a8bac319652b8f82d9541ba40bfa45ce"/>
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -12,20 +12,20 @@
   <!--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="cc67f31dc638c0b7edba3cf7e3d87cadf0ed52bf">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8712915c5d36f681308afb475ea387af185e3715"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="7fbcfbacf286c2a8e41a3a96b6d82f1541880617"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="777194d772c831b5dab40cf919523d5665f2a46c"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="d99553937cc53b82965421da1ca950c17f16a324"/>
   <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="e95b4ce22c825da44d14299e1190ea39a5260bde"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="471afab478649078ad7c75ec6b252481a59e19b8"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
         "git_revision": "", 
         "remote": "", 
         "branch": ""
     }, 
-    "revision": "91e64acd39e0f8841bcb11fdee8300e871410666", 
+    "revision": "4d18ecb3653e80055af734c17f35b2abd8fd2a19", 
     "repo_path": "/integration/gaia-central"
 }
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -12,22 +12,22 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="0d616942c300d9fb142483210f1dda9096c9a9fc">
     <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="8712915c5d36f681308afb475ea387af185e3715"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="7fbcfbacf286c2a8e41a3a96b6d82f1541880617"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="777194d772c831b5dab40cf919523d5665f2a46c"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="d99553937cc53b82965421da1ca950c17f16a324"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="746bc48f34f5060f90801925dcdd964030c1ab6d"/>
   <project name="platform/development" path="development" revision="2460485184bc8535440bb63876d4e63ec1b4770c"/>
   <project name="device/common" path="device/common" revision="0dcc1e03659db33b77392529466f9eb685cdd3c7"/>
   <project name="device/sample" path="device/sample" revision="68b1cb978a20806176123b959cb05d4fa8adaea4"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="0d616942c300d9fb142483210f1dda9096c9a9fc">
     <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="8712915c5d36f681308afb475ea387af185e3715"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="7fbcfbacf286c2a8e41a3a96b6d82f1541880617"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,20 +12,20 @@
   <!--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="cc67f31dc638c0b7edba3cf7e3d87cadf0ed52bf">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8712915c5d36f681308afb475ea387af185e3715"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="7fbcfbacf286c2a8e41a3a96b6d82f1541880617"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="777194d772c831b5dab40cf919523d5665f2a46c"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="d99553937cc53b82965421da1ca950c17f16a324"/>
   <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"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="b89fda71fcd0fa0cf969310e75be3ea33e048b44"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="2e7d5348f35575870b3c7e567a9a9f6d66f8d6c5"/>
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -12,22 +12,22 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="0d616942c300d9fb142483210f1dda9096c9a9fc">
     <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="8712915c5d36f681308afb475ea387af185e3715"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="7fbcfbacf286c2a8e41a3a96b6d82f1541880617"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="777194d772c831b5dab40cf919523d5665f2a46c"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="d99553937cc53b82965421da1ca950c17f16a324"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="cd5dfce80bc3f0139a56b58aca633202ccaee7f8"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="e0a9ac010df3afaa47ba107192c05ac8b5516435"/>
   <project name="platform/development" path="development" revision="a384622f5fcb1d2bebb9102591ff7ae91fe8ed2d"/>
   <project name="device/common" path="device/common" revision="7c65ea240157763b8ded6154a17d3c033167afb7"/>
   <project name="device/sample" path="device/sample" revision="c328f3d4409db801628861baa8d279fb8855892f"/>
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1302,16 +1302,19 @@ pref("devtools.debugger.ui.variables-onl
 pref("devtools.debugger.ui.variables-searchbox-visible", false);
 
 // Enable the Profiler
 pref("devtools.profiler.enabled", true);
 
 // The default Profiler UI settings
 pref("devtools.profiler.ui.show-platform-data", false);
 
+// The default cache UI setting
+pref("devtools.cache.disabled", false);
+
 // Enable the Network Monitor
 pref("devtools.netmonitor.enabled", true);
 
 // The default Network Monitor UI settings
 pref("devtools.netmonitor.panes-network-details-width", 550);
 pref("devtools.netmonitor.panes-network-details-height", 450);
 pref("devtools.netmonitor.statistics", true);
 pref("devtools.netmonitor.filters", "[\"all\"]");
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -6,16 +6,18 @@
 let Ci = Components.interfaces;
 let Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/NotificationDB.jsm");
 Cu.import("resource:///modules/RecentWindow.jsm");
 Cu.import("resource://gre/modules/WindowsPrefSync.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "BrowserUITelemetry",
+                                  "resource:///modules/BrowserUITelemetry.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils",
                                   "resource://gre/modules/BrowserUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Task",
                                   "resource://gre/modules/Task.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "CharsetMenu",
                                   "resource://gre/modules/CharsetMenu.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ShortcutUtils",
                                   "resource://gre/modules/ShortcutUtils.jsm");
@@ -3090,16 +3092,17 @@ const BrowserSearch = {
    *
    * @param engine
    *        (nsISearchEngine) The engine handling the search.
    * @param source
    *        (string) Where the search originated from. See the FHR
    *        SearchesProvider for allowed values.
    */
   recordSearchInHealthReport: function (engine, source) {
+    BrowserUITelemetry.countSearchEvent(source);
 #ifdef MOZ_SERVICES_HEALTHREPORT
     let reporter = Cc["@mozilla.org/datareporting/service;1"]
                      .getService()
                      .wrappedJSObject
                      .healthReporter;
 
     // This can happen if the FHR component of the data reporting service is
     // disabled. This is controlled by a pref that most will never use.
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -316,22 +316,24 @@ BrowserGlue.prototype = {
         }
         break;
       case "profile-before-change":
          // Any component depending on Places should be finalized in
          // _onPlacesShutdown.  Any component that doesn't need to act after
          // the UI has gone should be finalized in _onQuitApplicationGranted.
         this._dispose();
         break;
-#ifdef MOZ_SERVICES_HEALTHREPORT
       case "keyword-search":
         // This is very similar to code in
         // browser.js:BrowserSearch.recordSearchInHealthReport(). The code could
         // be consolidated if there is will. We need the observer in
         // nsBrowserGlue to prevent double counting.
+        let win = this.getMostRecentBrowserWindow();
+        BrowserUITelemetry.countSearchEvent("urlbar", win.gURLBar.value);
+#ifdef MOZ_SERVICES_HEALTHREPORT
         let reporter = Cc["@mozilla.org/datareporting/service;1"]
                          .getService()
                          .wrappedJSObject
                          .healthReporter;
 
         if (!reporter) {
           return;
         }
@@ -339,18 +341,18 @@ BrowserGlue.prototype = {
         reporter.onInit().then(function record() {
           try {
             let engine = subject.QueryInterface(Ci.nsISearchEngine);
             reporter.getProvider("org.mozilla.searches").recordSearch(engine, "urlbar");
           } catch (ex) {
             Cu.reportError(ex);
           }
         });
+#endif
         break;
-#endif
       case "browser-search-engine-modified":
         if (data != "engine-default" && data != "engine-current") {
           break;
         }
         // Enforce that the search service's defaultEngine is always equal to
         // its currentEngine. The search service will notify us any time either
         // of them are changed (either by directly setting the relevant prefs,
         // i.e. if add-ons try to change this directly, or if the
--- a/browser/devtools/commandline/test/browser_cmd_jsb.js
+++ b/browser/devtools/commandline/test/browser_cmd_jsb.js
@@ -40,17 +40,17 @@ function testTask() {
   let scratchpad = scratchpadWin.Scratchpad;
 
   yield observeOnce(scratchpad);
 
   let result = scratchpad.getText();
   result = result.replace(/[\r\n]]*/g, "\n");
   let correct = "function somefunc() {\n" +
             "  if (true) // Some comment\n" +
-            "  doSomething();\n" +
+            "    doSomething();\n" +
             "  for (let n = 0; n < 500; n++) {\n" +
             "    if (n % 2 == 1) {\n" +
             "      console.log(n);\n" +
             "      console.log(n + 1);\n" +
             "    }\n" +
             "  }\n" +
             "}";
   is(result, correct, "JS has been correctly prettified");
--- a/browser/devtools/framework/test/browser_devtools_api.js
+++ b/browser/devtools/framework/test/browser_devtools_api.js
@@ -6,19 +6,17 @@
 const Cu = Components.utils;
 const toolId = "test-tool";
 
 let tempScope = {};
 Cu.import("resource://gre/modules/devtools/event-emitter.js", tempScope);
 let EventEmitter = tempScope.EventEmitter;
 
 function test() {
-  addTab("about:blank", function(aBrowser, aTab) {
-    runTests(aTab);
-  });
+  addTab("about:blank").then(runTests);
 }
 
 function runTests(aTab) {
   let toolDefinition = {
     id: toolId,
     isTargetSupported: function() true,
     visibilityswitch: "devtools.test-tool.enabled",
     url: "about:blank",
--- a/browser/devtools/framework/test/browser_dynamic_tool_enabling.js
+++ b/browser/devtools/framework/test/browser_dynamic_tool_enabling.js
@@ -32,9 +32,10 @@ function test() {
     checkItem(el, prefs);
     for (let pref of prefs) {
       Services.prefs.setBoolPref(pref, !Services.prefs.getBoolPref(pref));
       checkItem(el, prefs);
       Services.prefs.setBoolPref(pref, !Services.prefs.getBoolPref(pref));
       checkItem(el, prefs);
     }
   }
+  finish();
 }
--- a/browser/devtools/framework/test/browser_new_activation_workflow.js
+++ b/browser/devtools/framework/test/browser_new_activation_workflow.js
@@ -5,17 +5,17 @@
 
 const Cu = Components.utils;
 
 let toolbox, target;
 
 let tempScope = {};
 
 function test() {
-  addTab("about:blank", function(aBrowser, aTab) {
+  addTab("about:blank").then(function(aTab) {
     target = TargetFactory.forTab(gBrowser.selectedTab);
     loadWebConsole(aTab).then(function() {
       console.log('loaded');
     });
   });
 }
 
 function loadWebConsole(aTab) {
--- a/browser/devtools/framework/test/browser_toolbox_custom_host.js
+++ b/browser/devtools/framework/test/browser_toolbox_custom_host.js
@@ -12,18 +12,16 @@ function test() {
 
   Cu.import("resource://gre/modules/devtools/Loader.jsm", temp);
   let devtools = temp.devtools;
 
   let Toolbox = devtools.Toolbox;
 
   let toolbox, iframe, target, tab;
 
-  waitForExplicitFinish();
-
   gBrowser.selectedTab = gBrowser.addTab();
   target = TargetFactory.forTab(gBrowser.selectedTab);
 
   window.addEventListener("message", onMessage);
 
   iframe = document.createElement("iframe");
   document.documentElement.appendChild(iframe);
 
--- a/browser/devtools/framework/test/browser_toolbox_dynamic_registration.js
+++ b/browser/devtools/framework/test/browser_toolbox_dynamic_registration.js
@@ -1,18 +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/ */
 
 let toolbox;
 
 function test()
 {
-  waitForExplicitFinish();
-
   gBrowser.selectedTab = gBrowser.addTab();
   let target = TargetFactory.forTab(gBrowser.selectedTab);
 
   gBrowser.selectedBrowser.addEventListener("load", function onLoad(evt) {
     gBrowser.selectedBrowser.removeEventListener(evt.type, onLoad, true);
     gDevTools.showToolbox(target).then(testRegister);
   }, true);
 
--- a/browser/devtools/framework/test/browser_toolbox_highlight.js
+++ b/browser/devtools/framework/test/browser_toolbox_highlight.js
@@ -4,24 +4,22 @@
 let Toolbox = devtools.Toolbox;
 let temp = {};
 Cu.import("resource://gre/modules/Services.jsm", temp);
 let Services = temp.Services;
 temp = null;
 let toolbox = null;
 
 function test() {
-  waitForExplicitFinish();
-
   const URL = "data:text/plain;charset=UTF-8,Nothing to see here, move along";
 
   const TOOL_ID_1 = "jsdebugger";
   const TOOL_ID_2 = "webconsole";
 
-  addTab(URL, () => {
+  addTab(URL).then(() => {
     let target = TargetFactory.forTab(gBrowser.selectedTab);
     gDevTools.showToolbox(target, TOOL_ID_1, Toolbox.HostType.BOTTOM)
              .then(aToolbox => {
                 toolbox = aToolbox;
                 // select tool 2
                 toolbox.selectTool(TOOL_ID_2)
                        // and highlight the first one
                        .then(highlightTab.bind(null, TOOL_ID_1))
--- a/browser/devtools/framework/test/browser_toolbox_hosts.js
+++ b/browser/devtools/framework/test/browser_toolbox_hosts.js
@@ -10,18 +10,16 @@ Cu.import("resource://gre/modules/devtoo
 let devtools = temp.devtools;
 
 let Toolbox = devtools.Toolbox;
 
 let toolbox, target;
 
 function test()
 {
-  waitForExplicitFinish();
-
   gBrowser.selectedTab = gBrowser.addTab();
   target = TargetFactory.forTab(gBrowser.selectedTab);
 
   gBrowser.selectedBrowser.addEventListener("load", function onLoad(evt) {
     gBrowser.selectedBrowser.removeEventListener(evt.type, onLoad, true);
     gDevTools.showToolbox(target)
              .then(testBottomHost, console.error)
              .then(null, console.error);
--- a/browser/devtools/framework/test/browser_toolbox_options.js
+++ b/browser/devtools/framework/test/browser_toolbox_options.js
@@ -1,19 +1,18 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 let doc = null, toolbox = null, panelWin = null, modifiedPrefs = [];
 
 function test() {
-  waitForExplicitFinish();
-
   const URL = "data:text/html;charset=utf8,test for dynamically registering and unregistering tools";
   Task.spawn(function* () {
-    let { target } = yield addTab(URL);
+    let tab = yield addTab(URL);
+    let target = TargetFactory.forTab(tab);
     let toolbox = yield gDevTools.showToolbox(target);
     yield testSelectTool(toolbox);
     yield testOptionsShortcut();
     yield testOptions();
     yield testToggleTools();
   }).then(cleanup, errorHandler);
 }
 
--- a/browser/devtools/framework/test/browser_toolbox_options_disable_buttons.js
+++ b/browser/devtools/framework/test/browser_toolbox_options_disable_buttons.js
@@ -1,16 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 let doc = null, toolbox = null, panelWin = null, modifiedPrefs = [];
 
 function test() {
-  waitForExplicitFinish();
-
   gBrowser.selectedTab = gBrowser.addTab();
   let target = TargetFactory.forTab(gBrowser.selectedTab);
 
   gBrowser.selectedBrowser.addEventListener("load", function onLoad(evt) {
     gBrowser.selectedBrowser.removeEventListener(evt.type, onLoad, true);
     gDevTools.showToolbox(target)
       .then(testSelectTool)
       .then(testToggleToolboxButtons)
--- a/browser/devtools/framework/test/browser_toolbox_options_disable_cache.js
+++ b/browser/devtools/framework/test/browser_toolbox_options_disable_cache.js
@@ -1,113 +1,159 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
-// Tests that disabling JavaScript for a tab works as it should.
+// Tests that disabling the cache for a tab works as it should.
 
 const TEST_URI = "http://mochi.test:8888/browser/browser/devtools/framework/" +
                  "test/browser_toolbox_options_disable_cache.sjs";
-
-let doc;
-let toolbox;
+let tabs = [
+{
+  title: "Tab 0",
+  desc: "Toggles cache on.",
+  startToolbox: true
+},
+{
+  title: "Tab 1",
+  desc: "Toolbox open before Tab 1 toggles cache.",
+  startToolbox: true
+},
+{
+  title: "Tab 2",
+  desc: "Opens toolbox after Tab 1 has toggled cache. Also closes and opens.",
+  startToolbox: false
+},
+{
+  title: "Tab 3",
+  desc: "No toolbox",
+  startToolbox: false
+}];
 
-function test() {
-  waitForExplicitFinish();
+let test = asyncTest(function*() {
+  // Initialise tabs: 1 and 2 with a toolbox, 3 and 4 without.
+  for (let tab of tabs) {
+    yield initTab(tab, tab.startToolbox);
+  }
 
-  gBrowser.selectedTab = gBrowser.addTab();
-  let target = TargetFactory.forTab(gBrowser.selectedTab);
+  // Ensure cache is enabled for all tabs.
+  yield checkCacheStateForAllTabs([true, true, true, true]);
 
-  gBrowser.selectedBrowser.addEventListener("load", function onLoad(evt) {
-    gBrowser.selectedBrowser.removeEventListener(evt.type, onLoad, true);
-    doc = content.document;
-    gDevTools.showToolbox(target).then(testSelectTool);
-  }, true);
+  // Check the checkbox in tab 0 and ensure cache is disabled for tabs 0 and 1.
+  yield setDisableCacheCheckboxChecked(tabs[0], true);
+  yield checkCacheStateForAllTabs([false, false, true, true]);
+
+  // Open toolbox in tab 2 and ensure the cache is then disabled.
+  tabs[2].toolbox = yield gDevTools.showToolbox(tabs[2].target, "options");
+  yield checkCacheEnabled(tabs[2], false);
+
+  // Close toolbox in tab 2 and ensure the cache is enabled again
+  yield tabs[2].toolbox.destroy();
+  tabs[2].target = TargetFactory.forTab(tabs[2].tab);
+  yield checkCacheEnabled(tabs[2], true);
 
-  content.location = TEST_URI;
-}
+  // Open toolbox in tab 2 and ensure the cache is then disabled.
+  tabs[2].toolbox = yield gDevTools.showToolbox(tabs[2].target, "options");
+  yield checkCacheEnabled(tabs[2], false);
+
+  // Check the checkbox in tab 2 and ensure cache is enabled for all tabs.
+  yield setDisableCacheCheckboxChecked(tabs[2], false);
+  yield checkCacheStateForAllTabs([true, true, true, true]);
 
-function testSelectTool(aToolbox) {
-  toolbox = aToolbox;
-  toolbox.once("options-selected", testCacheEnabled);
-  toolbox.selectTool("options");
+  yield finishUp();
+});
+
+function* initTab(tabX, startToolbox) {
+  tabX.tab = yield addTab(TEST_URI);
+  tabX.target = TargetFactory.forTab(tabX.tab);
+
+  if (startToolbox) {
+    tabX.toolbox = yield gDevTools.showToolbox(tabX.target, "options");
+  }
 }
 
-function testCacheEnabled() {
-  let prevTimestamp = getGUID();
-
-  gBrowser.selectedBrowser.addEventListener("load", function onLoad(evt) {
-    gBrowser.selectedBrowser.removeEventListener(evt.type, onLoad, true);
-    doc = content.document;
-    is(prevTimestamp, getGUID(), "GUID has not changed (page is cached)");
-
-    testCacheEnabled2();
-  }, true);
-
-  doc.location.reload(false);
+function* checkCacheStateForAllTabs(states) {
+  for (let i = 0; i < tabs.length; i ++) {
+    let tab = tabs[i];
+    yield checkCacheEnabled(tab, states[i]);
+  }
 }
 
-function testCacheEnabled2() {
-  let prevTimestamp = getGUID();
+function* checkCacheEnabled(tabX, expected) {
+  gBrowser.selectedTab = tabX.tab;
+
+  yield reloadTab(tabX);
+
+  let doc = content.document;
+  let h1 = doc.querySelector("h1");
+  let oldGuid = h1.textContent;
 
-  gBrowser.selectedBrowser.addEventListener("load", function onLoad(evt) {
-    gBrowser.selectedBrowser.removeEventListener(evt.type, onLoad, true);
-    doc = content.document;
-    is(prevTimestamp, getGUID(),
-       "GUID has not changed after page refresh (page is cached)");
+  yield reloadTab(tabX);
+
+  doc = content.document;
+  h1 = doc.querySelector("h1");
+  let guid = h1.textContent;
 
-    testCacheDisabled();
-  }, true);
-
-  doc.location.reload(false);
+  if (expected) {
+    is(guid, oldGuid, tabX.title + " cache is enabled");
+  } else {
+    isnot(guid, oldGuid, tabX.title + " cache is not enabled");
+  }
 }
 
-function testCacheDisabled() {
-  let prevTimestamp = getGUID();
-
-  let panel = toolbox.getCurrentPanel();
-  let cbx = panel.panelDoc.getElementById("devtools-disable-cache");
-  let browser = gBrowser.selectedBrowser;
+function* setDisableCacheCheckboxChecked(tabX, state) {
+  gBrowser.selectedTab = tabX.tab;
 
-  browser.addEventListener("load", function onLoad(evt) {
-    browser.removeEventListener(evt.type, onLoad, true);
-    doc = content.document;
-    isnot(prevTimestamp, getGUID(), "GUID has changed (page is not cached)");
-    testCacheDisabled2();
-  }, true);
-
-  info("disabling cache");
+  let panel = tabX.toolbox.getCurrentPanel();
+  let cbx = panel.panelDoc.getElementById("devtools-disable-cache");
 
   cbx.scrollIntoView();
 
-  // After uising scrollIntoView() we need to use executeSoon() to wait for the
-  // browser to scroll.
-  executeSoon(function() {
+  // After uising scrollIntoView() we need to wait for the browser to scroll.
+  yield waitForTick();
+
+  if (cbx.checked !== state) {
+    info("Setting disable cache checkbox to " + state + " for " + tabX.title);
     EventUtils.synthesizeMouseAtCenter(cbx, {}, panel.panelWin);
-  });
+
+    // We need to wait for all checkboxes to be updated and the docshells to
+    // apply the new cache settings.
+    yield waitForTick();
+  }
 }
 
-function testCacheDisabled2() {
-  let prevTimestamp = getGUID();
-
+function reloadTab(tabX) {
+  let def = promise.defer();
   let browser = gBrowser.selectedBrowser;
 
-  browser.addEventListener("load", function onLoad(evt) {
-    browser.removeEventListener(evt.type, onLoad, true);
-    doc = content.document;
-    isnot(prevTimestamp, getGUID(),
-       "GUID has changed after page refresh (page is not cached)");
-    finishUp();
+  // once() doesn't work here so we use a standard handler instead.
+  browser.addEventListener("load", function onLoad() {
+    browser.removeEventListener("load", onLoad, true);
+    info("Reloaded tab " + tabX.title);
+    def.resolve();
   }, true);
 
-  doc.location.reload(false);
+  info("Reloading tab " + tabX.title);
+  content.document.location.reload(false);
+
+  return def.promise;
 }
 
-function getGUID() {
-  return doc.querySelector("h1").textContent;
+function* destroyTab(tabX) {
+  let toolbox = gDevTools.getToolbox(tabX.target);
+
+  info("Removing tab " + tabX.title);
+  gBrowser.removeTab(tabX.tab);
+  info("Removed tab " + tabX.title);
+
+  if (toolbox) {
+    info("Waiting for toolbox-destroyed");
+    yield gDevTools.once("toolbox-destroyed");
+    info("toolbox-destroyed event received for " + tabX.title);
+  }
 }
 
-function finishUp() {
-  toolbox.destroy().then(() => {
-    gBrowser.removeCurrentTab();
-    toolbox = doc = null;
-    finish();
-  }).then(null, console.error);
+function* finishUp() {
+  for (let tab of tabs) {
+    yield destroyTab(tab);
+  }
+
+  tabs = null;
 }
--- a/browser/devtools/framework/test/browser_toolbox_options_disable_js.js
+++ b/browser/devtools/framework/test/browser_toolbox_options_disable_js.js
@@ -5,18 +5,16 @@
 
 const TEST_URI = "http://example.com/browser/browser/devtools/framework/" +
                  "test/browser_toolbox_options_disable_js.html";
 
 let doc;
 let toolbox;
 
 function test() {
-  waitForExplicitFinish();
-
   gBrowser.selectedTab = gBrowser.addTab();
   let target = TargetFactory.forTab(gBrowser.selectedTab);
 
   gBrowser.selectedBrowser.addEventListener("load", function onLoad(evt) {
     gBrowser.selectedBrowser.removeEventListener(evt.type, onLoad, true);
     doc = content.document;
     gDevTools.showToolbox(target).then(testSelectTool);
   }, true);
--- a/browser/devtools/framework/test/browser_toolbox_raise.js
+++ b/browser/devtools/framework/test/browser_toolbox_raise.js
@@ -10,18 +10,16 @@ let DevTools = temp.DevTools;
 Cu.import("resource://gre/modules/devtools/Loader.jsm", temp);
 let devtools = temp.devtools;
 
 let Toolbox = devtools.Toolbox;
 
 let toolbox, target, tab1, tab2;
 
 function test() {
-  waitForExplicitFinish();
-
   gBrowser.selectedTab = tab1 = gBrowser.addTab();
   tab2 = gBrowser.addTab();
   target = TargetFactory.forTab(gBrowser.selectedTab);
 
   gBrowser.selectedBrowser.addEventListener("load", function onLoad(evt) {
     gBrowser.selectedBrowser.removeEventListener(evt.type, onLoad, true);
     gDevTools.showToolbox(target)
              .then(testBottomHost, console.error)
--- a/browser/devtools/framework/test/browser_toolbox_ready.js
+++ b/browser/devtools/framework/test/browser_toolbox_ready.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()
 {
-  waitForExplicitFinish();
-
   gBrowser.selectedTab = gBrowser.addTab();
   let target = TargetFactory.forTab(gBrowser.selectedTab);
 
   gBrowser.selectedBrowser.addEventListener("load", function onLoad(evt) {
     gBrowser.selectedBrowser.removeEventListener(evt.type, onLoad, true);
     gDevTools.showToolbox(target).then(testReady);
   }, true);
 
--- a/browser/devtools/framework/test/browser_toolbox_select_event.js
+++ b/browser/devtools/framework/test/browser_toolbox_select_event.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 let toolbox;
 
 function test() {
-  addTab("about:blank", function() {
+  addTab("about:blank").then(function() {
     let target = TargetFactory.forTab(gBrowser.selectedTab);
     gDevTools.showToolbox(target, "webconsole").then(testSelect);
   });
 }
 
 let called = {
   inspector: false,
   webconsole: false,
--- a/browser/devtools/framework/test/browser_toolbox_sidebar.js
+++ b/browser/devtools/framework/test/browser_toolbox_sidebar.js
@@ -40,18 +40,18 @@ function test() {
         });
       });
       return deferred.promise;
     },
   };
 
   gDevTools.registerTool(toolDefinition);
 
-  addTab("about:blank", function(aBrowser, aTab) {
-    let target = TargetFactory.forTab(gBrowser.selectedTab);
+  addTab("about:blank").then(function(aTab) {
+    let target = TargetFactory.forTab(aTab);
     gDevTools.showToolbox(target, toolDefinition.id).then(function(toolbox) {
       let panel = toolbox.getPanel(toolDefinition.id);
       ok(true, "Tool open");
 
       let tabbox = panel.panelDoc.getElementById("sidebar");
       panel.sidebar = new ToolSidebar(tabbox, panel, "testbug865688", true);
 
       panel.sidebar.on("new-tab-registered", function(event, id) {
@@ -137,14 +137,15 @@ function test() {
       finishUp(panel);
     });
   }
 
   function finishUp(panel) {
     panel.sidebar.destroy();
     gDevTools.unregisterTool(toolDefinition.id);
 
+    gBrowser.removeCurrentTab();
+
     executeSoon(function() {
-      gBrowser.removeCurrentTab();
       finish();
     });
   }
 }
--- a/browser/devtools/framework/test/browser_toolbox_tabsswitch_shortcuts.js
+++ b/browser/devtools/framework/test/browser_toolbox_tabsswitch_shortcuts.js
@@ -2,19 +2,17 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 let Toolbox = devtools.Toolbox;
 
 let toolbox, toolIDs, idIndex, secondTime = false,
     reverse = false, nextKey = null, prevKey = null;
 
 function test() {
-  waitForExplicitFinish();
-
-  addTab("about:blank", function() {
+  addTab("about:blank").then(function() {
     let target = TargetFactory.forTab(gBrowser.selectedTab);
     idIndex = 0;
 
     target.makeRemote().then(() => {
       toolIDs = gDevTools.getToolDefinitionArray()
                   .filter(def => def.isTargetSupported(target))
                   .map(def => def.id);
       gDevTools.showToolbox(target, toolIDs[0], Toolbox.HostType.BOTTOM)
--- a/browser/devtools/framework/test/browser_toolbox_tool_ready.js
+++ b/browser/devtools/framework/test/browser_toolbox_tool_ready.js
@@ -1,26 +1,27 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 function test() {
-  addTab().then(function(data) {
-    data.target.makeRemote().then(performChecks.bind(null, data));
+  addTab("about:blank").then(function(tab) {
+    let target = TargetFactory.forTab(tab);
+    target.makeRemote().then(performChecks.bind(null, target));
   }).then(null, console.error);
 
-  function performChecks(data) {
+  function performChecks(target) {
     let toolIds = gDevTools.getToolDefinitionArray()
-                    .filter(def => def.isTargetSupported(data.target))
+                    .filter(def => def.isTargetSupported(target))
                     .map(def => def.id);
 
     let open = function(index) {
       let toolId = toolIds[index];
 
       info("About to open " + index + "/" + toolId);
-      gDevTools.showToolbox(data.target, toolId).then(function(toolbox) {
+      gDevTools.showToolbox(target, toolId).then(function(toolbox) {
         ok(toolbox, "toolbox exists for " + toolId);
         is(toolbox.currentToolId, toolId, "currentToolId should be " + toolId);
 
         let panel = toolbox.getCurrentPanel();
         ok(panel.isReady, toolId + " panel should be ready");
 
         let nextIndex = index + 1;
         if (nextIndex >= toolIds.length) {
--- a/browser/devtools/framework/test/browser_toolbox_window_reload_target.js
+++ b/browser/devtools/framework/test/browser_toolbox_window_reload_target.js
@@ -1,22 +1,21 @@
 /* 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 target, toolbox, description, reloadsSent, toolIDs;
 
 function test() {
-  waitForExplicitFinish();
-
-  addTab("data:text/html;charset=utf-8,"+
-         "<html><head><title>Test reload</title></head>"+
-         "<body><h1>Testing reload from devtools</h1></body></html>",
-         () => {
+  addTab(TEST_URL).then(() => {
     target = TargetFactory.forTab(gBrowser.selectedTab);
 
     target.makeRemote().then(() => {
       toolIDs = gDevTools.getToolDefinitionArray()
                   .filter(def => def.isTargetSupported(target))
                   .map(def => def.id);
       gDevTools.showToolbox(target, toolIDs[0], Toolbox.HostType.BOTTOM)
                .then(startReloadTest);
--- a/browser/devtools/framework/test/browser_toolbox_window_shortcuts.js
+++ b/browser/devtools/framework/test/browser_toolbox_window_shortcuts.js
@@ -1,25 +1,23 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 let Toolbox = devtools.Toolbox;
 
 let toolbox, toolIDs, idIndex, modifiedPrefs = [];
 
 function test() {
-  waitForExplicitFinish();
-
   if (window.navigator.userAgent.indexOf("Mac OS X 10.8") != -1 ||
       window.navigator.userAgent.indexOf("Windows NT 5.1") != -1) {
     info("Skipping Mac OSX 10.8 and Windows xp, see bug 838069");
     finish();
     return;
   }
-  addTab("about:blank", function() {
+  addTab("about:blank").then(function() {
     toolIDs = [];
     for (let [id, definition] of gDevTools._tools) {
       if (definition.key) {
         toolIDs.push(id);
 
         // Enable disabled tools
         let pref = definition.visibilityswitch, prefValue;
         try {
--- a/browser/devtools/framework/test/browser_toolbox_window_title_changes.js
+++ b/browser/devtools/framework/test/browser_toolbox_window_title_changes.js
@@ -3,30 +3,28 @@
 
 let Toolbox = devtools.Toolbox;
 let temp = {};
 Cu.import("resource://gre/modules/Services.jsm", temp);
 let Services = temp.Services;
 temp = null;
 
 function test() {
-  waitForExplicitFinish();
-
   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";
 
   const LABEL_1 = "Console";
   const LABEL_2 = "Debugger";
 
   let toolbox;
 
-  addTab(URL_1, function () {
+  addTab(URL_1).then(function () {
     let target = TargetFactory.forTab(gBrowser.selectedTab);
     gDevTools.showToolbox(target, null, Toolbox.HostType.BOTTOM)
       .then(function (aToolbox) { toolbox = aToolbox; })
       .then(function () toolbox.selectTool(TOOL_ID_1))
 
     // undock toolbox and check title
       .then(function () toolbox.switchHost(Toolbox.HostType.WINDOW))
       .then(checkTitle.bind(null, LABEL_1, URL_1, "toolbox undocked"))
--- a/browser/devtools/framework/test/browser_toolbox_zoom.js
+++ b/browser/devtools/framework/test/browser_toolbox_zoom.js
@@ -3,21 +3,17 @@
 
 let modifiers = {
   accelKey: true
 };
 
 let toolbox;
 
 function test() {
-  waitForExplicitFinish();
-
-  addTab("about:blank", function() {
-    openToolbox();
-  });
+  addTab("about:blank").then(openToolbox);
 }
 
 function openToolbox() {
   let target = TargetFactory.forTab(gBrowser.selectedTab);
 
   gDevTools.showToolbox(target).then((aToolbox) => {
     toolbox = aToolbox;
     toolbox.selectTool("styleeditor").then(testZoom);
--- a/browser/devtools/framework/test/head.js
+++ b/browser/devtools/framework/test/head.js
@@ -1,58 +1,57 @@
 /* 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 TargetFactory = gDevTools.TargetFactory;
 
-let tempScope = {};
-Components.utils.import("resource://gre/modules/devtools/Console.jsm", tempScope);
-let console = tempScope.console;
-Components.utils.import("resource://gre/modules/Promise.jsm", tempScope);
-let promise = tempScope.Promise;
+const { console } = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
+const { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
+const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+
+let TargetFactory = devtools.TargetFactory;
 
-let {devtools} = Components.utils.import("resource://gre/modules/devtools/Loader.jsm", {});
-let TargetFactory = devtools.TargetFactory;
+// All test are asynchronous
+waitForExplicitFinish();
+
+// Uncomment this pref to dump all devtools emitted events to the console.
+// Services.prefs.setBoolPref("devtools.dump.emit", true);
 
 gDevTools.testing = true;
 SimpleTest.registerCleanupFunction(() => {
   gDevTools.testing = false;
+  Services.prefs.clearUserPref("devtools.dump.emit");
 });
 
 /**
- * Open a new tab at a URL and call a callback on load
+ * Define an async test based on a generator function
  */
-function addTab(aURL, aCallback)
-{
-  waitForExplicitFinish();
-
-  gBrowser.selectedTab = gBrowser.addTab();
-  if (aURL != null) {
-    content.location = aURL;
-  }
-
-  let deferred = promise.defer();
+function asyncTest(generator) {
+  return () => Task.spawn(generator).then(null, ok.bind(null, false)).then(finish);
+}
 
-  let tab = gBrowser.selectedTab;
-  let target = TargetFactory.forTab(gBrowser.selectedTab);
-  let browser = gBrowser.getBrowserForTab(tab);
-
-  function onTabLoad() {
-    browser.removeEventListener("load", onTabLoad, true);
+/**
+ * Add a new test tab in the browser and load the given url.
+ * @param {String} url The url to be loaded in the new tab
+ * @return a promise that resolves to the tab object when the url is loaded
+ */
+function addTab(url) {
+  info("Adding a new tab with URL: '" + url + "'");
+  let def = promise.defer();
 
-    if (aCallback != null) {
-      aCallback(browser, tab, browser.contentDocument);
-    }
+  let tab = gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function onload() {
+    gBrowser.selectedBrowser.removeEventListener("load", onload, true);
+    info("URL '" + url + "' loading complete");
+    def.resolve(tab);
+  }, true);
+  content.location = url;
 
-    deferred.resolve({ browser: browser, tab: tab, target: target });
-  }
-
-  browser.addEventListener("load", onTabLoad, true);
-  return deferred.promise;
+  return def.promise;
 }
 
 registerCleanupFunction(function tearDown() {
   while (gBrowser.tabs.length > 1) {
     gBrowser.removeCurrentTab();
   }
 });
 
@@ -72,12 +71,49 @@ function synthesizeKeyFromKeyTag(aKeyId,
   isnot(name, null, "Successfully retrieved keycode/key");
 
   let modifiers = {
     shiftKey: modifiersAttr.match("shift"),
     ctrlKey: modifiersAttr.match("ctrl"),
     altKey: modifiersAttr.match("alt"),
     metaKey: modifiersAttr.match("meta"),
     accelKey: modifiersAttr.match("accel")
-  }
+  };
 
   EventUtils.synthesizeKey(name, modifiers);
 }
+
+/**
+ * Wait for eventName on target.
+ * @param {Object} target An observable object that either supports on/off or
+ * addEventListener/removeEventListener
+ * @param {String} eventName
+ * @param {Boolean} useCapture Optional, for addEventListener/removeEventListener
+ * @return A promise that resolves when the event has been handled
+ */
+function once(target, eventName, useCapture=false) {
+  info("Waiting for event: '" + eventName + "' on " + target + ".");
+
+  let deferred = promise.defer();
+
+  for (let [add, remove] of [
+    ["addEventListener", "removeEventListener"],
+    ["addListener", "removeListener"],
+    ["on", "off"]
+  ]) {
+    if ((add in target) && (remove in target)) {
+      target[add](eventName, function onEvent(...aArgs) {
+        info("Got event: '" + eventName + "' on " + target + ".");
+        target[remove](eventName, onEvent, useCapture);
+        deferred.resolve.apply(deferred, aArgs);
+      }, useCapture);
+      break;
+    }
+  }
+
+  return deferred.promise;
+}
+
+function waitForTick() {
+  let deferred = promise.defer();
+  executeSoon(deferred.resolve);
+  return deferred.promise;
+}
--- a/browser/devtools/framework/toolbox-options.js
+++ b/browser/devtools/framework/toolbox-options.js
@@ -32,16 +32,20 @@ XPCOMUtils.defineLazyGetter(this, "l10n"
  * Represents the Options Panel in the Toolbox.
  */
 function OptionsPanel(iframeWindow, toolbox) {
   this.panelDoc = iframeWindow.document;
   this.panelWin = iframeWindow;
   this.toolbox = toolbox;
   this.isReady = false;
 
+  this._prefChanged = this._prefChanged.bind(this);
+
+  this._addListeners();
+
   const EventEmitter = require("devtools/toolkit/event-emitter");
   EventEmitter.decorate(this);
 }
 
 OptionsPanel.prototype = {
 
   get target() {
     return this.toolbox.target;
@@ -58,33 +62,46 @@ OptionsPanel.prototype = {
     }
 
     return targetPromise.then(() => {
       this.setupToolsList();
       this.setupToolbarButtonsList();
       this.populatePreferences();
 
       this._disableJSClicked = this._disableJSClicked.bind(this);
-      this._disableCacheClicked = this._disableCacheClicked.bind(this);
 
       let disableJSNode = this.panelDoc.getElementById("devtools-disable-javascript");
       disableJSNode.addEventListener("click", this._disableJSClicked, false);
-
-      let disableCacheNode = this.panelDoc.getElementById("devtools-disable-cache");
-      disableCacheNode.addEventListener("click", this._disableCacheClicked, false);
     }).then(() => {
       this.isReady = true;
       this.emit("ready");
       return this;
     }).then(null, function onError(aReason) {
       Cu.reportError("OptionsPanel open failed. " +
                      aReason.error + ": " + aReason.message);
     });
   },
 
+  _addListeners: function() {
+    gDevTools.on("pref-changed", this._prefChanged);
+  },
+
+  _removeListeners: function() {
+    gDevTools.off("pref-changed", this._prefChanged);
+  },
+
+  _prefChanged: function(event, data) {
+    if (data.pref === "devtools.cache.disabled") {
+      let cacheDisabled = data.newValue;
+      let cbx = this.panelDoc.getElementById("devtools-disable-cache");
+
+      cbx.checked = cacheDisabled;
+    }
+  },
+
   setupToolbarButtonsList: function() {
     let enabledToolbarButtonsBox = this.panelDoc.getElementById("enabled-toolbox-buttons-box");
     enabledToolbarButtonsBox.textContent = "";
 
     let toggleableButtons = this.toolbox.toolboxButtons;
     let setToolboxButtonsVisibility =
       this.toolbox.setToolboxButtonsVisibility.bind(this.toolbox);
 
@@ -237,33 +254,26 @@ OptionsPanel.prototype = {
         data.oldValue = Services.prefs.getCharPref(data.pref);
         Services.prefs.setCharPref(data.pref, data.newValue);
         gDevTools.emit("pref-changed", data);
       }.bind(menulist));
     }
 
     this.target.client.attachTab(this.target.activeTab._actor, (response) => {
       this._origJavascriptEnabled = response.javascriptEnabled;
-      this._origCacheEnabled = response.cacheEnabled;
 
       this._populateDisableJSCheckbox();
-      this._populateDisableCacheCheckbox();
     });
   },
 
   _populateDisableJSCheckbox: function() {
     let cbx = this.panelDoc.getElementById("devtools-disable-javascript");
     cbx.checked = !this._origJavascriptEnabled;
   },
 
-  _populateDisableCacheCheckbox: function() {
-    let cbx = this.panelDoc.getElementById("devtools-disable-cache");
-    cbx.checked = !this._origCacheEnabled;
-  },
-
   /**
    * Disables JavaScript for the currently loaded tab. We force a page refresh
    * here because setting docShell.allowJavascript to true fails to block JS
    * execution from event listeners added using addEventListener(), AJAX calls
    * and timers. The page refresh prevents these things from being added in the
    * first place.
    *
    * @param {Event} event
@@ -274,54 +284,35 @@ OptionsPanel.prototype = {
 
     let options = {
       "javascriptEnabled": !checked
     };
 
     this.target.activeTab.reconfigure(options);
   },
 
-  /**
-   * Disables the cache for the currently loaded tab.
-   *
-   * @param {Event} event
-   *        The event sent by checking / unchecking the disable cache checkbox.
-   */
-  _disableCacheClicked: function(event) {
-    let checked = event.target.checked;
-
-    let options = {
-      "cacheEnabled": !checked
-    };
-
-    this.target.activeTab.reconfigure(options);
-  },
-
   destroy: function() {
     if (this.destroyPromise) {
       return this.destroyPromise;
     }
 
     let deferred = promise.defer();
 
     this.destroyPromise = deferred.promise;
 
     let disableJSNode = this.panelDoc.getElementById("devtools-disable-javascript");
     disableJSNode.removeEventListener("click", this._disableJSClicked, false);
 
-    let disableCacheNode = this.panelDoc.getElementById("devtools-disable-cache");
-    disableCacheNode.removeEventListener("click", this._disableCacheClicked, false);
+    this._removeListeners();
 
     this.panelWin = this.panelDoc = null;
-    this._disableJSClicked = this._disableCacheClicked = null;
+    this._disableJSClicked = null;
 
-    // If the cache or JavaScript is disabled we need to revert them to their
-    // original values.
+    // If JavaScript is disabled we need to revert it to it's original value.
     let options = {
-      "cacheEnabled": this._origCacheEnabled,
       "javascriptEnabled": this._origJavascriptEnabled
     };
     this.target.activeTab.reconfigure(options, () => {
       this.toolbox = null;
       deferred.resolve();
     }, true);
 
     return deferred.promise;
--- a/browser/devtools/framework/toolbox-options.xul
+++ b/browser/devtools/framework/toolbox-options.xul
@@ -83,18 +83,19 @@
         <vbox id="profiler-options" class="options-groupbox">
           <checkbox label="&options.showPlatformData.label;"
                     tooltiptext="&options.showPlatformData.tooltip;"
                     data-pref="devtools.profiler.ui.show-platform-data"/>
         </vbox>
         <label value="&options.context.advancedSettings;"/>
         <vbox id="context-options" class="options-groupbox">
           <checkbox id="devtools-disable-cache"
-                    label="&options.disableCache.label;"
-                    tooltiptext="&options.disableCache.tooltip;"/>
+                    label="&options.disableCache.label2;"
+                    tooltiptext="&options.disableCache.tooltip2;"
+                    data-pref="devtools.cache.disabled"/>
           <checkbox id="devtools-disable-javascript"
                     label="&options.disableJavaScript.label;"
                     tooltiptext="&options.disableJavaScript.tooltip;"/>
           <hbox class="hidden-labels-box">
             <checkbox label="&options.enableChrome.label4;"
                       tooltiptext="&options.enableChrome.tooltip2;"
                       data-pref="devtools.chrome.enabled"/>
           </hbox>
--- a/browser/devtools/framework/toolbox.js
+++ b/browser/devtools/framework/toolbox.js
@@ -62,21 +62,22 @@ loader.lazyGetter(this, "InspectorFront"
 function Toolbox(target, selectedTool, hostType, hostOptions) {
   this._target = target;
   this._toolPanels = new Map();
   this._telemetry = new Telemetry();
 
   this._toolRegistered = this._toolRegistered.bind(this);
   this._toolUnregistered = this._toolUnregistered.bind(this);
   this._refreshHostTitle = this._refreshHostTitle.bind(this);
-  this._splitConsoleOnKeypress = this._splitConsoleOnKeypress.bind(this)
+  this._splitConsoleOnKeypress = this._splitConsoleOnKeypress.bind(this);
   this.destroy = this.destroy.bind(this);
   this.highlighterUtils = getHighlighterUtils(this);
   this._highlighterReady = this._highlighterReady.bind(this);
   this._highlighterHidden = this._highlighterHidden.bind(this);
+  this._prefChanged = this._prefChanged.bind(this);
 
   this._target.on("close", this.destroy);
 
   if (!hostType) {
     hostType = Services.prefs.getCharPref(this._prefs.LAST_HOST);
   }
   if (!selectedTool) {
     selectedTool = Services.prefs.getCharPref(this._prefs.LAST_TOOL);
@@ -236,20 +237,23 @@ Toolbox.prototype = {
       let deferred = promise.defer();
 
       let domReady = () => {
         this.isReady = true;
 
         let closeButton = this.doc.getElementById("toolbox-close");
         closeButton.addEventListener("command", this.destroy, true);
 
+        gDevTools.on("pref-changed", this._prefChanged);
+
         this._buildDockButtons();
         this._buildOptions();
         this._buildTabs();
         let buttonsPromise = this._buildButtons();
+        this._applyCacheSettings();
         this._addKeysToWindow();
         this._addReloadKeys();
         this._addToolSwitchingKeys();
         this._addZoomKeys();
         this._loadInitialZoom();
 
         this._telemetry.toolOpened("toolbox");
 
@@ -268,16 +272,34 @@ Toolbox.prototype = {
         let domHelper = new DOMHelpers(iframe.contentWindow);
         domHelper.onceDOMReady(domReady);
       });
 
       return deferred.promise;
     });
   },
 
+  /**
+   * Because our panels are lazy loaded this is a good place to watch for
+   * "pref-changed" events.
+   * @param  {String} event
+   *         The event type, "pref-changed".
+   * @param  {Object} data
+   *         {
+   *           newValue: The new value
+   *           oldValue:  The old value
+   *           pref: The name of the preference that has changed
+   *         }
+   */
+  _prefChanged: function(event, data) {
+    if (data.pref === "devtools.cache.disabled") {
+      this._applyCacheSettings();
+    }
+  },
+
   _buildOptions: function() {
     let key = this.doc.getElementById("toolbox-options-key");
     key.addEventListener("command", () => {
       this.selectTool("options");
     }, true);
   },
 
   _isResponsiveModeActive: function() {
@@ -583,16 +605,29 @@ Toolbox.prototype = {
     let container = this.doc.querySelector("#toolbox-picker-container");
     container.appendChild(this._pickerButton);
 
     this._togglePicker = this.highlighterUtils.togglePicker.bind(this.highlighterUtils);
     this._pickerButton.addEventListener("command", this._togglePicker, false);
   },
 
   /**
+   * Apply the current cache setting from devtools.cache.disabled to this
+   * toolbox's tab.
+   */
+  _applyCacheSettings: function() {
+    let pref = "devtools.cache.disabled";
+    let cacheDisabled = Services.prefs.getBoolPref(pref);
+
+    if (this.target.activeTab) {
+      this.target.activeTab.reconfigure({"cacheDisabled": cacheDisabled});
+    }
+  },
+
+  /**
    * Setter for the checked state of the picker button in the toolbar
    * @param {Boolean} isChecked
    */
   set pickerButtonChecked(isChecked) {
     if (isChecked) {
       this._pickerButton.setAttribute("checked", "true");
     } else {
       this._pickerButton.removeAttribute("checked");
@@ -1257,26 +1292,34 @@ Toolbox.prototype = {
 
     this._target.off("navigate", this._refreshHostTitle);
     this.off("select", this._refreshHostTitle);
     this.off("host-changed", this._refreshHostTitle);
 
     gDevTools.off("tool-registered", this._toolRegistered);
     gDevTools.off("tool-unregistered", this._toolUnregistered);
 
+    gDevTools.off("pref-changed", this._prefChanged);
+
     let outstanding = [];
     for (let [id, panel] of this._toolPanels) {
       try {
         outstanding.push(panel.destroy());
       } catch (e) {
         // We don't want to stop here if any panel fail to close.
         console.error("Panel " + id + ":", e);
       }
     }
 
+    // Now that we are closing the toolbox we can re-enable JavaScript for the
+    // current tab.
+    if (this.target.activeTab) {
+      this.target.activeTab.reconfigure({"cacheDisabled": false});
+    }
+
     // Destroying the walker and inspector fronts
     outstanding.push(this.destroyInspector());
     // Removing buttons
     outstanding.push(() => {
       this._pickerButton.removeEventListener("command", this._togglePicker, false);
       this._pickerButton = null;
       let container = this.doc.getElementById("toolbox-buttons");
       while (container.firstChild) {
--- a/browser/devtools/inspector/test/head.js
+++ b/browser/devtools/inspector/test/head.js
@@ -8,17 +8,18 @@ const Cu = Components.utils;
 const Ci = Components.interfaces;
 const Cc = Components.classes;
 
 // Services.prefs.setBoolPref("devtools.debugger.log", true);
 // SimpleTest.registerCleanupFunction(() => {
 //   Services.prefs.clearUserPref("devtools.debugger.log");
 // });
 
-//Services.prefs.setBoolPref("devtools.dump.emit", true);
+// Uncomment this pref to dump all devtools emitted events to the console.
+// Services.prefs.setBoolPref("devtools.dump.emit", true);
 
 const TEST_URL_ROOT = "http://example.com/browser/browser/devtools/inspector/test/";
 const { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
 
 // All test are asynchronous
 waitForExplicitFinish();
 
 let tempScope = {};
--- a/browser/devtools/layoutview/test/head.js
+++ b/browser/devtools/layoutview/test/head.js
@@ -11,18 +11,19 @@ let TargetFactory = devtools.TargetFacto
 let {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
 let {console} = Components.utils.import("resource://gre/modules/devtools/Console.jsm", {});
 
 // All test are asynchronous
 waitForExplicitFinish();
 
 const TEST_URL_ROOT = "http://example.com/browser/browser/devtools/layoutview/test/";
 
-// Uncomment to log events
+// Uncomment this pref to dump all devtools emitted events to the console.
 // Services.prefs.setBoolPref("devtools.dump.emit", true);
+
 // Services.prefs.setBoolPref("devtools.debugger.log", true);
 
 // Set the testing flag on gDevTools and reset it when the test ends
 gDevTools.testing = true;
 registerCleanupFunction(() => gDevTools.testing = false);
 
 // Clean-up all prefs that might have been changed during a test run
 // (safer here because if the test fails, then the pref is never reverted)
--- a/browser/devtools/markupview/test/head.js
+++ b/browser/devtools/markupview/test/head.js
@@ -7,17 +7,18 @@ let {devtools} = Cu.import("resource://g
 let TargetFactory = devtools.TargetFactory;
 let {console} = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
 let promise = devtools.require("devtools/toolkit/deprecated-sync-thenables");
 let {getInplaceEditorForSpan: inplaceEditor} = devtools.require("devtools/shared/inplace-editor");
 
 // All test are asynchronous
 waitForExplicitFinish();
 
-//Services.prefs.setBoolPref("devtools.dump.emit", true);
+// Uncomment this pref to dump all devtools emitted events to the console.
+// Services.prefs.setBoolPref("devtools.dump.emit", true);
 
 // Set the testing flag on gDevTools and reset it when the test ends
 gDevTools.testing = true;
 registerCleanupFunction(() => gDevTools.testing = false);
 
 // Clear preferences that may be set during the course of tests.
 registerCleanupFunction(() => {
   Services.prefs.clearUserPref("devtools.inspector.htmlPanelOpen");
--- a/browser/devtools/netmonitor/netmonitor-controller.js
+++ b/browser/devtools/netmonitor/netmonitor-controller.js
@@ -357,30 +357,30 @@ let NetMonitorController = {
       return reconfigureTab(aOptions).then(() => navigationFinished);
     }
     if (aType == ACTIVITY_TYPE.RELOAD.WITH_CACHE_DEFAULT) {
       return reconfigureTabAndWaitForNavigation({}).then(standBy);
     }
     if (aType == ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED) {
       this._currentActivity = ACTIVITY_TYPE.ENABLE_CACHE;
       this._target.once("will-navigate", () => this._currentActivity = aType);
-      return reconfigureTabAndWaitForNavigation({ cacheEnabled: true }).then(standBy);
+      return reconfigureTabAndWaitForNavigation({ cacheDisabled: false, performReload: true }).then(standBy);
     }
     if (aType == ACTIVITY_TYPE.RELOAD.WITH_CACHE_DISABLED) {
       this._currentActivity = ACTIVITY_TYPE.DISABLE_CACHE;
       this._target.once("will-navigate", () => this._currentActivity = aType);
-      return reconfigureTabAndWaitForNavigation({ cacheEnabled: false }).then(standBy);
+      return reconfigureTabAndWaitForNavigation({ cacheDisabled: true, performReload: true }).then(standBy);
     }
     if (aType == ACTIVITY_TYPE.ENABLE_CACHE) {
       this._currentActivity = aType;
-      return reconfigureTab({ cacheEnabled: true, performReload: false }).then(standBy);
+      return reconfigureTab({ cacheDisabled: false, performReload: false }).then(standBy);
     }
     if (aType == ACTIVITY_TYPE.DISABLE_CACHE) {
       this._currentActivity = aType;
-      return reconfigureTab({ cacheEnabled: false, performReload: false }).then(standBy);
+      return reconfigureTab({ cacheDisabled: true, performReload: false }).then(standBy);
     }
     this._currentActivity = ACTIVITY_TYPE.NONE;
     return promise.reject(new Error("Invalid activity type"));
   },
 
   /**
    * Getter that tells if the server supports sending custom network requests.
    * @type boolean
--- a/browser/devtools/netmonitor/test/head.js
+++ b/browser/devtools/netmonitor/test/head.js
@@ -54,24 +54,29 @@ SimpleTest.registerCleanupFunction(() =>
 
 // All tests are asynchronous.
 waitForExplicitFinish();
 
 const 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);
 
+// Uncomment this pref to dump all devtools emitted events to the console.
+// Services.prefs.setBoolPref("devtools.dump.emit", true);
+
 // Always reset some prefs to their original values after the test finishes.
 const gDefaultFilters = Services.prefs.getCharPref("devtools.netmonitor.filters");
 
 registerCleanupFunction(() => {
   info("finish() was called, cleaning up...");
 
   Services.prefs.setBoolPref("devtools.debugger.log", gEnableLogging);
   Services.prefs.setCharPref("devtools.netmonitor.filters", gDefaultFilters);
+  Services.prefs.clearUserPref("devtools.cache.disabled");
+  Services.prefs.clearUserPref("devtools.dump.emit");
 });
 
 function addTab(aUrl, aWindow) {
   info("Adding tab: " + aUrl);
 
   let deferred = promise.defer();
   let targetWindow = aWindow || window;
   let targetBrowser = targetWindow.gBrowser;
@@ -108,37 +113,41 @@ function waitForNavigation(aTarget) {
 }
 
 function reconfigureTab(aTarget, aOptions) {
   let deferred = promise.defer();
   aTarget.activeTab.reconfigure(aOptions, deferred.resolve);
   return deferred.promise;
 };
 
-function toggleCache(aTarget, aEnabled) {
-  let options = { cacheEnabled: aEnabled, performReload: true };
+function toggleCache(aTarget, aDisabled) {
+  let options = { cacheDisabled: aDisabled, performReload: true };
   let navigationFinished = waitForNavigation(aTarget);
+
+  // Disable the cache for any toolbox that it is opened from this point on.
+  Services.prefs.setBoolPref("devtools.cache.disabled", aDisabled);
+
   return reconfigureTab(aTarget, options).then(() => navigationFinished);
 }
 
 function initNetMonitor(aUrl, aWindow) {
   info("Initializing a network monitor pane.");
 
   return Task.spawn(function*() {
     let tab = yield addTab(aUrl);
     info("Net tab added successfully: " + aUrl);
 
     let debuggee = tab.linkedBrowser.contentWindow.wrappedJSObject;
     let target = TargetFactory.forTab(tab);
 
     yield target.makeRemote();
     info("Target remoted.");
 
-    yield toggleCache(target, false);
-    info("Network cache disabled");
+    yield toggleCache(target, true);
+    info("Cache disabled when the current and all future toolboxes are open.");
 
     let toolbox = yield gDevTools.showToolbox(target, "netmonitor");
     info("Netork monitor pane shown successfully.");
 
     let monitor = toolbox.getCurrentPanel();
     return [tab, debuggee, monitor];
   });
 }
--- a/browser/devtools/projecteditor/test/head.js
+++ b/browser/devtools/projecteditor/test/head.js
@@ -13,17 +13,18 @@ const ProjectEditor = devtools.require("
 
 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;
 
 // All test are asynchronous
 waitForExplicitFinish();
 
-//Services.prefs.setBoolPref("devtools.dump.emit", true);
+// Uncomment this pref to dump all devtools emitted events to the console.
+// Services.prefs.setBoolPref("devtools.dump.emit", true);
 
 // Set the testing flag on gDevTools and reset it when the test ends
 gDevTools.testing = true;
 registerCleanupFunction(() => gDevTools.testing = false);
 
 // Clear preferences that may be set during the course of tests.
 registerCleanupFunction(() => {
   // Services.prefs.clearUserPref("devtools.dump.emit");
--- a/browser/devtools/scratchpad/test/browser_scratchpad_reload_and_run.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_reload_and_run.js
@@ -49,27 +49,28 @@ function runTests()
   // body to make sure that the page has been reloaded and Scratchpad
   // code has been executed.
 
   sp.setContentContext();
   sp.setText(EDITOR_TEXT);
 
   let browser = gBrowser.selectedBrowser;
 
+  let deferred = promise.defer();
   browser.addEventListener("DOMWindowCreated", function onWindowCreated() {
     browser.removeEventListener("DOMWindowCreated", onWindowCreated, true);
 
     browser.contentWindow.addEventListener("foo", function onFoo() {
       browser.contentWindow.removeEventListener("foo", onFoo, true);
 
       is(browser.contentWindow.document.body.innerHTML, "Modified text",
         "After reloading, HTML is different.");
 
       Services.prefs.clearUserPref(DEVTOOLS_CHROME_ENABLED);
-      finish();
+      deferred.resolve();
     }, true);
   }, true);
 
   ok(browser.contentWindow.document.body.innerHTML !== "Modified text",
       "Before reloading, HTML is intact.");
-  sp.reloadAndRun();
+  sp.reloadAndRun().then(deferred.promise).then(finish);
 }
 
--- a/browser/devtools/shared/Jsbeautify.jsm
+++ b/browser/devtools/shared/Jsbeautify.jsm
@@ -1,1303 +1,16 @@
-/*jslint onevar: false, plusplus: false */
-/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
-/*
-
- JS Beautifier
----------------
-
-
-  Written by Einar Lielmanis, <einar@jsbeautifier.org>
-      http://jsbeautifier.org/
-
-  Originally converted to javascript by Vital, <vital76@gmail.com>
-  "End braces on own line" added by Chris J. Shull, <chrisjshull@gmail.com>
-
-  You are free to use this in any way you want, in case you find this useful or working for you.
-
-  Usage:
-    js_beautify(js_source_text);
-    js_beautify(js_source_text, options);
-
-  The options are:
-    indent_size (default 4)          - indentation size,
-    indent_char (default space)      - character to indent with,
-    preserve_newlines (default true) - whether existing line breaks should be preserved,
-    max_preserve_newlines (default unlimited) - maximum number of line breaks to be preserved in one chunk,
-
-    jslint_happy (default false) - if true, then jslint-stricter mode is enforced.
-
-            jslint_happy   !jslint_happy
-            ---------------------------------
-             function ()      function()
-
-    brace_style (default "collapse") - "collapse" | "expand" | "end-expand" | "expand-strict"
-            put braces on the same line as control statements (default), or put braces on own line (Allman / ANSI style), or just put end braces on own line.
-
-            expand-strict: put brace on own line even in such cases:
-
-                var a =
-                {
-                    a: 5,
-                    b: 6
-                }
-            This mode may break your scripts - e.g "return { a: 1 }" will be broken into two lines, so beware.
-
-    space_before_conditional (default true) - should the space before conditional statement be added, "if(true)" vs "if (true)",
-
-    unescape_strings (default false) - should printable characters in strings encoded in \xNN notation be unescaped, "example" vs "\x65\x78\x61\x6d\x70\x6c\x65"
-
-    e.g
-
-    js_beautify(js_source_text, {
-      'indent_size': 1,
-      'indent_char': '\t'
-    });
-
-
-*/
-
-this.EXPORTED_SYMBOLS = ["js_beautify"];
-
-this.js_beautify = function js_beautify(js_source_text, options) {
-
-    var input, output, token_text, last_type, last_text, last_last_text, last_word, flags, flag_store, indent_string;
-    var whitespace, wordchar, punct, parser_pos, line_starters, digits;
-    var prefix, token_type, do_block_just_closed;
-    var wanted_newline, just_added_newline, n_newlines;
-    var preindent_string = '';
-
-
-    // Some interpreters have unexpected results with foo = baz || bar;
-    options = options ? options : {};
-
-    var opt_brace_style;
-
-    // compatibility
-    if (options.space_after_anon_function !== undefined && options.jslint_happy === undefined) {
-        options.jslint_happy = options.space_after_anon_function;
-    }
-    if (options.braces_on_own_line !== undefined) { //graceful handling of deprecated option
-        opt_brace_style = options.braces_on_own_line ? "expand" : "collapse";
-    }
-    opt_brace_style = options.brace_style ? options.brace_style : (opt_brace_style ? opt_brace_style : "collapse");
-
-
-    var opt_indent_size = options.indent_size ? options.indent_size : 4;
-    var opt_indent_char = options.indent_char ? options.indent_char : ' ';
-    var opt_preserve_newlines = typeof options.preserve_newlines === 'undefined' ? true : options.preserve_newlines;
-    var opt_max_preserve_newlines = typeof options.max_preserve_newlines === 'undefined' ? false : options.max_preserve_newlines;
-    var opt_jslint_happy = options.jslint_happy === 'undefined' ? false : options.jslint_happy;
-    var opt_keep_array_indentation = typeof options.keep_array_indentation === 'undefined' ? false : options.keep_array_indentation;
-    var opt_space_before_conditional = typeof options.space_before_conditional === 'undefined' ? true : options.space_before_conditional;
-    var opt_indent_case = typeof options.indent_case === 'undefined' ? false : options.indent_case;
-    var opt_unescape_strings = typeof options.unescape_strings === 'undefined' ? false : options.unescape_strings;
-
-    just_added_newline = false;
-
-    // cache the source's length.
-    var input_length = js_source_text.length;
-
-    function trim_output(eat_newlines) {
-        eat_newlines = typeof eat_newlines === 'undefined' ? false : eat_newlines;
-        while (output.length && (output[output.length - 1] === ' '
-            || output[output.length - 1] === indent_string
-            || output[output.length - 1] === preindent_string
-            || (eat_newlines && (output[output.length - 1] === '\n' || output[output.length - 1] === '\r')))) {
-            output.pop();
-        }
-    }
-
-    function trim(s) {
-        return s.replace(/^\s\s*|\s\s*$/, '');
-    }
-
-    // we could use just string.split, but
-    // IE doesn't like returning empty strings
-    function split_newlines(s) {
-        return s.split(/\x0d\x0a|\x0a/);
-    }
-
-    function force_newline() {
-        var old_keep_array_indentation = opt_keep_array_indentation;
-        opt_keep_array_indentation = false;
-        print_newline();
-        opt_keep_array_indentation = old_keep_array_indentation;
-    }
-
-    function print_newline(ignore_repeated) {
-
-        flags.eat_next_space = false;
-        if (opt_keep_array_indentation && is_array(flags.mode)) {
-            return;
-        }
-
-        ignore_repeated = typeof ignore_repeated === 'undefined' ? true : ignore_repeated;
-
-        flags.if_line = false;
-        trim_output();
-
-        if (!output.length) {
-            return; // no newline on start of file
-        }
-
-        if (output[output.length - 1] !== "\n" || !ignore_repeated) {
-            just_added_newline = true;
-            output.push("\n");
-        }
-        if (preindent_string) {
-            output.push(preindent_string);
-        }
-        for (var i = 0; i < flags.indentation_level; i += 1) {
-            output.push(indent_string);
-        }
-        if (flags.var_line && flags.var_line_reindented) {
-            output.push(indent_string); // skip space-stuffing, if indenting with a tab
-        }
-        if (flags.case_body) {
-            output.push(indent_string);
-        }
-    }
-
-
-
-    function print_single_space() {
-
-        if (last_type === 'TK_COMMENT') {
-            return print_newline();
-        }
-        if (flags.eat_next_space) {
-            flags.eat_next_space = false;
-            return;
-        }
-        var last_output = ' ';
-        if (output.length) {
-            last_output = output[output.length - 1];
-        }
-        if (last_output !== ' ' && last_output !== '\n' && last_output !== indent_string) { // prevent occassional duplicate space
-            output.push(' ');
-        }
-    }
-
-
-    function print_token() {
-        just_added_newline = false;
-        flags.eat_next_space = false;
-        output.push(token_text);
-    }
-
-    function indent() {
-        flags.indentation_level += 1;
-    }
-
-
-    function remove_indent() {
-        if (output.length && output[output.length - 1] === indent_string) {
-            output.pop();
-        }
-    }
-
-    function set_mode(mode) {
-        if (flags) {
-            flag_store.push(flags);
-        }
-        flags = {
-            previous_mode: flags ? flags.mode : 'BLOCK',
-            mode: mode,
-            var_line: false,
-            var_line_tainted: false,
-            var_line_reindented: false,
-            in_html_comment: false,
-            if_line: false,
-            in_case_statement: false, // switch(..){ INSIDE HERE }
-            in_case: false, // we're on the exact line with "case 0:"
-            case_body: false, // the indented case-action block
-            eat_next_space: false,
-            indentation_baseline: -1,
-            indentation_level: (flags ? flags.indentation_level + (flags.case_body ? 1 : 0) + ((flags.var_line && flags.var_line_reindented) ? 1 : 0) : 0),
-            ternary_depth: 0
-        };
-    }
-
-    function is_array(mode) {
-        return mode === '[EXPRESSION]' || mode === '[INDENTED-EXPRESSION]';
-    }
-
-    function is_expression(mode) {
-        return in_array(mode, ['[EXPRESSION]', '(EXPRESSION)', '(FOR-EXPRESSION)', '(COND-EXPRESSION)']);
-    }
-
-    function restore_mode() {
-        do_block_just_closed = flags.mode === 'DO_BLOCK';
-        if (flag_store.length > 0) {
-            var mode = flags.mode;
-            flags = flag_store.pop();
-            flags.previous_mode = mode;
-        }
-    }
-
-    function all_lines_start_with(lines, c) {
-        for (var i = 0; i < lines.length; i++) {
-            var line = trim(lines[i]);
-            if (line.charAt(0) !== c) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    function is_special_word(word) {
-        return in_array(word, ['case', 'return', 'do', 'if', 'throw', 'else']);
-    }
-
-    function in_array(what, arr) {
-        for (var i = 0; i < arr.length; i += 1) {
-            if (arr[i] === what) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    function look_up(exclude) {
-        var local_pos = parser_pos;
-        var c = input.charAt(local_pos);
-        while (in_array(c, whitespace) && c !== exclude) {
-            local_pos++;
-            if (local_pos >= input_length) {
-                return 0;
-            }
-            c = input.charAt(local_pos);
-        }
-        return c;
-    }
-
-    function get_next_token() {
-        var i;
-        var resulting_string;
-
-        n_newlines = 0;
-
-        if (parser_pos >= input_length) {
-            return ['', 'TK_EOF'];
-        }
-
-        wanted_newline = false;
-
-        var c = input.charAt(parser_pos);
-        parser_pos += 1;
-
-
-        var keep_whitespace = opt_keep_array_indentation && is_array(flags.mode);
-
-        if (keep_whitespace) {
-
-            //
-            // slight mess to allow nice preservation of array indentation and reindent that correctly
-            // first time when we get to the arrays:
-            // var a = [
-            // ....'something'
-            // we make note of whitespace_count = 4 into flags.indentation_baseline
-            // so we know that 4 whitespaces in original source match indent_level of reindented source
-            //
-            // and afterwards, when we get to
-            //    'something,
-            // .......'something else'
-            // we know that this should be indented to indent_level + (7 - indentation_baseline) spaces
-            //
-            var whitespace_count = 0;
-
-            while (in_array(c, whitespace)) {
-
-                if (c === "\n") {
-                    trim_output();
-                    output.push("\n");
-                    just_added_newline = true;
-                    whitespace_count = 0;
-                } else {
-                    if (c === '\t') {
-                        whitespace_count += 4;
-                    } else if (c === '\r') {
-                        // nothing
-                    } else {
-                        whitespace_count += 1;
-                    }
-                }
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-                if (parser_pos >= input_length) {
-                    return ['', 'TK_EOF'];
-                }
-
-                c = input.charAt(parser_pos);
-                parser_pos += 1;
-
-            }
-            if (flags.indentation_baseline === -1) {
-                flags.indentation_baseline = whitespace_count;
-            }
-
-            if (just_added_newline) {
-                for (i = 0; i < flags.indentation_level + 1; i += 1) {
-                    output.push(indent_string);
-                }
-                if (flags.indentation_baseline !== -1) {
-                    for (i = 0; i < whitespace_count - flags.indentation_baseline; i++) {
-                        output.push(' ');
-                    }
-                }
-            }
-
-        } else {
-            while (in_array(c, whitespace)) {
-
-                if (c === "\n") {
-                    n_newlines += ((opt_max_preserve_newlines) ? (n_newlines <= opt_max_preserve_newlines) ? 1 : 0 : 1);
-                }
-
-
-                if (parser_pos >= input_length) {
-                    return ['', 'TK_EOF'];
-                }
-
-                c = input.charAt(parser_pos);
-                parser_pos += 1;
-
-            }
-
-            if (opt_preserve_newlines) {
-                if (n_newlines > 1) {
-                    for (i = 0; i < n_newlines; i += 1) {
-                        print_newline(i === 0);
-                        just_added_newline = true;
-                    }
-                }
-            }
-            wanted_newline = n_newlines > 0;
-        }
-
-
-        if (in_array(c, wordchar)) {
-            if (parser_pos < input_length) {
-                while (in_array(input.charAt(parser_pos), wordchar)) {
-                    c += input.charAt(parser_pos);
-                    parser_pos += 1;
-                    if (parser_pos === input_length) {
-                        break;
-                    }
-                }
-            }
-
-            // small and surprisingly unugly hack for 1E-10 representation
-            if (parser_pos !== input_length && c.match(/^[0-9]+[Ee]$/) && (input.charAt(parser_pos) === '-' || input.charAt(parser_pos) === '+')) {
-
-                var sign = input.charAt(parser_pos);
-                parser_pos += 1;
-
-                var t = get_next_token();
-                c += sign + t[0];
-                return [c, 'TK_WORD'];
-            }
-
-            if (c === 'in') { // hack for 'in' operator
-                return [c, 'TK_OPERATOR'];
-            }
-            if (wanted_newline && last_type !== 'TK_OPERATOR'
-                && last_type !== 'TK_EQUALS'
-                && !flags.if_line && (opt_preserve_newlines || last_text !== 'var')) {
-                print_newline();
-            }
-            return [c, 'TK_WORD'];
-        }
-
-        if (c === '(' || c === '[') {
-            return [c, 'TK_START_EXPR'];
-        }
-
-        if (c === ')' || c === ']') {
-            return [c, 'TK_END_EXPR'];
-        }
-
-        if (c === '{') {
-            return [c, 'TK_START_BLOCK'];
-        }
-
-        if (c === '}') {
-            return [c, 'TK_END_BLOCK'];
-        }
-
-        if (c === ';') {
-            return [c, 'TK_SEMICOLON'];
-        }
-
-        if (c === '/') {
-            var comment = '';
-            // peek for comment /* ... */
-            var inline_comment = true;
-            if (input.charAt(parser_pos) === '*') {
-                parser_pos += 1;
-                if (parser_pos < input_length) {
-                    while (parser_pos < input_length &&
-                        ! (input.charAt(parser_pos) === '*' && input.charAt(parser_pos + 1) && input.charAt(parser_pos + 1) === '/')) {
-                        c = input.charAt(parser_pos);
-                        comment += c;
-                        if (c === "\n" || c === "\r") {
-                            inline_comment = false;
-                        }
-                        parser_pos += 1;
-                        if (parser_pos >= input_length) {
-                            break;
-                        }
-                    }
-                }
-                parser_pos += 2;
-                if (inline_comment && n_newlines === 0) {
-                    return ['/*' + comment + '*/', 'TK_INLINE_COMMENT'];
-                } else {
-                    return ['/*' + comment + '*/', 'TK_BLOCK_COMMENT'];
-                }
-            }
-            // peek for comment // ...
-            if (input.charAt(parser_pos) === '/') {
-                comment = c;
-                while (input.charAt(parser_pos) !== '\r' && input.charAt(parser_pos) !== '\n') {
-                    comment += input.charAt(parser_pos);
-                    parser_pos += 1;
-                    if (parser_pos >= input_length) {
-                        break;
-                    }
-                }
-                if (wanted_newline) {
-                    print_newline();
-                }
-                return [comment, 'TK_COMMENT'];
-            }
-
-        }
-
-        if (c === "'" || // string
-        c === '"' || // string
-        (c === '/' &&
-            ((last_type === 'TK_WORD' && is_special_word(last_text)) ||
-                (last_text === ')' && in_array(flags.previous_mode, ['(COND-EXPRESSION)', '(FOR-EXPRESSION)'])) ||
-                (last_type === 'TK_COMMA' || last_type === 'TK_COMMENT' || last_type === 'TK_START_EXPR' || last_type === 'TK_START_BLOCK' || last_type === 'TK_END_BLOCK' || last_type === 'TK_OPERATOR' || last_type === 'TK_EQUALS' || last_type === 'TK_EOF' || last_type === 'TK_SEMICOLON')))) { // regexp
-            var sep = c;
-            var esc = false;
-            var esc1 = 0;
-            var esc2 = 0;
-            resulting_string = c;
-
-            if (parser_pos < input_length) {
-                if (sep === '/') {
-                    //
-                    // handle regexp separately...
-                    //
-                    var in_char_class = false;
-                    while (esc || in_char_class || input.charAt(parser_pos) !== sep) {
-                        resulting_string += input.charAt(parser_pos);
-                        if (!esc) {
-                            esc = input.charAt(parser_pos) === '\\';
-                            if (input.charAt(parser_pos) === '[') {
-                                in_char_class = true;
-                            } else if (input.charAt(parser_pos) === ']') {
-                                in_char_class = false;
-                            }
-                        } else {
-                            esc = false;
-                        }
-                        parser_pos += 1;
-                        if (parser_pos >= input_length) {
-                            // incomplete string/rexp when end-of-file reached.
-                            // bail out with what had been received so far.
-                            return [resulting_string, 'TK_STRING'];
-                        }
-                    }
-
-                } else {
-                    //
-                    // and handle string also separately
-                    //
-                    while (esc || input.charAt(parser_pos) !== sep) {
-                        resulting_string += input.charAt(parser_pos);
-                        if (esc1 && esc1 >= esc2) {
-                            esc1 = parseInt(resulting_string.substr(-esc2), 16);
-                            if (esc1 && esc1 >= 0x20 && esc1 <= 0x7e) {
-                                esc1 = String.fromCharCode(esc1);
-                                resulting_string = resulting_string.substr(0, resulting_string.length - esc2 - 2) + (((esc1 === sep) || (esc1 === '\\')) ? '\\' : '') + esc1;
-                            }
-                            esc1 = 0;
-                        }
-                        if (esc1) {
-                            esc1++;
-                        } else if (!esc) {
-                            esc = input.charAt(parser_pos) === '\\';
-                        } else {
-                            esc = false;
-                            if (opt_unescape_strings) {
-                                if (input.charAt(parser_pos) === 'x') {
-                                    esc1++;
-                                    esc2 = 2;
-                                } else if (input.charAt(parser_pos) === 'u') {
-                                    esc1++;
-                                    esc2 = 4;
-                                }
-                            }
-                        }
-                        parser_pos += 1;
-                        if (parser_pos >= input_length) {
-                            // incomplete string/rexp when end-of-file reached.
-                            // bail out with what had been received so far.
-                            return [resulting_string, 'TK_STRING'];
-                        }
-                    }
-                }
-
-
-
-            }
-
-            parser_pos += 1;
-
-            resulting_string += sep;
-
-            if (sep === '/') {
-                // regexps may have modifiers /regexp/MOD , so fetch those, too
-                while (parser_pos < input_length && in_array(input.charAt(parser_pos), wordchar)) {
-                    resulting_string += input.charAt(parser_pos);
-                    parser_pos += 1;
-                }
-            }
-            return [resulting_string, 'TK_STRING'];
-        }
-
-        if (c === '#') {
-
-
-            if (output.length === 0 && input.charAt(parser_pos) === '!') {
-                // shebang
-                resulting_string = c;
-                while (parser_pos < input_length && c !== '\n') {
-                    c = input.charAt(parser_pos);
-                    resulting_string += c;
-                    parser_pos += 1;
-                }
-                output.push(trim(resulting_string) + '\n');
-                print_newline();
-                return get_next_token();
-            }
-
-
-
-            // Spidermonkey-specific sharp variables for circular references
-            // https://developer.mozilla.org/En/Sharp_variables_in_JavaScript
-            // http://mxr.mozilla.org/mozilla-central/source/js/src/jsscan.cpp around line 1935
-            var sharp = '#';
-            if (parser_pos < input_length && in_array(input.charAt(parser_pos), digits)) {
-                do {
-                    c = input.charAt(parser_pos);
-                    sharp += c;
-                    parser_pos += 1;
-                } while (parser_pos < input_length && c !== '#' && c !== '=');
-                if (c === '#') {
-                    //
-                } else if (input.charAt(parser_pos) === '[' && input.charAt(parser_pos + 1) === ']') {
-                    sharp += '[]';
-                    parser_pos += 2;
-                } else if (input.charAt(parser_pos) === '{' && input.charAt(parser_pos + 1) === '}') {
-                    sharp += '{}';
-                    parser_pos += 2;
-                }
-                return [sharp, 'TK_WORD'];
-            }
-        }
-
-        if (c === '<' && input.substring(parser_pos - 1, parser_pos + 3) === '<!--') {
-            parser_pos += 3;
-            c = '<!--';
-            while (input.charAt(parser_pos) !== '\n' && parser_pos < input_length) {
-                c += input.charAt(parser_pos);
-                parser_pos++;
-            }
-            flags.in_html_comment = true;
-            return [c, 'TK_COMMENT'];
-        }
-
-        if (c === '-' && flags.in_html_comment && input.substring(parser_pos - 1, parser_pos + 2) === '-->') {
-            flags.in_html_comment = false;
-            parser_pos += 2;
-            if (wanted_newline) {
-                print_newline();
-            }
-            return ['-->', 'TK_COMMENT'];
-        }
-
-        if (in_array(c, punct)) {
-            while (parser_pos < input_length && in_array(c + input.charAt(parser_pos), punct)) {
-                c += input.charAt(parser_pos);
-                parser_pos += 1;
-                if (parser_pos >= input_length) {
-                    break;
-                }
-            }
-
-            if (c === ',') {
-                return [c, 'TK_COMMA'];
-            } else if (c === '=') {
-                return [c, 'TK_EQUALS'];
-            } else {
-                return [c, 'TK_OPERATOR'];
-            }
-        }
-
-        return [c, 'TK_UNKNOWN'];
-    }
+"use strict";
 
-    //----------------------------------
-    indent_string = '';
-    while (opt_indent_size > 0) {
-        indent_string += opt_indent_char;
-        opt_indent_size -= 1;
-    }
-
-    while (js_source_text && (js_source_text.charAt(0) === ' ' || js_source_text.charAt(0) === '\t')) {
-        preindent_string += js_source_text.charAt(0);
-        js_source_text = js_source_text.substring(1);
-    }
-    input = js_source_text;
-
-    last_word = ''; // last 'TK_WORD' passed
-    last_type = 'TK_START_EXPR'; // last token type
-    last_text = ''; // last token text
-    last_last_text = ''; // pre-last token text
-    output = [];
-
-    do_block_just_closed = false;
-
-    whitespace = "\n\r\t ".split('');
-    wordchar = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$'.split('');
-    digits = '0123456789'.split('');
-
-    punct = '+ - * / % & ++ -- = += -= *= /= %= == === != !== > < >= <= >> << >>> >>>= >>= <<= && &= | || ! !! , : ? ^ ^= |= ::';
-    punct += ' <%= <% %> <?= <? ?>'; // try to be a good boy and try not to break the markup language identifiers
-    punct = punct.split(' ');
-
-    // words which should always start on new line.
-    line_starters = 'continue,try,throw,return,var,if,switch,case,default,for,while,break,function'.split(',');
-
-    // states showing if we are currently in expression (i.e. "if" case) - 'EXPRESSION', or in usual block (like, procedure), 'BLOCK'.
-    // some formatting depends on that.
-    flag_store = [];
-    set_mode('BLOCK');
-
-    parser_pos = 0;
-    while (true) {
-        var t = get_next_token();
-        token_text = t[0];
-        token_type = t[1];
-        if (token_type === 'TK_EOF') {
-            break;
-        }
-
-        switch (token_type) {
-
-        case 'TK_START_EXPR':
-
-            if (token_text === '[') {
-
-                if (last_type === 'TK_WORD' || last_text === ')') {
-                    // this is array index specifier, break immediately
-                    // a[x], fn()[x]
-                    if (in_array(last_text, line_starters)) {
-                        print_single_space();
-                    }
-                    set_mode('(EXPRESSION)');
-                    print_token();
-                    break;
-                }
-
-                if (flags.mode === '[EXPRESSION]' || flags.mode === '[INDENTED-EXPRESSION]') {
-                    if (last_last_text === ']' && last_text === ',') {
-                        // ], [ goes to new line
-                        if (flags.mode === '[EXPRESSION]') {
-                            flags.mode = '[INDENTED-EXPRESSION]';
-                            if (!opt_keep_array_indentation) {
-                                indent();
-                            }
-                        }
-                        set_mode('[EXPRESSION]');
-                        if (!opt_keep_array_indentation) {
-                            print_newline();
-                        }
-                    } else if (last_text === '[') {
-                        if (flags.mode === '[EXPRESSION]') {
-                            flags.mode = '[INDENTED-EXPRESSION]';
-                            if (!opt_keep_array_indentation) {
-                                indent();
-                            }
-                        }
-                        set_mode('[EXPRESSION]');
-
-                        if (!opt_keep_array_indentation) {
-                            print_newline();
-                        }
-                    } else {
-                        set_mode('[EXPRESSION]');
-                    }
-                } else {
-                    set_mode('[EXPRESSION]');
-                }
-
-
-
-            } else {
-                if (last_word === 'for') {
-                    set_mode('(FOR-EXPRESSION)');
-                } else if (in_array(last_word, ['if', 'while'])) {
-                    set_mode('(COND-EXPRESSION)');
-                } else {
-                    set_mode('(EXPRESSION)');
-                }
-            }
-
-            if (last_text === ';' || last_type === 'TK_START_BLOCK') {
-                print_newline();
-            } else if (last_type === 'TK_END_EXPR' || last_type === 'TK_START_EXPR' || last_type === 'TK_END_BLOCK' || last_text === '.') {
-                if (wanted_newline) {
-                    print_newline();
-                }
-                // do nothing on (( and )( and ][ and ]( and .(
-            } else if (last_type !== 'TK_WORD' && last_type !== 'TK_OPERATOR') {
-                print_single_space();
-            } else if (last_word === 'function' || last_word === 'typeof') {
-                // function() vs function ()
-                if (opt_jslint_happy) {
-                    print_single_space();
-                }
-            } else if (in_array(last_text, line_starters) || last_text === 'catch') {
-                if (opt_space_before_conditional) {
-                    print_single_space();
-                }
-            }
-            print_token();
-
-            break;
-
-        case 'TK_END_EXPR':
-            if (token_text === ']') {
-                if (opt_keep_array_indentation) {
-                    if (last_text === '}') {
-                        // trim_output();
-                        // print_newline(true);
-                        remove_indent();
-                        print_token();
-                        restore_mode();
-                        break;
-                    }
-                } else {
-                    if (flags.mode === '[INDENTED-EXPRESSION]') {
-                        if (last_text === ']') {
-                            restore_mode();
-                            print_newline();
-                            print_token();
-                            break;
-                        }
-                    }
-                }
-            }
-            restore_mode();
-            print_token();
-            break;
-
-        case 'TK_START_BLOCK':
-
-            if (last_word === 'do') {
-                set_mode('DO_BLOCK');
-            } else {
-                set_mode('BLOCK');
-            }
-            if (opt_brace_style === "expand" || opt_brace_style === "expand-strict") {
-                var empty_braces = false;
-                if (opt_brace_style === "expand-strict") {
-                    empty_braces = (look_up() === '}');
-                    if (!empty_braces) {
-                        print_newline(true);
-                    }
-                } else {
-                    if (last_type !== 'TK_OPERATOR') {
-                        if (last_text === '=' || (is_special_word(last_text) && last_text !== 'else')) {
-                            print_single_space();
-                        } else {
-                            print_newline(true);
-                        }
-                    }
-                }
-                print_token();
-                if (!empty_braces) {
-                    indent();
-                }
-            } else {
-                if (last_type !== 'TK_OPERATOR' && last_type !== 'TK_START_EXPR') {
-                    if (last_type === 'TK_START_BLOCK') {
-                        print_newline();
-                    } else {
-                        print_single_space();
-                    }
-                } else {
-                    // if TK_OPERATOR or TK_START_EXPR
-                    if (is_array(flags.previous_mode) && last_text === ',') {
-                        if (last_last_text === '}') {
-                            // }, { in array context
-                            print_single_space();
-                        } else {
-                            print_newline(); // [a, b, c, {
-                        }
-                    }
-                }
-                indent();
-                print_token();
-            }
-
-            break;
-
-        case 'TK_END_BLOCK':
-            restore_mode();
-            if (opt_brace_style === "expand" || opt_brace_style === "expand-strict") {
-                if (last_text !== '{') {
-                    print_newline();
-                }
-                print_token();
-            } else {
-                if (last_type === 'TK_START_BLOCK') {
-                    // nothing
-                    if (just_added_newline) {
-                        remove_indent();
-                    } else {
-                        // {}
-                        trim_output();
-                    }
-                } else {
-                    if (is_array(flags.mode) && opt_keep_array_indentation) {
-                        // we REALLY need a newline here, but newliner would skip that
-                        opt_keep_array_indentation = false;
-                        print_newline();
-                        opt_keep_array_indentation = true;
-
-                    } else {
-                        print_newline();
-                    }
-                }
-                print_token();
-            }
-            break;
-
-        case 'TK_WORD':
-
-            // no, it's not you. even I have problems understanding how this works
-            // and what does what.
-            if (do_block_just_closed) {
-                // do {} ## while ()
-                print_single_space();
-                print_token();
-                print_single_space();
-                do_block_just_closed = false;
-                break;
-            }
-
-            prefix = 'NONE';
-
-            if (token_text === 'function') {
-                if (flags.var_line && last_type !== 'TK_EQUALS' ) {
-                    flags.var_line_reindented = true;
-                }
-                if ((just_added_newline || last_text === ';') && last_text !== '{'
-                && last_type !== 'TK_BLOCK_COMMENT' && last_type !== 'TK_COMMENT') {
-                    // make sure there is a nice clean space of at least one blank line
-                    // before a new function definition
-                    n_newlines = just_added_newline ? n_newlines : 0;
-                    if (!opt_preserve_newlines) {
-                        n_newlines = 1;
-                    }
-
-                    for (var i = 0; i < 2 - n_newlines; i++) {
-                        print_newline(false);
-                    }
-                }
-                if (last_type === 'TK_WORD') {
-                    if (last_text === 'get' || last_text === 'set' || last_text === 'new' || last_text === 'return') {
-                        print_single_space();
-                    } else {
-                        print_newline();
-                    }
-                } else if (last_type === 'TK_OPERATOR' || last_text === '=') {
-                    // foo = function
-                    print_single_space();
-                } else if (is_expression(flags.mode)) {
-                        //ää print nothing
-                } else {
-                    print_newline();
-                }
-
-                print_token();
-                last_word = token_text;
-                break;
-            }
-
-            if (token_text === 'case' || (token_text === 'default' && flags.in_case_statement)) {
-                if (last_text === ':' || flags.case_body) {
-                    // switch cases following one another
-                    remove_indent();
-                } else {
-                    // case statement starts in the same line where switch
-                    if (!opt_indent_case) {
-                        flags.indentation_level--;
-                    }
-                    print_newline();
-                    if (!opt_indent_case) {
-                        flags.indentation_level++;
-                    }
-                }
-                print_token();
-                flags.in_case = true;
-                flags.in_case_statement = true;
-                flags.case_body = false;
-                break;
-            }
-
-            if (last_type === 'TK_END_BLOCK') {
+/*
+ * JS Beautifier. Please use devtools.require("devtools/jsbeautify") instead of
+ * this JSM.
+ */
 
-                if (!in_array(token_text.toLowerCase(), ['else', 'catch', 'finally'])) {
-                    prefix = 'NEWLINE';
-                } else {
-                    if (opt_brace_style === "expand" || opt_brace_style === "end-expand" || opt_brace_style === "expand-strict") {
-                        prefix = 'NEWLINE';
-                    } else {
-                        prefix = 'SPACE';
-                        print_single_space();
-                    }
-                }
-            } else if (last_type === 'TK_SEMICOLON' && (flags.mode === 'BLOCK' || flags.mode === 'DO_BLOCK')) {
-                prefix = 'NEWLINE';
-            } else if (last_type === 'TK_SEMICOLON' && is_expression(flags.mode)) {
-                prefix = 'SPACE';
-            } else if (last_type === 'TK_STRING') {
-                prefix = 'NEWLINE';
-            } else if (last_type === 'TK_WORD') {
-                if (last_text === 'else') {
-                    // eat newlines between ...else *** some_op...
-                    // won't preserve extra newlines in this place (if any), but don't care that much
-                    trim_output(true);
-                }
-                prefix = 'SPACE';
-            } else if (last_type === 'TK_START_BLOCK') {
-                prefix = 'NEWLINE';
-            } else if (last_type === 'TK_END_EXPR') {
-                print_single_space();
-                prefix = 'NEWLINE';
-            }
-
-            if (in_array(token_text, line_starters) && last_text !== ')') {
-                if (last_text === 'else') {
-                    prefix = 'SPACE';
-                } else {
-                    prefix = 'NEWLINE';
-                }
-
-            }
-
-            if (flags.if_line && last_type === 'TK_END_EXPR') {
-                flags.if_line = false;
-            }
-            if (in_array(token_text.toLowerCase(), ['else', 'catch', 'finally'])) {
-                if (last_type !== 'TK_END_BLOCK' || opt_brace_style === "expand" || opt_brace_style === "end-expand" || opt_brace_style === "expand-strict") {
-                    print_newline();
-                } else {
-                    trim_output(true);
-                    print_single_space();
-                }
-            } else if (prefix === 'NEWLINE') {
-                if (is_special_word(last_text)) {
-                    // no newline between 'return nnn'
-                    print_single_space();
-                } else if (last_type !== 'TK_END_EXPR') {
-                    if ((last_type !== 'TK_START_EXPR' || token_text !== 'var') && last_text !== ':') {
-                        // no need to force newline on 'var': for (var x = 0...)
-                        if (token_text === 'if' && last_word === 'else' && last_text !== '{') {
-                            // no newline for } else if {
-                            print_single_space();
-                        } else {
-                            flags.var_line = false;
-                            flags.var_line_reindented = false;
-                            print_newline();
-                        }
-                    }
-                } else if (in_array(token_text, line_starters) && last_text !== ')') {
-                    flags.var_line = false;
-                    flags.var_line_reindented = false;
-                    print_newline();
-                }
-            } else if (is_array(flags.mode) && last_text === ',' && last_last_text === '}') {
-                print_newline(); // }, in lists get a newline treatment
-            } else if (prefix === 'SPACE') {
-                print_single_space();
-            }
-            print_token();
-            last_word = token_text;
-
-            if (token_text === 'var') {
-                flags.var_line = true;
-                flags.var_line_reindented = false;
-                flags.var_line_tainted = false;
-            }
-
-            if (token_text === 'if') {
-                flags.if_line = true;
-            }
-            if (token_text === 'else') {
-                flags.if_line = false;
-            }
-
-            break;
-
-        case 'TK_SEMICOLON':
-
-            print_token();
-            flags.var_line = false;
-            flags.var_line_reindented = false;
-            if (flags.mode === 'OBJECT') {
-                // OBJECT mode is weird and doesn't get reset too well.
-                flags.mode = 'BLOCK';
-            }
-            break;
-
-        case 'TK_STRING':
-
-            if (last_type === 'TK_END_EXPR' && in_array(flags.previous_mode, ['(COND-EXPRESSION)', '(FOR-EXPRESSION)'])) {
-                print_single_space();
-            } else if (last_type === 'TK_COMMENT' || last_type === 'TK_STRING' || last_type === 'TK_START_BLOCK' || last_type === 'TK_END_BLOCK' || last_type === 'TK_SEMICOLON') {
-                print_newline();
-            } else if (last_type === 'TK_WORD') {
-                print_single_space();
-            }
-            print_token();
-            break;
-
-        case 'TK_EQUALS':
-            if (flags.var_line) {
-                // just got an '=' in a var-line, different formatting/line-breaking, etc will now be done
-                flags.var_line_tainted = true;
-            }
-            print_single_space();
-            print_token();
-            print_single_space();
-            break;
-
-        case 'TK_COMMA':
-            if (flags.var_line) {
-                if (is_expression(flags.mode) || last_type === 'TK_END_BLOCK' ) {
-                    // do not break on comma, for(var a = 1, b = 2)
-                    flags.var_line_tainted = false;
-                }
-                if (flags.var_line_tainted) {
-                    print_token();
-                    flags.var_line_reindented = true;
-                    flags.var_line_tainted = false;
-                    print_newline();
-                    break;
-                } else {
-                    flags.var_line_tainted = false;
-                }
-
-                print_token();
-                print_single_space();
-                break;
-            }
-
-            if (last_type === 'TK_COMMENT') {
-                print_newline();
-            }
+this.EXPORTED_SYMBOLS = [ "jsBeautify" ];
 
-            if (last_type === 'TK_END_BLOCK' && flags.mode !== "(EXPRESSION)") {
-                print_token();
-                if (flags.mode === 'OBJECT' && last_text === '}') {
-                    print_newline();
-                } else {
-                    print_single_space();
-                }
-            } else {
-                if (flags.mode === 'OBJECT') {
-                    print_token();
-                    print_newline();
-                } else {
-                    // EXPR or DO_BLOCK
-                    print_token();
-                    print_single_space();
-                }
-            }
-            break;
-
-
-        case 'TK_OPERATOR':
-
-            var space_before = true;
-            var space_after = true;
-
-            if (is_special_word(last_text)) {
-                // "return" had a special handling in TK_WORD. Now we need to return the favor
-                print_single_space();
-                print_token();
-                break;
-            }
-
-            // hack for actionscript's import .*;
-            if (token_text === '*' && last_type === 'TK_UNKNOWN' && !last_last_text.match(/^\d+$/)) {
-                print_token();
-                break;
-            }
-
-            if (token_text === ':' && flags.in_case) {
-                if (opt_indent_case) {
-                    flags.case_body = true;
-                }
-                print_token(); // colon really asks for separate treatment
-                print_newline();
-                flags.in_case = false;
-                break;
-            }
-
-            if (token_text === '::') {
-                // no spaces around exotic namespacing syntax operator
-                print_token();
-                break;
-            }
-
-            if (in_array(token_text, ['--', '++', '!']) || (in_array(token_text, ['-', '+']) && (in_array(last_type, ['TK_START_BLOCK', 'TK_START_EXPR', 'TK_EQUALS', 'TK_OPERATOR']) || in_array(last_text, line_starters)))) {
-                // unary operators (and binary +/- pretending to be unary) special cases
-
-                space_before = false;
-                space_after = false;
-
-                if (last_text === ';' && is_expression(flags.mode)) {
-                    // for (;; ++i)
-                    //        ^^^
-                    space_before = true;
-                }
-                if (last_type === 'TK_WORD' && in_array(last_text, line_starters)) {
-                    space_before = true;
-                }
-
-                if (flags.mode === 'BLOCK' && (last_text === '{' || last_text === ';')) {
-                    // { foo; --i }
-                    // foo(); --bar;
-                    print_newline();
-                }
-            } else if (token_text === '.') {
-                // decimal digits or object.property
-                space_before = false;
-
-            } else if (token_text === ':') {
-                if (flags.ternary_depth === 0) {
-                    if (flags.mode === 'BLOCK') {
-                        flags.mode = 'OBJECT';
-                    }
-                    space_before = false;
-                } else {
-                    flags.ternary_depth -= 1;
-                }
-            } else if (token_text === '?') {
-                flags.ternary_depth += 1;
-            }
-            if (space_before) {
-                print_single_space();
-            }
-
-            print_token();
-
-            if (space_after) {
-                print_single_space();
-            }
-
-            break;
-
-        case 'TK_BLOCK_COMMENT':
-
-            var lines = split_newlines(token_text);
-            var j; // iterator for this case
-
-            if (all_lines_start_with(lines.slice(1), '*')) {
-                // javadoc: reformat and reindent
-                print_newline();
-                output.push(lines[0]);
-                for (j = 1; j < lines.length; j++) {
-                    print_newline();
-                    output.push(' ');
-                    output.push(trim(lines[j]));
-                }
-
-            } else {
-
-                // simple block comment: leave intact
-                if (lines.length > 1) {
-                    // multiline comment block starts with a new line
-                    print_newline();
-                } else {
-                    // single-line /* comment */ stays where it is
-                    if (last_type === 'TK_END_BLOCK') {
-                        print_newline();
-                    } else {
-                        print_single_space();
-                    }
-
-                }
-
-                for (j = 0; j < lines.length; j++) {
-                    output.push(lines[j]);
-                    output.push("\n");
-                }
-
-            }
-            if (look_up('\n') !== '\n') {
-                print_newline();
-            }
-            break;
-
-        case 'TK_INLINE_COMMENT':
-            print_single_space();
-            print_token();
-            if (is_expression(flags.mode)) {
-                print_single_space();
-            } else {
-                force_newline();
-            }
-            break;
-
-        case 'TK_COMMENT':
-
-            if (last_text === ',' && !wanted_newline) {
-                trim_output(true);
-            }
-            if (last_type !== 'TK_COMMENT') {
-                if (wanted_newline) {
-                    print_newline();
-                } else {
-                    print_single_space();
-                }
-            }
-            print_token();
-            print_newline();
-            break;
-
-        case 'TK_UNKNOWN':
-            if (is_special_word(last_text)) {
-                print_single_space();
-            }
-            print_token();
-            break;
-        }
-
-        last_last_text = last_text;
-        last_type = token_type;
-        last_text = token_text;
-    }
-
-    var sweet_code = preindent_string + output.join('').replace(/[\r\n ]+$/, '');
-    return sweet_code;
-
-}
+const { devtools } = Components.utils.import("resource://gre/modules/devtools/Loader.jsm", {});
+const { beautify } = devtools.require("devtools/jsbeautify");
+const jsBeautify = beautify.js;
--- a/browser/devtools/styleinspector/test/head.js
+++ b/browser/devtools/styleinspector/test/head.js
@@ -29,17 +29,17 @@ registerCleanupFunction(() => {
   } catch (ex) {
     dump(ex);
   }
   while (gBrowser.tabs.length > 1) {
     gBrowser.removeCurrentTab();
   }
 });
 
-// Uncomment to log events
+// Uncomment this pref to dump all devtools emitted events to the console.
 // Services.prefs.setBoolPref("devtools.dump.emit", true);
 
 // Set the testing flag on gDevTools and reset it when the test ends
 gDevTools.testing = true;
 registerCleanupFunction(() => gDevTools.testing = false);
 
 // Clean-up all prefs that might have been changed during a test run
 // (safer here because if the test fails, then the pref is never reverted)
--- a/browser/locales/en-US/chrome/browser/devtools/toolbox.dtd
+++ b/browser/locales/en-US/chrome/browser/devtools/toolbox.dtd
@@ -78,21 +78,21 @@
 <!ENTITY options.enableRemote.tooltip   "Turning this option on will allow the developer tools to debug remote Firefox instance like Firefox OS">
 
 <!-- LOCALIZATION NOTE (options.disableJavaScript.label,
   -  options.disableJavaScript.tooltip): This is the options panel label and
   -  tooltip for the checkbox that toggles JavaScript on or off. -->
 <!ENTITY options.disableJavaScript.label     "Disable JavaScript *">
 <!ENTITY options.disableJavaScript.tooltip   "Turning this option on will disable JavaScript for the current tab. If the tab or the toolbox is closed then this setting will be forgotten.">
 
-<!-- LOCALIZATION NOTE (options.disableCache.label,
-  -  options.disableCache.tooltip): This is the options panel label and
+<!-- LOCALIZATION NOTE (options.disableCache.label2,
+  -  options.disableCache.tooltip2): This is the options panel label and
   -  tooltip for the checkbox that toggles the cache on or off. -->
-<!ENTITY options.disableCache.label     "Disable Cache *">
-<!ENTITY options.disableCache.tooltip   "Turning this option on will disable the cache for the current tab. If the tab or the toolbox is closed then this setting will be forgotten.">
+<!ENTITY options.disableCache.label2     "Disable Cache (when toolbox is open)">
+<!ENTITY options.disableCache.tooltip2   "Turning this option on will disable the cache for all tabs that have the toolbox open.">
 
 <!-- LOCALIZATION NOTE (options.selectDefaultTools.label): This is the label for
   -  the heading of group of checkboxes corresponding to the default developer
   -  tools. -->
 <!ENTITY options.selectDefaultTools.label     "Default Firefox Developer Tools">
 
 <!-- LOCALIZATION NOTE (options.selectAdditionalTools.label): This is the label for
   -  the heading of group of checkboxes corresponding to the developer tools
--- a/browser/modules/BrowserUITelemetry.jsm
+++ b/browser/modules/BrowserUITelemetry.jsm
@@ -546,16 +546,23 @@ this.BrowserUITelemetry = {
     result.durations = this._durations;
     return result;
   },
 
   countCustomizationEvent: function(aEventType) {
     this._countEvent(["customize", aEventType]);
   },
 
+  countSearchEvent: function(source, query) {
+    this._countEvent(["search", source]);
+    if ((/^[a-zA-Z]+:[^\/\\]/).test(query)) {
+      this._countEvent(["search", "urlbar-keyword"]);
+    }
+  },
+
   _durations: {
     customization: [],
   },
 
   onCustomizeStart: function(aWindow) {
     this._countEvent(["customize", "start"]);
     let durationMap = WINDOW_DURATION_MAP.get(aWindow);
     if (!durationMap) {
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -2294,24 +2294,17 @@ chatbox {
 
 #UITourTooltipButtons {
   margin-bottom: -10px;
 }
 
 %include ../shared/contextmenu.inc.css
 
 #context-navigation {
-  background-color: -moz-dialog;
-  padding-bottom: 2px;
-  margin-bottom: 2px;
-  -moz-appearance: menuitem;
+  padding-top: 4px;
 }
 
 #context-navigation > .menuitem-iconic > .menu-iconic-left {
   visibility: visible;
   /* override toolkit/themes/linux/global/menu.css */
   -moz-padding-end: 0 !important;
   -moz-margin-end: 0 !important;
 }
-
-#context-sep-navigation {
-  margin-top: -4px;
-}
new file mode 100644
--- /dev/null
+++ b/browser/themes/linux/content-contextmenu.svg
@@ -0,0 +1,97 @@
+<?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/. -->
+<svg xmlns="http://www.w3.org/2000/svg"
+     x="0px" y="0px"
+     viewBox="0 0 16 16"
+     enable-background="new 0 0 16 16"
+     xml:space="preserve">
+<style>
+g:not(:target) {
+  display: none;
+}
+
+path {
+  fill: menutext;
+}
+
+g.active > path {
+  fill: -moz-menuhovertext;
+}
+</style>
+<g id="back">
+  <path fill-rule="evenodd" clip-rule="evenodd" d="M1.192,8.893L2.21,9.964c0.064,0.065,0.136,0.117,0.214,0.159
+    l5.199,5.301c0.607,0.63,1.465,0.764,1.915,0.297l1.02-1.082c0.449-0.467,0.32-1.357-0.288-1.99l-2.116-2.158h5.705
+    c0.671,0,1.215-0.544,1.215-1.215v-2.43c0-0.671-0.544-1.215-1.215-1.215H8.094l2.271-2.309c0.609-0.626,0.737-1.512,0.288-1.974
+    L9.635,0.278C9.184-0.188,8.327-0.055,7.718,0.575L2.479,5.901C2.38,5.946,2.289,6.008,2.21,6.089L1.192,7.171
+    c-0.21,0.219-0.293,0.53-0.26,0.864C0.899,8.367,0.981,8.676,1.192,8.893z"/>
+</g>
+<g id="forward">
+  <path fill-rule="evenodd" clip-rule="evenodd" d="M14.808,7.107L13.79,6.036c-0.064-0.065-0.136-0.117-0.214-0.159
+    L8.377,0.576C7.77-0.054,6.912-0.189,6.461,0.278L5.441,1.36c-0.449,0.467-0.32,1.357,0.288,1.99l2.116,2.158H2.14
+    c-0.671,0-1.215,0.544-1.215,1.215v2.43c0,0.671,0.544,1.215,1.215,1.215h5.765l-2.271,2.309c-0.609,0.626-0.737,1.512-0.288,1.974
+    l1.019,1.072c0.451,0.465,1.308,0.332,1.917-0.297l5.238-5.326c0.1-0.045,0.191-0.107,0.269-0.188l1.019-1.082
+    c0.21-0.219,0.293-0.53,0.26-0.864C15.101,7.633,15.019,7.324,14.808,7.107z"/>
+</g>
+<g id="reload">
+  <path fill-rule="evenodd" clip-rule="evenodd" d="M15.429,8h-8l3.207-3.207C9.889,4.265,8.986,3.947,8,3.947
+    c-2.554,0-4.625,2.071-4.625,4.625S5.446,13.196,8,13.196c1.638,0,3.069-0.857,3.891-2.141l2.576,1.104
+    C13.199,14.439,10.794,16,8,16c-4.103,0-7.429-3.326-7.429-7.429S3.897,1.143,8,1.143c1.762,0,3.366,0.624,4.631,1.654L15.429,0V8z"/>
+</g>
+<g id="stop">
+  <polygon fill-rule="evenodd" clip-rule="evenodd" points="16,2.748 13.338,0.079 8.038,5.391 2.661,0 0,2.669
+    5.377,8.059 0.157,13.292 2.819,15.961 8.039,10.728 13.298,16 15.959,13.331 10.701,8.06"/>
+</g>
+<g id="bookmark">
+  <path d="M8.008,3.632l0.986,2.012l0.452,0.922l1.014,0.169l2.326,0.389l-1.719,1.799l-0.676,0.708l0.145,0.967
+    L10.896,13l-1.959-1.039l-0.937-0.497l-0.937,0.497l-1.957,1.038L5.468,10.6l0.146-0.968L4.937,8.924L3.219,7.126l2.351-0.39
+    l1.023-0.17l0.45-0.934L8.008,3.632 M8,0C7.72,0,7.44,0.217,7.228,0.65L5.242,4.766L0.907,5.485c-0.958,0.159-1.195,0.861-0.53,1.56
+    l3.113,3.258l-0.69,4.583c-0.105,0.689,0.172,1.092,0.658,1.092c0.185,0,0.399-0.058,0.635-0.181l3.906-2.072l3.906,2.072
+    c0.236,0.123,0.45,0.181,0.635,0.181c0.486,0,0.762-0.403,0.659-1.092l-0.687-4.583l3.109-3.255c0.666-0.702,0.428-1.404-0.53-1.564
+    l-4.303-0.719L8.772,0.65C8.56,0.217,8.28,0,8,0L8,0z"/>
+</g>
+<g id="bookmark-starred">
+  <path d="M8,0C7.719,0,7.438,0.217,7.225,0.651L5.233,4.773l-4.35,0.72c-0.961,0.159-1.199,0.862-0.531,1.562
+    l3.124,3.262l-0.692,4.589C2.679,15.596,2.957,16,3.444,16c0.185,0,0.401-0.058,0.637-0.181L8,13.744l3.919,2.075
+    C12.156,15.942,12.372,16,12.557,16c0.487,0,0.764-0.404,0.661-1.094l-0.69-4.589l3.12-3.259c0.668-0.703,0.43-1.406-0.532-1.566
+    l-4.317-0.72L8.775,0.651C8.562,0.217,8.281,0,8,0L8,0z"/>
+</g>
+<g id="back-active" class="active">
+  <path fill-rule="evenodd" clip-rule="evenodd" d="M1.192,8.893L2.21,9.964c0.064,0.065,0.136,0.117,0.214,0.159
+    l5.199,5.301c0.607,0.63,1.465,0.764,1.915,0.297l1.02-1.082c0.449-0.467,0.32-1.357-0.288-1.99l-2.116-2.158h5.705
+    c0.671,0,1.215-0.544,1.215-1.215v-2.43c0-0.671-0.544-1.215-1.215-1.215H8.094l2.271-2.309c0.609-0.626,0.737-1.512,0.288-1.974
+    L9.635,0.278C9.184-0.188,8.327-0.055,7.718,0.575L2.479,5.901C2.38,5.946,2.289,6.008,2.21,6.089L1.192,7.171
+    c-0.21,0.219-0.293,0.53-0.26,0.864C0.899,8.367,0.981,8.676,1.192,8.893z"/>
+</g>
+<g id="forward-active" class="active">
+  <path fill-rule="evenodd" clip-rule="evenodd" d="M14.808,7.107L13.79,6.036c-0.064-0.065-0.136-0.117-0.214-0.159
+    L8.377,0.576C7.77-0.054,6.912-0.189,6.461,0.278L5.441,1.36c-0.449,0.467-0.32,1.357,0.288,1.99l2.116,2.158H2.14
+    c-0.671,0-1.215,0.544-1.215,1.215v2.43c0,0.671,0.544,1.215,1.215,1.215h5.765l-2.271,2.309c-0.609,0.626-0.737,1.512-0.288,1.974
+    l1.019,1.072c0.451,0.465,1.308,0.332,1.917-0.297l5.238-5.326c0.1-0.045,0.191-0.107,0.269-0.188l1.019-1.082
+    c0.21-0.219,0.293-0.53,0.26-0.864C15.101,7.633,15.019,7.324,14.808,7.107z"/>
+</g>
+<g id="reload-active" class="active">
+  <path fill-rule="evenodd" clip-rule="evenodd" d="M15.429,8h-8l3.207-3.207C9.889,4.265,8.986,3.947,8,3.947
+    c-2.554,0-4.625,2.071-4.625,4.625S5.446,13.196,8,13.196c1.638,0,3.069-0.857,3.891-2.141l2.576,1.104
+    C13.199,14.439,10.794,16,8,16c-4.103,0-7.429-3.326-7.429-7.429S3.897,1.143,8,1.143c1.762,0,3.366,0.624,4.631,1.654L15.429,0V8z"/>
+</g>
+<g id="stop-active" class="active">
+  <polygon fill-rule="evenodd" clip-rule="evenodd" points="16,2.748 13.338,0.079 8.038,5.391 2.661,0 0,2.669
+    5.377,8.059 0.157,13.292 2.819,15.961 8.039,10.728 13.298,16 15.959,13.331 10.701,8.06"/>
+</g>
+<g id="bookmark-active" class="active">
+  <path d="M8.008,3.632l0.986,2.012l0.452,0.922l1.014,0.169l2.326,0.389l-1.719,1.799l-0.676,0.708l0.145,0.967
+    L10.896,13l-1.959-1.039l-0.937-0.497l-0.937,0.497l-1.957,1.038L5.468,10.6l0.146-0.968L4.937,8.924L3.219,7.126l2.351-0.39
+    l1.023-0.17l0.45-0.934L8.008,3.632 M8,0C7.72,0,7.44,0.217,7.228,0.65L5.242,4.766L0.907,5.485c-0.958,0.159-1.195,0.861-0.53,1.56
+    l3.113,3.258l-0.69,4.583c-0.105,0.689,0.172,1.092,0.658,1.092c0.185,0,0.399-0.058,0.635-0.181l3.906-2.072l3.906,2.072
+    c0.236,0.123,0.45,0.181,0.635,0.181c0.486,0,0.762-0.403,0.659-1.092l-0.687-4.583l3.109-3.255c0.666-0.702,0.428-1.404-0.53-1.564
+    l-4.303-0.719L8.772,0.65C8.56,0.217,8.28,0,8,0L8,0z"/>
+</g>
+<g id="bookmark-starred-active" class="active">
+  <path d="M8,0C7.719,0,7.438,0.217,7.225,0.651L5.233,4.773l-4.35,0.72c-0.961,0.159-1.199,0.862-0.531,1.562
+    l3.124,3.262l-0.692,4.589C2.679,15.596,2.957,16,3.444,16c0.185,0,0.401-0.058,0.637-0.181L8,13.744l3.919,2.075
+    C12.156,15.942,12.372,16,12.557,16c0.487,0,0.764-0.404,0.661-1.094l-0.69-4.589l3.12-3.259c0.668-0.703,0.43-1.406-0.532-1.566
+    l-4.317-0.72L8.775,0.651C8.562,0.217,8.281,0,8,0L8,0z"/>
+</g>
+</svg>
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -18,16 +18,17 @@ browser.jar:
 #ifdef MOZ_SERVICES_SYNC
   skin/classic/browser/aboutSyncTabs.css
 #endif
   skin/classic/browser/aboutTabCrashed.css
   skin/classic/browser/actionicon-tab.png
 * skin/classic/browser/browser.css
 * skin/classic/browser/browser-lightweightTheme.css
   skin/classic/browser/click-to-play-warning-stripes.png
+* skin/classic/browser/content-contextmenu.svg
 * skin/classic/browser/engineManager.css
   skin/classic/browser/fullscreen-darknoise.png
   skin/classic/browser/Geolocation-16.png
   skin/classic/browser/Geolocation-64.png
   skin/classic/browser/identity.png
   skin/classic/browser/identity-icons-generic.png
   skin/classic/browser/identity-icons-https.png
   skin/classic/browser/identity-icons-https-ev.png
new file mode 100644
--- /dev/null
+++ b/browser/themes/osx/content-contextmenu.svg
@@ -0,0 +1,97 @@
+<?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/. -->
+<svg xmlns="http://www.w3.org/2000/svg"
+     x="0px" y="0px"
+     viewBox="0 0 16 16"
+     enable-background="new 0 0 16 16"
+     xml:space="preserve">
+<style>
+g:not(:target) {
+  display: none;
+}
+
+path {
+  fill: menutext;
+}
+
+g.active > path {
+  fill: -moz-mac-menutextselect;
+}
+</style>
+<g id="back">
+  <path fill-rule="evenodd" clip-rule="evenodd" d="M1.192,8.893L2.21,9.964c0.064,0.065,0.136,0.117,0.214,0.159
+    l5.199,5.301c0.607,0.63,1.465,0.764,1.915,0.297l1.02-1.082c0.449-0.467,0.32-1.357-0.288-1.99l-2.116-2.158h5.705
+    c0.671,0,1.215-0.544,1.215-1.215v-2.43c0-0.671-0.544-1.215-1.215-1.215H8.094l2.271-2.309c0.609-0.626,0.737-1.512,0.288-1.974
+    L9.635,0.278C9.184-0.188,8.327-0.055,7.718,0.575L2.479,5.901C2.38,5.946,2.289,6.008,2.21,6.089L1.192,7.171
+    c-0.21,0.219-0.293,0.53-0.26,0.864C0.899,8.367,0.981,8.676,1.192,8.893z"/>
+</g>
+<g id="forward">
+  <path fill-rule="evenodd" clip-rule="evenodd" d="M14.808,7.107L13.79,6.036c-0.064-0.065-0.136-0.117-0.214-0.159
+    L8.377,0.576C7.77-0.054,6.912-0.189,6.461,0.278L5.441,1.36c-0.449,0.467-0.32,1.357,0.288,1.99l2.116,2.158H2.14
+    c-0.671,0-1.215,0.544-1.215,1.215v2.43c0,0.671,0.544,1.215,1.215,1.215h5.765l-2.271,2.309c-0.609,0.626-0.737,1.512-0.288,1.974
+    l1.019,1.072c0.451,0.465,1.308,0.332,1.917-0.297l5.238-5.326c0.1-0.045,0.191-0.107,0.269-0.188l1.019-1.082
+    c0.21-0.219,0.293-0.53,0.26-0.864C15.101,7.633,15.019,7.324,14.808,7.107z"/>
+</g>
+<g id="reload">
+  <path fill-rule="evenodd" clip-rule="evenodd" d="M15.429,8h-8l3.207-3.207C9.889,4.265,8.986,3.947,8,3.947
+    c-2.554,0-4.625,2.071-4.625,4.625S5.446,13.196,8,13.196c1.638,0,3.069-0.857,3.891-2.141l2.576,1.104
+    C13.199,14.439,10.794,16,8,16c-4.103,0-7.429-3.326-7.429-7.429S3.897,1.143,8,1.143c1.762,0,3.366,0.624,4.631,1.654L15.429,0V8z"/>
+</g>
+<g id="stop">
+  <polygon fill-rule="evenodd" clip-rule="evenodd" points="16,2.748 13.338,0.079 8.038,5.391 2.661,0 0,2.669
+    5.377,8.059 0.157,13.292 2.819,15.961 8.039,10.728 13.298,16 15.959,13.331 10.701,8.06"/>
+</g>
+<g id="bookmark">
+  <path d="M8.008,3.632l0.986,2.012l0.452,0.922l1.014,0.169l2.326,0.389l-1.719,1.799l-0.676,0.708l0.145,0.967
+    L10.896,13l-1.959-1.039l-0.937-0.497l-0.937,0.497l-1.957,1.038L5.468,10.6l0.146-0.968L4.937,8.924L3.219,7.126l2.351-0.39
+    l1.023-0.17l0.45-0.934L8.008,3.632 M8,0C7.72,0,7.44,0.217,7.228,0.65L5.242,4.766L0.907,5.485c-0.958,0.159-1.195,0.861-0.53,1.56
+    l3.113,3.258l-0.69,4.583c-0.105,0.689,0.172,1.092,0.658,1.092c0.185,0,0.399-0.058,0.635-0.181l3.906-2.072l3.906,2.072
+    c0.236,0.123,0.45,0.181,0.635,0.181c0.486,0,0.762-0.403,0.659-1.092l-0.687-4.583l3.109-3.255c0.666-0.702,0.428-1.404-0.53-1.564
+    l-4.303-0.719L8.772,0.65C8.56,0.217,8.28,0,8,0L8,0z"/>
+</g>
+<g id="bookmark-starred">
+  <path d="M8,0C7.719,0,7.438,0.217,7.225,0.651L5.233,4.773l-4.35,0.72c-0.961,0.159-1.199,0.862-0.531,1.562
+    l3.124,3.262l-0.692,4.589C2.679,15.596,2.957,16,3.444,16c0.185,0,0.401-0.058,0.637-0.181L8,13.744l3.919,2.075
+    C12.156,15.942,12.372,16,12.557,16c0.487,0,0.764-0.404,0.661-1.094l-0.69-4.589l3.12-3.259c0.668-0.703,0.43-1.406-0.532-1.566
+    l-4.317-0.72L8.775,0.651C8.562,0.217,8.281,0,8,0L8,0z"/>
+</g>
+<g id="back-active" class="active">
+  <path fill-rule="evenodd" clip-rule="evenodd" d="M1.192,8.893L2.21,9.964c0.064,0.065,0.136,0.117,0.214,0.159
+    l5.199,5.301c0.607,0.63,1.465,0.764,1.915,0.297l1.02-1.082c0.449-0.467,0.32-1.357-0.288-1.99l-2.116-2.158h5.705
+    c0.671,0,1.215-0.544,1.215-1.215v-2.43c0-0.671-0.544-1.215-1.215-1.215H8.094l2.271-2.309c0.609-0.626,0.737-1.512,0.288-1.974
+    L9.635,0.278C9.184-0.188,8.327-0.055,7.718,0.575L2.479,5.901C2.38,5.946,2.289,6.008,2.21,6.089L1.192,7.171
+    c-0.21,0.219-0.293,0.53-0.26,0.864C0.899,8.367,0.981,8.676,1.192,8.893z"/>
+</g>
+<g id="forward-active" class="active">
+  <path fill-rule="evenodd" clip-rule="evenodd" d="M14.808,7.107L13.79,6.036c-0.064-0.065-0.136-0.117-0.214-0.159
+    L8.377,0.576C7.77-0.054,6.912-0.189,6.461,0.278L5.441,1.36c-0.449,0.467-0.32,1.357,0.288,1.99l2.116,2.158H2.14
+    c-0.671,0-1.215,0.544-1.215,1.215v2.43c0,0.671,0.544,1.215,1.215,1.215h5.765l-2.271,2.309c-0.609,0.626-0.737,1.512-0.288,1.974
+    l1.019,1.072c0.451,0.465,1.308,0.332,1.917-0.297l5.238-5.326c0.1-0.045,0.191-0.107,0.269-0.188l1.019-1.082
+    c0.21-0.219,0.293-0.53,0.26-0.864C15.101,7.633,15.019,7.324,14.808,7.107z"/>
+</g>
+<g id="reload-active" class="active">
+  <path fill-rule="evenodd" clip-rule="evenodd" d="M15.429,8h-8l3.207-3.207C9.889,4.265,8.986,3.947,8,3.947
+    c-2.554,0-4.625,2.071-4.625,4.625S5.446,13.196,8,13.196c1.638,0,3.069-0.857,3.891-2.141l2.576,1.104
+    C13.199,14.439,10.794,16,8,16c-4.103,0-7.429-3.326-7.429-7.429S3.897,1.143,8,1.143c1.762,0,3.366,0.624,4.631,1.654L15.429,0V8z"/>
+</g>
+<g id="stop-active" class="active">
+  <polygon fill-rule="evenodd" clip-rule="evenodd" points="16,2.748 13.338,0.079 8.038,5.391 2.661,0 0,2.669
+    5.377,8.059 0.157,13.292 2.819,15.961 8.039,10.728 13.298,16 15.959,13.331 10.701,8.06"/>
+</g>
+<g id="bookmark-active" class="active">
+  <path d="M8.008,3.632l0.986,2.012l0.452,0.922l1.014,0.169l2.326,0.389l-1.719,1.799l-0.676,0.708l0.145,0.967
+    L10.896,13l-1.959-1.039l-0.937-0.497l-0.937,0.497l-1.957,1.038L5.468,10.6l0.146-0.968L4.937,8.924L3.219,7.126l2.351-0.39
+    l1.023-0.17l0.45-0.934L8.008,3.632 M8,0C7.72,0,7.44,0.217,7.228,0.65L5.242,4.766L0.907,5.485c-0.958,0.159-1.195,0.861-0.53,1.56
+    l3.113,3.258l-0.69,4.583c-0.105,0.689,0.172,1.092,0.658,1.092c0.185,0,0.399-0.058,0.635-0.181l3.906-2.072l3.906,2.072
+    c0.236,0.123,0.45,0.181,0.635,0.181c0.486,0,0.762-0.403,0.659-1.092l-0.687-4.583l3.109-3.255c0.666-0.702,0.428-1.404-0.53-1.564
+    l-4.303-0.719L8.772,0.65C8.56,0.217,8.28,0,8,0L8,0z"/>
+</g>
+<g id="bookmark-starred-active" class="active">
+  <path d="M8,0C7.719,0,7.438,0.217,7.225,0.651L5.233,4.773l-4.35,0.72c-0.961,0.159-1.199,0.862-0.531,1.562
+    l3.124,3.262l-0.692,4.589C2.679,15.596,2.957,16,3.444,16c0.185,0,0.401-0.058,0.637-0.181L8,13.744l3.919,2.075
+    C12.156,15.942,12.372,16,12.557,16c0.487,0,0.764-0.404,0.661-1.094l-0.69-4.589l3.12-3.259c0.668-0.703,0.43-1.406-0.532-1.566
+    l-4.317-0.72L8.775,0.651C8.562,0.217,8.281,0,8,0L8,0z"/>
+</g>
+</svg>
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -18,16 +18,17 @@ browser.jar:
   skin/classic/browser/aboutSyncTabs.css
 #endif
   skin/classic/browser/aboutTabCrashed.css
   skin/classic/browser/actionicon-tab.png
   skin/classic/browser/actionicon-tab@2x.png
 * skin/classic/browser/browser.css                          (browser.css)
 * skin/classic/browser/browser-lightweightTheme.css
   skin/classic/browser/click-to-play-warning-stripes.png
+* skin/classic/browser/content-contextmenu.svg
 * skin/classic/browser/engineManager.css                    (engineManager.css)
   skin/classic/browser/fullscreen-darknoise.png
   skin/classic/browser/Geolocation-16.png
   skin/classic/browser/Geolocation-16@2x.png
   skin/classic/browser/Geolocation-64.png
   skin/classic/browser/Geolocation-64@2x.png
   skin/classic/browser/identity.png
   skin/classic/browser/identity@2x.png
--- a/browser/themes/shared/contextmenu.inc.css
+++ b/browser/themes/shared/contextmenu.inc.css
@@ -8,44 +8,59 @@ menugroup > .menuitem-iconic[disabled="t
   -moz-box-pack: center;
   -moz-box-align: center;
 }
 
 #context-navigation > .menuitem-iconic > .menu-iconic-left {
   -moz-appearance: none;
 }
 
-#context-navigation > #context-back > .menu-iconic-left {
-  list-style-image: url("chrome://browser/skin/Toolbar.png");
-  -moz-image-region: rect(0, 36px, 18px, 18px);
+#context-back > .menu-iconic-left {
+  list-style-image: url("chrome://browser/skin/content-contextmenu.svg#back");
 }
 
-#context-navigation > #context-forward > .menu-iconic-left {
-  list-style-image: url("chrome://browser/skin/Toolbar.png");
-  -moz-image-region: rect(0, 72px, 18px, 54px);
+#context-back[_moz-menuactive=true]:not([disabled]) > .menu-iconic-left {
+  list-style-image: url("chrome://browser/skin/content-contextmenu.svg#back-active");
+}
+
+#context-forward > .menu-iconic-left {
+  list-style-image: url("chrome://browser/skin/content-contextmenu.svg#forward");
+}
+
+#context-forward[_moz-menuactive=true]:not([disabled]) > .menu-iconic-left {
+  list-style-image: url("chrome://browser/skin/content-contextmenu.svg#forward-active");
 }
 
-#context-navigation > #context-reload > .menu-iconic-left {
-  list-style-image: url("chrome://browser/skin/reload-stop-go.png");
-  -moz-image-region: rect(0, 14px, 14px, 0);
+#context-reload > .menu-iconic-left {
+  list-style-image: url("chrome://browser/skin/content-contextmenu.svg#reload");
+}
+
+#context-reload[_moz-menuactive=true]:not([disabled]) > .menu-iconic-left {
+  list-style-image: url("chrome://browser/skin/content-contextmenu.svg#reload-active");
 }
 
-#context-navigation > #context-stop > .menu-iconic-left {
-  list-style-image: url("chrome://browser/skin/reload-stop-go.png");
-  -moz-image-region: rect(0, 28px, 14px, 14px);
+#context-stop > .menu-iconic-left {
+  list-style-image: url("chrome://browser/skin/content-contextmenu.svg#stop");
 }
 
-#context-navigation > #context-bookmarkpage > .menu-iconic-left {
-  list-style-image: url("chrome://browser/skin/Toolbar.png");
-  -moz-image-region: rect(0, 144px, 18px, 126px);
+#context-stop[_moz-menuactive=true]:not([disabled]) > .menu-iconic-left {
+  list-style-image: url("chrome://browser/skin/content-contextmenu.svg#stop-active");
+}
+
+#context-bookmarkpage > .menu-iconic-left {
+  list-style-image: url("chrome://browser/skin/content-contextmenu.svg#bookmark");
+}
+
+#context-bookmarkpage[_moz-menuactive=true]:not([disabled]) > .menu-iconic-left {
+  list-style-image: url("chrome://browser/skin/content-contextmenu.svg#bookmark-active");
 }
 
 #context-back:-moz-locale-dir(rtl),
 #context-forward:-moz-locale-dir(rtl),
 #context-reload:-moz-locale-dir(rtl) {
   transform: scaleX(-1);
 }
 
 #context-navigation > .menuitem-iconic > .menu-iconic-left > .menu-iconic-icon {
-  width: 18px;
-  height: 18px;
+  width: 16px;
+  height: 16px;
   margin: 7px;
 }
--- a/browser/themes/shared/devtools/highlighter.inc.css
+++ b/browser/themes/shared/devtools/highlighter.inc.css
@@ -47,16 +47,18 @@ svg|line.box-model-guide-left {
 .highlighter-nodeinfobar {
   color: hsl(216,33%,97%);
   border-radius: 3px;
   background: hsl(214,13%,24%) no-repeat padding-box;
   padding: 5px;
   /* Avoid cases where the infobar is smaller than the arrow, when the text is
   short */
   min-width: 75px;
+   /* Avoid a shadow with lightweight themes - Bug 1037908 */
+  text-shadow: none;
 }
 
 /* Highlighter - Node Infobar - text */
 
 .highlighter-nodeinfobar-text {
   text-align: center;
   /* 100% - size of the buttons and margins */
   max-width: calc(100% - 2 * (26px + 6px));
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -2986,17 +2986,16 @@ chatbox {
 
 #UITourTooltipButtons {
   margin: 24px -4px -4px;
 }
 
 %include ../shared/contextmenu.inc.css
 
 #context-navigation {
-  background-color: -moz-dialog;
-  padding-bottom: 2px;
-  margin-bottom: 2px;
+  background-color: menu;
+  padding-bottom: 4px;
 }
 
 #context-sep-navigation {
   -moz-margin-start: -28px;
   margin-top: -4px;
 }
new file mode 100644
--- /dev/null
+++ b/browser/themes/windows/content-contextmenu.svg
@@ -0,0 +1,97 @@
+<?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/. -->
+<svg xmlns="http://www.w3.org/2000/svg"
+     x="0px" y="0px"
+     viewBox="0 0 16 16"
+     enable-background="new 0 0 16 16"
+     xml:space="preserve">
+<style>
+g:not(:target) {
+  display: none;
+}
+
+path {
+  fill: menutext;
+}
+
+g.active > path {
+  fill: -moz-menuhovertext;
+}
+</style>
+<g id="back">
+  <path fill-rule="evenodd" clip-rule="evenodd" d="M1.192,8.893L2.21,9.964c0.064,0.065,0.136,0.117,0.214,0.159
+    l5.199,5.301c0.607,0.63,1.465,0.764,1.915,0.297l1.02-1.082c0.449-0.467,0.32-1.357-0.288-1.99l-2.116-2.158h5.705
+    c0.671,0,1.215-0.544,1.215-1.215v-2.43c0-0.671-0.544-1.215-1.215-1.215H8.094l2.271-2.309c0.609-0.626,0.737-1.512,0.288-1.974
+    L9.635,0.278C9.184-0.188,8.327-0.055,7.718,0.575L2.479,5.901C2.38,5.946,2.289,6.008,2.21,6.089L1.192,7.171
+    c-0.21,0.219-0.293,0.53-0.26,0.864C0.899,8.367,0.981,8.676,1.192,8.893z"/>
+</g>
+<g id="forward">
+  <path fill-rule="evenodd" clip-rule="evenodd" d="M14.808,7.107L13.79,6.036c-0.064-0.065-0.136-0.117-0.214-0.159
+    L8.377,0.576C7.77-0.054,6.912-0.189,6.461,0.278L5.441,1.36c-0.449,0.467-0.32,1.357,0.288,1.99l2.116,2.158H2.14
+    c-0.671,0-1.215,0.544-1.215,1.215v2.43c0,0.671,0.544,1.215,1.215,1.215h5.765l-2.271,2.309c-0.609,0.626-0.737,1.512-0.288,1.974
+    l1.019,1.072c0.451,0.465,1.308,0.332,1.917-0.297l5.238-5.326c0.1-0.045,0.191-0.107,0.269-0.188l1.019-1.082
+    c0.21-0.219,0.293-0.53,0.26-0.864C15.101,7.633,15.019,7.324,14.808,7.107z"/>
+</g>
+<g id="reload">
+  <path fill-rule="evenodd" clip-rule="evenodd" d="M15.429,8h-8l3.207-3.207C9.889,4.265,8.986,3.947,8,3.947
+    c-2.554,0-4.625,2.071-4.625,4.625S5.446,13.196,8,13.196c1.638,0,3.069-0.857,3.891-2.141l2.576,1.104
+    C13.199,14.439,10.794,16,8,16c-4.103,0-7.429-3.326-7.429-7.429S3.897,1.143,8,1.143c1.762,0,3.366,0.624,4.631,1.654L15.429,0V8z"/>
+</g>
+<g id="stop">
+  <polygon fill-rule="evenodd" clip-rule="evenodd" points="16,2.748 13.338,0.079 8.038,5.391 2.661,0 0,2.669
+    5.377,8.059 0.157,13.292 2.819,15.961 8.039,10.728 13.298,16 15.959,13.331 10.701,8.06"/>
+</g>
+<g id="bookmark">
+  <path d="M8.008,3.632l0.986,2.012l0.452,0.922l1.014,0.169l2.326,0.389l-1.719,1.799l-0.676,0.708l0.145,0.967
+    L10.896,13l-1.959-1.039l-0.937-0.497l-0.937,0.497l-1.957,1.038L5.468,10.6l0.146-0.968L4.937,8.924L3.219,7.126l2.351-0.39
+    l1.023-0.17l0.45-0.934L8.008,3.632 M8,0C7.72,0,7.44,0.217,7.228,0.65L5.242,4.766L0.907,5.485c-0.958,0.159-1.195,0.861-0.53,1.56
+    l3.113,3.258l-0.69,4.583c-0.105,0.689,0.172,1.092,0.658,1.092c0.185,0,0.399-0.058,0.635-0.181l3.906-2.072l3.906,2.072
+    c0.236,0.123,0.45,0.181,0.635,0.181c0.486,0,0.762-0.403,0.659-1.092l-0.687-4.583l3.109-3.255c0.666-0.702,0.428-1.404-0.53-1.564
+    l-4.303-0.719L8.772,0.65C8.56,0.217,8.28,0,8,0L8,0z"/>
+</g>
+<g id="bookmark-starred">
+  <path d="M8,0C7.719,0,7.438,0.217,7.225,0.651L5.233,4.773l-4.35,0.72c-0.961,0.159-1.199,0.862-0.531,1.562
+    l3.124,3.262l-0.692,4.589C2.679,15.596,2.957,16,3.444,16c0.185,0,0.401-0.058,0.637-0.181L8,13.744l3.919,2.075
+    C12.156,15.942,12.372,16,12.557,16c0.487,0,0.764-0.404,0.661-1.094l-0.69-4.589l3.12-3.259c0.668-0.703,0.43-1.406-0.532-1.566
+    l-4.317-0.72L8.775,0.651C8.562,0.217,8.281,0,8,0L8,0z"/>
+</g>
+<g id="back-active" class="active">
+  <path fill-rule="evenodd" clip-rule="evenodd" d="M1.192,8.893L2.21,9.964c0.064,0.065,0.136,0.117,0.214,0.159
+    l5.199,5.301c0.607,0.63,1.465,0.764,1.915,0.297l1.02-1.082c0.449-0.467,0.32-1.357-0.288-1.99l-2.116-2.158h5.705
+    c0.671,0,1.215-0.544,1.215-1.215v-2.43c0-0.671-0.544-1.215-1.215-1.215H8.094l2.271-2.309c0.609-0.626,0.737-1.512,0.288-1.974
+    L9.635,0.278C9.184-0.188,8.327-0.055,7.718,0.575L2.479,5.901C2.38,5.946,2.289,6.008,2.21,6.089L1.192,7.171
+    c-0.21,0.219-0.293,0.53-0.26,0.864C0.899,8.367,0.981,8.676,1.192,8.893z"/>
+</g>
+<g id="forward-active" class="active">
+  <path fill-rule="evenodd" clip-rule="evenodd" d="M14.808,7.107L13.79,6.036c-0.064-0.065-0.136-0.117-0.214-0.159
+    L8.377,0.576C7.77-0.054,6.912-0.189,6.461,0.278L5.441,1.36c-0.449,0.467-0.32,1.357,0.288,1.99l2.116,2.158H2.14
+    c-0.671,0-1.215,0.544-1.215,1.215v2.43c0,0.671,0.544,1.215,1.215,1.215h5.765l-2.271,2.309c-0.609,0.626-0.737,1.512-0.288,1.974
+    l1.019,1.072c0.451,0.465,1.308,0.332,1.917-0.297l5.238-5.326c0.1-0.045,0.191-0.107,0.269-0.188l1.019-1.082
+    c0.21-0.219,0.293-0.53,0.26-0.864C15.101,7.633,15.019,7.324,14.808,7.107z"/>
+</g>
+<g id="reload-active" class="active">
+  <path fill-rule="evenodd" clip-rule="evenodd" d="M15.429,8h-8l3.207-3.207C9.889,4.265,8.986,3.947,8,3.947
+    c-2.554,0-4.625,2.071-4.625,4.625S5.446,13.196,8,13.196c1.638,0,3.069-0.857,3.891-2.141l2.576,1.104
+    C13.199,14.439,10.794,16,8,16c-4.103,0-7.429-3.326-7.429-7.429S3.897,1.143,8,1.143c1.762,0,3.366,0.624,4.631,1.654L15.429,0V8z"/>
+</g>
+<g id="stop-active" class="active">
+  <polygon fill-rule="evenodd" clip-rule="evenodd" points="16,2.748 13.338,0.079 8.038,5.391 2.661,0 0,2.669
+    5.377,8.059 0.157,13.292 2.819,15.961 8.039,10.728 13.298,16 15.959,13.331 10.701,8.06"/>
+</g>
+<g id="bookmark-active" class="active">
+  <path d="M8.008,3.632l0.986,2.012l0.452,0.922l1.014,0.169l2.326,0.389l-1.719,1.799l-0.676,0.708l0.145,0.967
+    L10.896,13l-1.959-1.039l-0.937-0.497l-0.937,0.497l-1.957,1.038L5.468,10.6l0.146-0.968L4.937,8.924L3.219,7.126l2.351-0.39
+    l1.023-0.17l0.45-0.934L8.008,3.632 M8,0C7.72,0,7.44,0.217,7.228,0.65L5.242,4.766L0.907,5.485c-0.958,0.159-1.195,0.861-0.53,1.56
+    l3.113,3.258l-0.69,4.583c-0.105,0.689,0.172,1.092,0.658,1.092c0.185,0,0.399-0.058,0.635-0.181l3.906-2.072l3.906,2.072
+    c0.236,0.123,0.45,0.181,0.635,0.181c0.486,0,0.762-0.403,0.659-1.092l-0.687-4.583l3.109-3.255c0.666-0.702,0.428-1.404-0.53-1.564
+    l-4.303-0.719L8.772,0.65C8.56,0.217,8.28,0,8,0L8,0z"/>
+</g>
+<g id="bookmark-starred-active" class="active">
+  <path d="M8,0C7.719,0,7.438,0.217,7.225,0.651L5.233,4.773l-4.35,0.72c-0.961,0.159-1.199,0.862-0.531,1.562
+    l3.124,3.262l-0.692,4.589C2.679,15.596,2.957,16,3.444,16c0.185,0,0.401-0.058,0.637-0.181L8,13.744l3.919,2.075
+    C12.156,15.942,12.372,16,12.557,16c0.487,0,0.764-0.404,0.661-1.094l-0.69-4.589l3.12-3.259c0.668-0.703,0.43-1.406-0.532-1.566
+    l-4.317-0.72L8.775,0.651C8.562,0.217,8.281,0,8,0L8,0z"/>
+</g>
+</svg>
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -20,16 +20,17 @@ browser.jar:
 #ifdef MOZ_SERVICES_SYNC
         skin/classic/browser/aboutSyncTabs.css
 #endif
         skin/classic/browser/aboutTabCrashed.css
         skin/classic/browser/actionicon-tab.png
 *       skin/classic/browser/browser.css
 *       skin/classic/browser/browser-lightweightTheme.css
         skin/classic/browser/click-to-play-warning-stripes.png
+*       skin/classic/browser/content-contextmenu.svg
 *       skin/classic/browser/engineManager.css
         skin/classic/browser/fullscreen-darknoise.png
         skin/classic/browser/Geolocation-16.png
         skin/classic/browser/Geolocation-64.png
         skin/classic/browser/Info.png
         skin/classic/browser/identity.png
         skin/classic/browser/identity-icons-generic.png
         skin/classic/browser/identity-icons-https.png
@@ -424,16 +425,17 @@ browser.jar:
         skin/classic/aero/browser/aboutSyncTabs.css
 #endif
         skin/classic/aero/browser/aboutTabCrashed.css
         skin/classic/aero/browser/aboutWelcomeBack.css               (../shared/aboutWelcomeBack.css)
         skin/classic/aero/browser/actionicon-tab.png
 *       skin/classic/aero/browser/browser.css                        (browser-aero.css)
 *       skin/classic/aero/browser/browser-lightweightTheme.css
         skin/classic/aero/browser/click-to-play-warning-stripes.png
+*       skin/classic/aero/browser/content-contextmenu.svg
 *       skin/classic/aero/browser/engineManager.css
         skin/classic/aero/browser/fullscreen-darknoise.png
         skin/classic/aero/browser/Geolocation-16.png
         skin/classic/aero/browser/Geolocation-64.png
         skin/classic/aero/browser/Info.png                           (Info-aero.png)
         skin/classic/aero/browser/identity.png                       (identity-aero.png)
         skin/classic/aero/browser/identity-icons-generic.png
         skin/classic/aero/browser/identity-icons-https.png
--- a/dom/alarm/AlarmsManager.js
+++ b/dom/alarm/AlarmsManager.js
@@ -56,25 +56,28 @@ AlarmsManager.prototype = {
         isIgnoreTimezone = true;
         break;
 
       default:
         throw Components.results.NS_ERROR_INVALID_ARG;
         break;
     }
 
+    // Run JSON.stringify() in the sand box with the principal of the calling
+    // web page to ensure no cross-origin object is involved. A "Permission
+    // Denied" error will be thrown in case of privilege violation.
     let sandbox = new Cu.Sandbox(Cu.getWebIDLCallerPrincipal());
     sandbox.data = aData;
     let data = Cu.evalInSandbox("JSON.stringify(data)", sandbox);
     let request = this.createRequest();
     this._cpmm.sendAsyncMessage("AlarmsManager:Add",
                                 { requestId: this.getRequestId(request),
                                   date: aDate,
                                   ignoreTimezone: isIgnoreTimezone,
-                                  data: data,
+                                  data: JSON.parse(data),
                                   pageURL: this._pageURL,
                                   manifestURL: this._manifestURL });
     return request;
   },
 
   remove: function remove(aId) {
     debug("remove()");
 
@@ -107,26 +110,23 @@ AlarmsManager.prototype = {
       case "AlarmsManager:Add:Return:OK":
         Services.DOMRequest.fireSuccess(request, json.id);
         break;
 
       case "AlarmsManager:GetAll:Return:OK":
         // We don't need to expose everything to the web content.
         let alarms = [];
         json.alarms.forEach(function trimAlarmInfo(aAlarm) {
-          let sandbox = new Cu.Sandbox(this._principal);
-          sandbox.data = aAlarm.data;
-          let data = Cu.evalInSandbox("JSON.parse(data)", sandbox);
           let alarm = { "id": aAlarm.id,
                         "date": aAlarm.date,
                         "respectTimezone": aAlarm.ignoreTimezone ?
                                              "ignoreTimezone" : "honorTimezone",
-                        "data": data };
+                        "data": aAlarm.data };
           alarms.push(alarm);
-        }.bind(this));
+        });
 
         Services.DOMRequest.fireSuccess(request,
                                         Cu.cloneInto(alarms, this._window));
         break;
 
       case "AlarmsManager:Add:Return:KO":
         Services.DOMRequest.fireError(request, json.errorMsg);
         break;
@@ -154,21 +154,20 @@ AlarmsManager.prototype = {
     this.initDOMRequestHelper(aWindow, ["AlarmsManager:Add:Return:OK",
                                         "AlarmsManager:Add:Return:KO",
                                         "AlarmsManager:GetAll:Return:OK",
                                         "AlarmsManager:GetAll:Return:KO"]);
 
     // Get the manifest URL if this is an installed app
     let appsService = Cc["@mozilla.org/AppsService;1"]
                         .getService(Ci.nsIAppsService);
+    let principal = aWindow.document.nodePrincipal;
+    this._pageURL = principal.URI.spec;
+    this._manifestURL = appsService.getManifestURLByLocalId(principal.appId);
     this._window = aWindow;
-    this._principal = this._window.document.nodePrincipal;
-    this._pageURL = this._principal.URI.spec;
-    this._manifestURL =
-      appsService.getManifestURLByLocalId(this._principal.appId);
   },
 
   // Called from DOMRequestIpcHelper.
   uninit: function uninit() {
     debug("uninit()");
   },
 }
 
--- a/dom/alarm/test/mochitest.ini
+++ b/dom/alarm/test/mochitest.ini
@@ -11,8 +11,10 @@ skip-if = (buildapp == 'b2g' && toolkit 
 [test_alarm_add_respectTimezone.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_alarm_non_permitted_app.html]
 [test_alarm_permitted_app.html]
 [test_alarm_remove.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_bug1015540.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
+[test_bug1037079.html]
+skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
new file mode 100644
--- /dev/null
+++ b/dom/alarm/test/test_bug1037079.html
@@ -0,0 +1,91 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test time alert is fired for Bug 1037079</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test">
+  <script type="application/javascript">
+
+  "use strict";
+
+  function testFireTimeAlert() {
+    var secondsLater = new Date();
+    secondsLater.setSeconds(secondsLater.getSeconds() + 10);
+
+    var domRequest;
+    try {
+      // Set system message handler.
+      navigator.mozSetMessageHandler('alarm', function(message){
+        ok(true, "Time alert has been fired.");
+        SimpleTest.finish();
+      });
+
+      domRequest = navigator.mozAlarms.add(secondsLater, "honorTimezone",
+    		                               {type: "timer"});
+    } catch (e) {
+      ok(false, "Unexpected exception trying to set time alert.");
+
+      return SimpleTest.finish();
+    }
+    domRequest.onsuccess = function(e) {
+      ok(true, "Set time alert. Waiting to be fired.");
+    };
+    domRequest.onerror = function(e) {
+      ok(false, "Unable to set time alert.");
+
+      SimpleTest.finish();
+    };
+  }
+
+  function startTests() {
+    SpecialPowers.pushPrefEnv({
+      "set": [["dom.mozAlarms.enabled", true]]
+    }, function() {
+      var isAllowedToTest = true;
+
+      if (navigator.appVersion.indexOf("Android") !== -1) {
+        ok(true, "mozAlarms is not allowed on Android for now. " +
+                 "TODO Bug 863557.");
+        isAllowedToTest = false;
+      } else if (SpecialPowers.wrap(document).nodePrincipal.appStatus ==
+                 SpecialPowers.Ci.nsIPrincipal.APP_STATUS_NOT_INSTALLED) {
+        ok(true, "mozAlarms is not allowed for non-installed apps. " +
+                 "TODO Bug 876981.");
+        isAllowedToTest = false;
+      }
+
+      if (isAllowedToTest) {
+        ok(true, "Start to test...");
+        testFireTimeAlert();
+      } else {
+        // A sanity check to make sure we must run tests on Firefox OS (B2G).
+        if (navigator.userAgent.indexOf("Mobile") != -1 &&
+            navigator.appVersion.indexOf("Android") == -1) {
+          ok(false, "Should run the test on Firefox OS (B2G)!");
+        }
+
+        SimpleTest.finish();
+      }
+    });
+  }
+
+  SimpleTest.expectAssertions(0, 9);
+  SimpleTest.waitForExplicitFinish();
+  if (SpecialPowers.hasPermission("alarms", document)) {
+    startTests();
+  } else {
+    // Add the permissions and reload so they propogate
+    SpecialPowers.addPermission("alarms", true, document);
+    window.location.reload();
+  }
+
+  </script>
+</pre>
+</body>
+</html>
--- a/dom/bluetooth2/BluetoothAdapter.cpp
+++ b/dom/bluetooth2/BluetoothAdapter.cpp
@@ -408,70 +408,81 @@ BluetoothAdapter::SetDiscoveryHandleInUs
   if (mDiscoveryHandleInUse) {
     mDiscoveryHandleInUse->DisconnectFromOwner();
   }
 
   mDiscoveryHandleInUse = aDiscoveryHandle;
 }
 
 already_AddRefed<Promise>
-BluetoothAdapter::StartStopDiscovery(bool aStart, ErrorResult& aRv)
+BluetoothAdapter::StartDiscovery(ErrorResult& aRv)
 {
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
   if (!global) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
   nsRefPtr<Promise> promise = new Promise(global);
 
   /**
    * Ensure
-   * - adapter does not already start/stop discovering,
-   *   (note we reject here to ensure each resolved promise of startDiscovery
-   *    returns a BluetoothDiscoveryHandle)
+   * - adapter is not discovering (note we reject here to ensure
+       each resolved promise returns a new BluetoothDiscoveryHandle),
    * - adapter is already enabled, and
    * - BluetoothService is available
    */
-  BT_ENSURE_TRUE_REJECT(mDiscovering != aStart,
-                        NS_ERROR_DOM_INVALID_STATE_ERR);
+  BT_ENSURE_TRUE_REJECT(!mDiscovering, NS_ERROR_DOM_INVALID_STATE_ERR);
   BT_ENSURE_TRUE_REJECT(mState == BluetoothAdapterState::Enabled,
                         NS_ERROR_DOM_INVALID_STATE_ERR);
   BluetoothService* bs = BluetoothService::Get();
   BT_ENSURE_TRUE_REJECT(bs, NS_ERROR_NOT_AVAILABLE);
 
-  BT_API2_LOGR("aStart %d", aStart);
-  nsresult rv;
-  if (aStart) {
-    // Start discovery: return BluetoothDiscoveryHandle in StartDiscoveryTask
-    nsRefPtr<BluetoothReplyRunnable> result =
-      new StartDiscoveryTask(this, promise);
-    rv = bs->StartDiscoveryInternal(result);
-  } else {
-    // Stop discovery: void return
-    nsRefPtr<BluetoothReplyRunnable> result =
-      new BluetoothVoidReplyRunnable(nullptr /* DOMRequest */,
-                                     promise,
-                                     NS_LITERAL_STRING("StopDiscovery"));
-    rv = bs->StopDiscoveryInternal(result);
-  }
-  BT_ENSURE_TRUE_REJECT(NS_SUCCEEDED(rv), NS_ERROR_DOM_OPERATION_ERR);
+  BT_API2_LOGR();
+
+  // Return BluetoothDiscoveryHandle in StartDiscoveryTask
+  nsRefPtr<BluetoothReplyRunnable> result =
+    new StartDiscoveryTask(this, promise);
+  BT_ENSURE_TRUE_REJECT(NS_SUCCEEDED(bs->StartDiscoveryInternal(result)),
+                        NS_ERROR_DOM_OPERATION_ERR);
 
   return promise.forget();
 }
 
 already_AddRefed<Promise>
-BluetoothAdapter::StartDiscovery(ErrorResult& aRv)
-{
-  return StartStopDiscovery(true, aRv);
-}
-
-already_AddRefed<Promise>
 BluetoothAdapter::StopDiscovery(ErrorResult& aRv)
 {
-  return StartStopDiscovery(false, aRv);
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
+  if (!global) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+  nsRefPtr<Promise> promise = new Promise(global);
+
+  /**
+   * Ensure
+   * - adapter is discovering,
+   * - adapter is already enabled, and
+   * - BluetoothService is available
+   */
+  BT_ENSURE_TRUE_RESOLVE(mDiscovering, JS::UndefinedHandleValue);
+  BT_ENSURE_TRUE_REJECT(mState == BluetoothAdapterState::Enabled,
+                        NS_ERROR_DOM_INVALID_STATE_ERR);
+  BluetoothService* bs = BluetoothService::Get();
+  BT_ENSURE_TRUE_REJECT(bs, NS_ERROR_NOT_AVAILABLE);
+
+  BT_API2_LOGR();
+
+  nsRefPtr<BluetoothReplyRunnable> result =
+    new BluetoothVoidReplyRunnable(nullptr /* DOMRequest */,
+                                   promise,
+                                   NS_LITERAL_STRING("StopDiscovery"));
+  BT_ENSURE_TRUE_REJECT(NS_SUCCEEDED(bs->StopDiscoveryInternal(result)),
+                        NS_ERROR_DOM_OPERATION_ERR);
+
+  return promise.forget();
 }
 
 void
 BluetoothAdapter::GetDevices(JSContext* aContext,
                              JS::MutableHandle<JS::Value> aDevices,
                              ErrorResult& aRv)
 {
   if (!mJsDeviceAddresses) {
--- a/dom/bluetooth2/BluetoothAdapter.h
+++ b/dom/bluetooth2/BluetoothAdapter.h
@@ -183,17 +183,16 @@ public:
     WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
 private:
   BluetoothAdapter(nsPIDOMWindow* aOwner, const BluetoothValue& aValue);
   ~BluetoothAdapter();
 
   void Root();
 
-  already_AddRefed<Promise> StartStopDiscovery(bool aStart, ErrorResult& aRv);
   already_AddRefed<mozilla::dom::DOMRequest>
     PairUnpair(bool aPair, const nsAString& aDeviceAddress, ErrorResult& aRv);
 
   bool IsAdapterAttributeChanged(BluetoothAdapterAttribute aType,
                                  const BluetoothValue& aValue);
   void HandlePropertyChanged(const BluetoothValue& aValue);
   void DispatchAttributeEvent(const nsTArray<nsString>& aTypes);
   BluetoothAdapterAttribute
--- a/dom/bluetooth2/bluedroid/BluetoothInterface.h
+++ b/dom/bluetooth2/bluedroid/BluetoothInterface.h
@@ -41,17 +41,17 @@ public:
                        int aConnectionState) { }
   virtual void Accept(int aSockFd, const nsAString& aBdAddress,
                       int aConnectionState) { }
 };
 
 class BluetoothSocketInterface
 {
 public:
-  friend BluetoothInterface;
+  friend class BluetoothInterface;
 
   // Init and Cleanup is handled by BluetoothInterface
 
   void Listen(btsock_type_t aType,
               const char* aServiceName, const uint8_t* aServiceUuid,
               int aChannel, int aFlags, BluetoothSocketResultHandler* aRes);
 
   void Connect(const bt_bdaddr_t* aBdAddr, btsock_type_t aType,
@@ -70,17 +70,17 @@ private:
 
 //
 // Handsfree Interface
 //
 
 class BluetoothHandsfreeInterface
 {
 public:
-  friend BluetoothInterface;
+  friend class BluetoothInterface;
 
   bt_status_t Init(bthf_callbacks_t* aCallbacks);
   void        Cleanup();
 
   /* Connect / Disconnect */
 
   bt_status_t Connect(bt_bdaddr_t* aBdAddr);
   bt_status_t Disconnect(bt_bdaddr_t* aBdAddr);
@@ -132,17 +132,17 @@ private:
 
 //
 // Bluetooth Advanced Audio Interface
 //
 
 class BluetoothA2dpInterface
 {
 public:
-  friend BluetoothInterface;
+  friend class BluetoothInterface;
 
   bt_status_t Init(btav_callbacks_t *aCallbacks);
   void        Cleanup();
 
   bt_status_t Connect(bt_bdaddr_t *aBdAddr);
   bt_status_t Disconnect(bt_bdaddr_t *aBdAddr);
 
 protected:
@@ -156,17 +156,17 @@ private:
 //
 // Bluetooth AVRCP Interface
 //
 
 class BluetoothAvrcpInterface
 {
 #if ANDROID_VERSION >= 18
 public:
-  friend BluetoothInterface;
+  friend class BluetoothInterface;
 
   bt_status_t Init(btrc_callbacks_t* aCallbacks);
   void        Cleanup();
 
   bt_status_t GetPlayStatusRsp(btrc_play_status_t aPlayStatus,
                                uint32_t aSongLen, uint32_t aSongPos);
 
   bt_status_t ListPlayerAppAttrRsp(int aNumAttr, btrc_player_attr_t* aPAttrs);
--- a/dom/bluetooth2/bluedroid/BluetoothSocket.cpp
+++ b/dom/bluetooth2/bluedroid/BluetoothSocket.cpp
@@ -140,18 +140,17 @@ public:
     mConsumer = nullptr;
   }
 
   void ShutdownOnIOThread()
   {
     MOZ_ASSERT(!NS_IsMainThread());
     MOZ_ASSERT(!mShuttingDownOnIOThread);
 
-    RemoveWatchers(READ_WATCHER | WRITE_WATCHER);
-
+    Close(); // will also remove fd from I/O loop
     mShuttingDownOnIOThread = true;
   }
 
   void Connect(int aFd);
   void Listen(int aFd);
   void Accept(int aFd);
 
   void ConnectClientFd()
--- a/dom/system/NetworkGeolocationProvider.js
+++ b/dom/system/NetworkGeolocationProvider.js
@@ -455,58 +455,69 @@ WifiGeoPositionProvider.prototype = {
 
     let useCached = isCachedRequestMoreAccurateThanServerRequest(data.cellTowers,
                                                                  data.wifiAccessPoints);
 
     LOG("Use request cache:" + useCached + " reason:" + gDebugCacheReasoning);
 
     if (useCached) {
       gCachedRequest.location.timestamp = Date.now();
-      this.listener.update(gCachedRequest.location);
+      this.notifyListener("update", [gCachedRequest.location]);
       return;
     }
 
     // From here on, do a network geolocation request //
     let url = Services.urlFormatter.formatURLPref("geo.wifi.uri");
-    let listener = this.listener;
     LOG("Sending request: " + url + "\n");
 
     let xhr = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
                         .createInstance(Ci.nsIXMLHttpRequest);
 
-    listener.locationUpdatePending();
+    this.notifyListener("locationUpdatePending");
 
     try {
       xhr.open("POST", url, true);
     } catch (e) {
-      listener.notifyError(POSITION_UNAVAILABLE);
+      this.notifyListener("notifyError",
+                          [POSITION_UNAVAILABLE]);
       return;
     }
     xhr.setRequestHeader("Content-Type", "application/json; charset=UTF-8");
     xhr.responseType = "json";
     xhr.mozBackgroundRequest = true;
     xhr.channel.loadFlags = Ci.nsIChannel.LOAD_ANONYMOUS;
-    xhr.onerror = function() {
-      listener.notifyError(POSITION_UNAVAILABLE);
-    };
-    xhr.onload = function() {
+    xhr.onerror = (function() {
+      this.notifyListener("notifyError",
+                          [POSITION_UNAVAILABLE]);
+    }).bind(this);
+    xhr.onload = (function() {
       LOG("gls returned status: " + xhr.status + " --> " +  JSON.stringify(xhr.response));
       if ((xhr.channel instanceof Ci.nsIHttpChannel && xhr.status != 200) ||
           !xhr.response || !xhr.response.location) {
-        listener.notifyError(POSITION_UNAVAILABLE);
+        this.notifyListener("notifyError",
+                            [POSITION_UNAVAILABLE]);
         return;
       }
 
       let newLocation = new WifiGeoPositionObject(xhr.response.location.lat,
                                                   xhr.response.location.lng,
                                                   xhr.response.accuracy);
 
-      listener.update(newLocation);
+      this.notifyListener("update", [newLocation]);
       gCachedRequest = new CachedRequest(newLocation, data.cellTowers, data.wifiAccessPoints);
-    };
+    }).bind(this);
 
     var requestData = JSON.stringify(data);
     LOG("sending " + requestData);
     xhr.send(requestData);
   },
+
+  notifyListener: function(listenerFunc, args) {
+    args = args || [];
+    try {
+      this.listener[listenerFunc].apply(this.listener, args);
+    } catch(e) {
+      Cu.reportError(e);
+    }
+  }
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([WifiGeoPositionProvider]);
--- a/dom/telephony/gonk/TelephonyService.js
+++ b/dom/telephony/gonk/TelephonyService.js
@@ -104,16 +104,18 @@ XPCOMUtils.defineLazyGetter(this, "gPhon
   return ns.PhoneNumberUtils;
 });
 
 function TelephonyService() {
   this._numClients = gRadioInterfaceLayer.numRadioInterfaces;
   this._listeners = [];
   this._currentCalls = {};
 
+  this._cdmaCallWaitingNumber = null;
+
   // _isActiveCall[clientId][callIndex] shows the active status of the call.
   this._isActiveCall = {};
   this._numActiveCall = 0;
 
   this._updateDebugFlag();
   this.defaultServiceId = this._getDefaultServiceId();
 
   Services.prefs.addObserver(kPrefRilDebuggingEnabled, this, false);
@@ -725,16 +727,22 @@ TelephonyService.prototype = {
       new Date().getTime() - aCall.started : 0;
     let data = {
       number: aCall.number,
       serviceId: aClientId,
       emergency: aCall.isEmergency,
       duration: duration,
       direction: aCall.isOutgoing ? "outgoing" : "incoming"
     };
+
+    if(this._cdmaCallWaitingNumber != null) {
+      data.secondNumber = this._cdmaCallWaitingNumber;
+      this._cdmaCallWaitingNumber = null;
+    }
+
     gSystemMessenger.broadcastMessage("telephony-call-ended", data);
 
     let manualConfStateChange = false;
     let childId = this._currentCalls[aClientId][aCall.callIndex].childId;
     if (childId) {
       // Child cannot live without parent.
       let childCall = this._currentCalls[aClientId][childId];
       this.notifyCallDisconnected(aClientId, childCall);
@@ -862,16 +870,19 @@ TelephonyService.prototype = {
     this._acquireCallRingWakeLock();
 
     let call = this._currentCalls[aClientId][CDMA_SECOND_CALL_INDEX];
     if (call) {
       // TODO: Bug 977503 - B2G RIL: [CDMA] update callNumber when a waiting
       // call comes after a 3way call.
       this.notifyCallDisconnected(aClientId, call);
     }
+
+    this._cdmaCallWaitingNumber = aCall.number;
+
     this._notifyAllListeners("notifyCdmaCallWaiting", [aClientId,
                                                        aCall.number,
                                                        aCall.numberPresentation,
                                                        aCall.name,
                                                        aCall.namePresentation]);
   },
 
   notifySupplementaryService: function(aClientId, aCallIndex, aNotification) {
--- a/mobile/android/base/AndroidManifest.xml.in
+++ b/mobile/android/base/AndroidManifest.xml.in
@@ -4,17 +4,17 @@
       package="@ANDROID_PACKAGE_NAME@"
       android:installLocation="auto"
       android:versionCode="@ANDROID_VERSION_CODE@"
       android:versionName="@MOZ_APP_VERSION@"
 #ifdef MOZ_ANDROID_SHARED_ID
       android:sharedUserId="@MOZ_ANDROID_SHARED_ID@"
 #endif
       >
-    <uses-sdk android:minSdkVersion="8"
+    <uses-sdk android:minSdkVersion="9"
               android:targetSdkVersion="@ANDROID_TARGET_SDK@"/>
 
 #include ../services/manifests/AnnouncementsAndroidManifest_permissions.xml.in
 #include ../services/manifests/FxAccountAndroidManifest_permissions.xml.in
 #include ../services/manifests/HealthReportAndroidManifest_permissions.xml.in
 #include ../services/manifests/SyncAndroidManifest_permissions.xml.in
 
 #ifdef MOZ_ANDROID_SEARCH_ACTIVITY
--- a/mobile/android/base/home/RecentTabsPanel.java
+++ b/mobile/android/base/home/RecentTabsPanel.java
@@ -244,18 +244,22 @@ public class RecentTabsPanel extends Hom
         }
 
         // Only modify mClosedTabs on the UI thread
         ThreadUtils.postToUiThread(new Runnable() {
             @Override
             public void run() {
                 mClosedTabs = closedTabs;
 
-                // Reload the cursor to show recently closed tabs.
-                getLoaderManager().restartLoader(LOADER_ID_RECENT_TABS, null, mCursorLoaderCallbacks);
+                // The fragment might have been detached before this code
+                // runs in the UI thread.
+                if (getActivity() != null) {
+                    // Reload the cursor to show recently closed tabs.
+                    getLoaderManager().restartLoader(LOADER_ID_RECENT_TABS, null, mCursorLoaderCallbacks);
+                }
             }
         });
     }
 
     private void openTabsWithType(int type) {
         final Cursor c = mAdapter.getCursor();
         if (c == null || !c.moveToFirst()) {
             return;
--- a/mobile/android/search/java/org/mozilla/search/PostSearchFragment.java
+++ b/mobile/android/search/java/org/mozilla/search/PostSearchFragment.java
@@ -7,47 +7,42 @@ package org.mozilla.search;
 import android.content.Intent;
 import android.graphics.Bitmap;
 import android.net.Uri;
 import android.os.Bundle;
 import android.support.v4.app.Fragment;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.webkit.WebChromeClient;
 import android.webkit.WebView;
 import android.webkit.WebViewClient;
 
 public class PostSearchFragment extends Fragment {
 
     private static final String LOGTAG = "PostSearchFragment";
     private WebView webview;
 
-    public PostSearchFragment() {}
+    private static String HIDE_BANNER_SCRIPT = "javascript:(function(){var tag=document.createElement('style');" +
+            "tag.type='text/css';document.getElementsByTagName('head')[0].appendChild(tag);tag.innerText='#nav,#header{display:none}'})();";
+
+    public PostSearchFragment() {
+    }
 
 
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
                              Bundle savedInstanceState) {
-        View mainView = inflater.inflate(R.layout.search_activity_detail, container, false);
-
+        final View mainView = inflater.inflate(R.layout.search_activity_detail, container, false);
 
         webview = (WebView) mainView.findViewById(R.id.webview);
-        webview.setWebViewClient(new WebViewClient(){
-            @Override
-            public void onPageStarted(WebView view, String url, Bitmap favicon) {
-                if (isSearchResultsPage(url)) {
-                    super.onPageStarted(view, url, favicon);
-                } else {
-                    webview.stopLoading();
-                    Intent i = new Intent(Intent.ACTION_VIEW);
-                    i.setData(Uri.parse(url));
-                    startActivity(i);
-                }
-            }
-        });
+        webview.setWebViewClient(new LinkInterceptingClient());
+        webview.setWebChromeClient(new StyleInjectingClient());
+        webview.getSettings().setJavaScriptEnabled(true);
+
         return mainView;
     }
 
     /**
      * Test if a given URL is a page of search results.
      * <p>
      * Search results pages will be shown in the embedded view.  Other pages are
      * opened in external browsers.
@@ -61,9 +56,46 @@ public class PostSearchFragment extends 
 
     public void startSearch(String query) {
         setUrl(Constants.YAHOO_WEB_SEARCH_BASE_URL + Uri.encode(query));
     }
 
     public void setUrl(String url) {
         webview.loadUrl(url);
     }
+
+    /**
+     * A custom WebViewClient that intercepts every page load. This allows
+     * us to decide whether to load the url here, or send it to Android
+     * as an intent.
+     */
+    private class LinkInterceptingClient extends WebViewClient {
+
+        @Override
+        public void onPageStarted(WebView view, String url, Bitmap favicon) {
+            if (isSearchResultsPage(url)) {
+                super.onPageStarted(view, url, favicon);
+            } else {
+                view.stopLoading();
+                Intent i = new Intent(Intent.ACTION_VIEW);
+                i.setData(Uri.parse(url));
+                startActivity(i);
+            }
+        }
+    }
+
+    /**
+     * A custom WebChromeClient that allows us to inject CSS into
+     * the head of the HTML.
+     *
+     * We use the WebChromeClient because it provides a hook to the titleReceived
+     * event. Once the title is available, the page will have started parsing the
+     * head element. The script injects its CSS into the head element.
+     */
+    private class StyleInjectingClient extends WebChromeClient {
+
+        @Override
+        public void onReceivedTitle(WebView view, String title) {
+            super.onReceivedTitle(view, title);
+            view.loadUrl(HIDE_BANNER_SCRIPT);
+        }
+    }
 }
--- a/mobile/android/search/res/layout/search_activity_main.xml
+++ b/mobile/android/search/res/layout/search_activity_main.xml
@@ -9,16 +9,17 @@
     android:layout_height="match_parent"
     android:orientation="vertical"
     tools:context=".MainActivity">
 
     <fragment
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:name="org.mozilla.search.PostSearchFragment"
+        android:layout_marginTop="@dimen/webview_height_offset"
         android:id="@+id/postsearch"
         />
 
     <fragment
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:name="org.mozilla.search.PreSearchFragment"
         android:id="@+id/presearch"
--- a/mobile/android/search/res/values/search_dimens.xml
+++ b/mobile/android/search/res/values/search_dimens.xml
@@ -1,9 +1,10 @@
 <!-- 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/. -->
 
 <resources>
-    <!-- Default screen margins, per the Android Design guidelines. -->
-    <dimen name="activity_horizontal_margin">16dp</dimen>
-    <dimen name="activity_vertical_margin">16dp</dimen>
+    <!--This is used to offset the webview so that it is not covered-->
+    <!--by the search bar. If we change the height of the search bar-->
+    <!--then this will need to be updated.-->
+    <dimen name="webview_height_offset">55dp</dimen>
 </resources>
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -649,16 +649,20 @@ pref("devtools.defaultColorUnit", "hex")
 
 // Used for devtools debugging
 pref("devtools.dump.emit", false);
 
 // Disable device discovery logging
 pref("devtools.discovery.log", false);
 // Disable scanning for DevTools devices via WiFi
 pref("devtools.remote.wifi.scan", false);
+// Hide UI options for controlling device visibility over WiFi
+// N.B.: This does not set whether the device can be discovered via WiFi, only
+// whether the UI control to make such a choice is shown to the user
+pref("devtools.remote.wifi.visible", false);
 
 // view source
 pref("view_source.syntax_highlight", true);
 pref("view_source.wrap_long_lines", false);
 pref("view_source.editor.external", false);
 pref("view_source.editor.path", "");
 // allows to add further arguments to the editor; use the %LINE% placeholder
 // for jumping to a specific line (e.g. "/line:%LINE%" or "--goto %LINE%")
@@ -4226,16 +4230,19 @@ pref("dom.voicemail.enabled", false);
 #endif
 // Numeric default service id for Voice Mail API calls with |serviceId|
 // parameter omitted.
 pref("dom.voicemail.defaultServiceId", 0);
 
 // DOM Inter-App Communication API.
 pref("dom.inter-app-communication-api.enabled", false);
 
+// Disable mapped array buffer by default.
+pref("dom.mapped_arraybuffer.enabled", false);
+
 // The tables used for Safebrowsing phishing and malware checks.
 pref("urlclassifier.malwareTable", "goog-malware-shavar,test-malware-simple");
 pref("urlclassifier.phishTable", "goog-phish-shavar,test-phish-simple");
 pref("urlclassifier.downloadBlockTable", "");
 pref("urlclassifier.downloadAllowTable", "");
 pref("urlclassifier.disallow_completions", "test-malware-simple,test-phish-simple,goog-downloadwhite-digest256,mozpub-track-digest256");
 
 // The table and update/gethash URLs for Safebrowsing phishing and malware
--- a/testing/mozbase/mozrunner/mozrunner/application.py
+++ b/testing/mozbase/mozrunner/mozrunner/application.py
@@ -2,17 +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/.
 
 from distutils.spawn import find_executable
 import glob
 import os
 import posixpath
 
-from mozdevice import DeviceManagerADB
+from mozdevice import DeviceManagerADB, DMError
 from mozprofile import (
     Profile,
     FirefoxProfile,
     MetroFirefoxProfile,
     ThunderbirdProfile
 )
 
 here = os.path.abspath(os.path.dirname(__file__))
@@ -31,34 +31,43 @@ def get_app_context(appname):
 class DefaultContext(object):
     profile_class = Profile
 
 
 class B2GContext(object):
     _bindir = None
     _dm = None
     _remote_profile = None
+    _remote_settings_db = None
     profile_class = Profile
 
     def __init__(self, b2g_home=None, adb_path=None):
         self.homedir = b2g_home or os.environ.get('B2G_HOME')
 
         if self.homedir is not None and not os.path.isdir(self.homedir):
             raise OSError('Homedir \'%s\' does not exist!' % self.homedir)
 
         self._adb = adb_path
         self._update_tools = None
         self._fastboot = None
 
         self.remote_binary = '/system/bin/b2g.sh'
-        self.remote_process = '/system/b2g/b2g'
         self.remote_bundles_dir = '/system/b2g/distribution/bundles'
         self.remote_busybox = '/system/bin/busybox'
+        self.remote_process = '/system/b2g/b2g'
         self.remote_profiles_ini = '/data/b2g/mozilla/profiles.ini'
+        self.remote_settings_json = '/system/b2g/defaults/settings.json'
+        self.remote_idb_dir = '/data/local/storage/persistent/chrome/idb'
         self.remote_test_root = '/data/local/tests'
+        self.remote_webapps_dir = '/data/local/webapps'
+
+        self.remote_backup_files = [
+            self.remote_settings_json,
+            self.remote_webapps_dir,
+        ]
 
     @property
     def fastboot(self):
         if self._fastboot is None:
             self._fastboot = self.which('fastboot')
         return self._fastboot
 
     @property
@@ -95,33 +104,89 @@ class B2GContext(object):
         return self._dm
 
     @property
     def remote_profile(self):
         if not self._remote_profile:
             self._remote_profile = posixpath.join(self.remote_test_root, 'profile')
         return self._remote_profile
 
+    @property
+    def remote_settings_db(self):
+        if not self._remote_settings_db:
+            for filename in self.dm.listFiles(self.remote_idb_dir):
+                if filename.endswith('ssegtnti.sqlite'):
+                    self._remote_settings_db = posixpath.join(self.remote_idb_dir, filename)
+                    break
+            else:
+                raise DMError("Could not find settings db in '%s'!" % self.remote_idb_dir)
+        return self._remote_settings_db
 
     def which(self, binary):
         paths = os.environ.get('PATH', {}).split(os.pathsep)
         if self.bindir is not None and os.path.abspath(self.bindir) not in paths:
             paths.insert(0, os.path.abspath(self.bindir))
             os.environ['PATH'] = os.pathsep.join(paths)
 
         return find_executable(binary)
 
     def stop_application(self):
         self.dm.shellCheckOutput(['stop', 'b2g'])
 
+    def setup_profile(self, profile):
         # For some reason user.js in the profile doesn't get picked up.
         # Manually copy it over to prefs.js. See bug 1009730 for more details.
         self.dm.moveTree(posixpath.join(self.remote_profile, 'user.js'),
                          posixpath.join(self.remote_profile, 'prefs.js'))
 
+        if self.dm.fileExists(posixpath.join(self.remote_profile, 'settings.json')):
+            # On devices, settings.json is only read from the profile if
+            # the system location doesn't exist.
+            if self.dm.fileExists(self.remote_settings_json):
+                self.dm.removeFile(self.remote_settings_json)
+
+            # Delete existing settings db and create a new empty one to force new
+            # settings to be loaded.
+            self.dm.removeFile(self.remote_settings_db)
+            self.dm.shellCheckOutput(['touch', self.remote_settings_db])
+
+        # On devices, the webapps are located in /data/local/webapps instead of the profile.
+        # In some cases we may need to replace the existing webapps, in others we may just
+        # need to leave them in the profile. If the system app is present in the profile
+        # webapps, it's a good indication that they should replace the existing ones wholesale.
+        profile_webapps = posixpath.join(self.remote_profile, 'webapps')
+        if self.dm.dirExists(posixpath.join(profile_webapps, 'system.gaiamobile.org')):
+            self.dm.removeDir(self.remote_webapps_dir)
+            self.dm.moveTree(profile_webapps, self.remote_webapps_dir)
+
+        # On devices extensions are installed in the system dir
+        extension_dir = os.path.join(profile.profile, 'extensions', 'staged')
+        if os.path.isdir(extension_dir):
+            # Copy the extensions to the B2G bundles dir.
+            for filename in os.listdir(extension_dir):
+                path = posixpath.join(self.remote_bundles_dir, filename)
+                if self.dm.fileExists(path):
+                    self.dm.removeFile(path)
+            self.dm.pushDir(extension_dir, self.remote_bundles_dir)
+
+    def cleanup_profile(self):
+        # Delete any bundled extensions
+        extension_dir = posixpath.join(self.remote_profile, 'extensions', 'staged')
+        if self.dm.dirExists(extension_dir):
+            for filename in self.dm.listFiles(extension_dir):
+                try:
+                    self.dm.removeDir(posixpath.join(self.remote_bundles_dir, filename))
+                except DMError:
+                    pass
+
+        if self.dm.fileExists(posixpath.join(self.remote_profile, 'settings.json')):
+            # Force settings.db to be restored to defaults
+            self.dm.removeFile(self.remote_settings_db)
+            self.dm.shellCheckOutput(['touch', self.remote_settings_db])
+
 
 class FirefoxContext(object):
     profile_class = FirefoxProfile
 
 
 class ThunderbirdContext(object):
     profile_class = ThunderbirdProfile
 
--- a/testing/mozbase/mozrunner/mozrunner/devices/base.py
+++ b/testing/mozbase/mozrunner/mozrunner/devices/base.py
@@ -73,26 +73,16 @@ class Device(object):
         """
         self.dm.remount()
 
         if self.dm.dirExists(self.app_ctx.remote_profile):
             self.dm.shellCheckOutput(['rm', '-r', self.app_ctx.remote_profile])
 
         self.dm.pushDir(profile.profile, self.app_ctx.remote_profile)
 
-        extension_dir = os.path.join(profile.profile, 'extensions', 'staged')
-        if os.path.isdir(extension_dir):
-            # Copy the extensions to the B2G bundles dir.
-            # need to write to read-only dir
-            for filename in os.listdir(extension_dir):
-                path = posixpath.join(self.app_ctx.remote_bundles_dir, filename)
-                if self.dm.fileExists(path):
-                    self.dm.shellCheckOutput(['rm', '-rf', path])
-            self.dm.pushDir(extension_dir, self.app_ctx.remote_bundles_dir)
-
         timeout = 5 # seconds
         starttime = datetime.datetime.now()
         while datetime.datetime.now() - starttime < datetime.timedelta(seconds=timeout):
             if self.dm.fileExists(self.app_ctx.remote_profiles_ini):
                 break
             time.sleep(1)
         else:
             print "timed out waiting for profiles.ini"
@@ -108,16 +98,23 @@ class Device(object):
                 config.set(section, 'Path', self.app_ctx.remote_profile)
 
         new_profiles_ini = tempfile.NamedTemporaryFile()
         config.write(open(new_profiles_ini.name, 'w'))
 
         self.backup_file(self.app_ctx.remote_profiles_ini)
         self.dm.pushFile(new_profiles_ini.name, self.app_ctx.remote_profiles_ini)
 
+        # Ideally all applications would read the profile the same way, but in practice
+        # this isn't true. Perform application specific profile-related setup if necessary.
+        if hasattr(self.app_ctx, 'setup_profile'):
+            for remote_path in self.app_ctx.remote_backup_files:
+                self.backup_file(remote_path)
+            self.app_ctx.setup_profile(profile)
+
     def _get_online_devices(self):
         return [d[0] for d in self.dm.devices() if d[1] != 'offline' if not d[0].startswith('emulator')]
 
     def connect(self):
         """
         Connects to a running device. If no serial was specified in the
         constructor, defaults to the first entry in `adb devices`.
         """
@@ -208,17 +205,17 @@ class Device(object):
                 traceback.print_exc()
             time.sleep(1)
         return False
 
     def backup_file(self, remote_path):
         if not self.restore:
             return
 
-        if self.dm.fileExists(remote_path):
+        if self.dm.fileExists(remote_path) or self.dm.dirExists(remote_path):
             self.dm.copyTree(remote_path, '%s.orig' % remote_path)
             self.backup_files.add(remote_path)
         else:
             self.added_files.add(remote_path)
 
     def cleanup(self):
         """
         Cleanup the device.
@@ -232,27 +229,23 @@ class Device(object):
             return
 
         self.dm.remount()
         # Restore the original profile
         for added_file in self.added_files:
             self.dm.removeFile(added_file)
 
         for backup_file in self.backup_files:
-            if self.dm.fileExists('%s.orig' % backup_file):
+            if self.dm.fileExists('%s.orig' % backup_file) or self.dm.dirExists('%s.orig' % backup_file):
                 self.dm.moveTree('%s.orig' % backup_file, backup_file)
 
-        # Delete any bundled extensions
-        extension_dir = posixpath.join(self.app_ctx.remote_profile, 'extensions', 'staged')
-        if self.dm.dirExists(extension_dir):
-            for filename in self.dm.listFiles(extension_dir):
-                try:
-                    self.dm.removeDir(posixpath.join(self.app_ctx.remote_bundles_dir, filename))
-                except DMError:
-                    pass
+        # Perform application specific profile cleanup if necessary
+        if hasattr(self.app_ctx, 'cleanup_profile'):
+            self.app_ctx.cleanup_profile()
+
         # Remove the test profile
         self.dm.removeDir(self.app_ctx.remote_profile)
 
     def _rotate_log(self, srclog, index=1):
         """
         Rotate a logfile, by recursively rotating logs further in the sequence,
         deleting the last file if necessary.
         """
--- a/toolkit/devtools/Loader.jsm
+++ b/toolkit/devtools/Loader.jsm
@@ -91,16 +91,17 @@ BuiltinProvider.prototype = {
         "devtools/toolkit/webconsole": "resource://gre/modules/devtools/toolkit/webconsole",
         "devtools/app-actor-front": "resource://gre/modules/devtools/app-actor-front.js",
         "devtools/styleinspector/css-logic": "resource://gre/modules/devtools/styleinspector/css-logic",
         "devtools/css-color": "resource://gre/modules/devtools/css-color",
         "devtools/output-parser": "resource://gre/modules/devtools/output-parser",
         "devtools/touch-events": "resource://gre/modules/devtools/touch-events",
         "devtools/client": "resource://gre/modules/devtools/client",
         "devtools/pretty-fast": "resource://gre/modules/devtools/pretty-fast.js",
+        "devtools/jsbeautify": "resource://gre/modules/devtools/jsbeautify/beautify.js",
         "devtools/async-utils": "resource://gre/modules/devtools/async-utils",
         "devtools/content-observer": "resource://gre/modules/devtools/content-observer",
         "gcli": "resource://gre/modules/devtools/gcli",
         "projecteditor": "resource:///modules/devtools/projecteditor",
         "acorn": "resource://gre/modules/devtools/acorn",
         "acorn/util/walk": "resource://gre/modules/devtools/acorn/walk.js",
         "tern": "resource://gre/modules/devtools/tern",
         "source-map": "resource://gre/modules/devtools/SourceMap.jsm",
@@ -146,16 +147,17 @@ SrcdirProvider.prototype = {
     let webconsoleURI = this.fileURI(OS.Path.join(toolkitDir, "webconsole"));
     let appActorURI = this.fileURI(OS.Path.join(toolkitDir, "apps", "app-actor-front.js"));
     let cssLogicURI = this.fileURI(OS.Path.join(toolkitDir, "styleinspector", "css-logic"));
     let cssColorURI = this.fileURI(OS.Path.join(toolkitDir, "css-color"));
     let outputParserURI = this.fileURI(OS.Path.join(toolkitDir, "output-parser"));
     let touchEventsURI = this.fileURI(OS.Path.join(toolkitDir, "touch-events"));
     let clientURI = this.fileURI(OS.Path.join(toolkitDir, "client"));
     let prettyFastURI = this.fileURI(OS.Path.join(toolkitDir), "pretty-fast.js");
+    let jsBeautifyURI = this.fileURI(OS.Path.join(toolkitDir, "jsbeautify", "beautify.js"));
     let asyncUtilsURI = this.fileURI(OS.Path.join(toolkitDir), "async-utils.js");
     let contentObserverURI = this.fileURI(OS.Path.join(toolkitDir), "content-observer.js");
     let gcliURI = this.fileURI(OS.Path.join(toolkitDir, "gcli", "source", "lib", "gcli"));
     let projecteditorURI = this.fileURI(OS.Path.join(devtoolsDir, "projecteditor"));
     let acornURI = this.fileURI(OS.Path.join(toolkitDir, "acorn"));
     let acornWalkURI = OS.Path.join(acornURI, "walk.js");
     let ternURI = OS.Path.join(toolkitDir, "tern");
     let sourceMapURI = this.fileURI(OS.Path.join(toolkitDir), "SourceMap.jsm");
@@ -171,16 +173,17 @@ SrcdirProvider.prototype = {
         "devtools/toolkit/webconsole": webconsoleURI,
         "devtools/app-actor-front": appActorURI,
         "devtools/styleinspector/css-logic": cssLogicURI,
         "devtools/css-color": cssColorURI,
         "devtools/output-parser": outputParserURI,
         "devtools/touch-events": touchEventsURI,
         "devtools/client": clientURI,
         "devtools/pretty-fast": prettyFastURI,
+        "devtools/jsbeautify": jsBeautifyURI,
         "devtools/async-utils": asyncUtilsURI,
         "devtools/content-observer": contentObserverURI,
         "gcli": gcliURI,
         "projecteditor": projecteditorURI,
         "acorn": acornURI,
         "acorn/util/walk": acornWalkURI,
         "tern": ternURI,
         "source-map": sourceMapURI,
--- a/toolkit/devtools/client/dbg-client.jsm
+++ b/toolkit/devtools/client/dbg-client.jsm
@@ -442,17 +442,17 @@ DebuggerClient.prototype = {
    * @param function aOnResponse
    *        Called with the response packet and a TabClient
    *        (which will be undefined on error).
    */
   attachTab: function (aTabActor, aOnResponse) {
     if (this._clients.has(aTabActor)) {
       let cachedTab = this._clients.get(aTabActor);
       let cachedResponse = {
-        cacheEnabled: cachedTab.cacheEnabled,
+        cacheDisabled: cachedTab.cacheDisabled,
         javascriptEnabled: cachedTab.javascriptEnabled,
         traits: cachedTab.traits,
       };
       setTimeout(() => aOnResponse(cachedResponse, cachedTab), 0);
       return;
     }
 
     let packet = {
@@ -1251,17 +1251,17 @@ const FeatureCompatibilityShim = {
  * @param aForm object
  *        The protocol form for this tab.
  */
 function TabClient(aClient, aForm) {
   this.client = aClient;
   this._actor = aForm.from;
   this._threadActor = aForm.threadActor;
   this.javascriptEnabled = aForm.javascriptEnabled;
-  this.cacheEnabled = aForm.cacheEnabled;
+  this.cacheDisabled = aForm.cacheDisabled;
   this.thread = null;
   this.request = this.client.request;
   this.traits = aForm.traits || {};
   this.events = [];
 }
 
 TabClient.prototype = {
   get actor() { return this._actor },
--- a/toolkit/devtools/gcli/commands/jsb.js
+++ b/toolkit/devtools/gcli/commands/jsb.js
@@ -2,19 +2,21 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { Cc, Ci, Cu } = require("chrome");
 const gcli = require("gcli/index");
 const XMLHttpRequest = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"];
+const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 
 loader.lazyImporter(this, "Preferences", "resource://gre/modules/Preferences.jsm");
-loader.lazyImporter(this, "js_beautify", "resource:///modules/devtools/Jsbeautify.jsm");
+
+devtools.lazyRequireGetter(this, "beautify", "devtools/jsbeautify");
 
 exports.items = [
   {
     name: "jsb",
     description: gcli.lookup("jsbDesc"),
     returnValue:"string",
     params: [
       {
@@ -111,17 +113,17 @@ exports.items = [
       let deferred = context.defer();
 
       xhr.onreadystatechange = function(aEvt) {
         if (xhr.readyState == 4) {
           if (xhr.status == 200 || xhr.status == 0) {
             let browserDoc = context.environment.chromeDocument;
             let browserWindow = browserDoc.defaultView;
             let gBrowser = browserWindow.gBrowser;
-            let result = js_beautify(xhr.responseText, opts);
+            let result = beautify.js(xhr.responseText, opts);
 
             browserWindow.Scratchpad.ScratchpadManager.openScratchpad({text: result});
 
             deferred.resolve();
           } else {
             deferred.resolve("Unable to load page to beautify: " + args.url + " " +
                              xhr.status + " " + xhr.statusText);
           }
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/jsbeautify/UPGRADING.md
@@ -0,0 +1,37 @@
+# UPGRADING
+
+1. `git clone https://github.com/beautify-web/js-beautify.git`
+
+2. Copy `js/lib/beautify.js` to `toolkit/devtools/jsbeautify/src/beautify-js.js`
+
+3. Remove the acorn section from the file and add the following to the top:
+
+ ```
+ const acorn = require("acorn/acorn");
+ ```
+
+4. Just above `function Beautifier(js_source_text, options) {` add:
+
+ ```
+ exports.jsBeautify = js_beautify;
+ ```
+
+5. Copy `beautify-html.js` to `toolkit/devtools/jsbeautify/src/beautify-html.js`
+
+6. Replace the require blocks at the bottom of the file with:
+
+ ```
+ var beautify = require('devtools/jsbeautify');
+
+ exports.htmlBeautify = function(html_source, options) {
+    return style_html(html_source, options, beautify.js, beautify.css);
+ };
+ ```
+
+7. Copy `beautify-css.js` to `toolkit/devtools/jsbeautify/src/beautify-css.js`
+
+8. Replace the global define block at the bottom of the file with:
+ ```
+ exports.cssBeautify = css_beautify;
+ ```
+9. Copy `js/test/beautify-tests.js` to `toolkit/devtools/jsbeautify/src/beautify-tests.js`
copy from browser/devtools/shared/Jsbeautify.jsm
copy to toolkit/devtools/jsbeautify/beautify.js
--- a/browser/devtools/shared/Jsbeautify.jsm
+++ b/toolkit/devtools/jsbeautify/beautify.js
@@ -1,1303 +1,7 @@
-/*jslint onevar: false, plusplus: false */
-/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
-/*
-
- JS Beautifier
----------------
-
-
-  Written by Einar Lielmanis, <einar@jsbeautifier.org>
-      http://jsbeautifier.org/
-
-  Originally converted to javascript by Vital, <vital76@gmail.com>
-  "End braces on own line" added by Chris J. Shull, <chrisjshull@gmail.com>
-
-  You are free to use this in any way you want, in case you find this useful or working for you.
-
-  Usage:
-    js_beautify(js_source_text);
-    js_beautify(js_source_text, options);
-
-  The options are:
-    indent_size (default 4)          - indentation size,
-    indent_char (default space)      - character to indent with,
-    preserve_newlines (default true) - whether existing line breaks should be preserved,
-    max_preserve_newlines (default unlimited) - maximum number of line breaks to be preserved in one chunk,
-
-    jslint_happy (default false) - if true, then jslint-stricter mode is enforced.
-
-            jslint_happy   !jslint_happy
-            ---------------------------------
-             function ()      function()
-
-    brace_style (default "collapse") - "collapse" | "expand" | "end-expand" | "expand-strict"
-            put braces on the same line as control statements (default), or put braces on own line (Allman / ANSI style), or just put end braces on own line.
-
-            expand-strict: put brace on own line even in such cases:
-
-                var a =
-                {
-                    a: 5,
-                    b: 6
-                }
-            This mode may break your scripts - e.g "return { a: 1 }" will be broken into two lines, so beware.
-
-    space_before_conditional (default true) - should the space before conditional statement be added, "if(true)" vs "if (true)",
-
-    unescape_strings (default false) - should printable characters in strings encoded in \xNN notation be unescaped, "example" vs "\x65\x78\x61\x6d\x70\x6c\x65"
-
-    e.g
-
-    js_beautify(js_source_text, {
-      'indent_size': 1,
-      'indent_char': '\t'
-    });
-
-
-*/
-
-this.EXPORTED_SYMBOLS = ["js_beautify"];
-
-this.js_beautify = function js_beautify(js_source_text, options) {
-
-    var input, output, token_text, last_type, last_text, last_last_text, last_word, flags, flag_store, indent_string;
-    var whitespace, wordchar, punct, parser_pos, line_starters, digits;
-    var prefix, token_type, do_block_just_closed;
-    var wanted_newline, just_added_newline, n_newlines;
-    var preindent_string = '';
-
-
-    // Some interpreters have unexpected results with foo = baz || bar;
-    options = options ? options : {};
-
-    var opt_brace_style;
-
-    // compatibility
-    if (options.space_after_anon_function !== undefined && options.jslint_happy === undefined) {
-        options.jslint_happy = options.space_after_anon_function;
-    }
-    if (options.braces_on_own_line !== undefined) { //graceful handling of deprecated option
-        opt_brace_style = options.braces_on_own_line ? "expand" : "collapse";
-    }
-    opt_brace_style = options.brace_style ? options.brace_style : (opt_brace_style ? opt_brace_style : "collapse");
-
-
-    var opt_indent_size = options.indent_size ? options.indent_size : 4;
-    var opt_indent_char = options.indent_char ? options.indent_char : ' ';
-    var opt_preserve_newlines = typeof options.preserve_newlines === 'undefined' ? true : options.preserve_newlines;
-    var opt_max_preserve_newlines = typeof options.max_preserve_newlines === 'undefined' ? false : options.max_preserve_newlines;
-    var opt_jslint_happy = options.jslint_happy === 'undefined' ? false : options.jslint_happy;
-    var opt_keep_array_indentation = typeof options.keep_array_indentation === 'undefined' ? false : options.keep_array_indentation;
-    var opt_space_before_conditional = typeof options.space_before_conditional === 'undefined' ? true : options.space_before_conditional;
-    var opt_indent_case = typeof options.indent_case === 'undefined' ? false : options.indent_case;
-    var opt_unescape_strings = typeof options.unescape_strings === 'undefined' ? false : options.unescape_strings;
-
-    just_added_newline = false;
-
-    // cache the source's length.
-    var input_length = js_source_text.length;
-
-    function trim_output(eat_newlines) {
-        eat_newlines = typeof eat_newlines === 'undefined' ? false : eat_newlines;
-        while (output.length && (output[output.length - 1] === ' '
-            || output[output.length - 1] === indent_string
-            || output[output.length - 1] === preindent_string
-            || (eat_newlines && (output[output.length - 1] === '\n' || output[output.length - 1] === '\r')))) {
-            output.pop();
-        }
-    }
-
-    function trim(s) {
-        return s.replace(/^\s\s*|\s\s*$/, '');
-    }
-
-    // we could use just string.split, but
-    // IE doesn't like returning empty strings
-    function split_newlines(s) {
-        return s.split(/\x0d\x0a|\x0a/);
-    }
-
-    function force_newline() {
-        var old_keep_array_indentation = opt_keep_array_indentation;
-        opt_keep_array_indentation = false;
-        print_newline();
-        opt_keep_array_indentation = old_keep_array_indentation;
-    }
-
-    function print_newline(ignore_repeated) {
-
-        flags.eat_next_space = false;
-        if (opt_keep_array_indentation && is_array(flags.mode)) {
-            return;
-        }
-
-        ignore_repeated = typeof ignore_repeated === 'undefined' ? true : ignore_repeated;
-
-        flags.if_line = false;
-        trim_output();
-
-        if (!output.length) {
-            return; // no newline on start of file
-        }
-
-        if (output[output.length - 1] !== "\n" || !ignore_repeated) {
-            just_added_newline = true;
-            output.push("\n");
-        }
-        if (preindent_string) {
-            output.push(preindent_string);
-        }
-        for (var i = 0; i < flags.indentation_level; i += 1) {
-            output.push(indent_string);
-        }
-        if (flags.var_line && flags.var_line_reindented) {
-            output.push(indent_string); // skip space-stuffing, if indenting with a tab
-        }
-        if (flags.case_body) {
-            output.push(indent_string);
-        }
-    }
-
-
-
-    function print_single_space() {
-
-        if (last_type === 'TK_COMMENT') {
-            return print_newline();
-        }
-        if (flags.eat_next_space) {
-            flags.eat_next_space = false;
-            return;
-        }
-        var last_output = ' ';
-        if (output.length) {
-            last_output = output[output.length - 1];
-        }
-        if (last_output !== ' ' && last_output !== '\n' && last_output !== indent_string) { // prevent occassional duplicate space
-            output.push(' ');
-        }
-    }
-
-
-    function print_token() {
-        just_added_newline = false;
-        flags.eat_next_space = false;
-        output.push(token_text);
-    }
-
-    function indent() {
-        flags.indentation_level += 1;
-    }
-
-
-    function remove_indent() {
-        if (output.length && output[output.length - 1] === indent_string) {
-            output.pop();
-        }
-    }
-
-    function set_mode(mode) {
-        if (flags) {
-            flag_store.push(flags);
-        }
-        flags = {
-            previous_mode: flags ? flags.mode : 'BLOCK',
-            mode: mode,
-            var_line: false,
-            var_line_tainted: false,
-            var_line_reindented: false,
-            in_html_comment: false,
-            if_line: false,
-            in_case_statement: false, // switch(..){ INSIDE HERE }
-            in_case: false, // we're on the exact line with "case 0:"
-            case_body: false, // the indented case-action block
-            eat_next_space: false,
-            indentation_baseline: -1,
-            indentation_level: (flags ? flags.indentation_level + (flags.case_body ? 1 : 0) + ((flags.var_line && flags.var_line_reindented) ? 1 : 0) : 0),
-            ternary_depth: 0
-        };
-    }
-
-    function is_array(mode) {
-        return mode === '[EXPRESSION]' || mode === '[INDENTED-EXPRESSION]';
-    }
-
-    function is_expression(mode) {
-        return in_array(mode, ['[EXPRESSION]', '(EXPRESSION)', '(FOR-EXPRESSION)', '(COND-EXPRESSION)']);
-    }
-
-    function restore_mode() {
-        do_block_just_closed = flags.mode === 'DO_BLOCK';
-        if (flag_store.length > 0) {
-            var mode = flags.mode;
-            flags = flag_store.pop();
-            flags.previous_mode = mode;
-        }
-    }
-
-    function all_lines_start_with(lines, c) {
-        for (var i = 0; i < lines.length; i++) {
-            var line = trim(lines[i]);
-            if (line.charAt(0) !== c) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    function is_special_word(word) {
-        return in_array(word, ['case', 'return', 'do', 'if', 'throw', 'else']);
-    }
-
-    function in_array(what, arr) {
-        for (var i = 0; i < arr.length; i += 1) {
-            if (arr[i] === what) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    function look_up(exclude) {
-        var local_pos = parser_pos;
-        var c = input.charAt(local_pos);
-        while (in_array(c, whitespace) && c !== exclude) {
-            local_pos++;
-            if (local_pos >= input_length) {
-                return 0;
-            }
-            c = input.charAt(local_pos);
-        }
-        return c;
-    }
-
-    function get_next_token() {
-        var i;
-        var resulting_string;
-
-        n_newlines = 0;
-
-        if (parser_pos >= input_length) {
-            return ['', 'TK_EOF'];
-        }
-
-        wanted_newline = false;
-
-        var c = input.charAt(parser_pos);
-        parser_pos += 1;
-
-
-        var keep_whitespace = opt_keep_array_indentation && is_array(flags.mode);
-
-        if (keep_whitespace) {
-
-            //
-            // slight mess to allow nice preservation of array indentation and reindent that correctly
-            // first time when we get to the arrays:
-            // var a = [
-            // ....'something'
-            // we make note of whitespace_count = 4 into flags.indentation_baseline
-            // so we know that 4 whitespaces in original source match indent_level of reindented source
-            //
-            // and afterwards, when we get to
-            //    'something,
-            // .......'something else'
-            // we know that this should be indented to indent_level + (7 - indentation_baseline) spaces
-            //
-            var whitespace_count = 0;
-
-            while (in_array(c, whitespace)) {
-
-                if (c === "\n") {
-                    trim_output();
-                    output.push("\n");
-                    just_added_newline = true;
-                    whitespace_count = 0;
-                } else {
-                    if (c === '\t') {
-                        whitespace_count += 4;
-                    } else if (c === '\r') {
-                        // nothing
-                    } else {
-                        whitespace_count += 1;
-                    }
-                }
-
-                if (parser_pos >= input_length) {
-                    return ['', 'TK_EOF'];
-                }
-
-                c = input.charAt(parser_pos);
-                parser_pos += 1;
-
-            }
-            if (flags.indentation_baseline === -1) {
-                flags.indentation_baseline = whitespace_count;
-            }
-
-            if (just_added_newline) {
-                for (i = 0; i < flags.indentation_level + 1; i += 1) {
-                    output.push(indent_string);
-                }
-                if (flags.indentation_baseline !== -1) {
-                    for (i = 0; i < whitespace_count - flags.indentation_baseline; i++) {
-                        output.push(' ');
-                    }
-                }
-            }
-
-        } else {
-            while (in_array(c, whitespace)) {
-
-                if (c === "\n") {
-                    n_newlines += ((opt_max_preserve_newlines) ? (n_newlines <= opt_max_preserve_newlines) ? 1 : 0 : 1);
-                }
-
-
-                if (parser_pos >= input_length) {
-                    return ['', 'TK_EOF'];
-                }
-
-                c = input.charAt(parser_pos);
-                parser_pos += 1;
-
-            }
-
-            if (opt_preserve_newlines) {
-                if (n_newlines > 1) {
-                    for (i = 0; i < n_newlines; i += 1) {
-                        print_newline(i === 0);
-                        just_added_newline = true;
-                    }
-                }
-            }
-            wanted_newline = n_newlines > 0;
-        }
-
-
-        if (in_array(c, wordchar)) {
-            if (parser_pos < input_length) {
-                while (in_array(input.charAt(parser_pos), wordchar)) {
-                    c += input.charAt(parser_pos);
-                    parser_pos += 1;
-                    if (parser_pos === input_length) {
-                        break;
-                    }
-                }
-            }
-
-            // small and surprisingly unugly hack for 1E-10 representation
-            if (parser_pos !== input_length && c.match(/^[0-9]+[Ee]$/) && (input.charAt(parser_pos) === '-' || input.charAt(parser_pos) === '+')) {
-
-                var sign = input.charAt(parser_pos);
-                parser_pos += 1;
-
-                var t = get_next_token();
-                c += sign + t[0];
-                return [c, 'TK_WORD'];
-            }
-
-            if (c === 'in') { // hack for 'in' operator
-                return [c, 'TK_OPERATOR'];
-            }
-            if (wanted_newline && last_type !== 'TK_OPERATOR'
-                && last_type !== 'TK_EQUALS'
-                && !flags.if_line && (opt_preserve_newlines || last_text !== 'var')) {
-                print_newline();
-            }
-            return [c, 'TK_WORD'];
-        }
-
-        if (c === '(' || c === '[') {
-            return [c, 'TK_START_EXPR'];
-        }
-
-        if (c === ')' || c === ']') {
-            return [c, 'TK_END_EXPR'];
-        }
-
-        if (c === '{') {
-            return [c, 'TK_START_BLOCK'];
-        }
-
-        if (c === '}') {
-            return [c, 'TK_END_BLOCK'];
-        }
-
-        if (c === ';') {
-            return [c, 'TK_SEMICOLON'];
-        }
-
-        if (c === '/') {
-            var comment = '';
-            // peek for comment /* ... */
-            var inline_comment = true;
-            if (input.charAt(parser_pos) === '*') {
-                parser_pos += 1;
-                if (parser_pos < input_length) {
-                    while (parser_pos < input_length &&
-                        ! (input.charAt(parser_pos) === '*' && input.charAt(parser_pos + 1) && input.charAt(parser_pos + 1) === '/')) {
-                        c = input.charAt(parser_pos);
-                        comment += c;
-                        if (c === "\n" || c === "\r") {
-                            inline_comment = false;
-                        }
-                        parser_pos += 1;
-                        if (parser_pos >= input_length) {
-                            break;
-                        }
-                    }
-                }
-                parser_pos += 2;
-                if (inline_comment && n_newlines === 0) {
-                    return ['/*' + comment + '*/', 'TK_INLINE_COMMENT'];
-                } else {
-                    return ['/*' + comment + '*/', 'TK_BLOCK_COMMENT'];
-                }
-            }
-            // peek for comment // ...
-            if (input.charAt(parser_pos) === '/') {
-                comment = c;
-                while (input.charAt(parser_pos) !== '\r' && input.charAt(parser_pos) !== '\n') {
-                    comment += input.charAt(parser_pos);
-                    parser_pos += 1;
-                    if (parser_pos >= input_length) {
-                        break;
-                    }
-                }
-                if (wanted_newline) {
-                    print_newline();
-                }
-                return [comment, 'TK_COMMENT'];
-            }
-
-        }
-
-        if (c === "'" || // string
-        c === '"' || // string
-        (c === '/' &&
-            ((last_type === 'TK_WORD' && is_special_word(last_text)) ||
-                (last_text === ')' && in_array(flags.previous_mode, ['(COND-EXPRESSION)', '(FOR-EXPRESSION)'])) ||
-                (last_type === 'TK_COMMA' || last_type === 'TK_COMMENT' || last_type === 'TK_START_EXPR' || last_type === 'TK_START_BLOCK' || last_type === 'TK_END_BLOCK' || last_type === 'TK_OPERATOR' || last_type === 'TK_EQUALS' || last_type === 'TK_EOF' || last_type === 'TK_SEMICOLON')))) { // regexp
-            var sep = c;
-            var esc = false;
-            var esc1 = 0;
-            var esc2 = 0;
-            resulting_string = c;
-
-            if (parser_pos < input_length) {
-                if (sep === '/') {
-                    //
-                    // handle regexp separately...
-                    //
-                    var in_char_class = false;
-                    while (esc || in_char_class || input.charAt(parser_pos) !== sep) {
-                        resulting_string += input.charAt(parser_pos);
-                        if (!esc) {
-                            esc = input.charAt(parser_pos) === '\\';
-                            if (input.charAt(parser_pos) === '[') {
-                                in_char_class = true;
-                            } else if (input.charAt(parser_pos) === ']') {
-                                in_char_class = false;
-                            }
-                        } else {
-                            esc = false;
-                        }
-                        parser_pos += 1;
-                        if (parser_pos >= input_length) {
-                            // incomplete string/rexp when end-of-file reached.
-                            // bail out with what had been received so far.
-                            return [resulting_string, 'TK_STRING'];
-                        }
-                    }
-
-                } else {
-                    //
-                    // and handle string also separately
-                    //
-                    while (esc || input.charAt(parser_pos) !== sep) {
-                        resulting_string += input.charAt(parser_pos);
-                        if (esc1 && esc1 >= esc2) {
-                            esc1 = parseInt(resulting_string.substr(-esc2), 16);
-                            if (esc1 && esc1 >= 0x20 && esc1 <= 0x7e) {
-                                esc1 = String.fromCharCode(esc1);
-                                resulting_string = resulting_string.substr(0, resulting_string.length - esc2 - 2) + (((esc1 === sep) || (esc1 === '\\')) ? '\\' : '') + esc1;
-                            }
-                            esc1 = 0;
-                        }
-                        if (esc1) {
-                            esc1++;
-                        } else if (!esc) {
-                            esc = input.charAt(parser_pos) === '\\';
-                        } else {
-                            esc = false;
-                            if (opt_unescape_strings) {
-                                if (input.charAt(parser_pos) === 'x') {
-                                    esc1++;
-                                    esc2 = 2;
-                                } else if (input.charAt(parser_pos) === 'u') {
-                                    esc1++;
-                                    esc2 = 4;
-                                }
-                            }
-                        }
-                        parser_pos += 1;
-                        if (parser_pos >= input_length) {
-                            // incomplete string/rexp when end-of-file reached.
-                            // bail out with what had been received so far.
-                            return [resulting_string, 'TK_STRING'];
-                        }
-                    }
-                }
-
-
-
-            }
-
-            parser_pos += 1;
-
-            resulting_string += sep;
-
-            if (sep === '/') {
-                // regexps may have modifiers /regexp/MOD , so fetch those, too
-                while (parser_pos < input_length && in_array(input.charAt(parser_pos), wordchar)) {
-                    resulting_string += input.charAt(parser_pos);
-                    parser_pos += 1;
-                }
-            }
-            return [resulting_string, 'TK_STRING'];
-        }
-
-        if (c === '#') {
-
-
-            if (output.length === 0 && input.charAt(parser_pos) === '!') {
-                // shebang
-                resulting_string = c;
-                while (parser_pos < input_length && c !== '\n') {
-                    c = input.charAt(parser_pos);
-                    resulting_string += c;
-                    parser_pos += 1;
-                }
-                output.push(trim(resulting_string) + '\n');
-                print_newline();
-                return get_next_token();
-            }
-
-
-
-            // Spidermonkey-specific sharp variables for circular references
-            // https://developer.mozilla.org/En/Sharp_variables_in_JavaScript
-            // http://mxr.mozilla.org/mozilla-central/source/js/src/jsscan.cpp around line 1935
-            var sharp = '#';
-            if (parser_pos < input_length && in_array(input.charAt(parser_pos), digits)) {
-                do {
-                    c = input.charAt(parser_pos);
-                    sharp += c;
-                    parser_pos += 1;
-                } while (parser_pos < input_length && c !== '#' && c !== '=');
-                if (c === '#') {
-                    //
-                } else if (input.charAt(parser_pos) === '[' && input.charAt(parser_pos + 1) === ']') {
-                    sharp += '[]';
-                    parser_pos += 2;
-                } else if (input.charAt(parser_pos) === '{' && input.charAt(parser_pos + 1) === '}') {
-                    sharp += '{}';
-                    parser_pos += 2;
-                }
-                return [sharp, 'TK_WORD'];
-            }
-        }
-
-        if (c === '<' && input.substring(parser_pos - 1, parser_pos + 3) === '<!--') {
-            parser_pos += 3;
-            c = '<!--';
-            while (input.charAt(parser_pos) !== '\n' && parser_pos < input_length) {
-                c += input.charAt(parser_pos);
-                parser_pos++;
-            }
-            flags.in_html_comment = true;
-            return [c, 'TK_COMMENT'];
-        }
-
-        if (c === '-' && flags.in_html_comment && input.substring(parser_pos - 1, parser_pos + 2) === '-->') {
-            flags.in_html_comment = false;
-            parser_pos += 2;
-            if (wanted_newline) {
-                print_newline();
-            }
-            return ['-->', 'TK_COMMENT'];
-        }
-
-        if (in_array(c, punct)) {
-            while (parser_pos < input_length && in_array(c + input.charAt(parser_pos), punct)) {
-                c += input.charAt(parser_pos);
-                parser_pos += 1;
-                if (parser_pos >= input_length) {
-                    break;
-                }
-            }
-
-            if (c === ',') {
-                return [c, 'TK_COMMA'];
-            } else if (c === '=') {
-                return [c, 'TK_EQUALS'];
-            } else {
-                return [c, 'TK_OPERATOR'];
-            }
-        }
-
-        return [c, 'TK_UNKNOWN'];
-    }
+var { cssBeautify } = require("devtools/toolkit/jsbeautify/beautify-css");
+var { htmlBeautify } = require("devtools/toolkit/jsbeautify/beautify-html");
+var { jsBeautify } = require("devtools/toolkit/jsbeautify/beautify-js");
 
-    //----------------------------------
-    indent_string = '';
-    while (opt_indent_size > 0) {
-        indent_string += opt_indent_char;
-        opt_indent_size -= 1;
-    }
-
-    while (js_source_text && (js_source_text.charAt(0) === ' ' || js_source_text.charAt(0) === '\t')) {
-        preindent_string += js_source_text.charAt(0);
-        js_source_text = js_source_text.substring(1);
-    }
-    input = js_source_text;
-
-    last_word = ''; // last 'TK_WORD' passed
-    last_type = 'TK_START_EXPR'; // last token type
-    last_text = ''; // last token text
-    last_last_text = ''; // pre-last token text
-    output = [];
-
-    do_block_just_closed = false;
-
-    whitespace = "\n\r\t ".split('');
-    wordchar = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$'.split('');
-    digits = '0123456789'.split('');
-
-    punct = '+ - * / % & ++ -- = += -= *= /= %= == === != !== > < >= <= >> << >>> >>>= >>= <<= && &= | || ! !! , : ? ^ ^= |= ::';
-    punct += ' <%= <% %> <?= <? ?>'; // try to be a good boy and try not to break the markup language identifiers
-    punct = punct.split(' ');
-
-    // words which should always start on new line.
-    line_starters = 'continue,try,throw,return,var,if,switch,case,default,for,while,break,function'.split(',');
-
-    // states showing if we are currently in expression (i.e. "if" case) - 'EXPRESSION', or in usual block (like, procedure), 'BLOCK'.
-    // some formatting depends on that.
-    flag_store = [];
-    set_mode('BLOCK');
-
-    parser_pos = 0;
-    while (true) {
-        var t = get_next_token();
-        token_text = t[0];
-        token_type = t[1];
-        if (token_type === 'TK_EOF') {
-            break;
-        }
-
-        switch (token_type) {
-
-        case 'TK_START_EXPR':
-
-            if (token_text === '[') {
-
-                if (last_type === 'TK_WORD' || last_text === ')') {
-                    // this is array index specifier, break immediately
-                    // a[x], fn()[x]
-                    if (in_array(last_text, line_starters)) {
-                        print_single_space();
-                    }
-                    set_mode('(EXPRESSION)');
-                    print_token();
-                    break;
-                }
-
-                if (flags.mode === '[EXPRESSION]' || flags.mode === '[INDENTED-EXPRESSION]') {
-                    if (last_last_text === ']' && last_text === ',') {
-                        // ], [ goes to new line
-                        if (flags.mode === '[EXPRESSION]') {
-                            flags.mode = '[INDENTED-EXPRESSION]';
-                            if (!opt_keep_array_indentation) {
-                                indent();
-                            }
-                        }
-                        set_mode('[EXPRESSION]');
-                        if (!opt_keep_array_indentation) {
-                            print_newline();
-                        }
-                    } else if (last_text === '[') {
-                        if (flags.mode === '[EXPRESSION]') {
-                            flags.mode = '[INDENTED-EXPRESSION]';
-                            if (!opt_keep_array_indentation) {
-                                indent();
-                            }
-                        }
-                        set_mode('[EXPRESSION]');
-
-                        if (!opt_keep_array_indentation) {
-                            print_newline();
-                        }
-                    } else {
-                        set_mode('[EXPRESSION]');
-                    }
-                } else {
-                    set_mode('[EXPRESSION]');
-                }
-
-
-
-            } else {
-                if (last_word === 'for') {
-                    set_mode('(FOR-EXPRESSION)');
-                } else if (in_array(last_word, ['if', 'while'])) {
-                    set_mode('(COND-EXPRESSION)');
-                } else {
-                    set_mode('(EXPRESSION)');
-                }
-            }
-
-            if (last_text === ';' || last_type === 'TK_START_BLOCK') {
-                print_newline();
-            } else if (last_type === 'TK_END_EXPR' || last_type === 'TK_START_EXPR' || last_type === 'TK_END_BLOCK' || last_text === '.') {
-                if (wanted_newline) {
-                    print_newline();
-                }
-                // do nothing on (( and )( and ][ and ]( and .(
-            } else if (last_type !== 'TK_WORD' && last_type !== 'TK_OPERATOR') {
-                print_single_space();
-            } else if (last_word === 'function' || last_word === 'typeof') {
-                // function() vs function ()
-                if (opt_jslint_happy) {
-                    print_single_space();
-                }
-            } else if (in_array(last_text, line_starters) || last_text === 'catch') {
-                if (opt_space_before_conditional) {
-                    print_single_space();
-                }
-            }
-            print_token();
-
-            break;
-
-        case 'TK_END_EXPR':
-            if (token_text === ']') {
-                if (opt_keep_array_indentation) {
-                    if (last_text === '}') {
-                        // trim_output();
-                        // print_newline(true);
-                        remove_indent();
-                        print_token();
-                        restore_mode();
-                        break;
-                    }
-                } else {
-                    if (flags.mode === '[INDENTED-EXPRESSION]') {
-                        if (last_text === ']') {
-                            restore_mode();
-                            print_newline();
-                            print_token();
-                            break;
-                        }
-                    }
-                }
-            }
-            restore_mode();
-            print_token();
-            break;
-
-        case 'TK_START_BLOCK':
-
-            if (last_word === 'do') {
-                set_mode('DO_BLOCK');
-            } else {
-                set_mode('BLOCK');
-            }
-            if (opt_brace_style === "expand" || opt_brace_style === "expand-strict") {
-                var empty_braces = false;
-                if (opt_brace_style === "expand-strict") {
-                    empty_braces = (look_up() === '}');
-                    if (!empty_braces) {
-                        print_newline(true);
-                    }
-                } else {
-                    if (last_type !== 'TK_OPERATOR') {
-                        if (last_text === '=' || (is_special_word(last_text) && last_text !== 'else')) {
-                            print_single_space();
-                        } else {
-                            print_newline(true);
-                        }
-                    }
-                }
-                print_token();
-                if (!empty_braces) {
-                    indent();
-                }
-            } else {
-                if (last_type !== 'TK_OPERATOR' && last_type !== 'TK_START_EXPR') {
-                    if (last_type === 'TK_START_BLOCK') {
-                        print_newline();
-                    } else {
-                        print_single_space();
-                    }
-                } else {
-                    // if TK_OPERATOR or TK_START_EXPR
-                    if (is_array(flags.previous_mode) && last_text === ',') {
-                        if (last_last_text === '}') {
-                            // }, { in array context
-                            print_single_space();
-                        } else {
-                            print_newline(); // [a, b, c, {
-                        }
-                    }
-                }
-                indent();
-                print_token();
-            }
-
-            break;
-
-        case 'TK_END_BLOCK':
-            restore_mode();
-            if (opt_brace_style === "expand" || opt_brace_style === "expand-strict") {
-                if (last_text !== '{') {
-                    print_newline();
-                }
-                print_token();
-            } else {
-                if (last_type === 'TK_START_BLOCK') {
-                    // nothing
-                    if (just_added_newline) {
-                        remove_indent();
-                    } else {
-                        // {}
-                        trim_output();
-                    }
-                } else {
-                    if (is_array(flags.mode) && opt_keep_array_indentation) {
-                        // we REALLY need a newline here, but newliner would skip that
-                        opt_keep_array_indentation = false;
-                        print_newline();
-                        opt_keep_array_indentation = true;
-
-                    } else {
-                        print_newline();
-                    }
-                }
-                print_token();
-            }
-            break;
-
-        case 'TK_WORD':
-
-            // no, it's not you. even I have problems understanding how this works
-            // and what does what.
-            if (do_block_just_closed) {
-                // do {} ## while ()
-                print_single_space();
-                print_token();
-                print_single_space();
-                do_block_just_closed = false;
-                break;
-            }
-
-            prefix = 'NONE';
-
-            if (token_text === 'function') {
-                if (flags.var_line && last_type !== 'TK_EQUALS' ) {
-                    flags.var_line_reindented = true;
-                }
-                if ((just_added_newline || last_text === ';') && last_text !== '{'
-                && last_type !== 'TK_BLOCK_COMMENT' && last_type !== 'TK_COMMENT') {
-                    // make sure there is a nice clean space of at least one blank line
-                    // before a new function definition
-                    n_newlines = just_added_newline ? n_newlines : 0;
-                    if (!opt_preserve_newlines) {
-                        n_newlines = 1;
-                    }
-
-                    for (var i = 0; i < 2 - n_newlines; i++) {
-                        print_newline(false);
-                    }
-                }
-                if (last_type === 'TK_WORD') {
-                    if (last_text === 'get' || last_text === 'set' || last_text === 'new' || last_text === 'return') {
-                        print_single_space();
-                    } else {
-                        print_newline();
-                    }
-                } else if (last_type === 'TK_OPERATOR' || last_text === '=') {
-                    // foo = function
-                    print_single_space();
-                } else if (is_expression(flags.mode)) {
-                        //ää print nothing
-                } else {
-                    print_newline();
-                }
-
-                print_token();
-                last_word = token_text;
-                break;
-            }
-
-            if (token_text === 'case' || (token_text === 'default' && flags.in_case_statement)) {
-                if (last_text === ':' || flags.case_body) {
-                    // switch cases following one another
-                    remove_indent();
-                } else {
-                    // case statement starts in the same line where switch
-                    if (!opt_indent_case) {
-                        flags.indentation_level--;
-                    }
-                    print_newline();
-                    if (!opt_indent_case) {
-                        flags.indentation_level++;
-                    }
-                }
-                print_token();
-                flags.in_case = true;
-                flags.in_case_statement = true;
-                flags.case_body = false;
-                break;
-            }
-
-            if (last_type === 'TK_END_BLOCK') {
-
-                if (!in_array(token_text.toLowerCase(), ['else', 'catch', 'finally'])) {
-                    prefix = 'NEWLINE';
-                } else {
-                    if (opt_brace_style === "expand" || opt_brace_style === "end-expand" || opt_brace_style === "expand-strict") {
-                        prefix = 'NEWLINE';
-                    } else {
-                        prefix = 'SPACE';
-                        print_single_space();
-                    }
-                }
-            } else if (last_type === 'TK_SEMICOLON' && (flags.mode === 'BLOCK' || flags.mode === 'DO_BLOCK')) {
-                prefix = 'NEWLINE';
-            } else if (last_type === 'TK_SEMICOLON' && is_expression(flags.mode)) {
-                prefix = 'SPACE';
-            } else if (last_type === 'TK_STRING') {
-                prefix = 'NEWLINE';
-            } else if (last_type === 'TK_WORD') {
-                if (last_text === 'else') {
-                    // eat newlines between ...else *** some_op...
-                    // won't preserve extra newlines in this place (if any), but don't care that much
-                    trim_output(true);
-                }
-                prefix = 'SPACE';
-            } else if (last_type === 'TK_START_BLOCK') {
-                prefix = 'NEWLINE';
-            } else if (last_type === 'TK_END_EXPR') {
-                print_single_space();
-                prefix = 'NEWLINE';
-            }
-
-            if (in_array(token_text, line_starters) && last_text !== ')') {
-                if (last_text === 'else') {
-                    prefix = 'SPACE';
-                } else {
-                    prefix = 'NEWLINE';
-                }
-
-            }
-
-            if (flags.if_line && last_type === 'TK_END_EXPR') {
-                flags.if_line = false;
-            }
-            if (in_array(token_text.toLowerCase(), ['else', 'catch', 'finally'])) {
-                if (last_type !== 'TK_END_BLOCK' || opt_brace_style === "expand" || opt_brace_style === "end-expand" || opt_brace_style === "expand-strict") {
-                    print_newline();
-                } else {
-                    trim_output(true);
-                    print_single_space();
-                }
-            } else if (prefix === 'NEWLINE') {
-                if (is_special_word(last_text)) {
-                    // no newline between 'return nnn'
-                    print_single_space();
-                } else if (last_type !== 'TK_END_EXPR') {
-                    if ((last_type !== 'TK_START_EXPR' || token_text !== 'var') && last_text !== ':') {
-                        // no need to force newline on 'var': for (var x = 0...)
-                        if (token_text === 'if' && last_word === 'else' && last_text !== '{') {
-                            // no newline for } else if {
-                            print_single_space();
-                        } else {
-                            flags.var_line = false;
-                            flags.var_line_reindented = false;
-                            print_newline();
-                        }
-                    }
-                } else if (in_array(token_text, line_starters) && last_text !== ')') {
-                    flags.var_line = false;
-                    flags.var_line_reindented = false;
-                    print_newline();
-                }
-            } else if (is_array(flags.mode) && last_text === ',' && last_last_text === '}') {
-                print_newline(); // }, in lists get a newline treatment
-            } else if (prefix === 'SPACE') {
-                print_single_space();
-            }
-            print_token();
-            last_word = token_text;
-
-            if (token_text === 'var') {
-                flags.var_line = true;
-                flags.var_line_reindented = false;
-                flags.var_line_tainted = false;
-            }
-
-            if (token_text === 'if') {
-                flags.if_line = true;
-            }
-            if (token_text === 'else') {
-                flags.if_line = false;
-            }
-
-            break;
-
-        case 'TK_SEMICOLON':
-
-            print_token();
-            flags.var_line = false;
-            flags.var_line_reindented = false;
-            if (flags.mode === 'OBJECT') {
-                // OBJECT mode is weird and doesn't get reset too well.
-                flags.mode = 'BLOCK';
-            }
-            break;
-
-        case 'TK_STRING':
-
-            if (last_type === 'TK_END_EXPR' && in_array(flags.previous_mode, ['(COND-EXPRESSION)', '(FOR-EXPRESSION)'])) {
-                print_single_space();
-            } else if (last_type === 'TK_COMMENT' || last_type === 'TK_STRING' || last_type === 'TK_START_BLOCK' || last_type === 'TK_END_BLOCK' || last_type === 'TK_SEMICOLON') {
-                print_newline();
-            } else if (last_type === 'TK_WORD') {
-                print_single_space();
-            }
-            print_token();
-            break;
-
-        case 'TK_EQUALS':
-            if (flags.var_line) {
-                // just got an '=' in a var-line, different formatting/line-breaking, etc will now be done
-                flags.var_line_tainted = true;
-            }
-            print_single_space();
-            print_token();
-            print_single_space();
-            break;
-
-        case 'TK_COMMA':
-            if (flags.var_line) {
-                if (is_expression(flags.mode) || last_type === 'TK_END_BLOCK' ) {
-                    // do not break on comma, for(var a = 1, b = 2)
-                    flags.var_line_tainted = false;
-                }
-                if (flags.var_line_tainted) {
-                    print_token();
-                    flags.var_line_reindented = true;
-                    flags.var_line_tainted = false;
-                    print_newline();
-                    break;
-                } else {
-                    flags.var_line_tainted = false;
-                }
-
-                print_token();
-                print_single_space();
-                break;
-            }
-
-            if (last_type === 'TK_COMMENT') {
-                print_newline();
-            }
-
-            if (last_type === 'TK_END_BLOCK' && flags.mode !== "(EXPRESSION)") {
-                print_token();
-                if (flags.mode === 'OBJECT' && last_text === '}') {
-                    print_newline();
-                } else {
-                    print_single_space();
-                }
-            } else {
-                if (flags.mode === 'OBJECT') {
-                    print_token();
-                    print_newline();
-                } else {
-                    // EXPR or DO_BLOCK
-                    print_token();
-                    print_single_space();
-                }
-            }
-            break;
-
-
-        case 'TK_OPERATOR':
-
-            var space_before = true;
-            var space_after = true;
-
-            if (is_special_word(last_text)) {
-                // "return" had a special handling in TK_WORD. Now we need to return the favor
-                print_single_space();
-                print_token();
-                break;
-            }
-
-            // hack for actionscript's import .*;
-            if (token_text === '*' && last_type === 'TK_UNKNOWN' && !last_last_text.match(/^\d+$/)) {
-                print_token();
-                break;
-            }
-
-            if (token_text === ':' && flags.in_case) {
-                if (opt_indent_case) {
-                    flags.case_body = true;
-                }
-                print_token(); // colon really asks for separate treatment
-                print_newline();
-                flags.in_case = false;
-                break;
-            }
-
-            if (token_text === '::') {
-                // no spaces around exotic namespacing syntax operator
-                print_token();
-                break;
-            }
-
-            if (in_array(token_text, ['--', '++', '!']) || (in_array(token_text, ['-', '+']) && (in_array(last_type, ['TK_START_BLOCK', 'TK_START_EXPR', 'TK_EQUALS', 'TK_OPERATOR']) || in_array(last_text, line_starters)))) {
-                // unary operators (and binary +/- pretending to be unary) special cases
-
-                space_before = false;
-                space_after = false;
-
-                if (last_text === ';' && is_expression(flags.mode)) {
-                    // for (;; ++i)
-                    //        ^^^
-                    space_before = true;
-                }
-                if (last_type === 'TK_WORD' && in_array(last_text, line_starters)) {
-                    space_before = true;
-                }
-
-                if (flags.mode === 'BLOCK' && (last_text === '{' || last_text === ';')) {
-                    // { foo; --i }
-                    // foo(); --bar;
-                    print_newline();
-                }
-            } else if (token_text === '.') {
-                // decimal digits or object.property
-                space_before = false;
-
-            } else if (token_text === ':') {
-                if (flags.ternary_depth === 0) {
-                    if (flags.mode === 'BLOCK') {
-                        flags.mode = 'OBJECT';
-                    }
-                    space_before = false;
-                } else {
-                    flags.ternary_depth -= 1;
-                }
-            } else if (token_text === '?') {
-                flags.ternary_depth += 1;
-            }
-            if (space_before) {
-                print_single_space();
-            }
-
-            print_token();
-
-            if (space_after) {
-                print_single_space();
-            }
-
-            break;
-
-        case 'TK_BLOCK_COMMENT':
-
-            var lines = split_newlines(token_text);
-            var j; // iterator for this case
-
-            if (all_lines_start_with(lines.slice(1), '*')) {
-                // javadoc: reformat and reindent
-                print_newline();
-                output.push(lines[0]);
-                for (j = 1; j < lines.length; j++) {
-                    print_newline();
-                    output.push(' ');
-                    output.push(trim(lines[j]));
-                }
-
-            } else {
-
-                // simple block comment: leave intact
-                if (lines.length > 1) {
-                    // multiline comment block starts with a new line
-                    print_newline();
-                } else {
-                    // single-line /* comment */ stays where it is
-                    if (last_type === 'TK_END_BLOCK') {
-                        print_newline();
-                    } else {
-                        print_single_space();
-                    }
-
-                }
-
-                for (j = 0; j < lines.length; j++) {
-                    output.push(lines[j]);
-                    output.push("\n");
-                }
-
-            }
-            if (look_up('\n') !== '\n') {
-                print_newline();
-            }
-            break;
-
-        case 'TK_INLINE_COMMENT':
-            print_single_space();
-            print_token();
-            if (is_expression(flags.mode)) {
-                print_single_space();
-            } else {
-                force_newline();
-            }
-            break;
-
-        case 'TK_COMMENT':
-
-            if (last_text === ',' && !wanted_newline) {
-                trim_output(true);
-            }
-            if (last_type !== 'TK_COMMENT') {
-                if (wanted_newline) {
-                    print_newline();
-                } else {
-                    print_single_space();
-                }
-            }
-            print_token();
-            print_newline();
-            break;
-
-        case 'TK_UNKNOWN':
-            if (is_special_word(last_text)) {
-                print_single_space();
-            }
-            print_token();
-            break;
-        }
-
-        last_last_text = last_text;
-        last_type = token_type;
-        last_text = token_text;
-    }
-
-    var sweet_code = preindent_string + output.join('').replace(/[\r\n ]+$/, '');
-    return sweet_code;
-
-}
+exports.css = cssBeautify;
+exports.html = htmlBeautify;
+exports.js = jsBeautify;
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/jsbeautify/lib/sanitytest.js
@@ -0,0 +1,137 @@
+//
+// simple testing interface
+// written by Einar Lielmanis, einar@jsbeautifier.org
+//
+// usage:
+//
+// var t = new SanityTest(function (x) { return x; }, 'my function');
+// t.expect('input', 'output');
+// t.expect('a', 'a');
+// output_somewhere(t.results()); // good for <pre>, html safe-ish
+// alert(t.results_raw());        // html unescaped
+
+
+function SanityTest (func, name_of_test) {
+
+    var test_func = func || function (x) {
+        return x;
+    };
+
+    var test_name = name_of_test || '';
+
+    var n_failed = 0;
+    var n_succeeded = 0;
+
+    this.failures = [];
+    this.successes = [];
+
+    this.test_function = function(func, name) {
+        test_func = func;
+        test_name = name || '';
+    };
+
+    this.get_exitcode = function() {
+        return n_succeeded === 0 || n_failed !== 0 ? 1 : 0;
+    };
+
+    this.expect = function(parameters, expected_value) {
+        // multi-parameter calls not supported (I don't need them now).
+        var result = test_func(parameters);
+        // proper array checking is a pain. i'll maybe do it later, compare strings representations instead
+        if ((result === expected_value) || (expected_value instanceof Array && result.join(', ') == expected_value.join(', '))) {
+            n_succeeded += 1;
+            this.successes.push([test_name, parameters, expected_value, result]);
+        } else {
+            n_failed += 1;
+            this.failures.push([test_name, parameters, expected_value, result]);
+        }
+    };
+
+
+    this.results_raw = function() {
+        var results = '';
+        if (n_failed === 0) {
+            if (n_succeeded === 0) {
+                results = 'No tests run.';
+            } else {
+                results = 'All ' + n_succeeded + ' tests passed.';
+            }
+        } else {
+            for (var i = 0 ; i < this.failures.length; i++) {
+                var f = this.failures[i];
+                if (f[0]) {
+                    f[0] = f[0] + ' ';
+                }
+                results += '---- ' + f[0] + 'input -------\n' + this.prettyprint(f[1]) + '\n';
+                results += '---- ' + f[0] + 'expected ----\n' + this.prettyprint(f[2]) + '\n';
+                results += '---- ' + f[0] + 'output ------\n' + this.prettyprint(f[3]) + '\n\n';
+
+            }
+            results += n_failed + ' tests failed.\n';
+        }
+        return results;
+    };
+
+
+    this.results = function() {
+        return this.lazy_escape(this.results_raw());
+    };
+
+
+    this.prettyprint = function(something, quote_strings) {
+        var type = typeof something;
+        switch(type.toLowerCase()) {
+        case 'string':
+            if (quote_strings) {
+                return "'" + something.replace("'", "\\'") + "'";
+            } else {
+                return something;
+            }
+        case 'number':
+            return '' + something;
+        case 'boolean':
+            return something ? 'true' : 'false';
+        case 'undefined':
+            return 'undefined';
+        case 'object':
+            if (something instanceof Array) {
+                var x = [];
+                var expected_index = 0;
+                for (var k in something) {
+                    if (k == expected_index) {
+                        x.push(this.prettyprint(something[k], true));
+                        expected_index += 1;
+                    } else {
+                        x.push('\n' + k + ': ' + this.prettyprint(something[k], true));
+                    }
+                }
+                return '[' + x.join(', ') + ']';
+            } else {
+                return 'object: ' + something;
+            }
+        default:
+            return type + ': ' + something;
+        }
+    };
+
+
+    this.lazy_escape = function (str) {
+        return str.replace(/</g, '&lt;').replace(/\>/g, '&gt;').replace(/\n/g, '<br />');
+    };
+
+
+    this.log = function () {
+        if (window.console) {
+            if (console.firebug) {
+                console.log.apply(console, Array.prototype.slice.call(arguments));
+            } else {
+                console.log.call(console, Array.prototype.slice.call(arguments));
+            }
+        }
+    };
+
+}
+
+if (typeof module !== 'undefined' && module.exports) {
+    module.exports = SanityTest;
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/jsbeautify/lib/urlencode_unpacker.js
@@ -0,0 +1,73 @@
+/*global unescape */
+/*jshint curly: false, scripturl: true */
+//
+// trivial bookmarklet/escaped script detector for the javascript beautifier
+// written by Einar Lielmanis <einar@jsbeautifier.org>
+//
+// usage:
+//
+// if (Urlencoded.detect(some_string)) {
+//     var unpacked = Urlencoded.unpack(some_string);
+// }
+//
+//
+
+var isNode = (typeof module !== 'undefined' && module.exports);
+if (isNode) {
+    var SanityTest = require("devtools/toolkit/jsbeautify/sanitytest");
+}
+
+var Urlencoded = {
+    detect: function (str) {
+        // the fact that script doesn't contain any space, but has %20 instead
+        // should be sufficient check for now.
+        if (str.indexOf(' ') == -1) {
+            if (str.indexOf('%2') != -1) return true;
+            if (str.replace(/[^%]+/g, '').length > 3) return true;
+        }
+        return false;
+    },
+
+    unpack: function (str) {
+        if (Urlencoded.detect(str)) {
+            if (str.indexOf('%2B') != -1 || str.indexOf('%2b') != -1) {
+                // "+" escaped as "%2B"
+                return unescape(str.replace(/\+/g, '%20'));
+            } else {
+                return unescape(str);
+            }
+        }
+        return str;
+    },
+
+
+
+    run_tests: function (sanity_test) {
+        var t = sanity_test || new SanityTest();
+        t.test_function(Urlencoded.detect, "Urlencoded.detect");
+        t.expect('', false);
+        t.expect('var a = b', false);
+        t.expect('var%20a+=+b', true);
+        t.expect('var%20a=b', true);
+        t.expect('var%20%21%22', true);
+        t.expect('javascript:(function(){var%20whatever={init:function(){alert(%22a%22+%22b%22)}};whatever.init()})();', true);
+        t.test_function(Urlencoded.unpack, 'Urlencoded.unpack');
+
+        t.expect('javascript:(function(){var%20whatever={init:function(){alert(%22a%22+%22b%22)}};whatever.init()})();',
+            'javascript:(function(){var whatever={init:function(){alert("a"+"b")}};whatever.init()})();'
+        );
+        t.expect('', '');
+        t.expect('abcd', 'abcd');
+        t.expect('var a = b', 'var a = b');
+        t.expect('var%20a=b', 'var a=b');
+        t.expect('var%20a=b+1', 'var a=b+1');
+        t.expect('var%20a=b%2b1', 'var a=b+1');
+        return t;
+    }
+
+
+};
+
+if (isNode) {
+    module.exports = Urlencoded;
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/jsbeautify/moz.build
@@ -0,0 +1,19 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
+
+JS_MODULES_PATH = 'modules/devtools/jsbeautify'
+
+EXTRA_JS_MODULES += [
+    'beautify.js',
+    'lib/sanitytest.js',
+    'lib/urlencode_unpacker.js',
+    'src/beautify-css.js',
+    'src/beautify-html.js',
+    'src/beautify-js.js',
+    'src/beautify-tests.js'
+]
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/jsbeautify/src/beautify-css.js
@@ -0,0 +1,367 @@
+/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
+/*
+
+  The MIT License (MIT)
+
+  Copyright (c) 2007-2013 Einar Lielmanis and contributors.
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation files
+  (the "Software"), to deal in the Software without restriction,
+  including without limitation the rights to use, copy, modify, merge,
+  publish, distribute, sublicense, and/or sell copies of the Software,
+  and to permit persons to whom the Software is furnished to do so,
+  subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+  SOFTWARE.
+
+
+ CSS Beautifier
+---------------
+
+    Written by Harutyun Amirjanyan, (amirjanyan@gmail.com)
+
+    Based on code initially developed by: Einar Lielmanis, <einar@jsbeautifier.org>
+        http://jsbeautifier.org/
+
+    Usage:
+        css_beautify(source_text);
+        css_beautify(source_text, options);
+
+    The options are (default in brackets):
+        indent_size (4)                   — indentation size,
+        indent_char (space)               — character to indent with,
+        selector_separator_newline (true) - separate selectors with newline or
+                                            not (e.g. "a,\nbr" or "a, br")
+        end_with_newline (false)          - end with a newline
+
+    e.g
+
+    css_beautify(css_source_text, {
+      'indent_size': 1,
+      'indent_char': '\t',
+      'selector_separator': ' ',
+      'end_with_newline': false,
+    });
+*/
+
+// http://www.w3.org/TR/CSS21/syndata.html#tokenization
+// http://www.w3.org/TR/css3-syntax/
+
+(function () {
+    function css_beautify(source_text, options) {
+        options = options || {};
+        var indentSize = options.indent_size || 4;
+        var indentCharacter = options.indent_char || ' ';
+        var selectorSeparatorNewline = (options.selector_separator_newline === undefined) ? true : options.selector_separator_newline;
+        var endWithNewline = (options.end_with_newline === undefined) ? false : options.end_with_newline;
+
+        // compatibility
+        if (typeof indentSize === "string") {
+            indentSize = parseInt(indentSize, 10);
+        }
+
+
+        // tokenizer
+        var whiteRe = /^\s+$/;
+        var wordRe = /[\w$\-_]/;
+
+        var pos = -1,
+            ch;
+
+        function next() {
+            ch = source_text.charAt(++pos);
+            return ch;
+        }
+
+        function peek() {
+            return source_text.charAt(pos + 1);
+        }
+
+        function eatString(endChar) {
+            var start = pos;
+            while (next()) {
+                if (ch === "\\") {
+                    next();
+                    next();
+                } else if (ch === endChar) {
+                    break;
+                } else if (ch === "\n") {
+                    break;
+                }
+            }
+            return source_text.substring(start, pos + 1);
+        }
+
+        function eatWhitespace() {
+            var start = pos;
+            while (whiteRe.test(peek())) {
+                pos++;
+            }
+            return pos !== start;
+        }
+
+        function skipWhitespace() {
+            var start = pos;
+            do {} while (whiteRe.test(next()));
+            return pos !== start + 1;
+        }
+
+        function eatComment(singleLine) {
+            var start = pos;
+            next();
+            while (next()) {
+                if (ch === "*" && peek() === "/") {
+                    pos++;
+                    break;
+                } else if (singleLine && ch === "\n") {
+                    break;
+                }
+            }
+
+            return source_text.substring(start, pos + 1);
+        }
+
+
+        function lookBack(str) {
+            return source_text.substring(pos - str.length, pos).toLowerCase() ===
+                str;
+        }
+
+        function isCommentOnLine() {
+            var endOfLine = source_text.indexOf('\n', pos);
+            if (endOfLine === -1) {
+                return false;
+            }
+            var restOfLine = source_text.substring(pos, endOfLine);
+            return restOfLine.indexOf('//') !== -1;
+        }
+
+        // printer
+        var indentString = source_text.match(/^[\r\n]*[\t ]*/)[0];
+        var singleIndent = new Array(indentSize + 1).join(indentCharacter);
+        var indentLevel = 0;
+        var nestedLevel = 0;
+
+        function indent() {
+            indentLevel++;
+            indentString += singleIndent;
+        }
+
+        function outdent() {
+            indentLevel--;
+            indentString = indentString.slice(0, -indentSize);
+        }
+
+        var print = {};
+        print["{"] = function (ch) {
+            print.singleSpace();
+            output.push(ch);
+            print.newLine();
+        };
+        print["}"] = function (ch) {
+            print.newLine();
+            output.push(ch);
+            print.newLine();
+        };
+
+        print._lastCharWhitespace = function () {
+            return whiteRe.test(output[output.length - 1]);
+        };
+
+        print.newLine = function (keepWhitespace) {
+            if (!keepWhitespace) {
+                while (print._lastCharWhitespace()) {
+                    output.pop();
+                }
+            }
+
+            if (output.length) {
+                output.push('\n');
+            }
+            if (indentString) {
+                output.push(indentString);
+            }
+        };
+        print.singleSpace = function () {
+            if (output.length && !print._lastCharWhitespace()) {
+                output.push(' ');
+            }
+        };
+        var output = [];
+        if (indentString) {
+            output.push(indentString);
+        }
+        /*_____________________--------------------_____________________*/
+
+        var insideRule = false;
+        var enteringConditionalGroup = false;
+
+        while (true) {
+            var isAfterSpace = skipWhitespace();
+
+            if (!ch) {
+                break;
+            } else if (ch === '/' && peek() === '*') { /* css comment */
+                print.newLine();
+                output.push(eatComment(), "\n", indentString);
+                var header = lookBack("");
+                if (header) {
+                    print.newLine();
+                }
+            } else if (ch === '/' && peek() === '/') { // single line comment
+                output.push(eatComment(true), indentString);
+            } else if (ch === '@') {
+                // strip trailing space, if present, for hash property checks
+                var atRule = eatString(" ").replace(/ $/, '');
+
+                // pass along the space we found as a separate item
+                output.push(atRule, ch);
+
+                // might be a nesting at-rule
+                if (atRule in css_beautify.NESTED_AT_RULE) {
+                    nestedLevel += 1;
+                    if (atRule in css_beautify.CONDITIONAL_GROUP_RULE) {
+                        enteringConditionalGroup = true;
+                    }
+                }
+            } else if (ch === '{') {
+                eatWhitespace();
+                if (peek() === '}') {
+                    next();
+                    output.push(" {}");
+                } else {
+                    indent();
+                    print["{"](ch);
+                    // when entering conditional groups, only rulesets are allowed
+                    if (enteringConditionalGroup) {
+                        enteringConditionalGroup = false;
+                        insideRule = (indentLevel > nestedLevel);
+                    } else {
+                        // otherwise, declarations are also allowed
+                        insideRule = (indentLevel >= nestedLevel);
+                    }
+                }
+            } else if (ch === '}') {
+                outdent();
+                print["}"](ch);
+                insideRule = false;
+                if (nestedLevel) {
+                    nestedLevel--;
+                }
+            } else if (ch === ":") {
+                eatWhitespace();
+                if (insideRule || enteringConditionalGroup) {
+                    // 'property: value' delimiter
+                    // which could be in a conditional group query
+                    output.push(ch, " ");
+                } else {
+                    if (peek() === ":") {
+                        // pseudo-element
+                        next();
+                        output.push("::");
+                    } else {
+                        // pseudo-class
+                        output.push(ch);
+                    }
+                }
+            } else if (ch === '"' || ch === '\'') {
+                output.push(eatString(ch));
+            } else if (ch === ';') {
+                if (isCommentOnLine()) {
+                    var beforeComment = eatString('/');
+                    var comment = eatComment(true);
+                    output.push(beforeComment, comment.substring(1, comment.length - 1), '\n', indentString);
+                } else {
+                    output.push(ch, '\n', indentString);
+                }
+            } else if (ch === '(') { // may be a url
+                if (lookBack("url")) {
+                    output.push(ch);
+                    eatWhitespace();
+                    if (next()) {
+                        if (ch !== ')' && ch !== '"' && ch !== '\'') {
+                            output.push(eatString(')'));
+                        } else {
+                            pos--;
+                        }
+                    }
+                } else {
+                    if (isAfterSpace) {
+                        print.singleSpace();
+                    }
+                    output.push(ch);
+                    eatWhitespace();
+                }
+            } else if (ch === ')') {
+                output.push(ch);
+            } else if (ch === ',') {
+                eatWhitespace();
+                output.push(ch);
+                if (!insideRule && selectorSeparatorNewline) {
+                    print.newLine();
+                } else {
+                    print.singleSpace();
+                }
+            } else if (ch === ']') {
+                output.push(ch);
+            } else if (ch === '[') {
+                if (isAfterSpace) {
+                    print.singleSpace();
+                }
+                output.push(ch);
+            } else if (ch === '=') { // no whitespace before or after
+                eatWhitespace();
+                output.push(ch);
+            } else {
+                if (isAfterSpace) {
+                    print.singleSpace();
+                }
+
+                output.push(ch);
+            }
+        }
+
+
+        var sweetCode = output.join('').replace(/[\n ]+$/, '');
+
+        // establish end_with_newline
+        var should = endWithNewline;
+        var actually = /\n$/.test(sweetCode);
+        if (should && !actually) {
+            sweetCode += "\n";
+        } else if (!should && actually) {
+            sweetCode = sweetCode.slice(0, -1);
+        }
+
+        return sweetCode;
+    }
+
+    // https://developer.mozilla.org/en-US/docs/Web/CSS/At-rule
+    css_beautify.NESTED_AT_RULE = {
+        "@page": true,
+        "@font-face": true,
+        "@keyframes": true,
+        // also in CONDITIONAL_GROUP_RULE below
+        "@media": true,
+        "@supports": true,
+        "@document": true
+    };
+    css_beautify.CONDITIONAL_GROUP_RULE = {
+        "@media": true,
+        "@supports": true,
+        "@document": true
+    };
+
+    exports.cssBeautify = css_beautify;
+}());
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/jsbeautify/src/beautify-html.js
@@ -0,0 +1,822 @@
+/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
+/*
+
+  The MIT License (MIT)
+
+  Copyright (c) 2007-2013 Einar Lielmanis and contributors.
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation files
+  (the "Software"), to deal in the Software without restriction,
+  including without limitation the rights to use, copy, modify, merge,
+  publish, distribute, sublicense, and/or sell copies of the Software,
+  and to permit persons to whom the Software is furnished to do so,
+  subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+  SOFTWARE.
+
+
+ Style HTML
+---------------
+
+  Written by Nochum Sossonko, (nsossonko@hotmail.com)
+
+  Based on code initially developed by: Einar Lielmanis, <einar@jsbeautifier.org>
+    http://jsbeautifier.org/
+
+  Usage:
+    style_html(html_source);
+
+    style_html(html_source, options);
+
+  The options are:
+    indent_inner_html (default false)  — indent <head> and <body> sections,
+    indent_size (default 4)          — indentation size,
+    indent_char (default space)      — character to indent with,
+    wrap_line_length (default 250)            -  maximum amount of characters per line (0 = disable)
+    brace_style (default "collapse") - "collapse" | "expand" | "end-expand"
+            put braces on the same line as control statements (default), or put braces on own line (Allman / ANSI style), or just put end braces on own line.
+    unformatted (defaults to inline tags) - list of tags, that shouldn't be reformatted
+    indent_scripts (default normal)  - "keep"|"separate"|"normal"
+    preserve_newlines (default true) - whether existing line breaks before elements should be preserved
+                                        Only works before elements, not inside tags or for text.
+    max_preserve_newlines (default unlimited) - maximum number of line breaks to be preserved in one chunk
+    indent_handlebars (default false) - format and indent {{#foo}} and {{/foo}}
+
+    e.g.
+
+    style_html(html_source, {
+      'indent_inner_html': false,
+      'indent_size': 2,
+      'indent_char': ' ',
+      'wrap_line_length': 78,
+      'brace_style': 'expand',
+      'unformatted': ['a', 'sub', 'sup', 'b', 'i', 'u'],
+      'preserve_newlines': true,
+      'max_preserve_newlines': 5,
+      'indent_handlebars': false
+    });
+*/
+
+(function() {
+
+    function trim(s) {
+        return s.replace(/^\s+|\s+$/g, '');
+    }
+
+    function ltrim(s) {
+        return s.replace(/^\s+/g, '');
+    }
+
+    function style_html(html_source, options, js_beautify, css_beautify) {
+        //Wrapper function to invoke all the necessary constructors and deal with the output.
+
+        var multi_parser,
+            indent_inner_html,
+            indent_size,
+            indent_character,
+            wrap_line_length,
+            brace_style,
+            unformatted,
+            preserve_newlines,
+            max_preserve_newlines,
+            indent_handlebars;
+
+        options = options || {};
+
+        // backwards compatibility to 1.3.4
+        if ((options.wrap_line_length === undefined || parseInt(options.wrap_line_length, 10) === 0) &&
+                (options.max_char !== undefined && parseInt(options.max_char, 10) !== 0)) {
+            options.wrap_line_length = options.max_char;
+        }
+
+        indent_inner_html = (options.indent_inner_html === undefined) ? false : options.indent_inner_html;
+        indent_size = (options.indent_size === undefined) ? 4 : parseInt(options.indent_size, 10);
+        indent_character = (options.indent_char === undefined) ? ' ' : options.indent_char;
+        brace_style = (options.brace_style === undefined) ? 'collapse' : options.brace_style;
+        wrap_line_length =  parseInt(options.wrap_line_length, 10) === 0 ? 32786 : parseInt(options.wrap_line_length || 250, 10);
+        unformatted = options.unformatted || ['a', 'span', 'bdo', 'em', 'strong', 'dfn', 'code', 'samp', 'kbd', 'var', 'cite', 'abbr', 'acronym', 'q', 'sub', 'sup', 'tt', 'i', 'b', 'big', 'small', 'u', 's', 'strike', 'font', 'ins', 'del', 'pre', 'address', 'dt', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
+        preserve_newlines = (options.preserve_newlines === undefined) ? true : options.preserve_newlines;
+        max_preserve_newlines = preserve_newlines ?
+            (isNaN(parseInt(options.max_preserve_newlines, 10)) ? 32786 : parseInt(options.max_preserve_newlines, 10))
+            : 0;
+        indent_handlebars = (options.indent_handlebars === undefined) ? false : options.indent_handlebars;
+
+        function Parser() {
+
+            this.pos = 0; //Parser position
+            this.token = '';
+            this.current_mode = 'CONTENT'; //reflects the current Parser mode: TAG/CONTENT
+            this.tags = { //An object to hold tags, their position, and their parent-tags, initiated with default values
+                parent: 'parent1',
+                parentcount: 1,
+                parent1: ''
+            };
+            this.tag_type = '';
+            this.token_text = this.last_token = this.last_text = this.token_type = '';
+            this.newlines = 0;
+            this.indent_content = indent_inner_html;
+
+            this.Utils = { //Uilities made available to the various functions
+                whitespace: "\n\r\t ".split(''),
+                single_token: 'br,input,link,meta,!doctype,basefont,base,area,hr,wbr,param,img,isindex,?xml,embed,?php,?,?='.split(','), //all the single tags for HTML
+                extra_liners: 'head,body,/html'.split(','), //for tags that need a line of whitespace before them
+                in_array: function(what, arr) {
+                    for (var i = 0; i < arr.length; i++) {
+                        if (what === arr[i]) {
+                            return true;
+                        }
+                    }
+                    return false;
+                }
+            };
+
+            this.traverse_whitespace = function() {
+                var input_char = '';
+
+                input_char = this.input.charAt(this.pos);
+                if (this.Utils.in_array(input_char, this.Utils.whitespace)) {
+                    this.newlines = 0;
+                    while (this.Utils.in_array(input_char, this.Utils.whitespace)) {
+                        if (preserve_newlines && input_char === '\n' && this.newlines <= max_preserve_newlines) {
+                            this.newlines += 1;
+                        }
+
+                        this.pos++;
+                        input_char = this.input.charAt(this.pos);
+                    }
+                    return true;
+                }
+                return false;
+            };
+
+            this.get_content = function() { //function to capture regular content between tags
+
+                var input_char = '',
+                    content = [],
+                    space = false; //if a space is needed
+
+                while (this.input.charAt(this.pos) !== '<') {
+                    if (this.pos >= this.input.length) {
+                        return content.length ? content.join('') : ['', 'TK_EOF'];
+                    }
+
+                    if (this.traverse_whitespace()) {
+                        if (content.length) {
+                            space = true;
+                        }
+                        continue; //don't want to insert unnecessary space
+                    }
+
+                    if (indent_handlebars) {
+                        // Handlebars parsing is complicated.
+                        // {{#foo}} and {{/foo}} are formatted tags.
+                        // {{something}} should get treated as content, except:
+                        // {{else}} specifically behaves like {{#if}} and {{/if}}
+                        var peek3 = this.input.substr(this.pos, 3);
+                        if (peek3 === '{{#' || peek3 === '{{/') {
+                            // These are tags and not content.
+                            break;
+                        } else if (this.input.substr(this.pos, 2) === '{{') {
+                            if (this.get_tag(true) === '{{else}}') {
+                                break;
+                            }
+                        }
+                    }
+
+                    input_char = this.input.charAt(this.pos);
+                    this.pos++;
+
+                    if (space) {
+                        if (this.line_char_count >= this.wrap_line_length) { //insert a line when the wrap_line_length is reached
+                            this.print_newline(false, content);
+                            this.print_indentation(content);
+                        } else {
+                            this.line_char_count++;
+                            content.push(' ');
+                        }
+                        space = false;
+                    }
+                    this.line_char_count++;
+                    content.push(input_char); //letter at-a-time (or string) inserted to an array
+                }
+                return content.length ? content.join('') : '';
+            };
+
+            this.get_contents_to = function(name) { //get the full content of a script or style to pass to js_beautify
+                if (this.pos === this.input.length) {
+                    return ['', 'TK_EOF'];
+                }
+                var input_char = '';
+                var content = '';
+                var reg_match = new RegExp('</' + name + '\\s*>', 'igm');
+                reg_match.lastIndex = this.pos;
+                var reg_array = reg_match.exec(this.input);
+                var end_script = reg_array ? reg_array.index : this.input.length; //absolute end of script
+                if (this.pos < end_script) { //get everything in between the script tags
+                    content = this.input.substring(this.pos, end_script);
+                    this.pos = end_script;
+                }
+                return content;
+            };
+
+            this.record_tag = function(tag) { //function to record a tag and its parent in this.tags Object
+                if (this.tags[tag + 'count']) { //check for the existence of this tag type
+                    this.tags[tag + 'count']++;
+                    this.tags[tag + this.tags[tag + 'count']] = this.indent_level; //and record the present indent level
+                } else { //otherwise initialize this tag type
+                    this.tags[tag + 'count'] = 1;
+                    this.tags[tag + this.tags[tag + 'count']] = this.indent_level; //and record the present indent level
+                }
+                this.tags[tag + this.tags[tag + 'count'] + 'parent'] = this.tags.parent; //set the parent (i.e. in the case of a div this.tags.div1parent)
+                this.tags.parent = tag + this.tags[tag + 'count']; //and make this the current parent (i.e. in the case of a div 'div1')
+            };
+
+            this.retrieve_tag = function(tag) { //function to retrieve the opening tag to the corresponding closer
+                if (this.tags[tag + 'count']) { //if the openener is not in the Object we ignore it
+                    var temp_parent = this.tags.parent; //check to see if it's a closable tag.
+                    while (temp_parent) { //till we reach '' (the initial value);
+                        if (tag + this.tags[tag + 'count'] === temp_parent) { //if this is it use it
+                            break;
+                        }
+                        temp_parent = this.tags[temp_parent + 'parent']; //otherwise keep on climbing up the DOM Tree
+                    }
+                    if (temp_parent) { //if we caught something
+                        this.indent_level = this.tags[tag + this.tags[tag + 'count']]; //set the indent_level accordingly
+                        this.tags.parent = this.tags[temp_parent + 'parent']; //and set the current parent
+                    }
+                    delete this.tags[tag + this.tags[tag + 'count'] + 'parent']; //delete the closed tags parent reference...
+                    delete this.tags[tag + this.tags[tag + 'count']]; //...and the tag itself
+                    if (this.tags[tag + 'count'] === 1) {
+                        delete this.tags[tag + 'count'];
+                    } else {
+                        this.tags[tag + 'count']--;
+                    }
+                }
+            };
+
+            this.indent_to_tag = function(tag) {
+                // Match the indentation level to the last use of this tag, but don't remove it.
+                if (!this.tags[tag + 'count']) {
+                    return;
+                }
+                var temp_parent = this.tags.parent;
+                while (temp_parent) {
+                    if (tag + this.tags[tag + 'count'] === temp_parent) {
+                        break;
+                    }
+                    temp_parent = this.tags[temp_parent + 'parent'];
+                }
+                if (temp_parent) {
+                    this.indent_level = this.tags[tag + this.tags[tag + 'count']];
+                }
+            };
+
+            this.get_tag = function(peek) { //function to get a full tag and parse its type
+                var input_char = '',
+                    content = [],
+                    comment = '',
+                    space = false,
+                    tag_start, tag_end,
+                    tag_start_char,
+                    orig_pos = this.pos,
+                    orig_line_char_count = this.line_char_count;
+
+                peek = peek !== undefined ? peek : false;
+
+                do {
+                    if (this.pos >= this.input.length) {
+                        if (peek) {
+                            this.pos = orig_pos;
+                            this.line_char_count = orig_line_char_count;
+                        }
+                        return content.length ? content.join('') : ['', 'TK_EOF'];
+                    }
+
+                    input_char = this.input.charAt(this.pos);
+                    this.pos++;
+
+                    if (this.Utils.in_array(input_char, this.Utils.whitespace)) { //don't want to insert unnecessary space
+                        space = true;
+                        continue;
+                    }
+
+                    if (input_char === "'" || input_char === '"') {
+                        input_char += this.get_unformatted(input_char);
+                        space = true;
+
+                    }
+
+                    if (input_char === '=') { //no space before =
+                        space = false;
+                    }
+
+                    if (content.length && content[content.length - 1] !== '=' && input_char !== '>' && space) {
+                        //no space after = or before >
+                        if (this.line_char_count >= this.wrap_line_length) {
+                            this.print_newline(false, content);
+                            this.print_indentation(content);
+                        } else {
+                            content.push(' ');
+                            this.line_char_count++;
+                        }
+                        space = false;
+                    }
+
+                    if (indent_handlebars && tag_start_char === '<') {
+                        // When inside an angle-bracket tag, put spaces around
+                        // handlebars not inside of strings.
+                        if ((input_char + this.input.charAt(this.pos)) === '{{') {
+                            input_char += this.get_unformatted('}}');
+                            if (content.length && content[content.length - 1] !== ' ' && content[content.length - 1] !== '<') {
+                                input_char = ' ' + input_char;
+                            }
+                            space = true;
+                        }
+                    }
+
+                    if (input_char === '<' && !tag_start_char) {
+                        tag_start = this.pos - 1;
+                        tag_start_char = '<';
+                    }
+
+                    if (indent_handlebars && !tag_start_char) {
+                        if (content.length >= 2 && content[content.length - 1] === '{' && content[content.length - 2] == '{') {
+                            if (input_char === '#' || input_char === '/') {
+                                tag_start = this.pos - 3;
+                            } else {
+                                tag_start = this.pos - 2;
+                            }
+                            tag_start_char = '{';
+                        }
+                    }
+
+                    this.line_char_count++;
+                    content.push(input_char); //inserts character at-a-time (or string)
+
+                    if (content[1] && content[1] === '!') { //if we're in a comment, do something special
+                        // We treat all comments as literals, even more than preformatted tags
+                        // we just look for the appropriate close tag
+                        content = [this.get_comment(tag_start)];
+                        break;
+                    }
+
+                    if (indent_handlebars && tag_start_char === '{' && content.length > 2 && content[content.length - 2] === '}' && content[content.length - 1] === '}') {
+                        break;
+                    }
+                } while (input_char !== '>');
+
+                var tag_complete = content.join('');
+                var tag_index;
+                var tag_offset;
+
+                if (tag_complete.indexOf(' ') !== -1) { //if there's whitespace, thats where the tag name ends
+                    tag_index = tag_complete.indexOf(' ');
+                } else if (tag_complete[0] === '{') {
+                    tag_index = tag_complete.indexOf('}');
+                } else { //otherwise go with the tag ending
+                    tag_index = tag_complete.indexOf('>');
+                }
+                if (tag_complete[0] === '<' || !indent_handlebars) {
+                    tag_offset = 1;
+                } else {
+                    tag_offset = tag_complete[2] === '#' ? 3 : 2;
+                }
+                var tag_check = tag_complete.substring(tag_offset, tag_index).toLowerCase();
+                if (tag_complete.charAt(tag_complete.length - 2) === '/' ||
+                    this.Utils.in_array(tag_check, this.Utils.single_token)) { //if this tag name is a single tag type (either in the list or has a closing /)
+                    if (!peek) {
+                        this.tag_type = 'SINGLE';
+                    }
+                } else if (indent_handlebars && tag_complete[0] === '{' && tag_check === 'else') {
+                    if (!peek) {
+                        this.indent_to_tag('if');
+                        this.tag_type = 'HANDLEBARS_ELSE';
+                        this.indent_content = true;
+                        this.traverse_whitespace();
+                    }
+                } else if (tag_check === 'script' &&
+                    (tag_complete.search('type') === -1 ||
+                    (tag_complete.search('type') > -1 &&
+                    tag_complete.search(/\b(text|application)\/(x-)?(javascript|ecmascript|jscript|livescript)/) > -1))) {
+                    if (!peek) {
+                        this.record_tag(tag_check);
+                        this.tag_type = 'SCRIPT';
+                    }
+                } else if (tag_check === 'style' &&
+                    (tag_complete.search('type') === -1 ||
+                    (tag_complete.search('type') > -1 && tag_complete.search('text/css') > -1))) {
+                    if (!peek) {
+                        this.record_tag(tag_check);
+                        this.tag_type = 'STYLE';
+                    }
+                } else if (this.is_unformatted(tag_check, unformatted)) { // do not reformat the "unformatted" tags
+                    comment = this.get_unformatted('</' + tag_check + '>', tag_complete); //...delegate to get_unformatted function
+                    content.push(comment);
+                    // Preserve collapsed whitespace either before or after this tag.
+                    if (tag_start > 0 && this.Utils.in_array(this.input.charAt(tag_start - 1), this.Utils.whitespace)) {
+                        content.splice(0, 0, this.input.charAt(tag_start - 1));
+                    }
+                    tag_end = this.pos - 1;
+                    if (this.Utils.in_array(this.input.charAt(tag_end + 1), this.Utils.whitespace)) {
+                        content.push(this.input.charAt(tag_end + 1));
+                    }
+                    this.tag_type = 'SINGLE';
+                } else if (tag_check.charAt(0) === '!') { //peek for <! comment
+                    // for comments content is already correct.
+                    if (!peek) {
+                        this.tag_type = 'SINGLE';
+                        this.traverse_whitespace();
+                    }
+                } else if (!peek) {
+                    if (tag_check.charAt(0) === '/') { //this tag is a double tag so check for tag-ending
+                        this.retrieve_tag(tag_check.substring(1)); //remove it and all ancestors
+                        this.tag_type = 'END';
+                        this.traverse_whitespace();
+                    } else { //otherwise it's a start-tag
+                        this.record_tag(tag_check); //push it on the tag stack
+                        if (tag_check.toLowerCase() !== 'html') {
+                            this.indent_content = true;
+                        }
+                        this.tag_type = 'START';
+
+                        // Allow preserving of newlines after a start tag
+                        this.traverse_whitespace();
+                    }
+                    if (this.Utils.in_array(tag_check, this.Utils.extra_liners)) { //check if this double needs an extra line
+                        this.print_newline(false, this.output);
+                        if (this.output.length && this.output[this.output.length - 2] !== '\n') {
+                            this.print_newline(true, this.output);
+                        }
+                    }
+                }
+
+                if (peek) {
+                    this.pos = orig_pos;
+                    this.line_char_count = orig_line_char_count;
+                }
+
+                return content.join(''); //returns fully formatted tag
+            };
+
+            this.get_comment = function(start_pos) { //function to return comment content in its entirety
+                // this is will have very poor perf, but will work for now.
+                var comment = '',
+                    delimiter = '>',
+                    matched = false;
+
+                this.pos = start_pos;
+                input_char = this.input.charAt(this.pos);
+                this.pos++;
+
+                while (this.pos <= this.input.length) {
+                    comment += input_char;
+
+                    // only need to check for the delimiter if the last chars match
+                    if (comment[comment.length - 1] === delimiter[delimiter.length - 1] &&
+                        comment.indexOf(delimiter) !== -1) {
+                        break;
+                    }
+
+                    // only need to search for custom delimiter for the first few characters
+                    if (!matched && comment.length < 10) {
+                        if (comment.indexOf('<![if') === 0) { //peek for <![if conditional comment
+                            delimiter = '<![endif]>';
+                            matched = true;
+                        } else if (comment.indexOf('<![cdata[') === 0) { //if it's a <[cdata[ comment...
+                            delimiter = ']]>';
+                            matched = true;
+                        } else if (comment.indexOf('<![') === 0) { // some other ![ comment? ...
+                            delimiter = ']>';
+                            matched = true;
+                        } else if (comment.indexOf('<!--') === 0) { // <!-- comment ...
+                            delimiter = '-->';
+                            matched = true;
+                        }
+                    }
+
+                    input_char = this.input.charAt(this.pos);
+                    this.pos++;
+                }
+
+                return comment;
+            };
+
+            this.get_unformatted = function(delimiter, orig_tag) { //function to return unformatted content in its entirety
+
+                if (orig_tag && orig_tag.toLowerCase().indexOf(delimiter) !== -1) {
+                    return '';
+                }
+                var input_char = '';
+                var content = '';
+                var min_index = 0;
+                var space = true;
+                do {
+
+                    if (this.pos >= this.input.length) {
+                        return content;
+                    }
+
+                    input_char = this.input.charAt(this.pos);
+                    this.pos++;
+
+                    if (this.Utils.in_array(input_char, this.Utils.whitespace)) {
+                        if (!space) {
+                            this.line_char_count--;
+                            continue;
+                        }
+                        if (input_char === '\n' || input_char === '\r') {
+                            content += '\n';
+                            /*  Don't change tab indention for unformatted blocks.  If using code for html editing, this will greatly affect <pre> tags if they are specified in the 'unformatted array'
+                for (var i=0; i<this.indent_level; i++) {
+                  content += this.indent_string;
+                }
+                space = false; //...and make sure other indentation is erased
+                */
+                            this.line_char_count = 0;
+                            continue;
+                        }
+                    }
+                    content += input_char;
+                    this.line_char_count++;
+                    space = true;
+
+                    if (indent_handlebars && input_char === '{' && content.length && content[content.length - 2] === '{') {
+                        // Handlebars expressions in strings should also be unformatted.
+                        content += this.get_unformatted('}}');
+                        // These expressions are opaque.  Ignore delimiters found in them.
+                        min_index = content.length;
+                    }
+                } while (content.toLowerCase().indexOf(delimiter, min_index) === -1);
+                return content;
+            };
+
+            this.get_token = function() { //initial handler for token-retrieval
+                var token;
+
+                if (this.last_token === 'TK_TAG_SCRIPT' || this.last_token === 'TK_TAG_STYLE') { //check if we need to format javascript
+                    var type = this.last_token.substr(7);
+                    token = this.get_contents_to(type);
+                    if (typeof token !== 'string') {
+                        return token;
+                    }
+                    return [token, 'TK_' + type];
+                }
+                if (this.current_mode === 'CONTENT') {
+                    token = this.get_content();
+                    if (typeof token !== 'string') {
+                        return token;
+                    } else {
+                        return [token, 'TK_CONTENT'];
+                    }
+                }
+
+                if (this.current_mode === 'TAG') {
+                    token = this.get_tag();
+                    if (typeof token !== 'string') {
+                        return token;
+                    } else {
+                        var tag_name_type = 'TK_TAG_' + this.tag_type;
+                        return [token, tag_name_type];
+                    }
+                }
+            };
+
+            this.get_full_indent = function(level) {
+                level = this.indent_level + level || 0;
+                if (level < 1) {
+                    return '';
+                }
+
+                return Array(level + 1).join(this.indent_string);
+            };
+
+            this.is_unformatted = function(tag_check, unformatted) {
+                //is this an HTML5 block-level link?
+                if (!this.Utils.in_array(tag_check, unformatted)) {
+                    return false;
+                }
+
+                if (tag_check.toLowerCase() !== 'a' || !this.Utils.in_array('a', unformatted)) {
+                    return true;
+                }
+
+                //at this point we have an  tag; is its first child something we want to remain
+                //unformatted?
+                var next_tag = this.get_tag(true /* peek. */ );
+
+                // test next_tag to see if it is just html tag (no external content)
+                var tag = (next_tag || "").match(/^\s*<\s*\/?([a-z]*)\s*[^>]*>\s*$/);
+
+                // if next_tag comes back but is not an isolated tag, then
+                // let's treat the 'a' tag as having content
+                // and respect the unformatted option
+                if (!tag || this.Utils.in_array(tag, unformatted)) {
+                    return true;
+                } else {
+                    return false;
+                }
+            };
+
+            this.printer = function(js_source, indent_character, indent_size, wrap_line_length, brace_style) { //handles input/output and some other printing functions
+
+                this.input = js_source || ''; //gets the input for the Parser
+                this.output = [];
+                this.indent_character = indent_character;
+                this.indent_string = '';
+                this.indent_size = indent_size;
+                this.brace_style = brace_style;
+                this.indent_level = 0;
+                this.wrap_line_length = wrap_line_length;
+                this.line_char_count = 0; //count to see if wrap_line_length was exceeded
+
+                for (var i = 0; i < this.indent_size; i++) {
+                    this.indent_string += this.indent_character;
+                }
+
+                this.print_newline = function(force, arr) {
+                    this.line_char_count = 0;
+                    if (!arr || !arr.length) {
+                        return;
+                    }
+                    if (force || (arr[arr.length - 1] !== '\n')) { //we might want the extra line
+                        arr.push('\n');
+                    }
+                };
+
+                this.print_indentation = function(arr) {
+                    for (var i = 0; i < this.indent_level; i++) {
+                        arr.push(this.indent_string);
+                        this.line_char_count += this.indent_string.length;
+                    }
+                };
+
+                this.print_token = function(text) {
+                    if (text || text !== '') {
+                        if (this.output.length && this.output[this.output.length - 1] === '\n') {
+                            this.print_indentation(this.output);
+                            text = ltrim(text);
+                        }
+                    }
+                    this.print_token_raw(text);
+                };
+
+                this.print_token_raw = function(text) {
+                    if (text && text !== '') {
+                        if (text.length > 1 && text[text.length - 1] === '\n') {
+                            // unformatted tags can grab newlines as their last character
+                            this.output.push(text.slice(0, -1));
+                            this.print_newline(false, this.output);
+                        } else {
+                            this.output.push(text);
+                        }
+                    }
+
+                    for (var n = 0; n < this.newlines; n++) {
+                        this.print_newline(n > 0, this.output);
+                    }
+                    this.newlines = 0;
+                };
+
+                this.indent = function() {
+                    this.indent_level++;
+                };
+
+                this.unindent = function() {
+                    if (this.indent_level > 0) {
+                        this.indent_level--;
+                    }
+                };
+            };
+            return this;
+        }
+
+        /*_____________________--------------------_____________________*/
+
+        multi_parser = new Parser(); //wrapping functions Parser
+        multi_parser.printer(html_source, indent_character, indent_size, wrap_line_length, brace_style); //initialize starting values
+
+        while (true) {
+            var t = multi_parser.get_token();
+            multi_parser.token_text = t[0];
+            multi_parser.token_type = t[1];
+
+            if (multi_parser.token_type === 'TK_EOF') {
+                break;
+            }
+
+            switch (multi_parser.token_type) {
+                case 'TK_TAG_START':
+                    multi_parser.print_newline(false, multi_parser.output);
+                    multi_parser.print_token(multi_parser.token_text);
+                    if (multi_parser.indent_content) {
+                        multi_parser.indent();
+                        multi_parser.indent_content = false;
+                    }
+                    multi_parser.current_mode = 'CONTENT';
+                    break;
+                case 'TK_TAG_STYLE':
+                case 'TK_TAG_SCRIPT':
+                    multi_parser.print_newline(false, multi_parser.output);
+                    multi_parser.print_token(multi_parser.token_text);
+                    multi_parser.current_mode = 'CONTENT';
+                    break;
+                case 'TK_TAG_END':
+                    //Print new line only if the tag has no content and has child
+                    if (multi_parser.last_token === 'TK_CONTENT' && multi_parser.last_text === '') {
+                        var tag_name = multi_parser.token_text.match(/\w+/)[0];
+                        var tag_extracted_from_last_output = null;
+                        if (multi_parser.output.length) {
+                            tag_extracted_from_last_output = multi_parser.output[multi_parser.output.length - 1].match(/(?:<|{{#)\s*(\w+)/);
+                        }
+                        if (tag_extracted_from_last_output === null ||
+                            tag_extracted_from_last_output[1] !== tag_name) {
+                            multi_parser.print_newline(false, multi_parser.output);
+                        }
+                    }
+                    multi_parser.print_token(multi_parser.token_text);
+                    multi_parser.current_mode = 'CONTENT';
+                    break;
+                case 'TK_TAG_SINGLE':
+                    // Don't add a newline before elements that should remain unformatted.
+                    var tag_check = multi_parser.token_text.match(/^\s*<([a-z]+)/i);
+                    if (!tag_check || !multi_parser.Utils.in_array(tag_check[1], unformatted)) {
+                        multi_parser.print_newline(false, multi_parser.output);
+                    }
+                    multi_parser.print_token(multi_parser.token_text);
+                    multi_parser.current_mode = 'CONTENT';
+                    break;
+                case 'TK_TAG_HANDLEBARS_ELSE':
+                    multi_parser.print_token(multi_parser.token_text);
+                    if (multi_parser.indent_content) {
+                        multi_parser.indent();
+                        multi_parser.indent_content = false;
+                    }
+                    multi_parser.current_mode = 'CONTENT';
+                    break;
+                case 'TK_CONTENT':
+                    multi_parser.print_token(multi_parser.token_text);
+                    multi_parser.current_mode = 'TAG';
+                    break;
+                case 'TK_STYLE':
+                case 'TK_SCRIPT':
+                    if (multi_parser.token_text !== '') {
+                        multi_parser.print_newline(false, multi_parser.output);
+                        var text = multi_parser.token_text,
+                            _beautifier,
+                            script_indent_level = 1;
+                        if (multi_parser.token_type === 'TK_SCRIPT') {
+                            _beautifier = typeof js_beautify === 'function' && js_beautify;
+                        } else if (multi_parser.token_type === 'TK_STYLE') {
+                            _beautifier = typeof css_beautify === 'function' && css_beautify;
+                        }
+
+                        if (options.indent_scripts === "keep") {
+                            script_indent_level = 0;
+                        } else if (options.indent_scripts === "separate") {
+                            script_indent_level = -multi_parser.indent_level;
+                        }
+
+                        var indentation = multi_parser.get_full_indent(script_indent_level);
+                        if (_beautifier) {
+                            // call the Beautifier if avaliable
+                            text = _beautifier(text.replace(/^\s*/, indentation), options);
+                        } else {
+                            // simply indent the string otherwise
+                            var white = text.match(/^\s*/)[0];
+                            var _level = white.match(/[^\n\r]*$/)[0].split(multi_parser.indent_string).length - 1;
+                            var reindent = multi_parser.get_full_indent(script_indent_level - _level);
+                            text = text.replace(/^\s*/, indentation)
+                                .replace(/\r\n|\r|\n/g, '\n' + reindent)
+                                .replace(/\s+$/, '');
+                        }
+                        if (text) {
+                            multi_parser.print_token_raw(indentation + trim(text));
+                            multi_parser.print_newline(false, multi_parser.output);
+                        }
+                    }
+                    multi_parser.current_mode = 'TAG';
+                    break;
+            }
+            multi_parser.last_token = multi_parser.token_type;
+            multi_parser.last_text = multi_parser.token_text;
+        }
+        return multi_parser.output.join('');
+    }
+
+    var beautify = require('devtools/jsbeautify');
+
+    exports.htmlBeautify = function(html_source, options) {
+        return style_html(html_source, options, beautify.js, beautify.css);
+    };
+}());
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/jsbeautify/src/beautify-js.js
@@ -0,0 +1,1662 @@
+/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
+
+"use strict";
+
+const acorn = require("acorn/acorn");
+
+/*
+
+  The MIT License (MIT)
+
+  Copyright (c) 2007-2013 Einar Lielmanis and contributors.
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation files
+  (the "Software"), to deal in the Software without restriction,
+  including without limitation the rights to use, copy, modify, merge,
+  publish, distribute, sublicense, and/or sell copies of the Software,
+  and to permit persons to whom the Software is furnished to do so,
+  subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+  SOFTWARE.
+
+ JS Beautifier
+---------------
+
+
+  Written by Einar Lielmanis, <einar@jsbeautifier.org>
+      http://jsbeautifier.org/
+
+  Originally converted to javascript by Vital, <vital76@gmail.com>
+  "End braces on own line" added by Chris J. Shull, <chrisjshull@gmail.com>
+  Parsing improvements for brace-less statements by Liam Newman <bitwiseman@gmail.com>
+
+
+  Usage:
+    js_beautify(js_source_text);
+    js_beautify(js_source_text, options);
+
+  The options are:
+    indent_size (default 4)          - indentation size,
+    indent_char (default space)      - character to indent with,
+    preserve_newlines (default true) - whether existing line breaks should be preserved,
+    max_preserve_newlines (default unlimited) - maximum number of line breaks to be preserved in one chunk,
+
+    jslint_happy (default false) - if true, then jslint-stricter mode is enforced.
+
+            jslint_happy       !jslint_happy
+            ---------------------------------
+            function ()        function()
+
+    brace_style (default "collapse") - "collapse" | "expand" | "end-expand"
+            put braces on the same line as control statements (default), or put braces on own line (Allman / ANSI style), or just put end braces on own line.
+
+    space_before_conditional (default true) - should the space before conditional statement be added, "if(true)" vs "if (true)",
+
+    unescape_strings (default false) - should printable characters in strings encoded in \xNN notation be unescaped, "example" vs "\x65\x78\x61\x6d\x70\x6c\x65"
+
+    wrap_line_length (default unlimited) - lines should wrap at next opportunity after this number of characters.
+          NOTE: This is not a hard limit. Lines will continue until a point where a newline would
+                be preserved if it were present.
+
+    e.g
+
+    js_beautify(js_source_text, {
+      'indent_size': 1,
+      'indent_char': '\t'
+    });
+
+*/
+
+let js_beautify = function js_beautify(js_source_text, options) {
+    var beautifier = new Beautifier(js_source_text, options);
+    return beautifier.beautify();
+};
+
+exports.jsBeautify = js_beautify;
+
+function Beautifier(js_source_text, options) {
+    var input, output_lines;
+    var token_text, token_type, last_type, last_last_text, indent_string;
+    var flags, previous_flags, flag_store;
+    var whitespace, wordchar, punct, parser_pos, line_starters, reserved_words, digits;
+    var prefix;
+    var input_wanted_newline;
+    var output_space_before_token;
+    var input_length, n_newlines, whitespace_before_token;
+    var handlers, MODE, opt;
+    var preindent_string = '';
+
+
+
+    whitespace = "\n\r\t ".split('');
+    wordchar = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$'.split('');
+    digits = '0123456789'.split('');
+
+    punct = '+ - * / % & ++ -- = += -= *= /= %= == === != !== > < >= <= >> << >>> >>>= >>= <<= && &= | || ! ~ , : ? ^ ^= |= :: =>';
+    punct += ' <%= <% %> <?= <? ?>'; // try to be a good boy and try not to break the markup language identifiers
+    punct = punct.split(' ');
+
+    // words which should always start on new line.
+    line_starters = 'continue,try,throw,return,var,let,const,if,switch,case,default,for,while,break,function,yield'.split(',');
+    reserved_words = line_starters.concat(['do', 'in', 'else', 'get', 'set', 'new', 'catch', 'finally', 'typeof']);
+
+
+    MODE = {
+        BlockStatement: 'BlockStatement', // 'BLOCK'
+        Statement: 'Statement', // 'STATEMENT'
+        ObjectLiteral: 'ObjectLiteral', // 'OBJECT',
+        ArrayLiteral: 'ArrayLiteral', //'[EXPRESSION]',
+        ForInitializer: 'ForInitializer', //'(FOR-EXPRESSION)',
+        Conditional: 'Conditional', //'(COND-EXPRESSION)',
+        Expression: 'Expression' //'(EXPRESSION)'
+    };
+
+    handlers = {
+        'TK_START_EXPR': handle_start_expr,
+        'TK_END_EXPR': handle_end_expr,
+        'TK_START_BLOCK': handle_start_block,
+        'TK_END_BLOCK': handle_end_block,
+        'TK_WORD': handle_word,
+        'TK_RESERVED': handle_word,
+        'TK_SEMICOLON': handle_semicolon,
+        'TK_STRING': handle_string,
+        'TK_EQUALS': handle_equals,
+        'TK_OPERATOR': handle_operator,
+        'TK_COMMA': handle_comma,
+        'TK_BLOCK_COMMENT': handle_block_comment,
+        'TK_INLINE_COMMENT': handle_inline_comment,
+        'TK_COMMENT': handle_comment,
+        'TK_DOT': handle_dot,
+        'TK_UNKNOWN': handle_unknown
+    };
+
+    function create_flags(flags_base, mode) {
+        var next_indent_level = 0;
+        if (flags_base) {
+            next_indent_level = flags_base.indentation_level;
+            if (!just_added_newline() &&
+                flags_base.line_indent_level > next_indent_level) {
+                next_indent_level = flags_base.line_indent_level;
+            }
+        }
+
+        var next_flags = {
+            mode: mode,
+            parent: flags_base,
+            last_text: flags_base ? flags_base.last_text : '', // last token text
+            last_word: flags_base ? flags_base.last_word : '', // last 'TK_WORD' passed
+            declaration_statement: false,
+            declaration_assignment: false,
+            in_html_comment: false,
+            multiline_frame: false,
+            if_block: false,
+            else_block: false,
+            do_block: false,
+            do_while: false,
+            in_case_statement: false, // switch(..){ INSIDE HERE }
+            in_case: false, // we're on the exact line with "case 0:"
+            case_body: false, // the indented case-action block
+            indentation_level: next_indent_level,
+            line_indent_level: flags_base ? flags_base.line_indent_level : next_indent_level,
+            start_line_index: output_lines.length,
+            had_comment: false,
+            ternary_depth: 0
+        };
+        return next_flags;
+    }
+
+    // Using object instead of string to allow for later expansion of info about each line
+
+    function create_output_line() {
+        return {
+            text: []
+        };
+    }
+
+    // Some interpreters have unexpected results with foo = baz || bar;
+    options = options ? options : {};
+    opt = {};
+
+    // compatibility
+    if (options.space_after_anon_function !== undefined && options.jslint_happy === undefined) {
+        options.jslint_happy = options.space_after_anon_function;
+    }
+    if (options.braces_on_own_line !== undefined) { //graceful handling of deprecated option
+        opt.brace_style = options.braces_on_own_line ? "expand" : "collapse";
+    }
+    opt.brace_style = options.brace_style ? options.brace_style : (opt.brace_style ? opt.brace_style : "collapse");
+
+    // graceful handling of deprecated option
+    if (opt.brace_style === "expand-strict") {
+        opt.brace_style = "expand";
+    }
+
+
+    opt.indent_size = options.indent_size ? parseInt(options.indent_size, 10) : 4;
+    opt.indent_char = options.indent_char ? options.indent_char : ' ';
+    opt.preserve_newlines = (options.preserve_newlines === undefined) ? true : options.preserve_newlines;
+    opt.break_chained_methods = (options.break_chained_methods === undefined) ? false : options.break_chained_methods;
+    opt.max_preserve_newlines = (options.max_preserve_newlines === undefined) ? 0 : parseInt(options.max_preserve_newlines, 10);
+    opt.space_in_paren = (options.space_in_paren === undefined) ? false : options.space_in_paren;
+    opt.space_in_empty_paren = (options.space_in_empty_paren === undefined) ? false : options.space_in_empty_paren;
+    opt.jslint_happy = (options.jslint_happy === undefined) ? false : options.jslint_happy;
+    opt.keep_array_indentation = (options.keep_array_indentation === undefined) ? false : options.keep_array_indentation;
+    opt.space_before_conditional = (options.space_before_conditional === undefined) ? true : options.space_before_conditional;
+    opt.unescape_strings = (options.unescape_strings === undefined) ? false : options.unescape_strings;
+    opt.wrap_line_length = (options.wrap_line_length === undefined) ? 0 : parseInt(options.wrap_line_length, 10);
+    opt.e4x = (options.e4x === undefined) ? false : options.e4x;
+
+    if(options.indent_with_tabs){
+        opt.indent_char = '\t';
+        opt.indent_size = 1;
+    }
+
+    //----------------------------------
+    indent_string = '';
+    while (opt.indent_size > 0) {
+        indent_string += opt.indent_char;
+        opt.indent_size -= 1;
+    }
+
+    while (js_source_text && (js_source_text.charAt(0) === ' ' || js_source_text.charAt(0) === '\t')) {
+        preindent_string += js_source_text.charAt(0);
+        js_source_text = js_source_text.substring(1);
+    }
+    input = js_source_text;
+    // cache the source's length.
+    input_length = js_source_text.length;
+
+    last_type = 'TK_START_BLOCK'; // last token type
+    last_last_text = ''; // pre-last token text
+    output_lines = [create_output_line()];
+    output_space_before_token = false;
+    whitespace_before_token = [];
+
+    // Stack of parsing/formatting states, including MODE.
+    // We tokenize, parse, and output in an almost purely a forward-only stream of token input
+    // and formatted output.  This makes the beautifier less accurate than full parsers
+    // but also far more tolerant of syntax errors.
+    //
+    // For example, the default mode is MODE.BlockStatement. If we see a '{' we push a new frame of type
+    // MODE.BlockStatement on the the stack, even though it could be object literal.  If we later
+    // encounter a ":", we'll switch to to MODE.ObjectLiteral.  If we then see a ";",
+    // most full parsers would die, but the beautifier gracefully falls back to
+    // MODE.BlockStatement and continues on.
+    flag_store = [];
+    set_mode(MODE.BlockStatement);
+
+    parser_pos = 0;
+
+    this.beautify = function() {
+        /*jshint onevar:true */
+        var t, i, keep_whitespace, sweet_code;
+
+        while (true) {
+            t = get_next_token();
+            token_text = t[0];
+            token_type = t[1];
+
+            if (token_type === 'TK_EOF') {
+                // Unwind any open statements
+                while (flags.mode === MODE.Statement) {
+                    restore_mode();
+                }
+                break;
+            }
+
+            keep_whitespace = opt.keep_array_indentation && is_array(flags.mode);
+            input_wanted_newline = n_newlines > 0;
+
+            if (keep_whitespace) {
+                for (i = 0; i < n_newlines; i += 1) {
+                    print_newline(i > 0);
+                }
+            } else {
+                if (opt.max_preserve_newlines && n_newlines > opt.max_preserve_newlines) {
+                    n_newlines = opt.max_preserve_newlines;
+                }
+
+                if (opt.preserve_newlines) {
+                    if (n_newlines > 1) {
+                        print_newline();
+                        for (i = 1; i < n_newlines; i += 1) {
+                            print_newline(true);
+                        }
+                    }
+                }
+            }
+
+            handlers[token_type]();
+
+            // The cleanest handling of inline comments is to treat them as though they aren't there.
+            // Just continue formatting and the behavior should be logical.
+            // Also ignore unknown tokens.  Again, this should result in better behavior.
+            if (token_type !== 'TK_INLINE_COMMENT' && token_type !== 'TK_COMMENT' &&
+                token_type !== 'TK_BLOCK_COMMENT' && token_type !== 'TK_UNKNOWN') {
+                last_last_text = flags.last_text;
+                last_type = token_type;
+                flags.last_text = token_text;
+            }
+            flags.had_comment = (token_type === 'TK_INLINE_COMMENT' || token_type === 'TK_COMMENT'
+                || token_type === 'TK_BLOCK_COMMENT');
+        }
+
+
+        sweet_code = output_lines[0].text.join('');
+        for (var line_index = 1; line_index < output_lines.length; line_index++) {
+            sweet_code += '\n' + output_lines[line_index].text.join('');
+        }
+        sweet_code = sweet_code.replace(/[\r\n ]+$/, '');
+        return sweet_code;
+    };
+
+    function trim_output(eat_newlines) {
+        eat_newlines = (eat_newlines === undefined) ? false : eat_newlines;
+
+        if (output_lines.length) {
+            trim_output_line(output_lines[output_lines.length - 1], eat_newlines);
+
+            while (eat_newlines && output_lines.length > 1 &&
+                output_lines[output_lines.length - 1].text.length === 0) {
+                output_lines.pop();
+                trim_output_line(output_lines[output_lines.length - 1], eat_newlines);
+            }
+        }
+    }
+
+    function trim_output_line(line) {
+        while (line.text.length &&
+            (line.text[line.text.length - 1] === ' ' ||
+                line.text[line.text.length - 1] === indent_string ||
+                line.text[line.text.length - 1] === preindent_string)) {
+            line.text.pop();
+        }
+    }
+
+    function trim(s) {
+        return s.replace(/^\s+|\s+$/g, '');
+    }
+
+    // we could use just string.split, but
+    // IE doesn't like returning empty strings
+
+    function split_newlines(s) {
+        //return s.split(/\x0d\x0a|\x0a/);
+
+        s = s.replace(/\x0d/g, '');
+        var out = [],
+            idx = s.indexOf("\n");
+        while (idx !== -1) {
+            out.push(s.substring(0, idx));
+            s = s.substring(idx + 1);
+            idx = s.indexOf("\n");
+        }
+        if (s.length) {
+            out.push(s);
+        }
+        return out;
+    }
+
+    function just_added_newline() {
+        var line = output_lines[output_lines.length - 1];
+        return line.text.length === 0;
+    }
+
+    function just_added_blankline() {
+        if (just_added_newline()) {
+            if (output_lines.length === 1) {
+                return true; // start of the file and newline = blank
+            }
+
+            var line = output_lines[output_lines.length - 2];
+            return line.text.length === 0;
+        }
+        return false;
+    }
+
+    function allow_wrap_or_preserved_newline(force_linewrap) {
+        force_linewrap = (force_linewrap === undefined) ? false : force_linewrap;
+        if (opt.wrap_line_length && !force_linewrap) {
+            var line = output_lines[output_lines.length - 1];
+            var proposed_line_length = 0;
+            // never wrap the first token of a line.
+            if (line.text.length > 0) {
+                proposed_line_length = line.text.join('').length + token_text.length +
+                    (output_space_before_token ? 1 : 0);
+                if (proposed_line_length >= opt.wrap_line_length) {
+                    force_linewrap = true;
+                }
+            }
+        }
+        if (((opt.preserve_newlines && input_wanted_newline) || force_linewrap) && !just_added_newline()) {
+            print_newline(false, true);
+
+        }
+    }
+
+    function print_newline(force_newline, preserve_statement_flags) {
+        output_space_before_token = false;
+
+        if (!preserve_statement_flags) {
+            if (flags.last_text !== ';' && flags.last_text !== ',' && flags.last_text !== '=' && last_type !== 'TK_OPERATOR') {
+                while (flags.mode === MODE.Statement && !flags.if_block && !flags.do_block) {
+                    restore_mode();
+                }
+            }
+        }
+
+        if (output_lines.length === 1 && just_added_newline()) {
+            return; // no newline on start of file
+        }
+
+        if (force_newline || !just_added_newline()) {
+            flags.multiline_frame = true;
+            output_lines.push(create_output_line());
+        }
+    }
+
+    function print_token_line_indentation() {
+        if (just_added_newline()) {
+            var line = output_lines[output_lines.length - 1];
+            if (opt.keep_array_indentation && is_array(flags.mode) && input_wanted_newline) {
+                // prevent removing of this whitespace as redundant
+                line.text.push('');
+                for (var i = 0; i < whitespace_before_token.length; i += 1) {
+                    line.text.push(whitespace_before_token[i]);
+                }
+            } else {
+                if (preindent_string) {
+                    line.text.push(preindent_string);
+                }
+
+                print_indent_string(flags.indentation_level);
+            }
+        }
+    }
+
+    function print_indent_string(level) {
+        // Never indent your first output indent at the start of the file
+        if (output_lines.length > 1) {
+            var line = output_lines[output_lines.length - 1];
+
+            flags.line_indent_level = level;
+            for (var i = 0; i < level; i += 1) {
+                line.text.push(indent_string);
+            }
+        }
+    }
+
+    function print_token_space_before() {
+        var line = output_lines[output_lines.length - 1];
+        if (output_space_before_token && line.text.length) {
+            var last_output = line.text[line.text.length - 1];
+            if (last_output !== ' ' && last_output !== indent_string) { // prevent occassional duplicate space
+                line.text.push(' ');
+            }
+        }
+    }
+
+    function print_token(printable_token) {
+        printable_token = printable_token || token_text;
+        print_token_line_indentation();
+        print_token_space_before();
+        output_space_before_token = false;
+        output_lines[output_lines.length - 1].text.push(printable_token);
+    }
+
+    function indent() {
+        flags.indentation_level += 1;
+    }
+
+    function deindent() {
+        if (flags.indentation_level > 0 &&
+            ((!flags.parent) || flags.indentation_level > flags.parent.indentation_level))
+            flags.indentation_level -= 1;
+    }
+
+    function remove_redundant_indentation(frame) {
+        // This implementation is effective but has some issues:
+        //     - less than great performance due to array splicing
+        //     - can cause line wrap to happen too soon due to indent removal
+        //           after wrap points are calculated
+        // These issues are minor compared to ugly indentation.
+
+        if (frame.multiline_frame) return;
+
+        // remove one indent from each line inside this section
+        var index = frame.start_line_index;
+        var splice_index = 0;
+        var line;
+
+        while (index < output_lines.length) {
+            line = output_lines[index];
+            index++;
+
+            // skip empty lines
+            if (line.text.length === 0) {
+                continue;
+            }
+
+            // skip the preindent string if present
+            if (preindent_string && line.text[0] === preindent_string) {
+                splice_index = 1;
+            } else {
+                splice_index = 0;
+            }
+
+            // remove one indent, if present
+            if (line.text[splice_index] === indent_string) {
+                line.text.splice(splice_index, 1);
+            }
+        }
+    }
+
+    function set_mode(mode) {
+        if (flags) {
+            flag_store.push(flags);
+            previous_flags = flags;
+        } else {
+            previous_flags = create_flags(null, mode);
+        }
+
+        flags = create_flags(previous_flags, mode);
+    }
+
+    function is_array(mode) {
+        return mode === MODE.ArrayLiteral;
+    }
+
+    function is_expression(mode) {
+        return in_array(mode, [MODE.Expression, MODE.ForInitializer, MODE.Conditional]);
+    }
+
+    function restore_mode() {
+        if (flag_store.length > 0) {
+            previous_flags = flags;
+            flags = flag_store.pop();
+            if (previous_flags.mode === MODE.Statement) {
+                remove_redundant_indentation(previous_flags);
+            }
+        }
+    }
+
+    function start_of_object_property() {
+        return flags.parent.mode === MODE.ObjectLiteral && flags.mode === MODE.Statement && flags.last_text === ':' &&
+            flags.ternary_depth === 0;
+    }
+
+    function start_of_statement() {
+        if (
+                (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['var', 'let', 'const']) && token_type === 'TK_WORD') ||
+                (last_type === 'TK_RESERVED' && flags.last_text === 'do') ||
+                (last_type === 'TK_RESERVED' && flags.last_text === 'return' && !input_wanted_newline) ||
+                (last_type === 'TK_RESERVED' && flags.last_text === 'else' && !(token_type === 'TK_RESERVED' && token_text === 'if')) ||
+                (last_type === 'TK_END_EXPR' && (previous_flags.mode === MODE.ForInitializer || previous_flags.mode === MODE.Conditional)) ||
+                (last_type === 'TK_WORD' && flags.mode === MODE.BlockStatement
+                    && !flags.in_case
+                    && !(token_text === '--' || token_text === '++')
+                    && token_type !== 'TK_WORD' && token_type !== 'TK_RESERVED') ||
+                (flags.mode === MODE.ObjectLiteral && flags.last_text === ':' && flags.ternary_depth === 0)
+
+            ) {
+
+            set_mode(MODE.Statement);
+            indent();
+
+            if (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['var', 'let', 'const']) && token_type === 'TK_WORD') {
+                flags.declaration_statement = true;
+            }
+
+            // Issue #276:
+            // If starting a new statement with [if, for, while, do], push to a new line.
+            // if (a) if (b) if(c) d(); else e(); else f();
+            if (!start_of_object_property()) {
+                allow_wrap_or_preserved_newline(
+                    token_type === 'TK_RESERVED' && in_array(token_text, ['do', 'for', 'if', 'while']));
+            }
+
+            return true;
+        }
+        return false;
+    }
+
+    function all_lines_start_with(lines, c) {
+        for (var i = 0; i < lines.length; i++) {
+            var line = trim(lines[i]);
+            if (line.charAt(0) !== c) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    function each_line_matches_indent(lines, indent) {
+        var i = 0,
+            len = lines.length,
+            line;
+        for (; i < len; i++) {
+            line = lines[i];
+            // allow empty lines to pass through
+            if (line && line.indexOf(indent) !== 0) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    function is_special_word(word) {
+        return in_array(word, ['case', 'return', 'do', 'if', 'throw', 'else']);
+    }
+
+    function in_array(what, arr) {
+        for (var i = 0; i < arr.length; i += 1) {
+            if (arr[i] === what) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    function unescape_string(s) {
+        var esc = false,
+            out = '',
+            pos = 0,
+            s_hex = '',
+            escaped = 0,
+            c;
+
+        while (esc || pos < s.length) {
+
+            c = s.charAt(pos);
+            pos++;
+
+            if (esc) {
+                esc = false;
+                if (c === 'x') {
+                    // simple hex-escape \x24
+                    s_hex = s.substr(pos, 2);
+                    pos += 2;
+                } else if (c === 'u') {
+                    // unicode-escape, \u2134
+                    s_hex = s.substr(pos, 4);
+                    pos += 4;
+                } else {
+                    // some common escape, e.g \n
+                    out += '\\' + c;
+                    continue;
+                }
+                if (!s_hex.match(/^[0123456789abcdefABCDEF]+$/)) {
+                    // some weird escaping, bail out,
+                    // leaving whole string intact
+                    return s;
+                }
+
+                escaped = parseInt(s_hex, 16);
+
+                if (escaped >= 0x00 && escaped < 0x20) {
+                    // leave 0x00...0x1f escaped
+                    if (c === 'x') {
+                        out += '\\x' + s_hex;
+                    } else {
+                        out += '\\u' + s_hex;
+                    }
+                    continue;
+                } else if (escaped === 0x22 || escaped === 0x27 || escaped === 0x5c) {
+                    // single-quote, apostrophe, backslash - escape these
+                    out += '\\' + String.fromCharCode(escaped);
+                } else if (c === 'x' && escaped > 0x7e && escaped <= 0xff) {
+                    // we bail out on \x7f..\xff,
+                    // leaving whole string escaped,
+                    // as it's probably completely binary
+                    return s;
+                } else {
+                    out += String.fromCharCode(escaped);
+                }
+            } else if (c === '\\') {
+                esc = true;
+            } else {
+                out += c;
+            }
+        }
+        return out;
+    }
+
+    function is_next(find) {
+        var local_pos = parser_pos;
+        var c = input.charAt(local_pos);
+        while (in_array(c, whitespace) && c !== find) {
+            local_pos++;
+            if (local_pos >= input_length) {
+                return false;
+            }
+            c = input.charAt(local_pos);
+        }
+        return c === find;
+    }
+
+    function get_next_token() {
+        var i, resulting_string;
+
+        n_newlines = 0;
+
+        if (parser_pos >= input_length) {
+            return ['', 'TK_EOF'];
+        }
+
+        input_wanted_newline = false;
+        whitespace_before_token = [];
+
+        var c = input.charAt(parser_pos);
+        parser_pos += 1;
+
+        while (in_array(c, whitespace)) {
+
+            if (c === '\n') {
+                n_newlines += 1;
+                whitespace_before_token = [];
+            } else if (n_newlines) {
+                if (c === indent_string) {
+                    whitespace_before_token.push(indent_string);
+                } else if (c !== '\r') {
+                    whitespace_before_token.push(' ');
+                }
+            }
+
+            if (parser_pos >= input_length) {
+                return ['', 'TK_EOF'];
+            }
+
+            c = input.charAt(parser_pos);
+            parser_pos += 1;
+        }
+
+        // NOTE: because beautifier doesn't fully parse, it doesn't use acorn.isIdentifierStart.
+        // It just treats all identifiers and numbers and such the same.
+        if (acorn.isIdentifierChar(input.charCodeAt(parser_pos-1))) {
+            if (parser_pos < input_length) {
+                while (acorn.isIdentifierChar(input.charCodeAt(parser_pos))) {
+                    c += input.charAt(parser_pos);
+                    parser_pos += 1;
+                    if (parser_pos === input_length) {
+                        break;
+                    }
+                }
+            }
+
+            // small and surprisingly unugly hack for 1E-10 representation
+            if (parser_pos !== input_length && c.match(/^[0-9]+[Ee]$/) && (input.charAt(parser_pos) === '-' || input.charAt(parser_pos) === '+')) {
+
+                var sign = input.charAt(parser_pos);
+                parser_pos += 1;
+
+                var t = get_next_token();
+                c += sign + t[0];
+                return [c, 'TK_WORD'];
+            }
+
+            if (!(last_type === 'TK_DOT' ||
+                    (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['set', 'get'])))
+                && in_array(c, reserved_words)) {
+                if (c === 'in') { // hack for 'in' operator
+                    return [c, 'TK_OPERATOR'];
+                }
+                return [c, 'TK_RESERVED'];
+            }
+            return [c, 'TK_WORD'];
+        }
+
+        if (c === '(' || c === '[') {
+            return [c, 'TK_START_EXPR'];
+        }
+
+        if (c === ')' || c === ']') {
+            return [c, 'TK_END_EXPR'];
+        }
+
+        if (c === '{') {
+            return [c, 'TK_START_BLOCK'];
+        }
+
+        if (c === '}') {
+            return [c, 'TK_END_BLOCK'];
+        }
+
+        if (c === ';') {
+            return [c, 'TK_SEMICOLON'];
+        }
+
+        if (c === '/') {
+            var comment = '';
+            // peek for comment /* ... */
+            var inline_comment = true;
+            if (input.charAt(parser_pos) === '*') {
+                parser_pos += 1;
+                if (parser_pos < input_length) {
+                    while (parser_pos < input_length && !(input.charAt(parser_pos) === '*' && input.charAt(parser_pos + 1) && input.charAt(parser_pos + 1) === '/')) {
+                        c = input.charAt(parser_pos);
+                        comment += c;
+                        if (c === "\n" || c === "\r") {
+                            inline_comment = false;
+                        }
+                        parser_pos += 1;
+                        if (parser_pos >= input_length) {
+                            break;
+                        }
+                    }
+                }
+                parser_pos += 2;
+                if (inline_comment && n_newlines === 0) {
+                    return ['/*' + comment + '*/', 'TK_INLINE_COMMENT'];
+                } else {
+                    return ['/*' + comment + '*/', 'TK_BLOCK_COMMENT'];
+                }
+            }
+            // peek for comment // ...
+            if (input.charAt(parser_pos) === '/') {
+                comment = c;
+                while (input.charAt(parser_pos) !== '\r' && input.charAt(parser_pos) !== '\n') {
+                    comment += input.charAt(parser_pos);
+                    parser_pos += 1;
+                    if (parser_pos >= input_length) {
+                        break;
+                    }
+                }
+                return [comment, 'TK_COMMENT'];
+            }
+
+        }
+
+
+        if (c === '`' || c === "'" || c === '"' || // string
+            (
+                (c === '/') || // regexp
+                (opt.e4x && c === "<" && input.slice(parser_pos - 1).match(/^<([-a-zA-Z:0-9_.]+|{[^{}]*}|!\[CDATA\[[\s\S]*?\]\])\s*([-a-zA-Z:0-9_.]+=('[^']*'|"[^"]*"|{[^{}]*})\s*)*\/?\s*>/)) // xml
+            ) && ( // regex and xml can only appear in specific locations during parsing
+                (last_type === 'TK_RESERVED' && is_special_word(flags.last_text)) ||
+                (last_type === 'TK_END_EXPR' && in_array(previous_flags.mode, [MODE.Conditional, MODE.ForInitializer])) ||
+                (in_array(last_type, ['TK_COMMENT', 'TK_START_EXPR', 'TK_START_BLOCK',
+                    'TK_END_BLOCK', 'TK_OPERATOR', 'TK_EQUALS', 'TK_EOF', 'TK_SEMICOLON', 'TK_COMMA'
+                ]))
+            )) {
+
+            var sep = c,
+                esc = false,
+                has_char_escapes = false;
+
+            resulting_string = c;
+
+            if (parser_pos < input_length) {
+                if (sep === '/') {
+                    //
+                    // handle regexp
+                    //
+                    var in_char_class = false;
+                    while (esc || in_char_class || input.charAt(parser_pos) !== sep) {
+                        resulting_string += input.charAt(parser_pos);
+                        if (!esc) {
+                            esc = input.charAt(parser_pos) === '\\';
+                            if (input.charAt(parser_pos) === '[') {
+                                in_char_class = true;
+                            } else if (input.charAt(parser_pos) === ']') {
+                                in_char_class = false;
+                            }
+                        } else {
+                            esc = false;
+                        }
+                        parser_pos += 1;
+                        if (parser_pos >= input_length) {
+                            // incomplete string/rexp when end-of-file reached.
+                            // bail out with what had been received so far.
+                            return [resulting_string, 'TK_STRING'];
+                        }
+                    }
+                } else if (opt.e4x && sep === '<') {
+                    //
+                    // handle e4x xml literals
+                    //
+                    var xmlRegExp = /<(\/?)([-a-zA-Z:0-9_.]+|{[^{}]*}|!\[CDATA\[[\s\S]*?\]\])\s*([-a-zA-Z:0-9_.]+=('[^']*'|"[^"]*"|{[^{}]*})\s*)*(\/?)\s*>/g;
+                    var xmlStr = input.slice(parser_pos - 1);
+                    var match = xmlRegExp.exec(xmlStr);
+                    if (match && match.index === 0) {
+                        var rootTag = match[2];
+                        var depth = 0;
+                        while (match) {
+                            var isEndTag = !! match[1];
+                            var tagName = match[2];
+                            var isSingletonTag = ( !! match[match.length - 1]) || (tagName.slice(0, 8) === "![CDATA[");
+                            if (tagName === rootTag && !isSingletonTag) {
+                                if (isEndTag) {
+                                    --depth;
+                                } else {
+                                    ++depth;
+                                }
+                            }
+                            if (depth <= 0) {
+                                break;
+                            }
+                            match = xmlRegExp.exec(xmlStr);
+                        }
+                        var xmlLength = match ? match.index + match[0].length : xmlStr.length;
+                        parser_pos += xmlLength - 1;
+                        return [xmlStr.slice(0, xmlLength), "TK_STRING"];
+                    }
+                } else {
+                    //
+                    // handle string
+                    //
+                    while (esc || input.charAt(parser_pos) !== sep) {
+                        resulting_string += input.charAt(parser_pos);
+                        if (esc) {
+                            if (input.charAt(parser_pos) === 'x' || input.charAt(parser_pos) === 'u') {
+                                has_char_escapes = true;
+                            }
+                            esc = false;
+                        } else {
+                            esc = input.charAt(parser_pos) === '\\';
+                        }
+                        parser_pos += 1;
+                        if (parser_pos >= input_length) {
+                            // incomplete string/rexp when end-of-file reached.
+                            // bail out with what had been received so far.
+                            return [resulting_string, 'TK_STRING'];
+                        }
+                    }
+
+                }
+            }
+
+            parser_pos += 1;
+            resulting_string += sep;
+
+            if (has_char_escapes && opt.unescape_strings) {
+                resulting_string = unescape_string(resulting_string);
+            }
+
+            if (sep === '/') {
+                // regexps may have modifiers /regexp/MOD , so fetch those, too
+                while (parser_pos < input_length && in_array(input.charAt(parser_pos), wordchar)) {
+                    resulting_string += input.charAt(parser_pos);
+                    parser_pos += 1;
+                }
+            }
+            return [resulting_string, 'TK_STRING'];
+        }
+
+        if (c === '#') {
+
+
+            if (output_lines.length === 1 && output_lines[0].text.length === 0 &&
+                input.charAt(parser_pos) === '!') {
+                // shebang
+                resulting_string = c;
+                while (parser_pos < input_length && c !== '\n') {
+                    c = input.charAt(parser_pos);
+                    resulting_string += c;
+                    parser_pos += 1;
+                }
+                return [trim(resulting_string) + '\n', 'TK_UNKNOWN'];
+            }
+
+
+
+            // Spidermonkey-specific sharp variables for circular references
+            // https://developer.mozilla.org/En/Sharp_variables_in_JavaScript
+            // http://mxr.mozilla.org/mozilla-central/source/js/src/jsscan.cpp around line 1935
+            var sharp = '#';
+            if (parser_pos < input_length && in_array(input.charAt(parser_pos), digits)) {
+                do {
+                    c = input.charAt(parser_pos);
+                    sharp += c;
+                    parser_pos += 1;
+                } while (parser_pos < input_length && c !== '#' && c !== '=');
+                if (c === '#') {
+                    //
+                } else if (input.charAt(parser_pos) === '[' && input.charAt(parser_pos + 1) === ']') {
+                    sharp += '[]';
+                    parser_pos += 2;
+                } else if (input.charAt(parser_pos) === '{' && input.charAt(parser_pos + 1) === '}') {
+                    sharp += '{}';
+                    parser_pos += 2;
+                }
+                return [sharp, 'TK_WORD'];
+            }
+        }
+
+        if (c === '<' && input.substring(parser_pos - 1, parser_pos + 3) === '<!--') {
+            parser_pos += 3;
+            c = '<!--';
+            while (input.charAt(parser_pos) !== '\n' && parser_pos < input_length) {
+                c += input.charAt(parser_pos);
+                parser_pos++;
+            }
+            flags.in_html_comment = true;
+            return [c, 'TK_COMMENT'];
+        }
+
+        if (c === '-' && flags.in_html_comment && input.substring(parser_pos - 1, parser_pos + 2) === '-->') {
+            flags.in_html_comment = false;
+            parser_pos += 2;
+            return ['-->', 'TK_COMMENT'];
+        }
+
+        if (c === '.') {
+            return [c, 'TK_DOT'];
+        }
+
+        if (in_array(c, punct)) {
+            while (parser_pos < input_length && in_array(c + input.charAt(parser_pos), punct)) {
+                c += input.charAt(parser_pos);
+                parser_pos += 1;
+                if (parser_pos >= input_length) {
+                    break;
+                }
+            }
+
+            if (c === ',') {
+                return [c, 'TK_COMMA'];
+            } else if (c === '=') {
+                return [c, 'TK_EQUALS'];
+            } else {
+                return [c, 'TK_OPERATOR'];
+            }
+        }
+
+        return [c, 'TK_UNKNOWN'];
+    }
+
+    function handle_start_expr() {
+        if (start_of_statement()) {
+            // The conditional starts the statement if appropriate.
+        }
+
+        var next_mode = MODE.Expression;
+        if (token_text === '[') {
+
+            if (last_type === 'TK_WORD' || flags.last_text === ')') {
+                // this is array index specifier, break immediately
+                // a[x], fn()[x]
+                if (last_type === 'TK_RESERVED' && in_array(flags.last_text, line_starters)) {
+                    output_space_before_token = true;
+                }
+                set_mode(next_mode);
+                print_token();
+                indent();
+                if (opt.space_in_paren) {
+                    output_space_before_token = true;
+                }
+                return;
+            }
+
+            next_mode = MODE.ArrayLiteral;
+            if (is_array(flags.mode)) {
+                if (flags.last_text === '[' ||
+                    (flags.last_text === ',' && (last_last_text === ']' || last_last_text === '}'))) {
+                    // ], [ goes to new line
+                    // }, [ goes to new line
+                    if (!opt.keep_array_indentation) {
+                        print_newline();
+                    }
+                }
+            }
+
+        } else {
+            if (last_type === 'TK_RESERVED' && flags.last_text === 'for') {
+                next_mode = MODE.ForInitializer;
+            } else if (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['if', 'while'])) {
+                next_mode = MODE.Conditional;
+            } else {
+                // next_mode = MODE.Expression;
+            }
+        }
+
+        if (flags.last_text === ';' || last_type === 'TK_START_BLOCK') {
+            print_newline();
+        } else if (last_type === 'TK_END_EXPR' || last_type === 'TK_START_EXPR' || last_type === 'TK_END_BLOCK' || flags.last_text === '.') {
+            // TODO: Consider whether forcing this is required.  Review failing tests when removed.
+            allow_wrap_or_preserved_newline(input_wanted_newline);
+            // do nothing on (( and )( and ][ and ]( and .(
+        } else if (!(last_type === 'TK_RESERVED' && token_text === '(') && last_type !== 'TK_WORD' && last_type !== 'TK_OPERATOR') {
+            output_space_before_token = true;
+        } else if ((last_type === 'TK_RESERVED' && (flags.last_word === 'function' || flags.last_word === 'typeof')) ||
+            (flags.last_text === '*' && last_last_text === 'function')) {
+            // function() vs function ()
+            if (opt.jslint_happy) {
+                output_space_before_token = true;
+            }
+        } else if (last_type === 'TK_RESERVED' && (in_array(flags.last_text, line_starters) || flags.last_text === 'catch')) {
+            if (opt.space_before_conditional) {
+                output_space_before_token = true;
+            }
+        }
+
+        // Support of this kind of newline preservation.
+        // a = (b &&
+        //     (c || d));
+        if (token_text === '(') {
+            if (last_type === 'TK_EQUALS' || last_type === 'TK_OPERATOR') {
+                if (!start_of_object_property()) {
+                    allow_wrap_or_preserved_newline();
+                }
+            }
+        }
+
+        set_mode(next_mode);
+        print_token();
+        if (opt.space_in_paren) {
+            output_space_before_token = true;
+        }
+
+        // In all cases, if we newline while inside an expression it should be indented.
+        indent();
+    }
+
+    function handle_end_expr() {
+        // statements inside expressions are not valid syntax, but...
+        // statements must all be closed when their container closes
+        while (flags.mode === MODE.Statement) {
+            restore_mode();
+        }
+
+        if (flags.multiline_frame) {
+            allow_wrap_or_preserved_newline(token_text === ']' && is_array(flags.mode) && !opt.keep_array_indentation);
+        }
+
+        if (opt.space_in_paren) {
+            if (last_type === 'TK_START_EXPR' && ! opt.space_in_empty_paren) {
+                // () [] no inner space in empty parens like these, ever, ref #320
+                trim_output();
+                output_space_before_token = false;
+            } else {
+                output_space_before_token = true;
+            }
+        }
+        if (token_text === ']' && opt.keep_array_indentation) {
+            print_token();
+            restore_mode();
+        } else {
+            restore_mode();
+            print_token();
+        }
+        remove_redundant_indentation(previous_flags);
+
+        // do {} while () // no statement required after
+        if (flags.do_while && previous_flags.mode === MODE.Conditional) {
+            previous_flags.mode = MODE.Expression;
+            flags.do_block = false;
+            flags.do_while = false;
+
+        }
+    }
+
+    function handle_start_block() {
+        set_mode(MODE.BlockStatement);
+
+        var empty_braces = is_next('}');
+        var empty_anonymous_function = empty_braces && flags.last_word === 'function' &&
+            last_type === 'TK_END_EXPR';
+
+        if (opt.brace_style === "expand") {
+            if (last_type !== 'TK_OPERATOR' &&
+                (empty_anonymous_function ||
+                    last_type === 'TK_EQUALS' ||
+                    (last_type === 'TK_RESERVED' && is_special_word(flags.last_text) && flags.last_text !== 'else'))) {
+                output_space_before_token = true;
+            } else {
+                print_newline(false, true);
+            }
+        } else { // collapse
+            if (last_type !== 'TK_OPERATOR' && last_type !== 'TK_START_EXPR') {
+                if (last_type === 'TK_START_BLOCK') {
+                    print_newline();
+                } else {
+                    output_space_before_token = true;
+                }
+            } else {
+                // if TK_OPERATOR or TK_START_EXPR
+                if (is_array(previous_flags.mode) && flags.last_text === ',') {
+                    if (last_last_text === '}') {
+                        // }, { in array context
+                        output_space_before_token = true;
+                    } else {