Merge m-c to inbound.
authorRyan VanderMeulen <ryanvm@gmail.com>
Wed, 12 Jun 2013 12:23:19 -0400
changeset 146315 447430ccbcfe5d9d5d47db28e4fa243ead68cf28
parent 146314 6ef1a5998cb6255291155434f19403e811f60814 (current diff)
parent 146268 b51316b2af6c4b8de998ce3f4ddcacb71258c375 (diff)
child 146316 2f1bb682362544f1b71375a33d90255e381ccd03
push id2697
push userbbajaj@mozilla.com
push dateMon, 05 Aug 2013 18:49:53 +0000
treeherdermozilla-beta@dfec938c7b63 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone24.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.
browser/devtools/netmonitor/test/browser_net_post-data.js
toolkit/components/telemetry/Histograms.json
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -138,18 +138,27 @@ pref("app.update.auto", true);
 // Defines how the Application Update Service notifies the user about updates:
 //
 // AUM Set to:        Minor Releases:     Major Releases:
 // 0                  download no prompt  download no prompt
 // 1                  download no prompt  download no prompt if no incompatibilities
 // 2                  download no prompt  prompt
 //
 // See chart in nsUpdateService.js source for more details
+// incompatibilities are ignored by updates in Metro
 //
-pref("app.update.mode", 1);
+pref("app.update.mode", 0);
+
+#ifdef XP_WIN
+#ifdef MOZ_METRO
+// Enables update checking in the Metro environment.
+// add-on incompatibilities are ignored by updates in Metro.
+pref("app.update.metro.enabled", true);
+#endif
+#endif
 
 // If set to true, the Update Service will present no UI for any event.
 pref("app.update.silent", false);
 
 // If set to true, the Update Service will apply updates in the background
 // when it finishes downloading them.
 pref("app.update.staging.enabled", true);
 
@@ -1062,17 +1071,17 @@ pref("devtools.responsiveUI.enabled", tr
 pref("devtools.debugger.enabled", true);
 pref("devtools.debugger.chrome-enabled", true);
 pref("devtools.debugger.chrome-debugging-host", "localhost");
 pref("devtools.debugger.chrome-debugging-port", 6080);
 pref("devtools.debugger.remote-host", "localhost");
 pref("devtools.debugger.remote-autoconnect", false);
 pref("devtools.debugger.remote-connection-retries", 3);
 pref("devtools.debugger.remote-timeout", 20000);
-pref("devtools.debugger.source-maps-enabled", false);
+pref("devtools.debugger.source-maps-enabled", true);
 
 // The default Debugger UI settings
 pref("devtools.debugger.ui.win-x", 0);
 pref("devtools.debugger.ui.win-y", 0);
 pref("devtools.debugger.ui.win-width", 900);
 pref("devtools.debugger.ui.win-height", 400);
 pref("devtools.debugger.ui.panes-sources-width", 200);
 pref("devtools.debugger.ui.panes-instruments-width", 300);
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1261,28 +1261,28 @@ var gBrowserInit = {
     // tabs and to postpone saving the pref to disk.
     try {
       const startupCrashEndDelay = 30 * 1000;
       setTimeout(Services.startup.trackStartupCrashEnd, startupCrashEndDelay);
     } catch (ex) {
       Cu.reportError("Could not end startup crash tracking: " + ex);
     }
 
-    Services.obs.notifyObservers(window, "browser-delayed-startup-finished", "");
-    setTimeout(function () { BrowserChromeTest.markAsReady(); }, 0);
-    TelemetryTimestamps.add("delayedStartupFinished");
-
 #ifdef XP_WIN
 #ifdef MOZ_METRO
     gMetroPrefs.prefDomain.forEach(function(prefName) {
       gMetroPrefs.pushDesktopControlledPrefToMetro(prefName);
       Services.prefs.addObserver(prefName, gMetroPrefs, false);
     }, this);
 #endif
 #endif
+
+    Services.obs.notifyObservers(window, "browser-delayed-startup-finished", "");
+    setTimeout(function () { BrowserChromeTest.markAsReady(); }, 0);
+    TelemetryTimestamps.add("delayedStartupFinished");
   },
 
   onUnload: function() {
     // In certain scenarios it's possible for unload to be fired before onload,
     // (e.g. if the window is being closed after browser.js loads but before the
     // load completes). In that case, there's nothing to do here.
     if (!this._loadHandled)
       return;
@@ -4853,17 +4853,18 @@ function fireSidebarFocusedEvent() {
 #ifdef MOZ_METRO
 /**
  * Some prefs that have consequences in both Metro and Desktop such as
  * app-update prefs, are automatically pushed from Desktop here for use
  * in Metro.
  */
 var gMetroPrefs = {
   prefDomain: ["app.update.auto", "app.update.enabled",
-               "app.update.service.enabled"],
+               "app.update.service.enabled",
+               "app.update.metro.enabled"],
   observe: function (aSubject, aTopic, aPrefName)
   {
     if (aTopic != "nsPref:changed")
       return;
 
     this.pushDesktopControlledPrefToMetro(aPrefName);
   },
 
--- a/browser/components/preferences/advanced.js
+++ b/browser/components/preferences/advanced.js
@@ -32,16 +32,31 @@ var gAdvancedPane = {
     this.updateSetDefaultBrowser();
 #ifdef XP_WIN
     // In Windows 8 we launch the control panel since it's the only
     // way to get all file type association prefs. So we don't know
     // when the user will select the default.  We refresh here periodically
     // in case the default changes.  On other Windows OS's defaults can also
     // be set while the prefs are open.
     window.setInterval(this.updateSetDefaultBrowser, 1000);
+
+#ifdef MOZ_METRO
+    // Pre Windows 8, we should hide the update related settings
+    // for the Metro browser
+    let version = Components.classes["@mozilla.org/system-info;1"].
+                  getService(Components.interfaces.nsIPropertyBag2).
+                  getProperty("version");
+    let preWin8 = parseFloat(version) < 6.2;
+    this._showingWin8Prefs = !preWin8;
+    if (preWin8) {
+      ["autoMetro", "autoMetroIndent"].forEach(
+        function(id) document.getElementById(id).collapsed = true
+      );
+    }
+#endif
 #endif
 #endif
 
 #ifdef MOZ_UPDATER
     this.updateReadPrefs();
 #endif
     this.updateOfflineApps();
 #ifdef MOZ_CRASHREPORTER
@@ -562,38 +577,57 @@ var gAdvancedPane = {
    *                   ii    *f*    t/f     *true*
    *                   iii   0/1/2  f       false
    *                   iii   0/1/2  *t*     *true*
    */
   updateReadPrefs: function ()
   {
     var enabledPref = document.getElementById("app.update.enabled");
     var autoPref = document.getElementById("app.update.auto");
+#ifdef XP_WIN
+#ifdef MOZ_METRO
+    var metroEnabledPref = document.getElementById("app.update.metro.enabled");
+#endif
+#endif
     var radiogroup = document.getElementById("updateRadioGroup");
 
     if (!enabledPref.value)   // Don't care for autoPref.value in this case.
-      radiogroup.value="manual"     // 3. Never check for updates.
+      radiogroup.value="manual";    // 3. Never check for updates.
+#ifdef XP_WIN
+#ifdef MOZ_METRO
+    // enabledPref.value && autoPref.value && metroEnabledPref.value
+    else if (metroEnabledPref.value && this._showingWin8Prefs)
+      radiogroup.value="autoMetro"; // 0. Automatically install updates for both Metro and Desktop
+#endif
+#endif
     else if (autoPref.value)  // enabledPref.value && autoPref.value
-      radiogroup.value="auto";      // 1. Automatically install updates
+      radiogroup.value="auto";      // 1. Automatically install updates for Desktop only
     else                      // enabledPref.value && !autoPref.value
       radiogroup.value="checkOnly"; // 2. Check, but let me choose
 
     var canCheck = Components.classes["@mozilla.org/updates/update-service;1"].
                      getService(Components.interfaces.nsIApplicationUpdateService).
                      canCheckForUpdates;
     // canCheck is false if the enabledPref is false and locked,
     // or the binary platform or OS version is not known.
     // A locked pref is sufficient to disable the radiogroup.
     radiogroup.disabled = !canCheck || enabledPref.locked || autoPref.locked;
 
     var modePref = document.getElementById("app.update.mode");
     var warnIncompatible = document.getElementById("warnIncompatible");
     // the warnIncompatible checkbox value is set by readAddonWarn
     warnIncompatible.disabled = radiogroup.disabled || modePref.locked ||
                                 !enabledPref.value || !autoPref.value;
+#ifdef XP_WIN
+#ifdef MOZ_METRO
+    if (this._showingWin8Prefs) {
+      warnIncompatible.disabled |= metroEnabledPref.value;
+    }
+#endif
+#endif
 
 #ifdef MOZ_MAINTENANCE_SERVICE
     // Check to see if the maintenance service is installed.
     // If it is don't show the preference at all.
     var installed;
     try {
       var wrk = Components.classes["@mozilla.org/windows-registry-key;1"]
                 .createInstance(Components.interfaces.nsIWindowsRegKey);
@@ -631,36 +665,62 @@ var gAdvancedPane = {
   /**
    * Sets the pref values based on the selected item of the radiogroup,
    * and sets the disabled state of the warnIncompatible checkbox accordingly.
    */
   updateWritePrefs: function ()
   {
     var enabledPref = document.getElementById("app.update.enabled");
     var autoPref = document.getElementById("app.update.auto");
+#ifdef XP_WIN
+#ifdef MOZ_METRO
+    var metroEnabledPref = document.getElementById("app.update.metro.enabled");
+    // Initialize the pref to false only if we're showing the option
+    if (this._showingWin8Prefs) {
+      metroEnabledPref.value = false;
+    }
+#endif
+#endif
     var radiogroup = document.getElementById("updateRadioGroup");
     switch (radiogroup.value) {
-      case "auto":      // 1. Automatically install updates
+      case "auto":      // 1. Automatically install updates for Desktop only
         enabledPref.value = true;
         autoPref.value = true;
         break;
+#ifdef XP_WIN
+#ifdef MOZ_METRO
+      case "autoMetro": // 0. Automatically install updates for both Metro and Desktop
+        enabledPref.value = true;
+        autoPref.value = true;
+        metroEnabledPref.value = true;
+        break;
+#endif
+#endif
       case "checkOnly": // 2. Check, but let me choose
         enabledPref.value = true;
         autoPref.value = false;
         break;
       case "manual":    // 3. Never check for updates.
         enabledPref.value = false;
         autoPref.value = false;
     }
 
     var warnIncompatible = document.getElementById("warnIncompatible");
     var modePref = document.getElementById("app.update.mode");
     warnIncompatible.disabled = enabledPref.locked || !enabledPref.value ||
                                 autoPref.locked || !autoPref.value ||
                                 modePref.locked;
+
+#ifdef XP_WIN
+#ifdef MOZ_METRO
+    if (this._showingWin8Prefs) {
+      warnIncompatible.disabled |= metroEnabledPref.value;
+    }
+#endif
+#endif
   },
 
   /**
    * Stores the value of the app.update.mode preference, which is a tristate
    * integer preference.  We store the value here so that we can properly
    * restore the preference value if the UI reflecting the preference value
    * is in a state which can represent either of two integer values (as
    * opposed to only one possible value in the other UI state).
--- a/browser/components/preferences/advanced.xul
+++ b/browser/components/preferences/advanced.xul
@@ -70,16 +70,21 @@
                   name="browser.cache.disk.smart_size.enabled"
                   inverted="true"
                   type="bool"/>
 
      <!-- Update tab -->
 #ifdef MOZ_UPDATER
       <preference id="app.update.enabled"              name="app.update.enabled"              type="bool"/>
       <preference id="app.update.auto"                 name="app.update.auto"                 type="bool"/>
+#ifdef XP_WIN
+#ifdef MOZ_METRO
+      <preference id="app.update.metro.enabled"        name="app.update.metro.enabled"        type="bool"/>
+#endif
+#endif
       <preference id="app.update.mode"                 name="app.update.mode"                 type="int"/>
 
       <preference id="app.update.disable_button.showUpdateHistory"
                   name="app.update.disable_button.showUpdateHistory"
                   type="bool"/>
 
 #ifdef MOZ_MAINTENANCE_SERVICE
       <preference id="app.update.service.enabled"
@@ -335,16 +340,28 @@
 
         <!-- Update -->
         <tabpanel id="updatePanel" orient="vertical">
 #ifdef MOZ_UPDATER
           <groupbox id="updateApp">
             <caption label="&updateApp.label;"/>
             <radiogroup id="updateRadioGroup"
                         oncommand="gAdvancedPane.updateWritePrefs();">
+#ifdef XP_WIN
+#ifdef MOZ_METRO
+              <radio id="autoMetro"
+                     value="autoMetro"
+                     label="&updateAutoMetro.label;"
+                     accesskey="&updateAutoMetro.accesskey;"/>
+              <hbox id="autoMetroIndent"
+                    class="indent">
+                <label value="&updateAutoMetroWarn.label;"/>
+              </hbox>
+#endif
+#endif
               <radio value="auto"
                      label="&updateAuto.label;"
                      accesskey="&updateAuto.accesskey;"/>
               <hbox class="indent">
                 <checkbox id="warnIncompatible"
                           label="&updateAutoAddonWarn.label;"
                           accesskey="&updateAutoAddonWarn.accesskey;"
                           preference="app.update.mode"
--- a/browser/components/preferences/in-content/advanced.js
+++ b/browser/components/preferences/in-content/advanced.js
@@ -24,16 +24,31 @@ var gAdvancedPane = {
     this.updateSetDefaultBrowser();
 #ifdef XP_WIN
     // In Windows 8 we launch the control panel since it's the only
     // way to get all file type association prefs. So we don't know
     // when the user will select the default.  We refresh here periodically
     // in case the default changes.  On other Windows OS's defaults can also
     // be set while the prefs are open.
     window.setInterval(this.updateSetDefaultBrowser, 1000);
+
+#ifdef MOZ_METRO
+    // Pre Windows 8, we should hide the update related settings
+    // for the Metro browser
+    let version = Components.classes["@mozilla.org/system-info;1"].
+                  getService(Components.interfaces.nsIPropertyBag2).
+                  getProperty("version");
+    let preWin8 = parseFloat(version) < 6.2;
+    this._showingWin8Prefs = !preWin8;
+    if (preWin8) {
+      ["autoMetro", "autoMetroIndent"].forEach(
+        function(id) document.getElementById(id).collapsed = true
+      );
+    }
+#endif
 #endif
 #endif
 #ifdef MOZ_UPDATER
     this.updateReadPrefs();
 #endif
     this.updateOfflineApps();
 #ifdef MOZ_CRASHREPORTER
     this.initSubmitCrashes();
@@ -545,20 +560,32 @@ var gAdvancedPane = {
    *                   ii    *f*    t/f     *true*
    *                   iii   0/1/2  f       false
    *                   iii   0/1/2  *t*     *true*
    */
   updateReadPrefs: function ()
   {
     var enabledPref = document.getElementById("app.update.enabled");
     var autoPref = document.getElementById("app.update.auto");
+#ifdef XP_WIN
+#ifdef MOZ_METRO
+    var metroEnabledPref = document.getElementById("app.update.metro.enabled");
+#endif
+#endif
     var radiogroup = document.getElementById("updateRadioGroup");
 
     if (!enabledPref.value)   // Don't care for autoPref.value in this case.
-      radiogroup.value="manual"     // 3. Never check for updates.
+      radiogroup.value="manual";    // 3. Never check for updates.
+#ifdef XP_WIN
+#ifdef MOZ_METRO
+    // enabledPref.value && autoPref.value && metroEnabledPref.value
+    else if (metroEnabledPref.value && this._showingWin8Prefs)
+      radiogroup.value="autoMetro"; // 0. Automatically install updates
+#endif
+#endif
     else if (autoPref.value)  // enabledPref.value && autoPref.value
       radiogroup.value="auto";      // 1. Automatically install updates
     else                      // enabledPref.value && !autoPref.value
       radiogroup.value="checkOnly"; // 2. Check, but let me choose
 
     var canCheck = Components.classes["@mozilla.org/updates/update-service;1"].
                      getService(Components.interfaces.nsIApplicationUpdateService).
                      canCheckForUpdates;
@@ -567,16 +594,23 @@ var gAdvancedPane = {
     // A locked pref is sufficient to disable the radiogroup.
     radiogroup.disabled = !canCheck || enabledPref.locked || autoPref.locked;
 
     var modePref = document.getElementById("app.update.mode");
     var warnIncompatible = document.getElementById("warnIncompatible");
     // the warnIncompatible checkbox value is set by readAddonWarn
     warnIncompatible.disabled = radiogroup.disabled || modePref.locked ||
                                 !enabledPref.value || !autoPref.value;
+#ifdef XP_WIN
+#ifdef MOZ_METRO
+    if (this._showingWin8Prefs) {
+      warnIncompatible.disabled |= metroEnabledPref.value;
+    }
+#endif
+#endif
 
 #ifdef MOZ_MAINTENANCE_SERVICE
     // Check to see if the maintenance service is installed.
     // If it is don't show the preference at all.
     var installed;
     try {
       var wrk = Components.classes["@mozilla.org/windows-registry-key;1"]
                 .createInstance(Components.interfaces.nsIWindowsRegKey);
@@ -596,36 +630,61 @@ var gAdvancedPane = {
   /**
    * Sets the pref values based on the selected item of the radiogroup,
    * and sets the disabled state of the warnIncompatible checkbox accordingly.
    */
   updateWritePrefs: function ()
   {
     var enabledPref = document.getElementById("app.update.enabled");
     var autoPref = document.getElementById("app.update.auto");
+#ifdef XP_WIN
+#ifdef MOZ_METRO
+    var metroEnabledPref = document.getElementById("app.update.metro.enabled");
+    // Initialize the pref to false only if we're showing the option
+    if (this._showingWin8Prefs) {
+      metroEnabledPref.value = false;
+    }
+#endif
+#endif
     var radiogroup = document.getElementById("updateRadioGroup");
     switch (radiogroup.value) {
-      case "auto":      // 1. Automatically install updates
+      case "auto":      // 1. Automatically install updates for Desktop only
         enabledPref.value = true;
         autoPref.value = true;
         break;
+#ifdef XP_WIN
+#ifdef MOZ_METRO
+      case "autoMetro": // 0. Automatically install updates for both Metro and Desktop
+        enabledPref.value = true;
+        autoPref.value = true;
+        metroEnabledPref.value = true;
+        break;
+#endif
+#endif
       case "checkOnly": // 2. Check, but let me choose
         enabledPref.value = true;
         autoPref.value = false;
         break;
       case "manual":    // 3. Never check for updates.
         enabledPref.value = false;
         autoPref.value = false;
     }
 
     var warnIncompatible = document.getElementById("warnIncompatible");
     var modePref = document.getElementById("app.update.mode");
     warnIncompatible.disabled = enabledPref.locked || !enabledPref.value ||
                                 autoPref.locked || !autoPref.value ||
                                 modePref.locked;
+#ifdef XP_WIN
+#ifdef MOZ_METRO
+    if (this._showingWin8Prefs) {
+      warnIncompatible.disabled |= metroEnabledPref.value;
+    }
+#endif
+#endif
   },
 
   /**
    * Stores the value of the app.update.mode preference, which is a tristate
    * integer preference.  We store the value here so that we can properly
    * restore the preference value if the UI reflecting the preference value
    * is in a state which can represent either of two integer values (as
    * opposed to only one possible value in the other UI state).
--- a/browser/components/preferences/in-content/advanced.xul
+++ b/browser/components/preferences/in-content/advanced.xul
@@ -78,16 +78,23 @@
  <!-- Update tab -->
 #ifdef MOZ_UPDATER
   <preference id="app.update.enabled"
               name="app.update.enabled"
               type="bool"/>
   <preference id="app.update.auto"
               name="app.update.auto"
               type="bool"/>
+#ifdef XP_WIN
+#ifdef MOZ_METRO
+  <preference id="app.update.metro.enabled"
+              name="app.update.metro.enabled"
+              type="bool"/>
+#endif
+#endif
   <preference id="app.update.mode"
               name="app.update.mode"
               type="int"/>
 
   <preference id="app.update.disable_button.showUpdateHistory"
               name="app.update.disable_button.showUpdateHistory"
               type="bool"/>
 
@@ -347,16 +354,28 @@
 
     <!-- Update -->
     <tabpanel id="updatePanel" orient="vertical">
 #ifdef MOZ_UPDATER
       <groupbox id="updateApp">
         <caption label="&updateApp.label;"/>
         <radiogroup id="updateRadioGroup"
                     oncommand="gAdvancedPane.updateWritePrefs();">
+#ifdef XP_WIN
+#ifdef MOZ_METRO
+              <radio id="autoMetro"
+                     value="autoMetro"
+                     label="&updateAutoMetro.label;"
+                     accesskey="&updateAutoMetro.accesskey;"/>
+              <hbox id="autoMetroIndent"
+                    class="indent">
+                <label value="&updateAutoMetroWarn.label;"/>
+              </hbox>
+#endif
+#endif
           <radio value="auto"
                 label="&updateAuto.label;"
                 accesskey="&updateAuto.accesskey;"/>
           <hbox class="indent">
             <checkbox id="warnIncompatible"
                       label="&updateAutoAddonWarn.label;"
                       accesskey="&updateAutoAddonWarn.accesskey;"
                       preference="app.update.mode"
--- a/browser/devtools/commandline/test/browser_cmd_appcache_invalid.js
+++ b/browser/devtools/commandline/test/browser_cmd_appcache_invalid.js
@@ -28,22 +28,22 @@ function test() {
             status: 'VALID',
             args: {}
           },
           exec: {
             completed: false,
             output: [
               /Manifest has a character encoding of ISO-8859-1\. Manifests must have the utf-8 character encoding\./,
               /The first line of the manifest must be "CACHE MANIFEST" at line 1\./,
-              /"CACHE MANIFEST" is only valid on the first line at line 3\./,
+              /"CACHE MANIFEST" is only valid on the first line but was found at line 3\./,
               /images\/sound-icon\.png points to a resource that is not available at line 9\./,
               /images\/background\.png points to a resource that is not available at line 10\./,
               /NETWORK section line 13 \(\/checking\.cgi\) prevents caching of line 13 \(\/checking\.cgi\) in the NETWORK section\./,
               /\/checking\.cgi points to a resource that is not available at line 13\./,
-              /Asterisk used as a wildcard in the NETWORK section at line 14\. A single line containing an asterisk is called the online whitelist wildcard flag and is only valid in the NETWORK section\. Other uses of the \* character are prohibited\. The presence of this flag indicates that any URI not listed as cached is to be implicitly treated as being in the online whitelist namespaces\. If the flag is not present then the blocking state indicates that URIs not listed explicitly in the manifest are to be treated as unavailable\./,
+              /Asterisk \(\*\) incorrectly used in the NETWORK section at line 14\. If a line in the NETWORK section contains only a single asterisk character, then any URI not listed in the manifest will be treated as if the URI was listed in the NETWORK section\. Otherwice such URIs will be treated as unavailable\. Other uses of the \* character are prohibited/,
               /\.\.\/rel\.html points to a resource that is not available at line 17\./,
               /\.\.\/\.\.\/rel\.html points to a resource that is not available at line 18\./,
               /\.\.\/\.\.\/\.\.\/rel\.html points to a resource that is not available at line 19\./,
               /\.\.\/\.\.\/\.\.\/\.\.\/rel\.html points to a resource that is not available at line 20\./,
               /\.\.\/\.\.\/\.\.\/\.\.\/\.\.\/rel\.html points to a resource that is not available at line 21\./,
               /\/\.\.\/ is not a valid URI prefix at line 22\./,
               /\/test\.css points to a resource that is not available at line 23\./,
               /\/test\.js points to a resource that is not available at line 24\./,
@@ -59,24 +59,24 @@ function test() {
               /\/settings\/home points to a resource that is not available at line 37\./,
               /\/settings\/app\.js points to a resource that is not available at line 38\./,
               /The file http:\/\/sub1\.test1\.example\.com\/browser\/browser\/devtools\/commandline\/test\/browser_cmd_appcache_invalid_page3\.html was modified after http:\/\/sub1\.test1\.example\.com\/browser\/browser\/devtools\/commandline\/test\/browser_cmd_appcache_invalid_appcache\.appcache\. Unless the text in the manifest file is changed the cached version will be used instead at line 39\./,
               /browser_cmd_appcache_invalid_page3\.html has cache-control set to no-store\. This will prevent the application cache from storing the file at line 39\./,
               /http:\/\/example\.com\/logo\.png points to a resource that is not available at line 40\./,
               /http:\/\/example\.com\/check\.png points to a resource that is not available at line 41\./,
               /Spaces in URIs need to be replaced with % at line 42\./,
               /http:\/\/example\.com\/cr oss\.png points to a resource that is not available at line 42\./,
-              /Asterisk used as a wildcard in the CACHE section at line 43\. A single line containing an asterisk is called the online whitelist wildcard flag and is only valid in the NETWORK section\. Other uses of the \* character are prohibited\. The presence of this flag indicates that any URI not listed as cached is to be implicitly treated as being in the online whitelist namespaces\. If the flag is not present then the blocking state indicates that URIs not listed explicitly in the manifest are to be treated as unavailable\./,
+              /Asterisk \(\*\) incorrectly used in the CACHE section at line 43\. If a line in the NETWORK section contains only a single asterisk character, then any URI not listed in the manifest will be treated as if the URI was listed in the NETWORK section\. Otherwice such URIs will be treated as unavailable\. Other uses of the \* character are prohibited/,
               /The SETTINGS section may only contain a single value, "prefer-online" or "fast" at line 47\./,
               /FALLBACK section line 50 \(\/section1\/ \/offline1\.html\) prevents caching of line 30 \(\/section1\/blockedbyfallback\.html\) in the CACHE section\./,
               /\/offline1\.html points to a resource that is not available at line 50\./,
               /FALLBACK section line 51 \(\/section2\/ offline2\.html\) prevents caching of line 32 \(\/section2\/blockedbyfallback\.html\) in the CACHE section\./,
               /offline2\.html points to a resource that is not available at line 51\./,
               /Only two URIs separated by spaces are allowed in the FALLBACK section at line 52\./,
-              /Asterisk \(\*\) incorrectly used as a wildcard in a fallback namespace at line 53\. Namespaces simply need to match a path prefix\./,
+              /Asterisk \(\*\) incorrectly used in the FALLBACK section at line 53\. URIs in the FALLBACK section simply need to match a prefix of the request URI\./,
               /offline3\.html points to a resource that is not available at line 53\./,
               /Invalid section name \(BLAH\) at line 55\./,
               /Only two URIs separated by spaces are allowed in the FALLBACK section at line 55\./
             ]
           },
         },
       ]));
     });
--- a/browser/devtools/debugger/debugger-controller.js
+++ b/browser/devtools/debugger/debugger-controller.js
@@ -423,16 +423,17 @@ StackFrames.prototype = {
   get activeThread() DebuggerController.activeThread,
   autoScopeExpand: false,
   currentFrame: null,
   syncedWatchExpressions: null,
   currentWatchExpressions: null,
   currentBreakpointLocation: null,
   currentEvaluation: null,
   currentException: null,
+  currentReturnedValue: null,
 
   /**
    * Connect to the current thread client.
    */
   connect: function() {
     dumpn("StackFrames is connecting...");
     this.activeThread.addListener("paused", this._onPaused);
     this.activeThread.addListener("resumed", this._onResumed);
@@ -480,16 +481,27 @@ StackFrames.prototype = {
       // If paused by a client evaluation, store the evaluated value.
       case "clientEvaluated":
         this.currentEvaluation = aPacket.why.frameFinished;
         break;
       // If paused by an exception, store the exception value.
       case "exception":
         this.currentException = aPacket.why.exception;
         break;
+      // If paused while stepping out of a frame, store the returned value or
+      // thrown exception.
+      case "resumeLimit":
+        if (!aPacket.why.frameFinished) {
+          break;
+        } else if (aPacket.why.frameFinished.throw) {
+          this.currentException = aPacket.why.frameFinished.throw;
+        } else if (aPacket.why.frameFinished.return) {
+          this.currentReturnedValue = aPacket.why.frameFinished.return;
+        }
+        break;
     }
 
     this.activeThread.fillFrames(CALL_STACK_PAGE_SIZE);
     DebuggerView.editor.focus();
   },
 
   /**
    * Handler for the thread client's resumed notification.
@@ -590,16 +602,17 @@ StackFrames.prototype = {
    * Handler for the thread client's framescleared notification.
    */
   _onFramesCleared: function() {
     this.currentFrame = null;
     this.currentWatchExpressions = null;
     this.currentBreakpointLocation = null;
     this.currentEvaluation = null;
     this.currentException = null;
+    this.currentReturnedValue = null;
     // After each frame step (in, over, out), framescleared is fired, which
     // forces the UI to be emptied and rebuilt on framesadded. Most of the times
     // this is not necessary, and will result in a brief redraw flicker.
     // To avoid it, invalidate the UI only after a short time if necessary.
     window.setTimeout(this._afterFramesCleared, FRAME_STEP_CLEAR_DELAY);
   },
 
   /**
@@ -827,16 +840,21 @@ StackFrames.prototype = {
    *        The frame to get some references from.
    */
   _insertScopeFrameReferences: function(aScope, aFrame) {
     // Add any thrown exception.
     if (this.currentException) {
       let excRef = aScope.addVar("<exception>", { value: this.currentException });
       this._addVarExpander(excRef, this.currentException);
     }
+    // Add any returned value.
+    if (this.currentReturnedValue) {
+      let retRef = aScope.addVar("<return>", { value: this.currentReturnedValue });
+      this._addVarExpander(retRef, this.currentReturnedValue);
+    }
     // Add "this".
     if (aFrame.this) {
       let thisRef = aScope.addVar("this", { value: aFrame.this });
       this._addVarExpander(thisRef, aFrame.this);
     }
   },
 
   /**
--- a/browser/devtools/debugger/test/Makefile.in
+++ b/browser/devtools/debugger/test/Makefile.in
@@ -97,16 +97,17 @@ MOCHITEST_BROWSER_TESTS = \
 	browser_dbg_pause-exceptions.js \
 	browser_dbg_multiple-windows.js \
 	browser_dbg_iframes.js \
 	browser_dbg_bfcache.js \
 	browser_dbg_progress-listener-bug.js \
 	browser_dbg_chrome-debugging.js \
 	browser_dbg_source_maps-01.js \
 	browser_dbg_source_maps-02.js \
+	browser_dbg_step-out.js \
 	head.js \
 	$(NULL)
 
 MOCHITEST_BROWSER_PAGES = \
 	browser_dbg_cmd_break.html \
 	browser_dbg_cmd.html \
 	testactors.js \
 	browser_dbg_tab1.html \
@@ -134,16 +135,17 @@ MOCHITEST_BROWSER_PAGES = \
 	test-function-search-02.js \
 	test-function-search-03.js \
 	binary_search.html \
 	binary_search.coffee \
 	binary_search.js \
 	binary_search.map \
 	test-location-changes-bp.js \
 	test-location-changes-bp.html \
+	test-step-out.html \
 	$(NULL)
 
 ifneq (Linux,$(OS_ARCH))
 MOCHITEST_BROWSER_TESTS += \
 	browser_dbg_createChrome.js \
 	$(NULL)
 else
 $(browser_dbg_createChrome.js disabled to fix for ubuntu hangs, bug 847558)
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_step-out.js
@@ -0,0 +1,133 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Make sure that stepping out of a function displays the right return value.
+ */
+
+const TAB_URL = EXAMPLE_URL + "test-step-out.html";
+
+var gPane = null;
+var gTab = null;
+var gDebugger = null;
+
+function test()
+{
+  debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
+    gTab = aTab;
+    gPane = aPane;
+    gDebugger = gPane.panelWin;
+
+    expectUncaughtException();
+    testNormalReturn();
+  });
+}
+
+function testNormalReturn()
+{
+  gPane.panelWin.gClient.addOneTimeListener("paused", function() {
+    gDebugger.addEventListener("Debugger:SourceShown", function dbgstmt(aEvent) {
+      gDebugger.removeEventListener(aEvent.type, dbgstmt);
+      is(gDebugger.DebuggerController.activeThread.state, "paused",
+        "Should be paused now.");
+
+      let count = 0;
+      gPane.panelWin.gClient.addOneTimeListener("paused", function() {
+        is(gDebugger.DebuggerController.activeThread.state, "paused",
+          "Should be paused again.");
+
+        gDebugger.addEventListener("Debugger:FetchedVariables", function stepout() {
+          ok(true, "Debugger:FetchedVariables event received.");
+          gDebugger.removeEventListener("Debugger:FetchedVariables", stepout, false);
+
+          Services.tm.currentThread.dispatch({ run: function() {
+
+            var scopes = gDebugger.DebuggerView.Variables._list,
+                innerScope = scopes.firstChild,
+                innerNodes = innerScope.querySelector(".variables-view-element-details").childNodes;
+
+            is(innerNodes[0].querySelector(".name").getAttribute("value"), "<return>",
+              "Should have the right property name for the return value.");
+
+            is(innerNodes[0].querySelector(".value").getAttribute("value"), 10,
+              "Should have the right property value for the return value.");
+
+            testReturnWithException();
+          }}, 0);
+        }, false);
+      });
+
+      EventUtils.sendMouseEvent({ type: "mousedown" },
+        gDebugger.document.getElementById("step-out"),
+        gDebugger);
+    });
+  });
+
+  EventUtils.sendMouseEvent({ type: "click" },
+    content.document.getElementById("return"),
+    content.window);
+}
+
+function testReturnWithException()
+{
+  gDebugger.DebuggerController.activeThread.resume(function() {
+    gPane.panelWin.gClient.addOneTimeListener("paused", function() {
+      gDebugger.addEventListener("Debugger:FetchedVariables", function dbgstmt(aEvent) {
+        gDebugger.removeEventListener(aEvent.type, dbgstmt, false);
+        is(gDebugger.DebuggerController.activeThread.state, "paused",
+          "Should be paused now.");
+
+        let count = 0;
+        gPane.panelWin.gClient.addOneTimeListener("paused", function() {
+          is(gDebugger.DebuggerController.activeThread.state, "paused",
+            "Should be paused again.");
+
+          gDebugger.addEventListener("Debugger:FetchedVariables", function stepout() {
+            ok(true, "Debugger:FetchedVariables event received.");
+            gDebugger.removeEventListener("Debugger:FetchedVariables", stepout, false);
+
+            Services.tm.currentThread.dispatch({ run: function() {
+
+              var scopes = gDebugger.DebuggerView.Variables._list,
+                  innerScope = scopes.firstChild,
+                  innerNodes = innerScope.querySelector(".variables-view-element-details").childNodes;
+
+              is(innerNodes[0].querySelector(".name").getAttribute("value"), "<exception>",
+                "Should have the right property name for the exception value.");
+
+              is(innerNodes[0].querySelector(".value").getAttribute("value"), '"boom"',
+                "Should have the right property value for the exception value.");
+
+              resumeAndFinish();
+
+            }}, 0);
+          }, false);
+        });
+
+        EventUtils.sendMouseEvent({ type: "mousedown" },
+          gDebugger.document.getElementById("step-out"),
+          gDebugger);
+      }, false);
+    });
+
+    EventUtils.sendMouseEvent({ type: "click" },
+      content.document.getElementById("throw"),
+      content.window);
+  });
+}
+
+function resumeAndFinish() {
+  gPane.panelWin.gClient.addOneTimeListener("resumed", function() {
+    Services.tm.currentThread.dispatch({ run: closeDebuggerAndFinish }, 0);
+  });
+
+  gDebugger.DebuggerController.activeThread.resume();
+}
+
+registerCleanupFunction(function() {
+  removeTab(gTab);
+  gPane = null;
+  gTab = null;
+  gDebugger = null;
+});
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/test-step-out.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <meta charset='utf-8'/>
+    <title>Debugger Step Out Return Value Test</title>
+    <!-- Any copyright is dedicated to the Public Domain.
+         http://creativecommons.org/publicdomain/zero/1.0/ -->
+  </head>
+  <body>
+    <button id="return">return</button>
+    <button id="throw">throw</button>
+  </body>
+  <script type="text/javascript">
+    window.addEventListener("load", function() {
+      function normal(aArg) {
+        debugger;
+        var r = 10;
+        return r;
+      }
+      function error(aArg) {
+        debugger;
+        var r = 10;
+        throw "boom";
+        return r;
+      }
+      var button = document.getElementById("return");
+      button.addEventListener("click", normal, false);
+      button = document.getElementById("throw");
+      button.addEventListener("click", error, false);
+    });
+  </script>
+</html>
--- a/browser/devtools/inspector/inspector-panel.js
+++ b/browser/devtools/inspector/inspector-panel.js
@@ -117,38 +117,50 @@ InspectorPanel.prototype = {
     this._initMarkup();
     this.isReady = false;
 
     this.once("markuploaded", function() {
       this.isReady = true;
 
       // All the components are initialized. Let's select a node.
       if (this.target.isLocalTab) {
-        let root = this.browser.contentDocument.documentElement;
-        this._selection.setNode(root);
+        this._selection.setNode(
+            this._getDefaultNodeForSelection(this.browser.contentDocument));
       } else if (this.target.window) {
-        let root = this.target.window.document.documentElement;
-        this._selection.setNode(root);
+        this._selection.setNode(
+            this._getDefaultNodeForSelection(this.target.window.document));
       }
 
       if (this.highlighter) {
         this.highlighter.unlock();
       }
 
+      this.markup.expandNode(this.selection.node);
+
       this.emit("ready");
       deferred.resolve(this);
     }.bind(this));
 
     this.setupSearchBox();
     this.setupSidebar();
 
     return deferred.promise;
   },
 
   /**
+   * Select node for default selection
+   */
+  _getDefaultNodeForSelection : function(document) {
+    // if available set body node as default selected node
+    // else set documentElement
+    var defaultNode = document.body || document.documentElement;
+    return defaultNode;
+  },
+
+  /**
    * Selection object (read only)
    */
   get selection() {
     return this._selection;
   },
 
   /**
    * Target getter.
@@ -248,31 +260,36 @@ InspectorPanel.prototype = {
   /**
    * Reset the inspector on navigate away.
    */
   onNavigatedAway: function InspectorPanel_onNavigatedAway(event, payload) {
     let newWindow = payload._navPayload || payload;
     this.selection.setNode(null);
     this._destroyMarkup();
     this.isDirty = false;
-    let self = this;
 
-    function onDOMReady() {
+    let onDOMReady = function() {
       newWindow.removeEventListener("DOMContentLoaded", onDOMReady, true);
 
-      if (self._destroyed) {
+      if (this._destroyed) {
         return;
       }
 
-      if (!self.selection.node) {
-        self.selection.setNode(newWindow.document.documentElement, "navigateaway");
+      if (!this.selection.node) {
+        let defaultNode = this._getDefaultNodeForSelection(newWindow.document);
+        this.selection.setNode(defaultNode, "navigateaway");
       }
-      self._initMarkup();
-      self.setupSearchBox();
-    }
+      this._initMarkup();
+
+      this.once("markuploaded", () => {
+        this.markup.expandNode(this.selection.node);
+      });
+
+      this.setupSearchBox();
+    }.bind(this);
 
     if (newWindow.document.readyState == "loading") {
       newWindow.addEventListener("DOMContentLoaded", onDOMReady, true);
     } else {
       onDOMReady();
     }
   },
 
--- a/browser/devtools/inspector/test/browser_inspector_bug_672902_keyboard_shortcuts.js
+++ b/browser/devtools/inspector/test/browser_inspector_bug_672902_keyboard_shortcuts.js
@@ -30,38 +30,26 @@ function test()
     openInspector(findAndHighlightNode);
   }
 
   function findAndHighlightNode(aInspector, aToolbox)
   {
     inspector = aInspector;
 
     executeSoon(function() {
-      inspector.selection.once("new-node", highlightBodyNode);
+      inspector.selection.once("new-node", highlightHeaderNode);
       // Test that navigating around without a selected node gets us to the
-      // body element.
-      node = doc.querySelector("body");
+      // head element.
+      node = doc.querySelector("h1");
       let bc = inspector.breadcrumbs;
       bc.nodeHierarchy[bc.currentIndex].button.focus();
       EventUtils.synthesizeKey("VK_RIGHT", { });
     });
   }
 
-  function highlightBodyNode()
-  {
-    is(inspector.selection.node, node, "selected body element");
-
-    executeSoon(function() {
-      inspector.selection.once("new-node", highlightHeaderNode);
-      // Test that moving to the child works.
-      node = doc.querySelector("h1");
-      EventUtils.synthesizeKey("VK_RIGHT", { });
-    });
-  }
-
   function highlightHeaderNode()
   {
     is(inspector.selection.node, node, "selected h1 element");
 
     executeSoon(function() {
       inspector.selection.once("new-node", highlightParagraphNode);
       // Test that moving to the next sibling works.
       node = doc.querySelector("p");
--- a/browser/devtools/markupview/test/browser_inspector_markup_edit.js
+++ b/browser/devtools/markupview/test/browser_inspector_markup_edit.js
@@ -250,24 +250,17 @@ function test() {
     waitForFocus(setupTest, content);
   }, true);
   content.location = "http://mochi.test:8888/browser/browser/devtools/markupview/test/browser_inspector_markup_edit.html";
 
   function setupTest() {
     var target = TargetFactory.forTab(gBrowser.selectedTab);
     gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
       inspector = toolbox.getCurrentPanel();
-      runTests();
-    });
-  }
-
-  function runTests() {
-    inspector.selection.once("new-node", startTests);
-    executeSoon(function() {
-      inspector.selection.setNode(doc.body);
+      startTests();
     });
   }
 
   function startTests() {
     let startNode = doc.documentElement.cloneNode();
     markup = inspector.markup;
     markup.expandAll();
 
--- a/browser/devtools/markupview/test/browser_inspector_markup_navigation.js
+++ b/browser/devtools/markupview/test/browser_inspector_markup_navigation.js
@@ -5,20 +5,20 @@ http://creativecommons.org/publicdomain/
 function test() {
   let inspector;
 
   waitForExplicitFinish();
 
   let doc;
 
   let keySequences = [
-    ["right", "html"],
+    ["pageup", "*doctype*"],
+    ["down", "html"],
     ["down", "head"],
     ["down", "body"],
-    ["right", "body"],
     ["down", "node0"],
     ["right", "node0"],
     ["down", "node1"],
     ["down", "node2"],
     ["down", "node3"],
     ["down", "*comment*"],
     ["down", "node4"],
     ["right", "node4"],
--- a/browser/devtools/netmonitor/netmonitor-view.js
+++ b/browser/devtools/netmonitor/netmonitor-view.js
@@ -1310,16 +1310,17 @@ NetworkDetailsView.prototype = {
    *
    * @param object aData
    *        The data source (this should be the attachment of a request item).
    */
   populate: function(aData) {
     $("#request-params-box").setAttribute("flex", "1");
     $("#request-params-box").hidden = false;
     $("#request-post-data-textarea-box").hidden = true;
+    $("#response-content-info-header").hidden = true;
     $("#response-content-json-box").hidden = true;
     $("#response-content-textarea-box").hidden = true;
     $("#response-content-image-box").hidden = true;
 
     this._headers.empty();
     this._cookies.empty();
     this._params.empty();
     this._json.empty();
@@ -1524,30 +1525,33 @@ NetworkDetailsView.prototype = {
     }
   },
 
   /**
    * Sets the network request post params shown in this view.
    *
    * @param object aHeadersResponse
    *        The "requestHeaders" message received from the server.
-   * @param object aPostResponse
+   * @param object aPostDataResponse
    *        The "requestPostData" message received from the server.
    */
-  _setRequestPostParams: function(aHeadersResponse, aPostResponse) {
-    if (!aHeadersResponse || !aPostResponse) {
+  _setRequestPostParams: function(aHeadersResponse, aPostDataResponse) {
+    if (!aHeadersResponse || !aPostDataResponse) {
       return;
     }
-    let contentType = aHeadersResponse.headers.filter(({ name }) => name == "Content-Type")[0];
-    let text = aPostResponse.postData.text;
-
-    gNetwork.getString(text).then((aString) => {
+    gNetwork.getString(aPostDataResponse.postData.text).then((aString) => {
       // Handle query strings (poor man's forms, e.g. "?foo=bar&baz=42").
-      if (contentType.value.contains("x-www-form-urlencoded")) {
-        this._addParams(this._paramsFormData, aString);
+      let cType = aHeadersResponse.headers.filter(({ name }) => name == "Content-Type")[0];
+      let cString = cType ? cType.value : "";
+      if (cString.contains("x-www-form-urlencoded") ||
+          aString.contains("x-www-form-urlencoded")) {
+        let formDataGroups = aString.split(/\r\n|\n|\r/);
+        for (let group of formDataGroups) {
+          this._addParams(this._paramsFormData, group);
+        }
       }
       // Handle actual forms ("multipart/form-data" content type).
       else {
         // This is really awkward, but hey, it works. Let's show an empty
         // scope in the params view and place the source editor containing
         // the raw post data directly underneath.
         $("#request-params-box").removeAttribute("flex");
         let paramsScope = this._params.addScope(this._paramsPostPayload);
@@ -1567,16 +1571,20 @@ NetworkDetailsView.prototype = {
    * Populates the params container in this view with the specified data.
    *
    * @param string aName
    *        The type of params to populate (get or post).
    * @param string aParams
    *        A query string of params (e.g. "?foo=bar&baz=42").
    */
   _addParams: function(aName, aParams) {
+    // Make sure there's at least one param available.
+    if (!aParams || !aParams.contains("=")) {
+      return;
+    }
     // Turn the params string into an array containing { name: value } tuples.
     let paramsArray = aParams.replace(/^[?&]/, "").split("&").map((e) =>
       let (param = e.split("=")) {
         name: NetworkHelper.convertToUnicode(unescape(param[0])),
         value: NetworkHelper.convertToUnicode(unescape(param[1]))
       });
 
     let paramsScope = this._params.addScope(aName);
@@ -1600,28 +1608,52 @@ NetworkDetailsView.prototype = {
     if (!aResponse) {
       return;
     }
     let { mimeType, text, encoding } = aResponse.content;
 
     gNetwork.getString(text).then((aString) => {
       // Handle json.
       if (mimeType.contains("/json")) {
-        $("#response-content-json-box").hidden = false;
         let jsonpRegex = /^[a-zA-Z0-9_$]+\(|\)$/g; // JSONP with callback.
         let sanitizedJSON = aString.replace(jsonpRegex, "");
         let callbackPadding = aString.match(jsonpRegex);
 
-        let jsonScopeName = callbackPadding
-          ? L10N.getFormatStr("jsonpScopeName", callbackPadding[0].slice(0, -1))
-          : L10N.getStr("jsonScopeName");
+        // Make sure this is an valid JSON object first. If so, nicely display
+        // the parsing results in a variables view. Otherwise, simply show
+        // the contents as plain text.
+        try {
+          var jsonObject = JSON.parse(sanitizedJSON);
+        } catch (e) {
+          var parsingError = e;
+        }
+
+        // Valid JSON.
+        if (jsonObject) {
+          $("#response-content-json-box").hidden = false;
+          let jsonScopeName = callbackPadding
+            ? L10N.getFormatStr("jsonpScopeName", callbackPadding[0].slice(0, -1))
+            : L10N.getStr("jsonScopeName");
 
-        let jsonScope = this._json.addScope(jsonScopeName);
-        jsonScope.addVar().populate(JSON.parse(sanitizedJSON), { expanded: true });
-        jsonScope.expanded = true;
+          let jsonScope = this._json.addScope(jsonScopeName);
+          jsonScope.addVar().populate(jsonObject, { expanded: true });
+          jsonScope.expanded = true;
+        }
+        // Malformed JSON.
+        else {
+          $("#response-content-textarea-box").hidden = false;
+          NetMonitorView.editor("#response-content-textarea").then((aEditor) => {
+            aEditor.setMode(SourceEditor.MODES.JAVASCRIPT);
+            aEditor.setText(aString);
+          });
+          let infoHeader = $("#response-content-info-header");
+          infoHeader.setAttribute("value", parsingError);
+          infoHeader.setAttribute("tooltiptext", parsingError);
+          infoHeader.hidden = false;
+        }
       }
       // Handle images.
       else if (mimeType.contains("image/")) {
         $("#response-content-image-box").setAttribute("align", "center");
         $("#response-content-image-box").setAttribute("pack", "center");
         $("#response-content-image-box").hidden = false;
         $("#response-content-image").src =
           "data:" + mimeType + ";" + encoding + "," + aString;
--- a/browser/devtools/netmonitor/netmonitor.xul
+++ b/browser/devtools/netmonitor/netmonitor.xul
@@ -267,16 +267,17 @@
             <vbox id="request-post-data-textarea-box" flex="1" hidden="true">
               <vbox id="request-post-data-textarea" flex="1"/>
             </vbox>
           </vbox>
         </tabpanel>
         <tabpanel id="response-tabpanel"
                   class="tabpanel-content">
           <vbox flex="1">
+            <label id="response-content-info-header"/>
             <vbox id="response-content-json-box" flex="1" hidden="true">
               <vbox id="response-content-json" flex="1"/>
             </vbox>
             <vbox id="response-content-textarea-box" flex="1" hidden="true">
               <vbox id="response-content-textarea" flex="1"/>
             </vbox>
             <vbox id="response-content-image-box" flex="1" hidden="true">
               <image id="response-content-image"/>
--- a/browser/devtools/netmonitor/test/Makefile.in
+++ b/browser/devtools/netmonitor/test/Makefile.in
@@ -22,19 +22,21 @@ MOCHITEST_BROWSER_TESTS = \
 	browser_net_simple-request.js \
 	browser_net_simple-request-data.js \
 	browser_net_simple-request-details.js \
 	browser_net_content-type.js \
 	browser_net_cyrillic-01.js \
 	browser_net_cyrillic-02.js \
 	browser_net_large-response.js \
 	browser_net_status-codes.js \
-	browser_net_post-data.js \
+	browser_net_post-data-01.js \
+	browser_net_post-data-02.js \
 	browser_net_jsonp.js \
 	browser_net_json-long.js \
+	browser_net_json-malformed.js \
 	browser_net_timeline_ticks.js \
 	browser_net_sort-01.js \
 	browser_net_sort-02.js \
 	browser_net_sort-03.js \
 	browser_net_filter-01.js \
 	browser_net_filter-02.js \
 	browser_net_filter-03.js \
 	browser_net_accessibility-01.js \
@@ -46,18 +48,20 @@ MOCHITEST_BROWSER_TESTS = \
 MOCHITEST_BROWSER_PAGES = \
 	test-image.png \
 	html_simple-test-page.html \
 	html_navigate-test-page.html \
 	html_content-type-test-page.html \
 	html_cyrillic-test-page.html \
 	html_status-codes-test-page.html \
 	html_post-data-test-page.html \
+	html_post-raw-test-page.html \
 	html_jsonp-test-page.html \
 	html_json-long-test-page.html \
+	html_json-malformed-test-page.html \
 	html_sorting-test-page.html \
 	html_filter-test-page.html \
 	html_infinite-get-page.html \
 	html_custom-get-page.html \
 	sjs_simple-test-server.sjs \
 	sjs_content-type-test-server.sjs \
 	sjs_status-codes-test-server.sjs \
 	sjs_sorting-test-server.sjs \
--- a/browser/devtools/netmonitor/test/browser_net_content-type.js
+++ b/browser/devtools/netmonitor/test/browser_net_content-type.js
@@ -104,24 +104,25 @@ function test() {
       function testResponseTab(aType) {
         let tab = document.querySelectorAll("#details-pane tab")[3];
         let tabpanel = document.querySelectorAll("#details-pane tabpanel")[3];
 
         is(tab.getAttribute("selected"), "true",
           "The response tab in the network details pane should be selected.");
 
         function checkVisibility(aBox) {
+          is(tabpanel.querySelector("#response-content-info-header")
+            .hasAttribute("hidden"), true,
+            "The response info header doesn't have the intended visibility.");
           is(tabpanel.querySelector("#response-content-json-box")
             .hasAttribute("hidden"), aBox != "json",
             "The response content json box doesn't have the intended visibility.");
-
           is(tabpanel.querySelector("#response-content-textarea-box")
             .hasAttribute("hidden"), aBox != "textarea",
             "The response content textarea box doesn't have the intended visibility.");
-
           is(tabpanel.querySelector("#response-content-image-box")
             .hasAttribute("hidden"), aBox != "image",
             "The response content image box doesn't have the intended visibility.");
         }
 
         switch (aType) {
           case "xml": {
             checkVisibility("textarea");
--- a/browser/devtools/netmonitor/test/browser_net_json-long.js
+++ b/browser/devtools/netmonitor/test/browser_net_json-long.js
@@ -41,24 +41,25 @@ function test() {
 
       function testResponseTab() {
         let tab = document.querySelectorAll("#details-pane tab")[3];
         let tabpanel = document.querySelectorAll("#details-pane tabpanel")[3];
 
         is(tab.getAttribute("selected"), "true",
           "The response tab in the network details pane should be selected.");
 
+        is(tabpanel.querySelector("#response-content-info-header")
+          .hasAttribute("hidden"), true,
+          "The response info header doesn't have the intended visibility.");
         is(tabpanel.querySelector("#response-content-json-box")
           .hasAttribute("hidden"), false,
           "The response content json box doesn't have the intended visibility.");
-
         is(tabpanel.querySelector("#response-content-textarea-box")
           .hasAttribute("hidden"), true,
           "The response content textarea box doesn't have the intended visibility.");
-
         is(tabpanel.querySelector("#response-content-image-box")
           .hasAttribute("hidden"), true,
           "The response content image box doesn't have the intended visibility.");
 
         is(tabpanel.querySelectorAll(".variables-view-scope").length, 1,
           "There should be 1 json scope displayed in this tabpanel.");
         is(tabpanel.querySelectorAll(".variables-view-property").length, 6057,
           "There should be 6057 json properties displayed in this tabpanel.");
new file mode 100644
--- /dev/null
+++ b/browser/devtools/netmonitor/test/browser_net_json-malformed.js
@@ -0,0 +1,71 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests if malformed JSON responses are handled correctly.
+ */
+
+function test() {
+  initNetMonitor(JSON_MALFORMED_URL).then(([aTab, aDebuggee, aMonitor]) => {
+    info("Starting test... ");
+
+    let { document, SourceEditor, NetMonitorView } = aMonitor.panelWin;
+    let { RequestsMenu } = NetMonitorView;
+
+    RequestsMenu.lazyUpdate = false;
+
+    waitForNetworkEvents(aMonitor, 1).then(() => {
+      verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0),
+        "GET", CONTENT_TYPE_SJS + "?fmt=json-malformed", {
+          status: 200,
+          statusText: "OK",
+          type: "json",
+          fullMimeType: "text/json; charset=utf-8"
+        });
+
+      EventUtils.sendMouseEvent({ type: "mousedown" },
+        document.getElementById("details-pane-toggle"));
+      EventUtils.sendMouseEvent({ type: "mousedown" },
+        document.querySelectorAll("#details-pane tab")[3]);
+
+      let tab = document.querySelectorAll("#details-pane tab")[3];
+      let tabpanel = document.querySelectorAll("#details-pane tabpanel")[3];
+
+      is(tab.getAttribute("selected"), "true",
+        "The response tab in the network details pane should be selected.");
+
+      is(tabpanel.querySelector("#response-content-info-header")
+        .hasAttribute("hidden"), false,
+        "The response info header doesn't have the intended visibility.");
+      is(tabpanel.querySelector("#response-content-info-header")
+        .getAttribute("value"),
+        "SyntaxError: JSON.parse: unexpected non-whitespace character after JSON data",
+        "The response info header doesn't have the intended value attribute.");
+      is(tabpanel.querySelector("#response-content-info-header")
+        .getAttribute("tooltiptext"),
+        "SyntaxError: JSON.parse: unexpected non-whitespace character after JSON data",
+        "The response info header doesn't have the intended tooltiptext attribute.");
+
+      is(tabpanel.querySelector("#response-content-json-box")
+        .hasAttribute("hidden"), true,
+        "The response content json box doesn't have the intended visibility.");
+      is(tabpanel.querySelector("#response-content-textarea-box")
+        .hasAttribute("hidden"), false,
+        "The response content textarea box doesn't have the intended visibility.");
+      is(tabpanel.querySelector("#response-content-image-box")
+        .hasAttribute("hidden"), true,
+        "The response content image box doesn't have the intended visibility.");
+
+      NetMonitorView.editor("#response-content-textarea").then((aEditor) => {
+        is(aEditor.getText(), "{ \"greeting\": \"Hello malformed JSON!\" },",
+          "The text shown in the source editor is incorrect.");
+        is(aEditor.getMode(), SourceEditor.MODES.JAVASCRIPT,
+          "The mode active in the source editor is incorrect.");
+
+        teardown(aMonitor).then(finish);
+      });
+    });
+
+    aDebuggee.performRequests();
+  });
+}
--- a/browser/devtools/netmonitor/test/browser_net_jsonp.js
+++ b/browser/devtools/netmonitor/test/browser_net_jsonp.js
@@ -35,24 +35,25 @@ function test() {
 
       function testResponseTab() {
         let tab = document.querySelectorAll("#details-pane tab")[3];
         let tabpanel = document.querySelectorAll("#details-pane tabpanel")[3];
 
         is(tab.getAttribute("selected"), "true",
           "The response tab in the network details pane should be selected.");
 
+        is(tabpanel.querySelector("#response-content-info-header")
+          .hasAttribute("hidden"), true,
+          "The response info header doesn't have the intended visibility.");
         is(tabpanel.querySelector("#response-content-json-box")
           .hasAttribute("hidden"), false,
           "The response content json box doesn't have the intended visibility.");
-
         is(tabpanel.querySelector("#response-content-textarea-box")
           .hasAttribute("hidden"), true,
           "The response content textarea box doesn't have the intended visibility.");
-
         is(tabpanel.querySelector("#response-content-image-box")
           .hasAttribute("hidden"), true,
           "The response content image box doesn't have the intended visibility.");
 
         is(tabpanel.querySelectorAll(".variables-view-scope").length, 1,
           "There should be 1 json scope displayed in this tabpanel.");
         is(tabpanel.querySelectorAll(".variables-view-property").length, 2,
           "There should be 2 json properties displayed in this tabpanel.");
rename from browser/devtools/netmonitor/test/browser_net_post-data.js
rename to browser/devtools/netmonitor/test/browser_net_post-data-01.js
--- a/browser/devtools/netmonitor/test/browser_net_post-data.js
+++ b/browser/devtools/netmonitor/test/browser_net_post-data-01.js
@@ -56,17 +56,16 @@ function test() {
 
         is(tab.getAttribute("selected"), "true",
           "The params tab in the network details pane should be selected.");
 
         function checkVisibility(aBox) {
           is(tabpanel.querySelector("#request-params-box")
             .hasAttribute("hidden"), !aBox.contains("params"),
             "The request params box doesn't have the indended visibility.");
-
           is(tabpanel.querySelector("#request-post-data-textarea-box")
             .hasAttribute("hidden"), !aBox.contains("textarea"),
             "The request post data textarea box doesn't have the indended visibility.");
         }
 
         is(tabpanel.querySelectorAll(".variables-view-scope").length, 2,
           "There should be 2 param scopes displayed in this tabpanel.");
         is(tabpanel.querySelectorAll(".variables-view-empty-notice").length, 0,
@@ -95,21 +94,21 @@ function test() {
           "type", "The third query param name was incorrect.");
         is(queryScope.querySelectorAll(".variables-view-variable .value")[2].getAttribute("value"),
           "\"" + aType + "\"", "The third query param value was incorrect.");
 
         if (aType == "urlencoded") {
           checkVisibility("params");
 
           is(tabpanel.querySelectorAll(".variables-view-variable").length, 5,
-            "There should be 6 param values displayed in this tabpanel.");
+            "There should be 5 param values displayed in this tabpanel.");
           is(queryScope.querySelectorAll(".variables-view-variable").length, 3,
             "There should be 3 param values displayed in the query scope.");
           is(postScope.querySelectorAll(".variables-view-variable").length, 2,
-            "There should be 3 param values displayed in the post scope.");
+            "There should be 2 param values displayed in the post scope.");
 
           is(postScope.querySelectorAll(".variables-view-variable .name")[0].getAttribute("value"),
             "foo", "The first post param name was incorrect.");
           is(postScope.querySelectorAll(".variables-view-variable .value")[0].getAttribute("value"),
             "\"bar\"", "The first post param value was incorrect.");
           is(postScope.querySelectorAll(".variables-view-variable .name")[1].getAttribute("value"),
             "baz", "The second post param name was incorrect.");
           is(postScope.querySelectorAll(".variables-view-variable .value")[1].getAttribute("value"),
new file mode 100644
--- /dev/null
+++ b/browser/devtools/netmonitor/test/browser_net_post-data-02.js
@@ -0,0 +1,62 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests if the POST requests display the correct information in the UI,
+ * even for raw payloads without attached content-type headers.
+ */
+
+function test() {
+  initNetMonitor(POST_RAW_URL).then(([aTab, aDebuggee, aMonitor]) => {
+    info("Starting test... ");
+
+    let { document, L10N, NetMonitorView } = aMonitor.panelWin;
+    let { RequestsMenu, NetworkDetails } = NetMonitorView;
+
+    RequestsMenu.lazyUpdate = false;
+    NetworkDetails._params.lazyEmpty = false;
+
+    waitForNetworkEvents(aMonitor, 0, 1).then(() => {
+      NetMonitorView.toggleDetailsPane({ visible: true }, 2)
+      RequestsMenu.selectedIndex = 0;
+
+      let tab = document.querySelectorAll("#details-pane tab")[2];
+      let tabpanel = document.querySelectorAll("#details-pane tabpanel")[2];
+
+      is(tab.getAttribute("selected"), "true",
+        "The params tab in the network details pane should be selected.");
+
+      is(tabpanel.querySelector("#request-params-box")
+        .hasAttribute("hidden"), false,
+        "The request params box doesn't have the indended visibility.");
+      is(tabpanel.querySelector("#request-post-data-textarea-box")
+        .hasAttribute("hidden"), true,
+        "The request post data textarea box doesn't have the indended visibility.");
+
+      is(tabpanel.querySelectorAll(".variables-view-scope").length, 1,
+        "There should be 1 param scopes displayed in this tabpanel.");
+      is(tabpanel.querySelectorAll(".variables-view-empty-notice").length, 0,
+        "The empty notice should not be displayed in this tabpanel.");
+
+      let postScope = tabpanel.querySelectorAll(".variables-view-scope")[0];
+      is(postScope.querySelector(".name").getAttribute("value"),
+        L10N.getStr("paramsFormData"),
+        "The post scope doesn't have the correct title.");
+
+      is(postScope.querySelectorAll(".variables-view-variable").length, 2,
+        "There should be 2 param values displayed in the post scope.");
+      is(postScope.querySelectorAll(".variables-view-variable .name")[0].getAttribute("value"),
+        "foo", "The first query param name was incorrect.");
+      is(postScope.querySelectorAll(".variables-view-variable .value")[0].getAttribute("value"),
+        "\"bar\"", "The first query param value was incorrect.");
+      is(postScope.querySelectorAll(".variables-view-variable .name")[1].getAttribute("value"),
+        "baz", "The second query param name was incorrect.");
+      is(postScope.querySelectorAll(".variables-view-variable .value")[1].getAttribute("value"),
+        "\"123\"", "The second query param value was incorrect.");
+
+      teardown(aMonitor).then(finish);
+    });
+
+    aDebuggee.performRequest();
+  });
+}
--- a/browser/devtools/netmonitor/test/browser_net_simple-request-details.js
+++ b/browser/devtools/netmonitor/test/browser_net_simple-request-details.js
@@ -158,40 +158,40 @@ function test() {
       is(tabpanel.querySelectorAll(".variable-or-property").length, 0,
         "There should be no param values displayed in this tabpanel.");
       is(tabpanel.querySelectorAll(".variables-view-empty-notice").length, 1,
         "The empty notice should be displayed in this tabpanel.");
 
       is(tabpanel.querySelector("#request-params-box")
         .hasAttribute("hidden"), false,
         "The request params box should not be hidden.");
-
       is(tabpanel.querySelector("#request-post-data-textarea-box")
         .hasAttribute("hidden"), true,
         "The request post data textarea box should be hidden.");
     }
 
     function testResponseTab() {
       EventUtils.sendMouseEvent({ type: "mousedown" },
         document.querySelectorAll("#details-pane tab")[3]);
 
       let tab = document.querySelectorAll("#details-pane tab")[3];
       let tabpanel = document.querySelectorAll("#details-pane tabpanel")[3];
 
       is(tab.getAttribute("selected"), "true",
         "The response tab in the network details pane should be selected.");
 
+      is(tabpanel.querySelector("#response-content-info-header")
+        .hasAttribute("hidden"), true,
+        "The response info header should be hidden.");
       is(tabpanel.querySelector("#response-content-json-box")
         .hasAttribute("hidden"), true,
         "The response content json box should be hidden.");
-
       is(tabpanel.querySelector("#response-content-textarea-box")
         .hasAttribute("hidden"), false,
         "The response content textarea box should not be hidden.");
-
       is(tabpanel.querySelector("#response-content-image-box")
         .hasAttribute("hidden"), true,
         "The response content image box should be hidden.");
 
       return NetMonitorView.editor("#response-content-textarea").then((aEditor) => {
         is(aEditor.getText(), "Hello world!",
           "The text shown in the source editor is incorrect.");
         is(aEditor.getMode(), SourceEditor.MODES.TEXT,
--- a/browser/devtools/netmonitor/test/browser_net_status-codes.js
+++ b/browser/devtools/netmonitor/test/browser_net_status-codes.js
@@ -139,17 +139,16 @@ function test() {
         is(paramsScope.querySelectorAll(".variables-view-variable .name")[0].getAttribute("value"),
           "sts", "The param name was incorrect.");
         is(paramsScope.querySelectorAll(".variables-view-variable .value")[0].getAttribute("value"),
           aStatusParamValue, "The param value was incorrect.");
 
         is(tabpanel.querySelector("#request-params-box")
           .hasAttribute("hidden"), false,
           "The request params box should not be hidden.");
-
         is(tabpanel.querySelector("#request-post-data-textarea-box")
           .hasAttribute("hidden"), true,
           "The request post data textarea box should be hidden.");
       }
     });
 
     aDebuggee.performRequests();
   });
--- a/browser/devtools/netmonitor/test/head.js
+++ b/browser/devtools/netmonitor/test/head.js
@@ -14,18 +14,20 @@ let Toolbox = devtools.Toolbox;
 const EXAMPLE_URL = "http://example.com/browser/browser/devtools/netmonitor/test/";
 
 const SIMPLE_URL = EXAMPLE_URL + "html_simple-test-page.html";
 const NAVIGATE_URL = EXAMPLE_URL + "html_navigate-test-page.html";
 const CONTENT_TYPE_URL = EXAMPLE_URL + "html_content-type-test-page.html";
 const CYRILLIC_URL = EXAMPLE_URL + "html_cyrillic-test-page.html";
 const STATUS_CODES_URL = EXAMPLE_URL + "html_status-codes-test-page.html";
 const POST_DATA_URL = EXAMPLE_URL + "html_post-data-test-page.html";
+const POST_RAW_URL = EXAMPLE_URL + "html_post-raw-test-page.html";
 const JSONP_URL = EXAMPLE_URL + "html_jsonp-test-page.html";
 const JSON_LONG_URL = EXAMPLE_URL + "html_json-long-test-page.html";
+const JSON_MALFORMED_URL = EXAMPLE_URL + "html_json-malformed-test-page.html";
 const SORTING_URL = EXAMPLE_URL + "html_sorting-test-page.html";
 const FILTERING_URL = EXAMPLE_URL + "html_filter-test-page.html";
 const INFINITE_GET_URL = EXAMPLE_URL + "html_infinite-get-page.html";
 const CUSTOM_GET_URL = EXAMPLE_URL + "html_custom-get-page.html";
 
 const SIMPLE_SJS = EXAMPLE_URL + "sjs_simple-test-server.sjs";
 const CONTENT_TYPE_SJS = EXAMPLE_URL + "sjs_content-type-test-server.sjs";
 const STATUS_CODES_SJS = EXAMPLE_URL + "sjs_status-codes-test-server.sjs";
new file mode 100644
--- /dev/null
+++ b/browser/devtools/netmonitor/test/html_json-malformed-test-page.html
@@ -0,0 +1,33 @@
+<!doctype html>
+
+<html>
+  <head>
+    <meta charset="utf-8"/>
+    <title>Network Monitor test page</title>
+  </head>
+
+  <body>
+    <p>JSON malformed test</p>
+
+    <script type="text/javascript">
+      function get(aAddress, aCallback) {
+        var xhr = new XMLHttpRequest();
+        xhr.open("GET", aAddress, true);
+
+        xhr.onreadystatechange = function() {
+          if (this.readyState == this.DONE) {
+            aCallback();
+          }
+        };
+        xhr.send(null);
+      }
+
+      function performRequests() {
+        get("sjs_content-type-test-server.sjs?fmt=json-malformed", function() {
+          // Done.
+        });
+      }
+    </script>
+  </body>
+
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/devtools/netmonitor/test/html_post-raw-test-page.html
@@ -0,0 +1,34 @@
+<!doctype html>
+
+<html>
+  <head>
+    <meta charset="utf-8"/>
+    <title>Network Monitor test page</title>
+  </head>
+
+  <body>
+    <p>POST raw test</p>
+
+    <script type="text/javascript">
+      function post(aAddress, aMessage, aCallback) {
+        var xhr = new XMLHttpRequest();
+        xhr.open("POST", aAddress, true);
+
+        xhr.onreadystatechange = function() {
+          if (this.readyState == this.DONE) {
+            aCallback();
+          }
+        };
+        xhr.send(aMessage);
+      }
+
+      function performRequest() {
+        var rawData = "Content-Type: application/x-www-form-urlencoded\r\n\r\nfoo=bar&baz=123";
+        post("sjs_simple-test-server.sjs", rawData, function() {
+          // Done.
+        });
+      }
+    </script>
+  </body>
+
+</html>
--- a/browser/devtools/netmonitor/test/sjs_content-type-test-server.sjs
+++ b/browser/devtools/netmonitor/test/sjs_content-type-test-server.sjs
@@ -73,16 +73,23 @@ function handleRequest(request, response
       case "json-long": {
         let str = "{ \"greeting\": \"Hello long string JSON!\" },";
         response.setStatusLine(request.httpVersion, 200, "OK");
         response.setHeader("Content-Type", "text/json; charset=utf-8", false);
         response.write("[" + new Array(2048).join(str).slice(0, -1) + "]");
         response.finish();
         break;
       }
+      case "json-malformed": {
+        response.setStatusLine(request.httpVersion, 200, "OK");
+        response.setHeader("Content-Type", "text/json; charset=utf-8", false);
+        response.write("{ \"greeting\": \"Hello malformed JSON!\" },");
+        response.finish();
+        break;
+      }
       case "font": {
         response.setStatusLine(request.httpVersion, 200, "OK");
         response.setHeader("Content-Type", "font/woff", false);
         response.finish();
         break;
       }
       case "image": {
         response.setStatusLine(request.httpVersion, 200, "OK");
--- a/browser/devtools/shared/AppCacheUtils.jsm
+++ b/browser/devtools/shared/AppCacheUtils.jsm
@@ -403,17 +403,17 @@ ManifestParser.prototype = {
 
       // Ignore comments
       if (/^#/.test(text) || !text.length) {
         continue;
       }
 
       if (text == "CACHE MANIFEST") {
         if (this.currentLine != 1) {
-          this._addError(this.currentLine, "cacheManifestOnlyFirstLine",
+          this._addError(this.currentLine, "cacheManifestOnlyFirstLine2",
                          this.currentLine);
         }
         continue;
       }
 
       if (this._maybeUpdateSectionName()) {
         continue;
       }
@@ -440,17 +440,17 @@ ManifestParser.prototype = {
     };
   },
 
   parseLine: function OCIMP_parseLine() {
     let text = this.text;
 
     if (text.indexOf("*") != -1) {
       if (this.currSection != "NETWORK" || text.length != 1) {
-        this._addError(this.currentLine, "asteriskInWrongSection",
+        this._addError(this.currentLine, "asteriskInWrongSection2",
                        this.currSection, this.currentLine);
         return;
       }
     }
 
     if (/\s/.test(text)) {
       this._addError(this.currentLine, "escapeSpaces", this.currentLine);
       text = text.replace(/\s/g, "%20")
@@ -496,17 +496,17 @@ ManifestParser.prototype = {
     if (split.length != 2) {
       this._addError(this.currentLine, "fallbackUseSpaces", this.currentLine);
       return;
     }
 
     let [ namespace, fallback ] = split;
 
     if (namespace.indexOf("*") != -1) {
-      this._addError(this.currentLine, "fallbackAsterisk", this.currentLine);
+      this._addError(this.currentLine, "fallbackAsterisk2", this.currentLine);
     }
 
     if (/\s/.test(namespace)) {
       this._addError(this.currentLine, "escapeSpaces", this.currentLine);
       namespace = namespace.replace(/\s/g, "%20")
     }
 
     if (namespace.substr(0, 4) == "/../") {
--- a/browser/devtools/shared/widgets/VariablesView.jsm
+++ b/browser/devtools/shared/widgets/VariablesView.jsm
@@ -2251,16 +2251,17 @@ ViewHelpers.create({ constructor: Variab
    * be attached to the owner view.
    *
    * @param boolean aImmediateFlag
    *        @see Scope.prototype._lazyAppend
    */
   _onInit: function(aImmediateFlag) {
     if (this._initialDescriptor.enumerable ||
         this._nameString == "this" ||
+        this._nameString == "<return>" ||
         this._nameString == "<exception>") {
       this.ownerView._lazyAppend(aImmediateFlag, true, this._target);
       this.ownerView._enumItems.push(this);
     } else {
       this.ownerView._lazyAppend(aImmediateFlag, false, this._target);
       this.ownerView._nonEnumItems.push(this);
     }
   },
@@ -2428,16 +2429,19 @@ ViewHelpers.create({ constructor: Variab
       this._target.setAttribute("safe-getter", "");
     }
     if (name == "this") {
       this._target.setAttribute("self", "");
     }
     else if (name == "<exception>") {
       this._target.setAttribute("exception", "");
     }
+    else if (name == "<return>") {
+      this._target.setAttribute("return", "");
+    }
     else if (name == "__proto__") {
       this._target.setAttribute("proto", "");
     }
   },
 
   /**
    * Adds the necessary event listeners for this variable.
    */
--- a/browser/devtools/styleinspector/test/browser_ruleview_focus.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_focus.js
@@ -13,20 +13,18 @@ function openRuleView()
 {
   var target = TargetFactory.forTab(gBrowser.selectedTab);
   gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
     inspector = toolbox.getCurrentPanel();
     inspector.sidebar.select("ruleview");
 
     // Highlight a node.
     let node = content.document.getElementsByTagName("h1")[0];
-    inspector.selection.once("new-node", testFocus);
 
-    inspector.sidebar.once("ruleview-ready",
-                           () => inspector.selection.setNode(doc.body));
+    inspector.sidebar.once("ruleview-ready", testFocus);
   });
 }
 
 function testFocus()
 {
   let win = inspector.sidebar.getWindowForTab("ruleview");
   let brace = win.document.querySelectorAll(".ruleview-ruleclose")[0];
 
--- a/browser/locales/en-US/chrome/browser/devtools/appcacheutils.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/appcacheutils.properties
@@ -65,25 +65,25 @@ noResults=Your search returned no result
 # an attempt is made to view offline data.
 cacheDisabled=Your disk cache is disabled. Please set browser.cache.disk.enable to true in about:config and try again.
 
 # LOCALIZATION NOTE (firstLineMustBeCacheManifest): the associated cache
 # manifest has a first line that is not "CACHE MANIFEST". Parameters: %S is
 # the line number.
 firstLineMustBeCacheManifest=The first line of the manifest must be "CACHE MANIFEST" at line %S.
 
-# LOCALIZATION NOTE (cacheManifestOnlyFirstLine): the associated cache
+# LOCALIZATION NOTE (cacheManifestOnlyFirstLine2): the associated cache
 # manifest has "CACHE MANIFEST" on a line other than the first line.
 # Parameters: %S is the line number where "CACHE MANIFEST" appears.
-cacheManifestOnlyFirstLine="CACHE MANIFEST" is only valid on the first line at line %S.
+cacheManifestOnlyFirstLine2="CACHE MANIFEST" is only valid on the first line but was found at line %S.
 
-# LOCALIZATION NOTE (asteriskInWrongSection): the associated cache manifest
+# LOCALIZATION NOTE (asteriskInWrongSection2): the associated cache manifest
 # has an asterisk (*) in a section other than the NETWORK section. Parameters:
 # %1$S is the section name, %2$S is the line number.
-asteriskInWrongSection=Asterisk used as a wildcard in the %1$S section at line %2$S. A single line containing an asterisk is called the online whitelist wildcard flag and is only valid in the NETWORK section. Other uses of the * character are prohibited. The presence of this flag indicates that any URI not listed as cached is to be implicitly treated as being in the online whitelist namespaces. If the flag is not present then the blocking state indicates that URIs not listed explicitly in the manifest are to be treated as unavailable.
+asteriskInWrongSection2=Asterisk (*) incorrectly used in the %1$S section at line %2$S. If a line in the NETWORK section contains only a single asterisk character, then any URI not listed in the manifest will be treated as if the URI was listed in the NETWORK section. Otherwice such URIs will be treated as unavailable. Other uses of the * character are prohibited.
 
 # LOCALIZATION NOTE (escapeSpaces): the associated cache manifest has a space
 # in a URI. Spaces must be replaced with %20. Parameters: %S is the line
 # number where this error occurs.
 escapeSpaces=Spaces in URIs need to be replaced with %20 at line %S.
 
 # LOCALIZATION NOTE (slashDotDotSlashBad): the associated cache manifest has a
 # URI containing /../, which is invalid. Parameters: %S is the line number
@@ -97,21 +97,21 @@ slashDotDotSlashBad=/../ is not a valid 
 tooManyDotDotSlashes=Too many dot dot slash operators (../) at line %S.
 
 # LOCALIZATION NOTE (fallbackUseSpaces): the associated cache manifest has a
 # FALLBACK section containing more or less than the standard two URIs
 # separated by a single space. Parameters: %S is the line number where this
 # error occurs.
 fallbackUseSpaces=Only two URIs separated by spaces are allowed in the FALLBACK section at line %S.
 
-# LOCALIZATION NOTE (fallbackAsterisk): the associated cache manifest has a
+# LOCALIZATION NOTE (fallbackAsterisk2): the associated cache manifest has a
 # FALLBACK section that attempts to use an asterisk (*) as a wildcard. In this
 # section the URI is simply a path prefix. Parameters: %S is the line number
 # where this error occurs.
-fallbackAsterisk=Asterisk (*) incorrectly used as a wildcard in a fallback namespace at line %S. Namespaces simply need to match a path prefix.
+fallbackAsterisk2=Asterisk (*) incorrectly used in the FALLBACK section at line %S. URIs in the FALLBACK section simply need to match a prefix of the request URI.
 
 # LOCALIZATION NOTE (settingsBadValue): the associated cache manifest has a
 # SETTINGS section containing something other than the valid "prefer-online"
 # or "fast". Parameters: %S is the line number where this error occurs.
 settingsBadValue=The SETTINGS section may only contain a single value, "prefer-online" or "fast" at line %S.
 
 # LOCALIZATION NOTE (invalidSectionName): the associated cache manifest
 # contains an invalid section name. Parameters: %1$S is the section name, %2$S
--- a/browser/locales/en-US/chrome/browser/preferences/advanced.dtd
+++ b/browser/locales/en-US/chrome/browser/preferences/advanced.dtd
@@ -79,26 +79,30 @@
 <!ENTITY clearOfflineAppCacheNow.label   "Clear Now">
 <!ENTITY clearOfflineAppCacheNow.accesskey "N">
 <!ENTITY overrideSmartCacheSize.label    "Override automatic cache management">
 <!ENTITY overrideSmartCacheSize.accesskey "O">
 
 <!ENTITY updateTab.label                 "Update">
 
 <!ENTITY updateApp.label                 "&brandShortName; updates:">
-<!ENTITY updateAuto.label                "Automatically install updates (recommended: improved security)">
+<!ENTITY updateAuto.label                "Automatically install updates from desktop &brandShortName;">
 <!ENTITY updateAuto.accesskey            "A">
+<!ENTITY updateAutoMetro.label           "Automatically update from desktop and Windows 8 style &brandShortName;">
+<!ENTITY updateAutoMetro.accesskey       "s">
 <!ENTITY updateCheck.label               "Check for updates, but let me choose whether to install them">
 <!ENTITY updateCheck.accesskey           "C">
 <!ENTITY updateManual.label              "Never check for updates (not recommended: security risk)">
 <!ENTITY updateManual.accesskey          "N">
 
 <!ENTITY updateAutoAddonWarn.label       "Warn me if this will disable any of my add-ons">
 <!ENTITY updateAutoAddonWarn.accesskey   "W">
 
+<!ENTITY updateAutoMetroWarn.label       "(Windows 8 style &brandShortName; does not check add-on compatibility)">
+
 <!ENTITY updateHistory.label             "Show Update History">
 <!ENTITY updateHistory.accesskey         "p">
 
 <!ENTITY useService.label                "Use a background service to install updates">
 <!ENTITY useService.accesskey            "b">
 
 <!ENTITY updateOthers.label              "Automatically update:">
 <!ENTITY enableSearchUpdate.label        "Search Engines">
--- a/browser/metro/base/content/aboutPanel.js
+++ b/browser/metro/base/content/aboutPanel.js
@@ -1,12 +1,15 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+// Services = object with smart getters for common XPCOM services
+Components.utils.import("resource://gre/modules/Services.jsm");
+
 var gAppUpdater;
 var AboutPanelUI = {
   get _aboutVersionLabel() {
     return document.getElementById('about-version-label');
   },
 
   init: function() {
     // Include the build ID if this is an "a#" (nightly or aurora) build
@@ -157,21 +160,31 @@ appUpdater.prototype =
   // true when updating is disabled by an administrator.
   get updateDisabledAndLocked() {
     return !this.updateEnabled &&
            Services.prefs.prefIsLocked("app.update.enabled");
   },
 
   // true when updating is enabled.
   get updateEnabled() {
+    let updatesEnabled = true;
     try {
-      return Services.prefs.getBoolPref("app.update.enabled");
+      updatesEnabled = Services.prefs.getBoolPref("app.update.metro.enabled");
     }
     catch (e) { }
-    return true; // Firefox default is true
+    if (!updatesEnabled) {
+      return false;
+    }
+
+    try {
+      updatesEnabled = Services.prefs.getBoolPref("app.update.enabled")
+    }
+    catch (e) { }
+
+    return updatesEnabled;
   },
 
   // true when updating in background is enabled.
   get backgroundUpdateEnabled() {
     return this.updateEnabled &&
            gAppUpdater.aus.canStageUpdates;
   },
 
@@ -219,27 +232,39 @@ appUpdater.prototype =
       let cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"].
                        createInstance(Components.interfaces.nsISupportsPRBool);
       Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
 
       // Something aborted the quit process.
       if (cancelQuit.data)
         return;
 
+      // It's not possible for the Metro browser to restart itself.
+      // The Windows background process ensures only one instance exists.
+      // So start the update while the browser is open and close the browser
+      // right after.
+      try {
+        Components.classes["@mozilla.org/updates/update-processor;1"].
+          createInstance(Components.interfaces.nsIUpdateProcessor).
+          processUpdate(null);
+      } catch (e) {
+        // If there was an error just close down and the next startup
+        // will do this.
+      }
+
       let appStartup = Components.classes["@mozilla.org/toolkit/app-startup;1"].
                        getService(Components.interfaces.nsIAppStartup);
 
       // If already in safe mode restart in safe mode (bug 327119)
       if (Services.appinfo.inSafeMode) {
         appStartup.restartInSafeMode(Components.interfaces.nsIAppStartup.eAttemptQuit);
         return;
       }
 
-      appStartup.quit(Components.interfaces.nsIAppStartup.eAttemptQuit |
-                      Components.interfaces.nsIAppStartup.eRestart);
+      appStartup.quit(Components.interfaces.nsIAppStartup.eAttemptQuit);
       return;
     }
 
     const URI_UPDATE_PROMPT_DIALOG = "chrome://mozapps/content/update/updates.xul";
     // Firefox no longer displays a license for updates and the licenseURL check
     // is just in case a distibution does.
     if (this.update && (this.update.billboardURL || this.update.licenseURL ||
         this.addons.length != 0)) {
--- a/browser/metro/profile/metro.js
+++ b/browser/metro/profile/metro.js
@@ -411,16 +411,20 @@ pref("app.update.auto", true);
 // AUM Set to:        Minor Releases:     Major Releases:
 // 0                  download no prompt  download no prompt
 // 1                  download no prompt  download no prompt if no incompatibilities
 // 2                  download no prompt  prompt
 //
 // See chart in nsUpdateService.js source for more details
 pref("app.update.mode", 0);
 
+// Enables update checking in the Metro environment.
+// add-on incompatibilities are ignored by updates in Metro.
+pref("app.update.metro.enabled", true);
+
 // If set to true, the Update Service will present no UI for any event.
 pref("app.update.silent", true);
 
 // If set to true, the Update Service will apply updates in the background
 // when it finishes downloading them.
 pref("app.update.staging.enabled", true);
 
 // Update service URL:
--- a/browser/themes/linux/devtools/netmonitor.css
+++ b/browser/themes/linux/devtools/netmonitor.css
@@ -376,16 +376,27 @@ box.requests-menu-status[code^="5"] {
 }
 
 #headers-summary-size {
   padding-top: 2px;
 }
 
 /* Response tabpanel */
 
+#response-content-info-header {
+  background:
+    url(background-noise-toolbar.png),
+    linear-gradient(hsl(0,61%,40%), hsl(0,61%,31%)) repeat-x top left;
+  box-shadow:
+    inset 0 1px 0 hsla(210,40%,83%,.15),
+    inset 0 -1px 0 hsla(210,40%,83%,.05);
+  margin: 0;
+  padding: 5px 8px;
+}
+
 #response-content-image-box {
   padding-top: 10px;
   padding-bottom: 10px;
 }
 
 #response-content-image {
   background: #fff;
   border: 1px dashed GrayText;
--- a/browser/themes/linux/devtools/widgets.css
+++ b/browser/themes/linux/devtools/widgets.css
@@ -489,17 +489,17 @@
 
 .variable-or-property:not(:focus) > .title > .token-other {
   color: #333;
 }
 
 /* Non enumerable, configurable and writable variables and properties */
 
 .variable-or-property[proto] > .title > .name,
-.variable-or-property[non-enumerable]:not([self]):not([exception]) > .title > .name {
+.variable-or-property[non-enumerable]:not([self]):not([exception]):not([return]) > .title > .name {
   opacity: 0.5;
 }
 
 .variable-or-property[non-configurable] > .title > .name {
   border-bottom: 1px dashed #99f;
 }
 
 .variable-or-property[non-configurable][non-writable] > .title > .name {
@@ -526,16 +526,21 @@
   }
 }
 
 .variable-or-property[exception]:not(:focus) > .title > .name {
   color: #a00;
   text-shadow: 0 0 8px #fcc;
 }
 
+.variable-or-property[return]:not(:focus) > .title > .name {
+  color: #0a0;
+  text-shadow: 0 0 8px #cfc;
+}
+
 .variable-or-property[non-extensible]:not([non-writable]) > .title:after {
   content: "N";
   display: inline-block;
 }
 
 .variable-or-property[sealed]:not([non-writable]) > .title:after {
   content: "S";
   display: inline-block;
--- a/browser/themes/osx/devtools/netmonitor.css
+++ b/browser/themes/osx/devtools/netmonitor.css
@@ -376,16 +376,27 @@ box.requests-menu-status[code^="5"] {
 }
 
 #headers-summary-size {
   padding-top: 2px;
 }
 
 /* Response tabpanel */
 
+#response-content-info-header {
+  background:
+    url(background-noise-toolbar.png),
+    linear-gradient(hsl(0,61%,40%), hsl(0,61%,31%)) repeat-x top left;
+  box-shadow:
+    inset 0 1px 0 hsla(210,40%,83%,.15),
+    inset 0 -1px 0 hsla(210,40%,83%,.05);
+  margin: 0;
+  padding: 5px 8px;
+}
+
 #response-content-image-box {
   padding-top: 10px;
   padding-bottom: 10px;
 }
 
 #response-content-image {
   background: #fff;
   border: 1px dashed GrayText;
--- a/browser/themes/osx/devtools/widgets.css
+++ b/browser/themes/osx/devtools/widgets.css
@@ -489,17 +489,17 @@
 
 .variable-or-property:not(:focus) > .title > .token-other {
   color: #333;
 }
 
 /* Non enumerable, configurable and writable variables and properties */
 
 .variable-or-property[proto] > .title > .name,
-.variable-or-property[non-enumerable]:not([self]):not([exception]) > .title > .name {
+.variable-or-property[non-enumerable]:not([self]):not([exception]):not([return]) > .title > .name {
   opacity: 0.5;
 }
 
 .variable-or-property[non-configurable] > .title > .name {
   border-bottom: 1px dashed #99f;
 }
 
 .variable-or-property[non-configurable][non-writable] > .title > .name {
@@ -526,16 +526,21 @@
   }
 }
 
 .variable-or-property[exception]:not(:focus) > .title > .name {
   color: #a00;
   text-shadow: 0 0 8px #fcc;
 }
 
+.variable-or-property[return]:not(:focus) > .title > .name {
+  color: #0a0;
+  text-shadow: 0 0 8px #cfc;
+}
+
 .variable-or-property[non-extensible]:not([non-writable]) > .title:after {
   content: "N";
   display: inline-block;
 }
 
 .variable-or-property[sealed]:not([non-writable]) > .title:after {
   content: "S";
   display: inline-block;
--- a/browser/themes/windows/devtools/netmonitor.css
+++ b/browser/themes/windows/devtools/netmonitor.css
@@ -376,16 +376,27 @@ box.requests-menu-status[code^="5"] {
 }
 
 #headers-summary-size {
   padding-top: 2px;
 }
 
 /* Response tabpanel */
 
+#response-content-info-header {
+  background:
+    url(background-noise-toolbar.png),
+    linear-gradient(hsl(0,61%,40%), hsl(0,61%,31%)) repeat-x top left;
+  box-shadow:
+    inset 0 1px 0 hsla(210,40%,83%,.15),
+    inset 0 -1px 0 hsla(210,40%,83%,.05);
+  margin: 0;
+  padding: 5px 8px;
+}
+
 #response-content-image-box {
   padding-top: 10px;
   padding-bottom: 10px;
 }
 
 #response-content-image {
   background: #fff;
   border: 1px dashed GrayText;
--- a/browser/themes/windows/devtools/widgets.css
+++ b/browser/themes/windows/devtools/widgets.css
@@ -492,17 +492,17 @@
 
 .variable-or-property:not(:focus) > .title > .token-other {
   color: #333;
 }
 
 /* Non enumerable, configurable and writable variables and properties */
 
 .variable-or-property[proto] > .title > .name,
-.variable-or-property[non-enumerable]:not([self]):not([exception]) > .title > .name {
+.variable-or-property[non-enumerable]:not([self]):not([exception]):not([return]) > .title > .name {
   opacity: 0.5;
 }
 
 .variable-or-property[non-configurable] > .title > .name {
   border-bottom: 1px dashed #99f;
 }
 
 .variable-or-property[non-configurable][non-writable] > .title > .name {
@@ -529,16 +529,21 @@
   }
 }
 
 .variable-or-property[exception]:not(:focus) > .title > .name {
   color: #a00;
   text-shadow: 0 0 8px #fcc;
 }
 
+.variable-or-property[return]:not(:focus) > .title > .name {
+  color: #0a0;
+  text-shadow: 0 0 8px #cfc;
+}
+
 .variable-or-property[non-extensible]:not([non-writable]) > .title:after {
   content: "N";
   display: inline-block;
 }
 
 .variable-or-property[sealed]:not([non-writable]) > .title:after {
   content: "S";
   display: inline-block;
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -2234,16 +2234,20 @@
     "kind": "enumerated",
     "n_values": 50,
     "description": "Updater: the status of the latest update performed"
   },
   "UPDATER_UPDATES_ENABLED": {
     "kind": "boolean",
     "description": "Updater: Whether or not updates are enabled"
   },
+  "UPDATER_UPDATES_METRO_ENABLED": {
+    "kind": "boolean",
+    "description": "Updater: Whether or not Metro updates are enabled"
+  },
   "UPDATER_UPDATES_AUTOMATIC": {
     "kind": "boolean",
     "description": "Updater: Whether or not updates are automatic"
   },
   "UPDATER_SERVICE_ENABLED": {
     "kind": "boolean",
     "description": "Updater: Whether or not the MozillaMaintenance service is enabled"
   },
@@ -3311,16 +3315,40 @@
     "description": "The time (in milliseconds) that it took a 'detach' request to go round trip."
   },
   "DEVTOOLS_DEBUGGER_RDP_REMOTE_TABDETACH_MS": {
     "kind": "exponential",
     "high": "10000",
     "n_buckets": "1000",
     "description": "The time (in milliseconds) that it took a 'detach' request to go round trip."
   },
+  "DEVTOOLS_DEBUGGER_RDP_LOCAL_BLACKBOX_MS": {
+    "kind": "exponential",
+    "high": "10000",
+    "n_buckets": "1000",
+    "description": "The time (in milliseconds) that it took a 'blackbox' request to go round trip."
+  },
+  "DEVTOOLS_DEBUGGER_RDP_REMOTE_BLACKBOX_MS": {
+    "kind": "exponential",
+    "high": "10000",
+    "n_buckets": "1000",
+    "description": "The time (in milliseconds) that it took a 'blackbox' request to go round trip."
+  },
+  "DEVTOOLS_DEBUGGER_RDP_LOCAL_UNBLACKBOX_MS": {
+    "kind": "exponential",
+    "high": "10000",
+    "n_buckets": "1000",
+    "description": "The time (in milliseconds) that it took an 'ublackbox' request to go round trip."
+  },
+  "DEVTOOLS_DEBUGGER_RDP_REMOTE_UNBLACKBOX_MS": {
+    "kind": "exponential",
+    "high": "10000",
+    "n_buckets": "1000",
+    "description": "The time (in milliseconds) that it took an 'unblackbox' request to go round trip."
+  },
   "DEVTOOLS_OPTIONS_OPENED_BOOLEAN": {
     "kind": "boolean",
     "description": "How many times has the devtool's Options panel been opened?"
   },
   "DEVTOOLS_WEBCONSOLE_OPENED_BOOLEAN": {
     "kind": "boolean",
     "description": "How many times has the devtool's Web Console been opened?"
   },
--- a/toolkit/devtools/client/dbg-client.jsm
+++ b/toolkit/devtools/client/dbg-client.jsm
@@ -1560,21 +1560,61 @@ LongStringClient.prototype = {
  *
  * @param aClient DebuggerClient
  *        The debugger client parent.
  * @param aForm Object
  *        The form sent across the remote debugging protocol.
  */
 function SourceClient(aClient, aForm) {
   this._form = aForm;
+  this._isBlackBoxed = aForm.isBlackBoxed;
   this._client = aClient;
 }
 
 SourceClient.prototype = {
-  get _transport() { return this._client._transport; },
+  get _transport() this._client._transport,
+  get isBlackBoxed() this._isBlackBoxed,
+  get actor() this._form.actor,
+  get request() this._client.request,
+
+  /**
+   * Black box this SourceClient's source.
+   *
+   * @param aCallback Function
+   *        The callback function called when we receive the response from the server.
+   */
+  blackBox: DebuggerClient.requester({
+    type: "blackbox"
+  }, {
+    telemetry: "BLACKBOX",
+    after: function (aResponse) {
+      if (!aResponse.error) {
+        this._isBlackBoxed = true;
+      }
+      return aResponse;
+    }
+  }),
+
+  /**
+   * Un-black box this SourceClient's source.
+   *
+   * @param aCallback Function
+   *        The callback function called when we receive the response from the server.
+   */
+  unblackBox: DebuggerClient.requester({
+    type: "unblackbox"
+  }, {
+    telemetry: "UNBLACKBOX",
+    after: function (aResponse) {
+      if (!aResponse.error) {
+        this._isBlackBoxed = false;
+      }
+      return aResponse;
+    }
+  }),
 
   /**
    * Get a long string grip for this SourceClient's source.
    */
   source: function SC_source(aCallback) {
     let packet = {
       to: this._form.actor,
       type: "source"
--- a/toolkit/devtools/server/actors/script.js
+++ b/toolkit/devtools/server/actors/script.js
@@ -322,55 +322,81 @@ ThreadActor.prototype = {
       let packet = this._resumed();
       DebuggerServer.xpcInspector.exitNestedEventLoop();
       return { type: "resumeLimit", frameFinished: aRequest.forceCompletion };
     }
 
     if (aRequest && aRequest.resumeLimit) {
       // Bind these methods because some of the hooks are called with 'this'
       // set to the current frame.
-      let pauseAndRespond = this._pauseAndRespond.bind(this);
+      let pauseAndRespond = (aFrame, onPacket=function (k) k) => {
+        this._pauseAndRespond(aFrame, { type: "resumeLimit" }, onPacket);
+      };
       let createValueGrip = this.createValueGrip.bind(this);
 
-      let startFrame = this._youngestFrame;
+      let startFrame = this.youngestFrame;
       let startLine;
-      if (this._youngestFrame.script) {
-        let offset = this._youngestFrame.offset;
-        startLine = this._youngestFrame.script.getOffsetLine(offset);
+      if (this.youngestFrame.script) {
+        let offset = this.youngestFrame.offset;
+        startLine = this.youngestFrame.script.getOffsetLine(offset);
       }
 
       // Define the JS hook functions for stepping.
 
-      let onEnterFrame = function TA_onEnterFrame(aFrame) {
-        return pauseAndRespond(aFrame, { type: "resumeLimit" });
+      let onEnterFrame = aFrame => {
+        if (this.sources.isBlackBoxed(aFrame.script.url)) {
+          return undefined;
+        }
+        return pauseAndRespond(aFrame);
       };
 
+      let thread = this;
+
       let onPop = function TA_onPop(aCompletion) {
         // onPop is called with 'this' set to the current frame.
+        if (thread.sources.isBlackBoxed(this.script.url)) {
+          return undefined;
+        }
 
         // Note that we're popping this frame; we need to watch for
         // subsequent step events on its caller.
         this.reportedPop = true;
 
-        return pauseAndRespond(this, { type: "resumeLimit" });
-      }
+        return pauseAndRespond(this, (aPacket) => {
+          aPacket.why.frameFinished = {};
+          if (!aCompletion) {
+            aPacket.why.frameFinished.terminated = true;
+          } else if (aCompletion.hasOwnProperty("return")) {
+            aPacket.why.frameFinished.return = createValueGrip(aCompletion.return);
+          } else if (aCompletion.hasOwnProperty("yield")) {
+            aPacket.why.frameFinished.return = createValueGrip(aCompletion.yield);
+          } else {
+            aPacket.why.frameFinished.throw = createValueGrip(aCompletion.throw);
+          }
+          return aPacket;
+        });
+      };
 
       let onStep = function TA_onStep() {
         // onStep is called with 'this' set to the current frame.
 
+        if (thread.sources.isBlackBoxed(this.script.url)) {
+          return undefined;
+        }
+
         // If we've changed frame or line, then report that.
         if (this !== startFrame ||
             (this.script &&
              this.script.getOffsetLine(this.offset) != startLine)) {
-          return pauseAndRespond(this, { type: "resumeLimit" });
+          return pauseAndRespond(this);
         }
 
         // Otherwise, let execution continue.
         return undefined;
-      }
+      };
 
       let steppingType = aRequest.resumeLimit.type;
       if (["step", "next", "finish"].indexOf(steppingType) == -1) {
             return { error: "badParameterType",
                      message: "Unknown resumeLimit type" };
       }
       // Make sure there is still a frame on the stack if we are to continue
       // stepping.
@@ -425,17 +451,17 @@ ThreadActor.prototype = {
       return { error: "notDebuggee",
                message: "cannot access the environment of this frame." };
     };
 
     // We'll clobber the youngest frame if the eval causes a pause, so
     // save our frame now to be restored after eval returns.
     // XXX: or we could just start using dbg.getNewestFrame() now that it
     // works as expected.
-    let youngest = this._youngestFrame;
+    let youngest = this.youngestFrame;
 
     // Put ourselves back in the running state and inform the client.
     let resumedPacket = this._resumed();
     this.conn.send(resumedPacket);
 
     // Run the expression.
     // XXX: test syntax errors
     let completion = frame.eval(aRequest.expression);
@@ -454,17 +480,17 @@ ThreadActor.prototype = {
       return { error: "wrongState",
                message: "Stack frames are only available while the debuggee is paused."};
     }
 
     let start = aRequest.start ? aRequest.start : 0;
     let count = aRequest.count;
 
     // Find the starting frame...
-    let frame = this._youngestFrame;
+    let frame = this.youngestFrame;
     let i = 0;
     while (frame && (i < start)) {
       frame = frame.older;
       i++;
     }
 
     // Return request.count frames, or all remaining
     // frames if count is not defined.
@@ -806,17 +832,17 @@ ThreadActor.prototype = {
     }
   },
 
   /**
    * Return the Debug.Frame for a frame mentioned by the protocol.
    */
   _requestFrame: function TA_requestFrame(aFrameID) {
     if (!aFrameID) {
-      return this._youngestFrame;
+      return this.youngestFrame;
     }
 
     if (this._framePool.has(aFrameID)) {
       return this._framePool.get(aFrameID).frame;
     }
 
     return undefined;
   },
@@ -840,17 +866,17 @@ ThreadActor.prototype = {
       aFrame.onStep = undefined;
       aFrame.onPop = undefined;
     }
 
     this._state = "paused";
 
     // Save the pause frame (if any) as the youngest frame for
     // stack viewing.
-    this._youngestFrame = aFrame;
+    this.youngestFrame = aFrame;
 
     // Create the actor pool that will hold the pause actor and its
     // children.
     dbg_assert(!this._pausePool);
     this._pausePool = new ActorPool(this.conn);
     this.conn.addActorPool(this._pausePool);
 
     // Give children of the pause pool a quick link back to the
@@ -903,17 +929,17 @@ ThreadActor.prototype = {
   _resumed: function TA_resumed() {
     this._state = "running";
 
     // Drop the actors in the pause actor pool.
     this.conn.removeActorPool(this._pausePool);
 
     this._pausePool = null;
     this._pauseActor = null;
-    this._youngestFrame = null;
+    this.youngestFrame = null;
 
     return { from: this.actorID, type: "resumed" };
   },
 
   /**
    * Expire frame actors for frames that have been popped.
    *
    * @returns A list of actor IDs whose frames have been popped.
@@ -1180,40 +1206,46 @@ ThreadActor.prototype = {
    * A function that the engine calls when a call to a debug event hook,
    * breakpoint handler, watchpoint handler, or similar function throws some
    * exception.
    *
    * @param aException exception
    *        The exception that was thrown in the debugger code.
    */
   uncaughtExceptionHook: function TA_uncaughtExceptionHook(aException) {
-    dumpn("Got an exception:" + aException);
+    dumpn("Got an exception: " + aException.message + "\n" + aException.stack);
   },
 
   /**
    * A function that the engine calls when a debugger statement has been
    * executed in the specified frame.
    *
    * @param aFrame Debugger.Frame
    *        The stack frame that contained the debugger statement.
    */
   onDebuggerStatement: function TA_onDebuggerStatement(aFrame) {
+    if (this.sources.isBlackBoxed(aFrame.script.url)) {
+      return undefined;
+    }
     return this._pauseAndRespond(aFrame, { type: "debuggerStatement" });
   },
 
   /**
    * A function that the engine calls when an exception has been thrown and has
    * propagated to the specified frame.
    *
    * @param aFrame Debugger.Frame
    *        The youngest remaining stack frame.
    * @param aValue object
    *        The exception that was thrown.
    */
   onExceptionUnwind: function TA_onExceptionUnwind(aFrame, aValue) {
+    if (this.sources.isBlackBoxed(aFrame.script.url)) {
+      return undefined;
+    }
     try {
       let packet = this._paused(aFrame);
       if (!packet) {
         return undefined;
       }
 
       packet.why = { type: "exception",
                      exception: this.createValueGrip(aValue) };
@@ -1451,17 +1483,18 @@ SourceActor.prototype = {
   actorPrefix: "source",
 
   get threadActor() this._threadActor,
   get url() this._url,
 
   form: function SA_form() {
     return {
       actor: this.actorID,
-      url: this._url
+      url: this._url,
+      isBlackBoxed: this.threadActor.sources.isBlackBoxed(this.url)
       // TODO bug 637572: introductionScript
     };
   },
 
   disconnect: function LSA_disconnect() {
     if (this.registeredPool && this.registeredPool.sourceActors) {
       delete this.registeredPool.sourceActors[this.actorID];
     }
@@ -1503,21 +1536,49 @@ SourceActor.prototype = {
         Cu.reportError(msg);
         dumpn(msg);
         return {
           "from": this.actorID,
           "error": "loadSourceError",
           "message": "Could not load the source for " + this._url + "."
         };
       });
+  },
+
+  /**
+   * Handler for the "blackbox" packet.
+   */
+  onBlackBox: function SA_onBlackBox(aRequest) {
+    this.threadActor.sources.blackBox(this.url);
+    let packet = {
+      from: this.actorID
+    };
+    if (this.threadActor.state == "paused"
+        && this.threadActor.youngestFrame
+        && this.threadActor.youngestFrame.script.url == this.url) {
+      packet.pausedInSource = true;
+    }
+    return packet;
+  },
+
+  /**
+   * Handler for the "unblackbox" packet.
+   */
+  onUnblackBox: function SA_onUnblackBox(aRequest) {
+    this.threadActor.sources.unblackBox(this.url);
+    return {
+      from: this.actorID
+    };
   }
 };
 
 SourceActor.prototype.requestTypes = {
-  "source": SourceActor.prototype.onSource
+  "source": SourceActor.prototype.onSource,
+  "blackbox": SourceActor.prototype.onBlackBox,
+  "unblackbox": SourceActor.prototype.onUnblackBox
 };
 
 
 /**
  * Creates an actor for the specified object.
  *
  * @param aObj Debugger.Object
  *        The debuggee object.
@@ -2054,16 +2115,17 @@ FrameActor.prototype = {
                                 this.frameLifetimePool);
       form.environment = envActor.form();
     }
     form.this = this.threadActor.createValueGrip(this.frame.this);
     form.arguments = this._args();
     if (this.frame.script) {
       form.where = { url: this.frame.script.url,
                      line: this.frame.script.getOffsetLine(this.frame.offset) };
+      form.isBlackBoxed = this.threadActor.sources.isBlackBoxed(this.frame.script.url)
     }
 
     if (!this.frame.older) {
       form.oldest = true;
     }
 
     return form;
   },
@@ -2152,16 +2214,20 @@ BreakpointActor.prototype = {
 
   /**
    * A function that the engine calls when a breakpoint has been hit.
    *
    * @param aFrame Debugger.Frame
    *        The stack frame that contained the breakpoint.
    */
   hit: function BA_hit(aFrame) {
+    if (this.threadActor.sources.isBlackBoxed(this.location.url)) {
+      return undefined;
+    }
+
     // TODO: add the rest of the breakpoints on that line (bug 676602).
     let reason = { type: "breakpoint", actors: [ this.actorID ] };
     return this.threadActor._pauseAndRespond(aFrame, reason, (aPacket) => {
       let { url, line } = aPacket.frame.where;
       return this.threadActor.sources.getOriginalLocation(url, line)
         .then(function (aOrigPosition) {
           aPacket.frame.where = aOrigPosition;
           return aPacket;
@@ -2489,18 +2555,18 @@ update(ChromeDebuggerActor.prototype, {
   }
 });
 
 
 /**
  * Manages the sources for a thread. Handles source maps, locations in the
  * sources, etc for ThreadActors.
  */
-function ThreadSources(aThreadActor, aUseSourceMaps,
-                       aAllowPredicate, aOnNewSource) {
+function ThreadSources(aThreadActor, aUseSourceMaps, aAllowPredicate,
+                       aOnNewSource) {
   this._thread = aThreadActor;
   this._useSourceMaps = aUseSourceMaps;
   this._allow = aAllowPredicate;
   this._onNewSource = aOnNewSource;
 
   // source map URL --> promise of SourceMapConsumer
   this._sourceMaps = Object.create(null);
   // generated source url --> promise of SourceMapConsumer
@@ -2508,16 +2574,22 @@ function ThreadSources(aThreadActor, aUs
   // original source url --> promise of SourceMapConsumer
   this._sourceMapsByOriginalSource = Object.create(null);
   // source url --> SourceActor
   this._sourceActors = Object.create(null);
   // original url --> generated url
   this._generatedUrlsByOriginalUrl = Object.create(null);
 }
 
+/**
+ * Must be a class property because it needs to persist across reloads, same as
+ * the breakpoint store.
+ */
+ThreadSources._blackBoxedSources = new Set();
+
 ThreadSources.prototype = {
   /**
    * Return the source actor representing |aURL|, creating one if none
    * exists already. Returns null if |aURL| is not allowed by the 'allow'
    * predicate.
    *
    * Right now this takes a URL, but in the future it should
    * take a Debugger.Source. See bug 637572.
@@ -2683,16 +2755,49 @@ ThreadSources.prototype = {
     // No source map
     return resolve({
       url: aSourceUrl,
       line: aLine
     });
   },
 
   /**
+   * Returns true if URL for the given source is black boxed.
+   *
+   * @param aURL String
+   *        The URL of the source which we are checking whether it is black
+   *        boxed or not.
+   */
+  isBlackBoxed: function TS_isBlackBoxed(aURL) {
+    return ThreadSources._blackBoxedSources.has(aURL);
+  },
+
+  /**
+   * Add the given source URL to the set of sources that are black boxed. If the
+   * thread is currently paused and we are black boxing the yougest frame's
+   * source, this will force a step.
+   *
+   * @param aURL String
+   *        The URL of the source which we are black boxing.
+   */
+  blackBox: function TS_blackBox(aURL) {
+    ThreadSources._blackBoxedSources.add(aURL);
+  },
+
+  /**
+   * Remove the given source URL to the set of sources that are black boxed.
+   *
+   * @param aURL String
+   *        The URL of the source which we are no longer black boxing.
+   */
+  unblackBox: function TS_unblackBox(aURL) {
+    ThreadSources._blackBoxedSources.delete(aURL);
+  },
+
+  /**
    * Normalize multiple relative paths towards the base paths on the right.
    */
   _normalize: function TS__normalize(...aURLs) {
     dbg_assert(aURLs.length > 1);
     let base = Services.io.newURI(aURLs.pop(), null, null);
     let url;
     while ((url = aURLs.pop())) {
       base = Services.io.newURI(url, null, base);
--- a/toolkit/devtools/server/actors/webbrowser.js
+++ b/toolkit/devtools/server/actors/webbrowser.js
@@ -18,18 +18,17 @@ function allAppShellDOMWindows(aWindowTy
 {
   let e = windowMediator.getEnumerator(aWindowType);
   while (e.hasMoreElements()) {
     yield e.getNext();
   }
 }
 
 /**
- * Return true if the top-level window |aWindow| is a "navigator:browser"
- * window.
+ * Retrieve the window type of the top-level window |aWindow|.
  */
 function appShellDOMWindowType(aWindow) {
   /* This is what nsIWindowMediator's enumerator checks. */
   return aWindow.document.documentElement.getAttribute('windowtype');
 }
 
 /**
  * Send Debugger:Shutdown events to all "navigator:browser" windows.
@@ -373,27 +372,17 @@ BrowserTabList.prototype._listenToMediat
  * actors or tables here.
  *
  * An nsIWindowMediatorListener's methods get passed all sorts of windows; we
  * only care about the tab containers. Those have 'getBrowser' methods.
  */
 BrowserTabList.prototype.onWindowTitleChange = () => { };
 
 BrowserTabList.prototype.onOpenWindow = makeInfallible(function(aWindow) {
-  /*
-   * You can hardly do anything at all with a XUL window at this point; it
-   * doesn't even have its document yet. Wait until its document has
-   * loaded, and then see what we've got. This also avoids
-   * nsIWindowMediator enumeration from within listeners (bug 873589).
-   */
-  aWindow = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                   .getInterface(Ci.nsIDOMWindow);
-  aWindow.addEventListener("load", makeInfallible(handleLoad.bind(this)), false);
-
-  function handleLoad(aEvent) {
+  let handleLoad = makeInfallible(() => {
     /* We don't want any further load events from this window. */
     aWindow.removeEventListener("load", handleLoad, false);
 
     if (appShellDOMWindowType(aWindow) !== "navigator:browser")
       return;
 
     // Listen for future tab activity.
     if (this._listeningForTabOpen) {
@@ -403,17 +392,28 @@ BrowserTabList.prototype.onOpenWindow = 
     if (this._listeningForTabClose) {
       aWindow.addEventListener("TabClose", this, false);
     }
 
     // As explained above, we will not receive a TabOpen event for this
     // document's initial tab, so we must notify our client of the new tab
     // this will have.
     this._notifyListChanged();
-  }
+  });
+
+  /*
+   * You can hardly do anything at all with a XUL window at this point; it
+   * doesn't even have its document yet. Wait until its document has
+   * loaded, and then see what we've got. This also avoids
+   * nsIWindowMediator enumeration from within listeners (bug 873589).
+   */
+  aWindow = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                   .getInterface(Ci.nsIDOMWindow);
+
+  aWindow.addEventListener("load", handleLoad, false);
 }, "BrowserTabList.prototype.onOpenWindow");
 
 BrowserTabList.prototype.onCloseWindow = makeInfallible(function(aWindow) {
   aWindow = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                    .getInterface(Ci.nsIDOMWindow);
 
   if (appShellDOMWindowType(aWindow) !== "navigator:browser")
     return;
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/server/tests/unit/test_blackboxing-01.js
@@ -0,0 +1,178 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Test basic black boxing.
+ */
+
+var gDebuggee;
+var gClient;
+var gThreadClient;
+
+function run_test()
+{
+  initTestDebuggerServer();
+  gDebuggee = addTestGlobal("test-black-box");
+  gClient = new DebuggerClient(DebuggerServer.connectPipe());
+  gClient.connect(function() {
+    attachTestTabAndResume(gClient, "test-black-box", function(aResponse, aTabClient, aThreadClient) {
+      gThreadClient = aThreadClient;
+      test_black_box();
+    });
+  });
+  do_test_pending();
+}
+
+const BLACK_BOXED_URL = "http://example.com/blackboxme.js";
+const SOURCE_URL = "http://example.com/source.js";
+
+function test_black_box()
+{
+  gClient.addOneTimeListener("paused", function () {
+    gThreadClient.setBreakpoint({
+      url: SOURCE_URL,
+      line: 2
+    }, function (aResponse) {
+      do_check_true(!aResponse.error, "Should be able to set breakpoint.");
+      gThreadClient.resume(test_black_box_default);
+    });
+  });
+
+  Components.utils.evalInSandbox(
+    "" + function doStuff(k) { // line 1
+      let arg = 15;            // line 2 - Step in here
+      k(arg);                  // line 3
+    },                         // line 4
+    gDebuggee,
+    "1.8",
+    BLACK_BOXED_URL,
+    1
+  );
+
+  Components.utils.evalInSandbox(
+    "" + function runTest() { // line 1
+      doStuff(                // line 2 - Break here
+        function (n) {        // line 3 - Step through `doStuff` to here
+          debugger;           // line 5
+        }                     // line 6
+      );                      // line 7
+    }                         // line 8
+    + "\n debugger;",         // line 9
+    gDebuggee,
+    "1.8",
+    SOURCE_URL,
+    1
+  );
+}
+
+function test_black_box_default() {
+  gThreadClient.getSources(function (aResponse) {
+    do_check_true(!aResponse.error, "Should be able to get sources.");
+
+    let sourceClient = gThreadClient.source(
+      aResponse.sources.filter(s => s.url == BLACK_BOXED_URL)[0]);
+    do_check_true(!sourceClient.isBlackBoxed,
+                  "By default the source is not black boxed.");
+
+    // Test that we can step into `doStuff` when we are not black boxed.
+    runTest(
+      function onSteppedLocation(aLocation) {
+        do_check_eq(aLocation.url, BLACK_BOXED_URL,
+                    "Should step into `doStuff`.");
+        do_check_eq(aLocation.line, 2,
+                    "Should step into `doStuff`.");
+      },
+      function onDebuggerStatementFrames(aFrames) {
+        do_check_true(!aFrames.some(f => f.isBlackBoxed));
+      },
+      test_black_boxing.bind(null, sourceClient)
+    );
+  });
+}
+
+function test_black_boxing(aSourceClient) {
+  aSourceClient.blackBox(function (aResponse) {
+    do_check_true(!aResponse.error, "Should not get an error black boxing.");
+    do_check_true(aSourceClient.isBlackBoxed,
+       "The source client should report itself as black boxed correctly.");
+
+    // Test that we step through `doStuff` when we are black boxed and its frame
+    // doesn't show up.
+    runTest(
+      function onSteppedLocation(aLocation) {
+        do_check_eq(aLocation.url, SOURCE_URL,
+                    "Should step through `doStuff`.");
+        do_check_eq(aLocation.line, 3,
+                    "Should step through `doStuff`.");
+      },
+      function onDebuggerStatementFrames(aFrames) {
+        for (let f of aFrames) {
+          if (f.where.url == BLACK_BOXED_URL) {
+            do_check_true(f.isBlackBoxed, "Should be black boxed");
+          } else {
+            do_check_true(!f.isBlackBoxed, "Should not be black boxed")
+          }
+        }
+      },
+      test_unblack_boxing.bind(null, aSourceClient)
+    );
+  });
+}
+
+function test_unblack_boxing(aSourceClient) {
+  aSourceClient.unblackBox(function (aResponse) {
+    do_check_true(!aResponse.error, "Should not get an error un-black boxing");
+    do_check_true(!aSourceClient.isBlackBoxed, "The source is not black boxed.");
+
+    // Test that we can step into `doStuff` again.
+    runTest(
+      function onSteppedLocation(aLocation) {
+        do_check_eq(aLocation.url, BLACK_BOXED_URL,
+                    "Should step into `doStuff`.");
+        do_check_eq(aLocation.line, 2,
+                    "Should step into `doStuff`.");
+      },
+      function onDebuggerStatementFrames(aFrames) {
+        do_check_true(!aFrames.some(f => f.isBlackBoxed));
+      },
+      finishClient.bind(null, gClient)
+    );
+  });
+}
+
+function runTest(aOnSteppedLocation, aOnDebuggerStatementFrames, aFinishedCallback) {
+  gClient.addOneTimeListener("paused", function (aEvent, aPacket) {
+    do_check_eq(aPacket.why.type, "breakpoint");
+    gClient.addOneTimeListener("paused", function () {
+      gClient.addOneTimeListener("paused", function () {
+        getCurrentLocation(function (aLocation) {
+          aOnSteppedLocation(aLocation);
+          gClient.addOneTimeListener("paused", function (aEvent, aPacket) {
+            do_check_eq(aPacket.why.type, "debuggerStatement");
+            gThreadClient.getFrames(0, 100, function ({frames}) {
+              aOnDebuggerStatementFrames(frames);
+              // We hit the breakpoint once more on the way out
+              gClient.addOneTimeListener("paused", function () {
+                gThreadClient.resume(aFinishedCallback);
+              });
+              gThreadClient.resume();
+            });
+          });
+          gThreadClient.resume();
+        });
+      });
+      gThreadClient.stepIn();
+    });
+    gThreadClient.stepIn();
+  });
+
+  gDebuggee.runTest();
+}
+
+function getCurrentLocation(aCallback) {
+  gThreadClient.getFrames(0, 1, function ({frames, error}) {
+    do_check_true(!error, "Should not get an error: " + error);
+    let [{where}] = frames;
+    aCallback(where);
+  });
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/server/tests/unit/test_blackboxing-02.js
@@ -0,0 +1,104 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Test that we don't hit breakpoints in black boxed sources, and that when we
+ * unblack box the source again, the breakpoint hasn't disappeared and we will
+ * hit it again.
+ */
+
+var gDebuggee;
+var gClient;
+var gThreadClient;
+
+function run_test()
+{
+  initTestDebuggerServer();
+  gDebuggee = addTestGlobal("test-black-box");
+  gClient = new DebuggerClient(DebuggerServer.connectPipe());
+  gClient.connect(function() {
+    attachTestTabAndResume(gClient, "test-black-box", function(aResponse, aTabClient, aThreadClient) {
+      gThreadClient = aThreadClient;
+      test_black_box();
+    });
+  });
+  do_test_pending();
+}
+
+const BLACK_BOXED_URL = "http://example.com/blackboxme.js";
+const SOURCE_URL = "http://example.com/source.js";
+
+function test_black_box()
+{
+  gClient.addOneTimeListener("paused", function () {
+    gThreadClient.setBreakpoint({
+      url: BLACK_BOXED_URL,
+      line: 2
+    }, function (aResponse) {
+      do_check_true(!aResponse.error, "Should be able to set breakpoint.");
+      gThreadClient.resume(test_black_box_breakpoint);
+    });
+  });
+
+  Components.utils.evalInSandbox(
+    "" + function doStuff(k) { // line 1
+      let arg = 15;            // line 2 - Break here
+      k(arg);                  // line 3
+    },                         // line 4
+    gDebuggee,
+    "1.8",
+    BLACK_BOXED_URL,
+    1
+  );
+
+  Components.utils.evalInSandbox(
+    "" + function runTest() { // line 1
+      doStuff(                // line 2
+        function (n) {        // line 3
+          debugger;           // line 5
+        }                     // line 6
+      );                      // line 7
+    }                         // line 8
+    + "\n debugger;",         // line 9
+    gDebuggee,
+    "1.8",
+    SOURCE_URL,
+    1
+  );
+}
+
+function test_black_box_breakpoint() {
+  gThreadClient.getSources(function ({error, sources}) {
+    do_check_true(!error, "Should not get an error: " + error);
+    let sourceClient = gThreadClient.source(sources.filter(s => s.url == BLACK_BOXED_URL)[0]);
+    sourceClient.blackBox(function ({error}) {
+      do_check_true(!error, "Should not get an error: " + error);
+
+      gClient.addOneTimeListener("paused", function (aEvent, aPacket) {
+        do_check_eq(aPacket.why.type, "debuggerStatement",
+                    "We should pass over the breakpoint since the source is black boxed.");
+        gThreadClient.resume(test_unblack_box_breakpoint.bind(null, sourceClient));
+      });
+      gDebuggee.runTest();
+    });
+  });
+}
+
+function test_unblack_box_breakpoint(aSourceClient) {
+  aSourceClient.unblackBox(function ({error}) {
+    do_check_true(!error, "Should not get an error: " + error);
+    gClient.addOneTimeListener("paused", function (aEvent, aPacket) {
+      do_check_eq(aPacket.why.type, "breakpoint",
+                  "We should hit the breakpoint again");
+
+      // We will hit the debugger statement on resume, so do this nastiness to skip over it.
+      gClient.addOneTimeListener(
+        "paused",
+        gThreadClient.resume.bind(
+          gThreadClient,
+          finishClient.bind(null, gClient)));
+      gThreadClient.resume();
+    });
+    gDebuggee.runTest();
+  });
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/server/tests/unit/test_blackboxing-03.js
@@ -0,0 +1,104 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Test that we don't stop at debugger statements inside black boxed sources.
+ */
+
+var gDebuggee;
+var gClient;
+var gThreadClient;
+
+function run_test()
+{
+  initTestDebuggerServer();
+  gDebuggee = addTestGlobal("test-black-box");
+  gClient = new DebuggerClient(DebuggerServer.connectPipe());
+  gClient.connect(function() {
+    attachTestTabAndResume(gClient, "test-black-box", function(aResponse, aTabClient, aThreadClient) {
+      gThreadClient = aThreadClient;
+      test_black_box();
+    });
+  });
+  do_test_pending();
+}
+
+const BLACK_BOXED_URL = "http://example.com/blackboxme.js";
+const SOURCE_URL = "http://example.com/source.js";
+
+function test_black_box()
+{
+  gClient.addOneTimeListener("paused", function () {
+    gThreadClient.setBreakpoint({
+      url: SOURCE_URL,
+      line: 4
+    }, function ({error}) {
+      do_check_true(!error, "Should not get an error: " + error);
+      gThreadClient.resume(test_black_box_dbg_statement);
+    });
+  });
+
+  Components.utils.evalInSandbox(
+    "" + function doStuff(k) { // line 1
+      debugger;                // line 2 - Break here
+      k(100);                  // line 3
+    },                         // line 4
+    gDebuggee,
+    "1.8",
+    BLACK_BOXED_URL,
+    1
+  );
+
+  Components.utils.evalInSandbox(
+    "" + function runTest() { // line 1
+      doStuff(                // line 2
+        function (n) {        // line 3
+          Math.abs(n);        // line 4 - Break here
+        }                     // line 5
+      );                      // line 6
+    }                         // line 7
+    + "\n debugger;",         // line 8
+    gDebuggee,
+    "1.8",
+    SOURCE_URL,
+    1
+  );
+}
+
+function test_black_box_dbg_statement() {
+  gThreadClient.getSources(function ({error, sources}) {
+    do_check_true(!error, "Should not get an error: " + error);
+    let sourceClient = gThreadClient.source(sources.filter(s => s.url == BLACK_BOXED_URL)[0]);
+
+    sourceClient.blackBox(function ({error}) {
+      do_check_true(!error, "Should not get an error: " + error);
+
+      gClient.addOneTimeListener("paused", function (aEvent, aPacket) {
+        do_check_eq(aPacket.why.type, "breakpoint",
+                    "We should pass over the debugger statement.");
+        gThreadClient.resume(test_unblack_box_dbg_statement.bind(null, sourceClient));
+      });
+      gDebuggee.runTest();
+    });
+  });
+}
+
+function test_unblack_box_dbg_statement(aSourceClient) {
+  aSourceClient.unblackBox(function ({error}) {
+    do_check_true(!error, "Should not get an error: " + error);
+
+    gClient.addOneTimeListener("paused", function (aEvent, aPacket) {
+      do_check_eq(aPacket.why.type, "debuggerStatement",
+                  "We should stop at the debugger statement again");
+
+      // We will hit the breakpoint on resume, so do this nastiness to skip over it.
+      gClient.addOneTimeListener(
+        "paused",
+        gThreadClient.resume.bind(
+          gThreadClient,
+          finishClient.bind(null, gClient)));
+      gThreadClient.resume();
+    });
+    gDebuggee.runTest();
+  });
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/server/tests/unit/test_blackboxing-04.js
@@ -0,0 +1,79 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Test behavior of blackboxing sources we are currently paused in.
+ */
+
+var gDebuggee;
+var gClient;
+var gThreadClient;
+
+function run_test()
+{
+  initTestDebuggerServer();
+  gDebuggee = addTestGlobal("test-black-box");
+  gClient = new DebuggerClient(DebuggerServer.connectPipe());
+  gClient.connect(function() {
+    attachTestTabAndResume(gClient, "test-black-box", function(aResponse, aTabClient, aThreadClient) {
+      gThreadClient = aThreadClient;
+      test_black_box();
+    });
+  });
+  do_test_pending();
+}
+
+const BLACK_BOXED_URL = "http://example.com/blackboxme.js";
+const SOURCE_URL = "http://example.com/source.js";
+
+function test_black_box()
+{
+  gClient.addOneTimeListener("paused", function () {
+    gThreadClient.setBreakpoint({
+      url: BLACK_BOXED_URL,
+      line: 2
+    }, function (aResponse) {
+      do_check_true(!aResponse.error, "Should be able to set breakpoint.");
+      test_black_box_paused();
+    });
+  });
+
+  Components.utils.evalInSandbox(
+    "" + function doStuff(k) { // line 1
+      debugger;                // line 2
+      k(100);                  // line 3
+    },                         // line 4
+    gDebuggee,
+    "1.8",
+    BLACK_BOXED_URL,
+    1
+  );
+
+  Components.utils.evalInSandbox(
+    "" + function runTest() { // line 1
+      doStuff(                // line 2
+        function (n) {        // line 3
+          n;                  // line 4
+        }                     // line 5
+      );                      // line 6
+    }                         // line 7
+    + "\n runTest();",        // line 8
+    gDebuggee,
+    "1.8",
+    SOURCE_URL,
+    1
+  );
+}
+
+function test_black_box_paused() {
+  gThreadClient.getSources(function ({error, sources}) {
+    do_check_true(!error, "Should not get an error: " + error);
+    let sourceClient = gThreadClient.source(sources.filter(s => s.url == BLACK_BOXED_URL)[0]);
+
+    sourceClient.blackBox(function ({error, pausedInSource}) {
+      do_check_true(!error, "Should not get an error: " + error);
+      do_check_true(pausedInSource, "We should be notified that we are currently paused in this source");
+      finishClient(gClient);
+    });
+  });
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/server/tests/unit/test_blackboxing-05.js
@@ -0,0 +1,79 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Test exceptions inside black boxed sources.
+ */
+
+var gDebuggee;
+var gClient;
+var gThreadClient;
+
+function run_test()
+{
+  initTestDebuggerServer();
+  gDebuggee = addTestGlobal("test-black-box");
+  gClient = new DebuggerClient(DebuggerServer.connectPipe());
+  gClient.connect(function() {
+    attachTestTabAndResume(gClient, "test-black-box", function(aResponse, aTabClient, aThreadClient) {
+      gThreadClient = aThreadClient;
+      test_black_box();
+    });
+  });
+  do_test_pending();
+}
+
+const BLACK_BOXED_URL = "http://example.com/blackboxme.js";
+const SOURCE_URL = "http://example.com/source.js";
+
+function test_black_box()
+{
+  gClient.addOneTimeListener("paused", test_black_box_exception);
+
+  Components.utils.evalInSandbox(
+    "" + function doStuff(k) {                                   // line 1
+      throw new Error("wu tang clan ain't nuthin' ta fuck wit"); // line 2
+      k(100);                                                    // line 3
+    },                                                           // line 4
+    gDebuggee,
+    "1.8",
+    BLACK_BOXED_URL,
+    1
+  );
+
+  Components.utils.evalInSandbox(
+    "" + function runTest() { // line 1
+      doStuff(                // line 2
+        function (n) {        // line 3
+          debugger;           // line 4
+        }                     // line 5
+      );                      // line 6
+    }                         // line 7
+    + "\ndebugger;\n"         // line 8
+    + "runTest()",            // line 9
+    gDebuggee,
+    "1.8",
+    SOURCE_URL,
+    1
+  );
+}
+
+function test_black_box_exception() {
+  gThreadClient.getSources(function ({error, sources}) {
+    do_check_true(!error, "Should not get an error: " + error);
+    let sourceClient = gThreadClient.source(sources.filter(s => s.url == BLACK_BOXED_URL)[0]);
+
+    sourceClient.blackBox(function ({error}) {
+      do_check_true(!error, "Should not get an error: " + error);
+      gThreadClient.pauseOnExceptions(true);
+
+      gClient.addOneTimeListener("paused", function (aEvent, aPacket) {
+        do_check_neq(aPacket.frame.where.url, BLACK_BOXED_URL,
+                     "We shouldn't pause while in the black boxed source.");
+        finishClient(gClient);
+      });
+
+      gThreadClient.resume();
+    });
+  });
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/server/tests/unit/test_stepping-06.js
@@ -0,0 +1,85 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Check that stepping out of a function returns the right return value.
+ */
+
+var gDebuggee;
+var gClient;
+var gThreadClient;
+
+function run_test()
+{
+  initTestDebuggerServer();
+  gDebuggee = addTestGlobal("test-stack");
+  gClient = new DebuggerClient(DebuggerServer.connectPipe());
+  gClient.connect(function () {
+    attachTestTabAndResume(gClient, "test-stack", function (aResponse, aTabClient, aThreadClient) {
+      gThreadClient = aThreadClient;
+      test_simple_stepping();
+    });
+  });
+  do_test_pending();
+}
+
+function test_simple_stepping()
+{
+  gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) {
+    gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) {
+      // Check that the return value is 10.
+      do_check_eq(aPacket.type, "paused");
+      do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 4);
+      do_check_eq(aPacket.why.type, "resumeLimit");
+      do_check_eq(aPacket.why.frameFinished.return, 10);
+
+      gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) {
+        gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) {
+          // Check that the return value is undefined.
+          do_check_eq(aPacket.type, "paused");
+          do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 7);
+          do_check_eq(aPacket.why.type, "resumeLimit");
+          do_check_eq(aPacket.why.frameFinished.return.type, "undefined");
+
+          gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) {
+            gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) {
+              // Check that the exception was thrown.
+              do_check_eq(aPacket.type, "paused");
+              do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 12);
+              do_check_eq(aPacket.why.type, "resumeLimit");
+              do_check_eq(aPacket.why.frameFinished.throw, "ah");
+
+              gThreadClient.resume(function () {
+                finishClient(gClient);
+              });
+            });
+            gThreadClient.stepOut();
+          });
+          gThreadClient.resume();
+        });
+        gThreadClient.stepOut();
+      });
+      gThreadClient.resume();
+    });
+    gThreadClient.stepOut();
+
+  });
+
+  gDebuggee.eval("var line0 = Error().lineNumber;\n" +
+                 "function f() {\n" + // line0 + 1
+                 "  debugger;\n" +    // line0 + 2
+                 "  var a = 10;\n" +  // line0 + 3
+                 "  return a;\n" +    // line0 + 4
+                 "}\n" +              // line0 + 5
+                 "function g() {\n" + // line0 + 6
+                 "  debugger;\n" +    // line0 + 7
+                 "}\n" +              // line0 + 8
+                 "function h() {\n" + // line0 + 9
+                 "  debugger;\n" +    // line0 + 10
+                 "  throw 'ah';\n" +  // line0 + 11
+                 "  return 2;\n" +    // line0 + 12
+                 "}\n" +              // line0 + 13
+                 "f();\n" +           // line0 + 14
+                 "g();\n" +           // line0 + 15
+                 "h();\n");           // line0 + 16
+}
--- a/toolkit/devtools/server/tests/unit/xpcshell.ini
+++ b/toolkit/devtools/server/tests/unit/xpcshell.ini
@@ -6,16 +6,21 @@ tail =
 [test_nsjsinspector.js]
 [test_dbgsocket.js]
 skip-if = toolkit == "gonk"
 reason = bug 821285
 [test_dbgactor.js]
 [test_dbgglobal.js]
 [test_dbgclient_debuggerstatement.js]
 [test_attach.js]
+[test_blackboxing-01.js]
+[test_blackboxing-02.js]
+[test_blackboxing-03.js]
+[test_blackboxing-04.js]
+[test_blackboxing-05.js]
 [test_frameactor-01.js]
 [test_frameactor-02.js]
 [test_frameactor-03.js]
 [test_frameactor-04.js]
 [test_frameactor-05.js]
 [test_framearguments-01.js]
 [test_pauselifetime-01.js]
 [test_pauselifetime-02.js]
@@ -99,16 +104,17 @@ reason = bug 820380
 [test_objectgrips-06.js]
 [test_objectgrips-07.js]
 [test_interrupt.js]
 [test_stepping-01.js]
 [test_stepping-02.js]
 [test_stepping-03.js]
 [test_stepping-04.js]
 [test_stepping-05.js]
+[test_stepping-06.js]
 [test_framebindings-01.js]
 [test_framebindings-02.js]
 [test_framebindings-03.js]
 [test_framebindings-04.js]
 [test_framebindings-05.js]
 [test_framebindings-06.js]
 [test_pause_exceptions-01.js]
 skip-if = toolkit == "gonk"
--- a/toolkit/mozapps/update/common/updatehelper.cpp
+++ b/toolkit/mozapps/update/common/updatehelper.cpp
@@ -764,22 +764,17 @@ IsUnpromptedElevation(BOOL &isUnprompted
     if (!GetDefaultBrowserAppModelID(appModelID, (sizeof(appModelID)/sizeof(WCHAR)))) {
       activateMgr->Release();
       CoUninitialize();
       return hr;
     }
 
     // Hand off focus rights to the out-of-process activation server. Without
     // this the metro interface won't launch.
-    hr = CoAllowSetForegroundWindow(activateMgr, NULL);
-    if (FAILED(hr)) {
-      activateMgr->Release();
-      CoUninitialize();
-      return hr;
-    }
+    CoAllowSetForegroundWindow(activateMgr, NULL);
 
     // Launch default browser in Metro
     DWORD processID;
     hr = activateMgr->ActivateApplication(appModelID, L"", AO_NOERRORUI,
                                           &processID);
     activateMgr->Release();
     CoUninitialize();
     return hr;
--- a/toolkit/mozapps/update/nsIUpdateService.idl
+++ b/toolkit/mozapps/update/nsIUpdateService.idl
@@ -449,16 +449,20 @@ interface nsIApplicationUpdateService : 
  * an update after it's been downloaded.
  */
 [scriptable, uuid(74439497-d796-4915-8cef-3dfe43027e4d)]
 interface nsIUpdateProcessor : nsISupports
 {
   /**
    * Processes the update which has been downloaded.
    * This happens without restarting the application.
+   * On Windows, this can also be used for switching to an applied
+   * update request.
+   * @param update The update being applied, or null if this is a switch
+   *               to updated application request.  Must be non-null on GONK.
    */
   void processUpdate(in nsIUpdate update);
 };
 
 /**
  * An interface describing a global application service that maintains a list
  * of updates previously performed as well as the current active update.
  */
--- a/toolkit/mozapps/update/nsUpdateService.js
+++ b/toolkit/mozapps/update/nsUpdateService.js
@@ -27,16 +27,17 @@ const PREF_APP_UPDATE_BACKGROUND_INTERVA
 const PREF_APP_UPDATE_BACKGROUNDERRORS    = "app.update.backgroundErrors";
 const PREF_APP_UPDATE_BACKGROUNDMAXERRORS = "app.update.backgroundMaxErrors";
 const PREF_APP_UPDATE_CERTS_BRANCH        = "app.update.certs.";
 const PREF_APP_UPDATE_CERT_CHECKATTRS     = "app.update.cert.checkAttributes";
 const PREF_APP_UPDATE_CERT_ERRORS         = "app.update.cert.errors";
 const PREF_APP_UPDATE_CERT_MAXERRORS      = "app.update.cert.maxErrors";
 const PREF_APP_UPDATE_CERT_REQUIREBUILTIN = "app.update.cert.requireBuiltIn";
 const PREF_APP_UPDATE_ENABLED             = "app.update.enabled";
+const PREF_APP_UPDATE_METRO_ENABLED       = "app.update.metro.enabled";
 const PREF_APP_UPDATE_IDLETIME            = "app.update.idletime";
 const PREF_APP_UPDATE_INCOMPATIBLE_MODE   = "app.update.incompatible.mode";
 const PREF_APP_UPDATE_INTERVAL            = "app.update.interval";
 const PREF_APP_UPDATE_LOG                 = "app.update.log";
 const PREF_APP_UPDATE_MODE                = "app.update.mode";
 const PREF_APP_UPDATE_NEVER_BRANCH        = "app.update.never.";
 const PREF_APP_UPDATE_POSTUPDATE          = "app.update.postupdate";
 const PREF_APP_UPDATE_PROMPTWAITTIME      = "app.update.promptWaitTime";
@@ -637,27 +638,62 @@ XPCOMUtils.defineLazyGetter(this, "gCanS
         "browser is already handling updates for this installation.");
     return false;
   }
 
   LOG("gCanStageUpdates - able to stage updates");
   return true;
 });
 
+XPCOMUtils.defineLazyGetter(this, "gIsMetro", function aus_gIsMetro() {
+#ifdef XP_WIN
+#ifdef MOZ_METRO
+  try {
+    let metroUtils = Cc["@mozilla.org/windows-metroutils;1"].
+                    createInstance(Ci.nsIWinMetroUtils);
+    return metroUtils && metroUtils.immersive;
+  } catch (e) {}
+#endif
+#endif
+
+  return false;
+});
+
+XPCOMUtils.defineLazyGetter(this, "gMetroUpdatesEnabled", function aus_gMetroUpdatesEnabled() {
+#ifdef XP_WIN
+#ifdef MOZ_METRO
+  if (gIsMetro) {
+    let metroUpdate = getPref("getBoolPref", PREF_APP_UPDATE_METRO_ENABLED, true);
+    if (!metroUpdate) {
+      LOG("gMetroUpdatesEnabled - unable to automatically check for metro" +
+          " updates, disabled by pref");
+      return false;
+    }
+  }
+#endif
+#endif
+
+  return true;
+});
+
 XPCOMUtils.defineLazyGetter(this, "gCanCheckForUpdates", function aus_gCanCheckForUpdates() {
   // If the administrator has locked the app update functionality
   // OFF - this is not just a user setting, so disable the manual
   // UI too.
   var enabled = getPref("getBoolPref", PREF_APP_UPDATE_ENABLED, true);
   if (!enabled && Services.prefs.prefIsLocked(PREF_APP_UPDATE_ENABLED)) {
     LOG("gCanCheckForUpdates - unable to automatically check for updates, " +
         "disabled by pref");
     return false;
   }
 
+  if (!gMetroUpdatesEnabled) {
+    return false;
+  }
+
   // If we don't know the binary platform we're updating, we can't update.
   if (!gABI) {
     LOG("gCanCheckForUpdates - unable to check for updates, unknown ABI");
     return false;
   }
 
   // If we don't know the OS version we're updating, we can't update.
   if (!gOSVersion) {
@@ -2020,16 +2056,18 @@ UpdateService.prototype = {
     if (status == STATE_SUCCEEDED) {
       // Report telemetry that we want after each successful update.
       // We do this only on successful updates so that we only get
       // one report from each user for each version.  If a user cancels
       // UAC for example, we don't want 2 reports from them on the same
       // version.
       this._sendBoolPrefTelemetryPing(PREF_APP_UPDATE_ENABLED,
                                       "UPDATER_UPDATES_ENABLED");
+      this._sendBoolPrefTelemetryPing(PREF_APP_UPDATE_METRO_ENABLED,
+                                      "UPDATER_UPDATES_METRO_ENABLED");
       this._sendBoolPrefTelemetryPing(PREF_APP_UPDATE_AUTO,
                                       "UPDATER_UPDATES_AUTOMATIC");
       this._sendBoolPrefTelemetryPing(PREF_APP_UPDATE_STAGE_ENABLED,
                                       "UPDATER_STAGE_ENABLED");
 
 #ifdef XP_WIN
       this._sendBoolPrefTelemetryPing(PREF_APP_UPDATE_SERVICE_ENABLED,
                                       "UPDATER_SERVICE_ENABLED");
@@ -2390,16 +2428,20 @@ UpdateService.prototype = {
 
     var updateEnabled = getPref("getBoolPref", PREF_APP_UPDATE_ENABLED, true);
     if (!updateEnabled) {
       LOG("UpdateService:_selectAndInstallUpdate - not prompting because " +
           "update is disabled");
       return;
     }
 
+    if (!gMetroUpdatesEnabled) {
+      return;
+    }
+
     if (!gCanApplyUpdates) {
       LOG("UpdateService:_selectAndInstallUpdate - the user is unable to " +
           "apply updates... prompting");
       this._showPrompt(update);
       return;
     }
 
     /**
@@ -2427,24 +2469,28 @@ UpdateService.prototype = {
 #      Minor         0      N/A                    Auto Install
 #      Minor         1      Yes                    Notify
 #      Minor         1      No                     Auto Install
      */
     if (update.showPrompt) {
       LOG("UpdateService:_selectAndInstallUpdate - prompting because the " +
           "update snippet specified showPrompt");
       this._showPrompt(update);
-      return;
+      if (!gIsMetro) {
+        return;
+      }
     }
 
     if (!getPref("getBoolPref", PREF_APP_UPDATE_AUTO, true)) {
       LOG("UpdateService:_selectAndInstallUpdate - prompting because silent " +
           "install is disabled");
       this._showPrompt(update);
-      return;
+      if (!gIsMetro) {
+        return;
+      }
     }
 
     if (getPref("getIntPref", PREF_APP_UPDATE_MODE, 1) == 0) {
       // Do not prompt regardless of add-on incompatibilities
       LOG("UpdateService:_selectAndInstallUpdate - no need to show prompt, " +
           "just download the update");
       var status = this.downloadUpdate(update, true);
       if (status == STATE_NONE)
@@ -3359,16 +3405,20 @@ Checker.prototype = {
     this._request = null;
   },
 
   /**
    * Whether or not we are allowed to do update checking.
    */
   _enabled: true,
   get enabled() {
+    if (!gMetroUpdatesEnabled) {
+      return false;
+    }
+
     return getPref("getBoolPref", PREF_APP_UPDATE_ENABLED, true) &&
            gCanCheckForUpdates && this._enabled;
   },
 
   /**
    * See nsIUpdateService.idl
    */
   stopChecking: function UC_stopChecking(duration) {
--- a/toolkit/mozapps/update/test/chrome/utils.js
+++ b/toolkit/mozapps/update/test/chrome/utils.js
@@ -162,20 +162,21 @@ var gTimeoutTimer;
 // update window to close before giving up.
 const CLOSE_WINDOW_TIMEOUT_MAXCOUNT = 10;
 // Counter for the SimpleTest.executeSoon when waiting on an update window to
 // close before giving up.
 var gCloseWindowTimeoutCounter = 0;
 
 // The following vars are for restoring previous preference values (if present)
 // when the test finishes.
-var gAppUpdateEnabled;    // app.update.enabled
-var gAppUpdateURLDefault; // app.update.url (default prefbranch)
-var gAppUpdateURL;        // app.update.url.override
-var gExtUpdateURL;        // extensions.update.url
+var gAppUpdateEnabled;      // app.update.enabled
+var gAppUpdateMetroEnabled; // app.update.metro.enabled
+var gAppUpdateURLDefault;   // app.update.url (default prefbranch)
+var gAppUpdateURL;          // app.update.url.override
+var gExtUpdateURL;          // extensions.update.url
 
 var gTestCounter = -1;
 var gWin;
 var gDocElem;
 var gPrefToCheck;
 var gDisableNoUpdateAddon = false;
 
 // Set to true to log additional information for debugging. To log additional
@@ -804,16 +805,21 @@ function setupPrefs() {
     gAppUpdateURL = Services.prefs.getCharPref(PREF_APP_UPDATE_URL_OVERRIDE);
   }
 
   if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_ENABLED)) {
     gAppUpdateEnabled = Services.prefs.getBoolPref(PREF_APP_UPDATE_ENABLED);
   }
   Services.prefs.setBoolPref(PREF_APP_UPDATE_ENABLED, true)
 
+  if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_METRO_ENABLED)) {
+    gAppUpdateMetroEnabled = Services.prefs.getBoolPref(PREF_APP_UPDATE_METRO_ENABLED);
+  }
+  Services.prefs.setBoolPref(PREF_APP_UPDATE_METRO_ENABLED, true)
+
   if (Services.prefs.prefHasUserValue(PREF_EXTENSIONS_UPDATE_URL)) {
     gExtUpdateURL = Services.prefs.getCharPref(PREF_EXTENSIONS_UPDATE_URL);
   }
   let extUpdateUrl = URL_UPDATE + "?addonID=%ITEM_ID%&platformVersion=" +
                      getNewerPlatformVersion();
   Services.prefs.setCharPref(PREF_EXTENSIONS_UPDATE_URL, extUpdateUrl);
   debugDump("extensions.update.url: " + extUpdateUrl);
 
@@ -840,16 +846,23 @@ function resetPrefs() {
 
   if (gAppUpdateEnabled !== undefined) {
     Services.prefs.setBoolPref(PREF_APP_UPDATE_ENABLED, gAppUpdateEnabled);
   }
   else if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_ENABLED)) {
     Services.prefs.clearUserPref(PREF_APP_UPDATE_ENABLED);
   }
 
+  if (gAppUpdateMetroEnabled !== undefined) {
+    Services.prefs.setBoolPref(PREF_APP_UPDATE_METRO_ENABLED, gAppUpdateMetroEnabled);
+  }
+  else if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_METRO_ENABLED)) {
+    Services.prefs.clearUserPref(PREF_APP_UPDATE_METRO_ENABLED);
+  }
+
   if (gExtUpdateURL !== undefined) {
     Services.prefs.setCharPref(PREF_EXTENSIONS_UPDATE_URL, gExtUpdateURL);
   }
   else if (Services.prefs.prefHasUserValue(PREF_EXTENSIONS_UPDATE_URL)) {
     Services.prefs.clearUserPref(PREF_EXTENSIONS_UPDATE_URL);
   }
 
   if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_IDLETIME)) {
--- a/toolkit/mozapps/update/test/shared.js
+++ b/toolkit/mozapps/update/test/shared.js
@@ -18,16 +18,17 @@ const PREF_APP_UPDATE_BACKGROUNDERRORS  
 const PREF_APP_UPDATE_BACKGROUNDMAXERRORS = "app.update.backgroundMaxErrors";
 const PREF_APP_UPDATE_CERTS_BRANCH        = "app.update.certs.";
 const PREF_APP_UPDATE_CERT_CHECKATTRS     = "app.update.cert.checkAttributes";
 const PREF_APP_UPDATE_CERT_ERRORS         = "app.update.cert.errors";
 const PREF_APP_UPDATE_CERT_MAXERRORS      = "app.update.cert.maxErrors";
 const PREF_APP_UPDATE_CERT_REQUIREBUILTIN = "app.update.cert.requireBuiltIn";
 const PREF_APP_UPDATE_CHANNEL             = "app.update.channel";
 const PREF_APP_UPDATE_ENABLED             = "app.update.enabled";
+const PREF_APP_UPDATE_METRO_ENABLED       = "app.update.metro.enabled";
 const PREF_APP_UPDATE_IDLETIME            = "app.update.idletime";
 const PREF_APP_UPDATE_LOG                 = "app.update.log";
 const PREF_APP_UPDATE_NEVER_BRANCH        = "app.update.never.";
 const PREF_APP_UPDATE_PROMPTWAITTIME      = "app.update.promptWaitTime";
 const PREF_APP_UPDATE_SERVICE_ENABLED     = "app.update.service.enabled";
 const PREF_APP_UPDATE_SHOW_INSTALLED_UI   = "app.update.showInstalledUI";
 const PREF_APP_UPDATE_SILENT              = "app.update.silent";
 const PREF_APP_UPDATE_URL                 = "app.update.url";
--- a/toolkit/mozapps/update/test/unit/head_update.js.in
+++ b/toolkit/mozapps/update/test/unit/head_update.js.in
@@ -353,16 +353,17 @@ function cleanUp() {
   gTestserver = null;
 }
 
 /**
  * Sets the most commonly used preferences used by tests
  */
 function setDefaultPrefs() {
   Services.prefs.setBoolPref(PREF_APP_UPDATE_ENABLED, true);
+  Services.prefs.setBoolPref(PREF_APP_UPDATE_METRO_ENABLED, true);
   // Don't display UI for a successful installation. Some apps may not set this
   // pref to false like Firefox does.
   Services.prefs.setBoolPref(PREF_APP_UPDATE_SHOW_INSTALLED_UI, false);
   // Enable Update logging
   Services.prefs.setBoolPref(PREF_APP_UPDATE_LOG, true);
 }
 
 /**
--- a/toolkit/mozapps/update/updater/updater.cpp
+++ b/toolkit/mozapps/update/updater/updater.cpp
@@ -53,16 +53,20 @@
 #include "mozilla/Compiler.h"
 
 // Amount of the progress bar to use in each of the 3 update stages,
 // should total 100.0.
 #define PROGRESS_PREPARE_SIZE 20.0f
 #define PROGRESS_EXECUTE_SIZE 75.0f
 #define PROGRESS_FINISH_SIZE   5.0f
 
+// Amount of time in ms to wait for the parent process to close
+#define PARENT_WAIT 5000
+#define IMMERSIVE_PARENT_WAIT 15000
+
 #if defined(XP_MACOSX)
 // These functions are defined in launchchild_osx.mm
 void LaunchChild(int argc, char **argv);
 void LaunchMacPostProcess(const char* aAppExe);
 #endif
 
 #ifndef _O_BINARY
 # define _O_BINARY 0
@@ -1672,16 +1676,33 @@ PatchIfFile::Finish(int status)
 
 //-----------------------------------------------------------------------------
 
 #ifdef XP_WIN
 #include "nsWindowsRestart.cpp"
 #include "nsWindowsHelpers.h"
 #include "uachelper.h"
 #include "pathhash.h"
+
+#ifdef MOZ_METRO
+/**
+ * Determines if the update came from an Immersive browser
+ * @return true if the update came from an immersive browser
+ */
+bool
+IsUpdateFromMetro(int argc, NS_tchar **argv)
+{
+  for (int i = 0; i < argc; i++) {
+    if (!wcsicmp(L"-ServerName:DefaultBrowserServer", argv[i])) {
+      return true;
+    }
+  }
+  return false;
+}
+#endif
 #endif
 
 static void
 LaunchCallbackApp(const NS_tchar *workingDir, 
                   int argc, 
                   NS_tchar **argv, 
                   bool usingService)
 {
@@ -1701,21 +1722,19 @@ LaunchCallbackApp(const NS_tchar *workin
   LaunchChild(argc, argv);
 #elif defined(XP_WIN)
   // Do not allow the callback to run when running an update through the
   // service as session 0.  The unelevated updater.exe will do the launching.
   if (!usingService) {
 #if defined(MOZ_METRO)
     // If our callback application is the default metro browser, then
     // launch it now.
-    for (int i = 0; i < argc; i++) {
-      if (!wcsicmp(L"-ServerName:DefaultBrowserServer", argv[i])) {
-        LaunchDefaultMetroBrowser();
-        return;
-      }
+    if (IsUpdateFromMetro(argc, argv)) {
+      LaunchDefaultMetroBrowser();
+      return;
     }
 #endif
     WinLaunchChild(argv[0], argc, argv, NULL);
   }
 #else
 # warning "Need implementaton of LaunchCallbackApp"
 #endif
 }
@@ -2461,19 +2480,25 @@ int NS_main(int argc, NS_tchar **argv)
 
 #ifdef XP_WIN
   if (pid > 0) {
     HANDLE parent = OpenProcess(SYNCHRONIZE, false, (DWORD) pid);
     // May return NULL if the parent process has already gone away.
     // Otherwise, wait for the parent process to exit before starting the
     // update.
     if (parent) {
-      DWORD result = WaitForSingleObject(parent, 5000);
+      bool updateFromMetro = false;
+#ifdef MOZ_METRO
+      updateFromMetro = IsUpdateFromMetro(argc, argv);
+#endif
+      DWORD waitTime = updateFromMetro ?
+                       IMMERSIVE_PARENT_WAIT : PARENT_WAIT;
+      DWORD result = WaitForSingleObject(parent, waitTime);
       CloseHandle(parent);
-      if (result != WAIT_OBJECT_0)
+      if (result != WAIT_OBJECT_0 && !updateFromMetro)
         return 1;
     }
   }
 #else
   if (pid > 0)
     waitpid(pid, NULL, 0);
 #endif
 
--- a/toolkit/xre/nsUpdateDriver.cpp
+++ b/toolkit/xre/nsUpdateDriver.cpp
@@ -1029,18 +1029,16 @@ nsUpdateProcessor::nsUpdateProcessor()
 NS_IMETHODIMP
 nsUpdateProcessor::ProcessUpdate(nsIUpdate* aUpdate)
 {
   nsCOMPtr<nsIFile> greDir, appDir, updRoot;
   nsAutoCString appVersion;
   int argc;
   char **argv;
 
-  NS_ENSURE_ARG_POINTER(aUpdate);
-
   nsAutoCString binPath;
   nsXREDirProvider* dirProvider = nsXREDirProvider::GetSingleton();
   if (dirProvider) { // Normal code path
     // Check for and process any available updates
     bool persistent;
     nsresult rv = NS_ERROR_FAILURE; // Take the NS_FAILED path when non-GONK
 #ifdef MOZ_WIDGET_GONK
     // Check in the sdcard for updates first, since that's our preferred
@@ -1127,16 +1125,18 @@ nsUpdateProcessor::ProcessUpdate(nsIUpda
     MOZ_ASSERT(argc == 1); // see above
     const size_t length = binPath.Length();
     mInfo.mArgv[0] = new char[length + 1];
     strcpy(mInfo.mArgv[0], binPath.get());
   }
   mInfo.mAppVersion = appVersion;
 
 #if defined(MOZ_WIDGET_GONK)
+  NS_ENSURE_ARG_POINTER(aUpdate);
+
   bool isOSUpdate;
   if (NS_SUCCEEDED(aUpdate->GetIsOSUpdate(&isOSUpdate)) &&
       isOSUpdate) {
     nsAutoCString osApplyToDir;
 
     // This needs to be done on the main thread, so we pass it along in
     // BackgroundThreadInfo
     nsresult rv = GetOSApplyToDir(osApplyToDir);
@@ -1209,15 +1209,15 @@ nsUpdateProcessor::WaitForProcess()
 
 void
 nsUpdateProcessor::UpdateDone()
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
 
   nsCOMPtr<nsIUpdateManager> um =
     do_GetService("@mozilla.org/updates/update-manager;1");
-  if (um) {
+  if (um && mUpdate) {
     um->RefreshUpdateStatus(mUpdate);
   }
 
   ShutdownWatcherThread();
 }