Merge inbound to m-c.
authorRyan VanderMeulen <ryanvm@gmail.com>
Sat, 22 Sep 2012 08:28:28 -0400
changeset 107808 b461a7cd250e
parent 107766 9cfb80a82883 (current diff)
parent 107807 a06ea10ddb04 (diff)
child 107809 156f00b9ac92
push id23509
push userryanvm@gmail.com
push dateSat, 22 Sep 2012 12:28:38 +0000
treeherdermozilla-central@b461a7cd250e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone18.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 inbound to m-c.
browser/base/content/browser.js
image/test/mochitest/bug733553-iframe.html
--- a/.gitignore
+++ b/.gitignore
@@ -42,11 +42,11 @@ parser/html/java/htmlparser/
 parser/html/java/javaparser/
 
 # Ignore the files and directory that Eclipse IDE creates
 .project
 .cproject
 .settings/
 
 # Python virtualenv artifacts.
-python/psutil/.*.so
-python/psutil/.*.pyd
+python/psutil/*.so
+python/psutil/*.pyd
 python/psutil/build/
--- a/accessible/tests/mochitest/events/test_contextmenu.html
+++ b/accessible/tests/mochitest/events/test_contextmenu.html
@@ -96,17 +96,17 @@
       return null;
     }
 
     ////////////////////////////////////////////////////////////////////////////
     // Do tests
 
     var gQueue = null;
     //gA11yEventDumpID = "eventdump"; // debug stuff
-    //gA11yEventDumpToConsole = true;
+    gA11yEventDumpToConsole = true;
 
     function doTests()
     {
       gQueue = new eventQueue();
 
       gQueue.push(new showContextMenu("input"));
       gQueue.push(new selectMenuItem());
       gQueue.push(new closeContextMenu());
--- a/accessible/tests/mochitest/states/test_link.html
+++ b/accessible/tests/mochitest/states/test_link.html
@@ -16,16 +16,18 @@
   <script type="application/javascript"
           src="../role.js"></script>
   <script type="application/javascript"
           src="../states.js"></script>
   <script type="application/javascript"
           src="../events.js"></script>
 
   <script type="application/javascript">
+    gA11yEventDumpToConsole = true; // debug stuff
+
     function doTest()
     {
       // a@href and its text node
       testStates("link_href", STATE_LINKED);
       testStates(getAccessible("link_href").firstChild, STATE_LINKED);
 
       // a@onclick
       testStates("link_click", STATE_LINKED);
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -174,16 +174,17 @@ var shell = {
     SettingsListener.observe("debug.fps.enabled", false, function(value) {
       Services.prefs.setBoolPref("layers.acceleration.draw-fps", value);
     });
     SettingsListener.observe("debug.paint-flashing.enabled", false, function(value) {
       Services.prefs.setBoolPref("nglayout.debug.paint_flashing", value);
     });
 
     this.contentBrowser.src = homeURL;
+    this.isHomeLoaded = false;
 
     ppmm.addMessageListener("content-handler", this);
   },
 
   stop: function shell_stop() {
     window.removeEventListener('keydown', this, true);
     window.removeEventListener('keypress', this, true);
     window.removeEventListener('keyup', this, true);
@@ -299,16 +300,28 @@ var shell = {
 
         let chromeWindow = window.QueryInterface(Ci.nsIDOMChromeWindow);
         chromeWindow.browserDOMWindow = new nsBrowserAccess();
 
         Cu.import('resource://gre/modules/Webapps.jsm');
         DOMApplicationRegistry.allAppsLaunchable = true;
 
         this.sendEvent(window, 'ContentStart');
+
+        let content = this.contentBrowser.contentWindow;
+        content.addEventListener('load', function shell_homeLoaded() {
+          content.removeEventListener('load', shell_homeLoaded);
+          shell.isHomeLoaded = true;
+
+          if ('pendingChromeEvents' in shell) {
+            shell.pendingChromeEvents.forEach((shell.sendChromeEvent).bind(shell));
+          }
+          delete shell.pendingChromeEvents;
+        });
+
         break;
       case 'MozApplicationManifest':
         try {
           if (!Services.prefs.getBoolPref('browser.cache.offline.enable'))
             return;
 
           let contentWindow = evt.originalTarget.defaultView;
           let documentElement = contentWindow.document.documentElement;
@@ -344,16 +357,25 @@ var shell = {
 
   sendEvent: function shell_sendEvent(content, type, details) {
     let event = content.document.createEvent('CustomEvent');
     event.initCustomEvent(type, true, true, details ? details : {});
     content.dispatchEvent(event);
   },
 
   sendChromeEvent: function shell_sendChromeEvent(details) {
+    if (!this.isHomeLoaded) {
+      if (!('pendingChromeEvents' in this)) {
+        this.pendingChromeEvents = [];
+      }
+
+      this.pendingChromeEvents.push(details);
+      return;
+    }
+
     this.sendEvent(getContentWindow(), "mozChromeEvent",
                    ObjectWrapper.wrap(details, getContentWindow()));
   },
 
   sendSystemMessage: function shell_sendSystemMessage(msg) {
     let origin = Services.io.newURI(msg.manifest, null, null).prePath;
     this.sendChromeEvent({
       type: 'open-app',
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -2071,31 +2071,38 @@ var gLastOpenDirectory = {
     this._lastDir = null;
   }
 };
 
 function BrowserOpenFileWindow()
 {
   // Get filepicker component.
   try {
-    const nsIFilePicker = Components.interfaces.nsIFilePicker;
-    var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
-    fp.init(window, gNavigatorBundle.getString("openFile"), nsIFilePicker.modeOpen);
-    fp.appendFilters(nsIFilePicker.filterAll | nsIFilePicker.filterText | nsIFilePicker.filterImages |
-                     nsIFilePicker.filterXML | nsIFilePicker.filterHTML);
+    const nsIFilePicker = Ci.nsIFilePicker;
+    let fp = Cc["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
+    let fpCallback = function fpCallback_done(aResult) {
+      if (aResult == nsIFilePicker.returnOK) {
+        try {
+          if (fp.file) {
+            gLastOpenDirectory.path =
+              fp.file.parent.QueryInterface(Ci.nsILocalFile);
+          }
+        } catch (ex) {
+        }
+        openUILinkIn(fp.fileURL.spec, "current");
+      }
+    };
+
+    fp.init(window, gNavigatorBundle.getString("openFile"),
+            nsIFilePicker.modeOpen);
+    fp.appendFilters(nsIFilePicker.filterAll | nsIFilePicker.filterText |
+                     nsIFilePicker.filterImages | nsIFilePicker.filterXML |
+                     nsIFilePicker.filterHTML);
     fp.displayDirectory = gLastOpenDirectory.path;
-
-    if (fp.show() == nsIFilePicker.returnOK) {
-      try {
-        if (fp.file)
-          gLastOpenDirectory.path = fp.file.parent.QueryInterface(Ci.nsILocalFile);
-      } catch(e) {
-      }
-      openUILinkIn(fp.fileURL.spec, "current");
-    }
+    fp.open(fpCallback);
   } catch (ex) {
   }
 }
 
 function BrowserCloseTabOrWindow() {
 #ifdef XP_MACOSX
   // If we're not a browser window, just close the window
   if (window.location.href != getBrowserURL()) {
--- a/browser/base/content/openLocation.js
+++ b/browser/base/content/openLocation.js
@@ -107,20 +107,27 @@ function createInstance(contractid, iidN
   var iid = Components.interfaces[iidName];
   return Components.classes[contractid].createInstance(iid);
 }
 
 const nsIFilePicker = Components.interfaces.nsIFilePicker;
 function onChooseFile()
 {
   try {
-    var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
-    fp.init(window, dialog.bundle.getString("chooseFileDialogTitle"), nsIFilePicker.modeOpen);
-    fp.appendFilters(nsIFilePicker.filterHTML | nsIFilePicker.filterText |
-                     nsIFilePicker.filterAll | nsIFilePicker.filterImages | nsIFilePicker.filterXML);
+    let fp = Components.classes["@mozilla.org/filepicker;1"].
+             createInstance(nsIFilePicker);
+    let fpCallback = function fpCallback_done(aResult) {
+      if (aResult == nsIFilePicker.returnOK && fp.fileURL.spec &&
+          fp.fileURL.spec.length > 0) {
+        dialog.input.value = fp.fileURL.spec;
+      }
+      doEnabling();
+    };
 
-    if (fp.show() == nsIFilePicker.returnOK && fp.fileURL.spec && fp.fileURL.spec.length > 0)
-      dialog.input.value = fp.fileURL.spec;
+    fp.init(window, dialog.bundle.getString("chooseFileDialogTitle"),
+            nsIFilePicker.modeOpen);
+    fp.appendFilters(nsIFilePicker.filterAll | nsIFilePicker.filterText |
+                     nsIFilePicker.filterImages | nsIFilePicker.filterXML |
+                     nsIFilePicker.filterHTML);
+    fp.open(fpCallback);
+  } catch (ex) {
   }
-  catch(ex) {
-  }
-  doEnabling();
 }
--- a/browser/base/content/pageinfo/pageInfo.js
+++ b/browser/base/content/pageinfo/pageInfo.js
@@ -756,41 +756,43 @@ function getSelectedRows(tree)
 }
 
 function getSelectedRow(tree)
 {
   var rows = getSelectedRows(tree);
   return (rows.length == 1) ? rows[0] : -1;
 }
 
-function selectSaveFolder()
+function selectSaveFolder(aCallback)
 {
   const nsILocalFile = Components.interfaces.nsILocalFile;
   const nsIFilePicker = Components.interfaces.nsIFilePicker;
-  var fp = Components.classes["@mozilla.org/filepicker;1"]
-                     .createInstance(nsIFilePicker);
-
-  var titleText = gBundle.getString("mediaSelectFolder");
-  fp.init(window, titleText, nsIFilePicker.modeGetFolder);
-  try {
-    var prefs = Components.classes[PREFERENCES_CONTRACTID]
-                          .getService(Components.interfaces.nsIPrefBranch);
+  let titleText = gBundle.getString("mediaSelectFolder");
+  let fp = Components.classes["@mozilla.org/filepicker;1"].
+           createInstance(nsIFilePicker);
+  let fpCallback = function fpCallback_done(aResult) {
+    if (aResult == nsIFilePicker.returnOK) {
+      aCallback(fp.file.QueryInterface(nsILocalFile));
+    } else {
+      aCallback(null);
+    }
+  };
 
-    var initialDir = prefs.getComplexValue("browser.download.dir", nsILocalFile);
-    if (initialDir)
+  fp.init(window, titleText, nsIFilePicker.modeGetFolder);
+  fp.appendFilters(nsIFilePicker.filterAll);
+  try {
+    let prefs = Components.classes[PREFERENCES_CONTRACTID].
+                getService(Components.interfaces.nsIPrefBranch);
+    let initialDir = prefs.getComplexValue("browser.download.dir", nsILocalFile);
+    if (initialDir) {
       fp.displayDirectory = initialDir;
+    }
+  } catch (ex) {
   }
-  catch (ex) { }
-
-  fp.appendFilters(nsIFilePicker.filterAll);
-  var ret = fp.show();
-
-  if (ret == nsIFilePicker.returnOK)
-    return fp.file.QueryInterface(nsILocalFile);
-  return null;
+  fp.open(fpCallback);
 }
 
 function saveMedia()
 {
   var tree = document.getElementById("imagetree");
   var rowArray = getSelectedRows(tree);
   if (rowArray.length == 1) {
     var row = rowArray[0];
@@ -802,47 +804,49 @@ function saveMedia()
 
       if (item instanceof HTMLVideoElement)
         titleKey = "SaveVideoTitle";
       else if (item instanceof HTMLAudioElement)
         titleKey = "SaveAudioTitle";
 
       saveURL(url, null, titleKey, false, false, makeURI(item.baseURI));
     }
-  }
-  else {
-    var odir  = selectSaveFolder();
-
-    var saveAnImage = function(aURIString, aChosenData, aBaseURI) {
-      internalSave(aURIString, null, null, null, null, false, "SaveImageTitle",
-                   aChosenData, aBaseURI);
-    }
-
-    for (var i = 0; i < rowArray.length; i++) {
-      var v = rowArray[i];
-      var dir = odir.clone();
-      var item = gImageView.data[v][COL_IMAGE_NODE];
-      var uriString = gImageView.data[v][COL_IMAGE_ADDRESS];
-      var uri = makeURI(uriString);
- 
-      try {
-        uri.QueryInterface(Components.interfaces.nsIURL);
-        dir.append(decodeURIComponent(uri.fileName));
+  } else {
+    selectSaveFolder(function(aDirectory) {
+      if (aDirectory) {
+        var saveAnImage = function(aURIString, aChosenData, aBaseURI) {
+          internalSave(aURIString, null, null, null, null, false, "SaveImageTitle",
+                       aChosenData, aBaseURI);
+        };
+      
+        for (var i = 0; i < rowArray.length; i++) {
+          var v = rowArray[i];
+          var dir = aDirectory.clone();
+          var item = gImageView.data[v][COL_IMAGE_NODE];
+          var uriString = gImageView.data[v][COL_IMAGE_ADDRESS];
+          var uri = makeURI(uriString);
+  
+          try {
+            uri.QueryInterface(Components.interfaces.nsIURL);
+            dir.append(decodeURIComponent(uri.fileName));
+          } catch(ex) {
+            /* data: uris */
+          }
+  
+          if (i == 0) {
+            saveAnImage(uriString, new AutoChosen(dir, uri), makeURI(item.baseURI));
+          } else {
+            // This delay is a hack which prevents the download manager
+            // from opening many times. See bug 377339.
+            setTimeout(saveAnImage, 200, uriString, new AutoChosen(dir, uri),
+                       makeURI(item.baseURI));
+          }
+        }
       }
-      catch(ex) { /* data: uris */ }
-
-      if (i == 0)
-        saveAnImage(uriString, new AutoChosen(dir, uri), makeURI(item.baseURI));
-      else {
-        // This delay is a hack which prevents the download manager
-        // from opening many times. See bug 377339.
-        setTimeout(saveAnImage, 200, uriString, new AutoChosen(dir, uri),
-                   makeURI(item.baseURI));
-      }
-    }
+    });
   }
 }
 
 function onBlockImage()
 {
   var permissionManager = Components.classes[PERMISSION_CONTRACTID]
                                     .getService(nsIPermissionManager);
 
--- a/browser/base/content/sync/utils.js
+++ b/browser/base/content/sync/utils.js
@@ -146,36 +146,38 @@ let gSyncUtils = {
    * Save passphrase backup document to disk as HTML file.
    * 
    * @param elid : ID of the form element containing the passphrase.
    */
   passphraseSave: function(elid) {
     let dialogTitle = this.bundle.GetStringFromName("save.recoverykey.title");
     let defaultSaveName = this.bundle.GetStringFromName("save.recoverykey.defaultfilename");
     this._preparePPiframe(elid, function(iframe) {
-      let filepicker = Cc["@mozilla.org/filepicker;1"]
-                         .createInstance(Ci.nsIFilePicker);
-      filepicker.init(window, dialogTitle, Ci.nsIFilePicker.modeSave);
-      filepicker.appendFilters(Ci.nsIFilePicker.filterHTML);
-      filepicker.defaultString = defaultSaveName;
-      let rv = filepicker.show();
-      if (rv == Ci.nsIFilePicker.returnOK
-          || rv == Ci.nsIFilePicker.returnReplace) {
-        let stream = Cc["@mozilla.org/network/file-output-stream;1"]
-                       .createInstance(Ci.nsIFileOutputStream);
-        stream.init(filepicker.file, -1, 0600, 0);
+      let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
+      let fpCallback = function fpCallback_done(aResult) {
+        if (aResult == Ci.nsIFilePicker.returnOK ||
+            aResult == Ci.nsIFilePicker.returnReplace) {
+          let stream = Cc["@mozilla.org/network/file-output-stream;1"].
+                       createInstance(Ci.nsIFileOutputStream);
+          stream.init(fp.file, -1, 0600, 0);
 
-        let serializer = new XMLSerializer();
-        let output = serializer.serializeToString(iframe.contentDocument);
-        output = output.replace(/<!DOCTYPE (.|\n)*?]>/,
-          '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" ' +
-          '"DTD/xhtml1-strict.dtd">');
-        output = Weave.Utils.encodeUTF8(output);
-        stream.write(output, output.length);
-      }
+          let serializer = new XMLSerializer();
+          let output = serializer.serializeToString(iframe.contentDocument);
+          output = output.replace(/<!DOCTYPE (.|\n)*?]>/,
+            '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" ' +
+            '"DTD/xhtml1-strict.dtd">');
+          output = Weave.Utils.encodeUTF8(output);
+          stream.write(output, output.length);
+        }
+      };
+
+      fp.init(window, dialogTitle, Ci.nsIFilePicker.modeSave);
+      fp.appendFilters(Ci.nsIFilePicker.filterHTML);
+      fp.defaultString = defaultSaveName;
+      fp.open(fpCallback);
       return false;
     });
   },
 
   /**
    * validatePassword
    *
    * @param el1 : the first textbox element in the form
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -831,16 +831,19 @@
       <method name="updateCurrentBrowser">
         <parameter name="aForceUpdate"/>
         <body>
           <![CDATA[
             var newBrowser = this.getBrowserAtIndex(this.tabContainer.selectedIndex);
             if (this.mCurrentBrowser == newBrowser && !aForceUpdate)
               return;
 
+            if (!aForceUpdate)
+              TelemetryStopwatch.start("FX_TAB_SWITCH_UPDATE_MS");
+
             var oldTab = this.mCurrentTab;
 
             // Preview mode should not reset the owner
             if (!this._previewMode && oldTab != this.selectedTab)
               oldTab.owner = null;
 
             if (this._lastRelatedTab) {
               if (this._lastRelatedTab != this.selectedTab)
@@ -1010,16 +1013,19 @@
                 let focusFlags = fm.FLAG_NOSCROLL;
                 if (newFocusedElement &&
                     (newFocusedElement instanceof HTMLAnchorElement ||
                      newFocusedElement.getAttributeNS("http://www.w3.org/1999/xlink", "type") == "simple"))
                   focusFlags |= fm.FLAG_SHOWRING;
                 fm.setFocus(newBrowser, focusFlags);
               } while (false);
             }
+
+            if (!aForceUpdate)
+              TelemetryStopwatch.finish("FX_TAB_SWITCH_UPDATE_MS");
           ]]>
         </body>
       </method>
 
       <method name="_tabAttrModified">
         <parameter name="aTab"/>
         <body><![CDATA[
           if (aTab.closing)
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -177,22 +177,26 @@
 
           let [, preDomain, domain] = matchedURL;
           let baseDomain = domain;
           let subDomain = "";
           // getBaseDomainFromHost doesn't recognize IPv6 literals in brackets as IPs (bug 667159)
           if (domain[0] != "[") {
             try {
               baseDomain = Services.eTLD.getBaseDomainFromHost(domain);
+              if (!domain.contains(baseDomain)) {
+                // getBaseDomainFromHost converts its resultant to ACE.
+                let IDNService = Cc["@mozilla.org/network/idn-service;1"]
+                                 .getService(Ci.nsIIDNService);
+                baseDomain = IDNService.convertACEtoUTF8(baseDomain);
+              }
             } catch (e) {}
           }
           if (baseDomain != domain) {
-            let segments = function (s) s.replace(/[^.]*/g, "").length + 1;
-            let subSegments = segments(domain) - segments(baseDomain);
-            subDomain = domain.match(new RegExp("(?:[^.]*\.){" + subSegments + "}"))[0];
+            subDomain = domain.slice(0, -baseDomain.length);
           }
 
           let rangeLength = preDomain.length + subDomain.length;
           if (rangeLength) {
             let range = document.createRange();
             range.setStart(textNode, 0);
             range.setEnd(textNode, rangeLength);
             selection.addRange(range);
--- a/browser/components/feeds/src/FeedWriter.js
+++ b/browser/components/feeds/src/FeedWriter.js
@@ -703,56 +703,63 @@ FeedWriter.prototype = {
    */
   _getUIElement: function FW__getUIElement(id) {
     return this._document.getAnonymousElementByAttribute(
       this._document.getElementById("feedSubscribeLine"), "anonid", id);
   },
 
   /**
    * Displays a prompt from which the user may choose a (client) feed reader.
-   * @return - true if a feed reader was selected, false otherwise.
+   * @param aCallback the callback method, passes in true if a feed reader was
+   *        selected, false otherwise.
    */
-  _chooseClientApp: function FW__chooseClientApp() {
+  _chooseClientApp: function FW__chooseClientApp(aCallback) {
     try {
-      var fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
-      fp.init(this._window,
-              this._getString("chooseApplicationDialogTitle"),
-              Ci.nsIFilePicker.modeOpen);
-      fp.appendFilters(Ci.nsIFilePicker.filterApps);
-
-      if (fp.show() == Ci.nsIFilePicker.returnOK) {
-        this._selectedApp = fp.file;
-        if (this._selectedApp) {
-          // XXXben - we need to compare this with the running instance executable
-          //          just don't know how to do that via script...
-          // XXXmano TBD: can probably add this to nsIShellService
+      let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
+      let fpCallback = function fpCallback_done(aResult) {
+        if (aResult == Ci.nsIFilePicker.returnOK) {
+          this._selectedApp = fp.file;
+          if (this._selectedApp) {
+            // XXXben - we need to compare this with the running instance
+            //          executable just don't know how to do that via script
+            // XXXmano TBD: can probably add this to nsIShellService
 #ifdef XP_WIN
-#expand           if (fp.file.leafName != "__MOZ_APP_NAME__.exe") {
+#expand             if (fp.file.leafName != "__MOZ_APP_NAME__.exe") {
 #else
 #ifdef XP_MACOSX
-#expand           if (fp.file.leafName != "__MOZ_MACBUNDLE_NAME__") {
+#expand             if (fp.file.leafName != "__MOZ_MACBUNDLE_NAME__") {
 #else
-#expand           if (fp.file.leafName != "__MOZ_APP_NAME__-bin") {
+#expand             if (fp.file.leafName != "__MOZ_APP_NAME__-bin") {
 #endif
 #endif
-            this._initMenuItemWithFile(this._contentSandbox.selectedAppMenuItem,
-                                       this._selectedApp);
+              this._initMenuItemWithFile(this._contentSandbox.selectedAppMenuItem,
+                                         this._selectedApp);
 
-            // Show and select the selected application menuitem
-            var codeStr = "selectedAppMenuItem.hidden = false;" +
-                          "selectedAppMenuItem.doCommand();"
-            Cu.evalInSandbox(codeStr, this._contentSandbox);
-            return true;
+              // Show and select the selected application menuitem
+              let codeStr = "selectedAppMenuItem.hidden = false;" +
+                            "selectedAppMenuItem.doCommand();"
+              Cu.evalInSandbox(codeStr, this._contentSandbox);
+              if (aCallback) {
+                aCallback(true);
+                return;
+              }
+            }
           }
         }
-      }
+        if (aCallback) {
+          aCallback(false);
+        }
+      }.bind(this);
+
+      fp.init(this._window, this._getString("chooseApplicationDialogTitle"),
+              Ci.nsIFilePicker.modeOpen);
+      fp.appendFilters(Ci.nsIFilePicker.filterApps);
+      fp.open(fpCallback);
+    } catch(ex) {
     }
-    catch(ex) { }
-
-    return false;
   },
 
   _setAlwaysUseCheckedState: function FW__setAlwaysUseCheckedState(feedType) {
     var checkbox = this._getUIElement("alwaysUse");
     if (checkbox) {
       var alwaysUse = false;
       try {
         var prefs = Cc["@mozilla.org/preferences-service;1"].
@@ -828,20 +835,24 @@ FeedWriter.prototype = {
            * by ignoring command events while the dropdown is closed (user
            * arrowing through the combobox), but handling them while the
            * combobox dropdown is open (user pressed enter when an item was
            * selected). If we don't show the filepicker here, it will be shown
            * when clicking "Subscribe Now".
            */
           var popupbox = this._handlersMenuList.firstChild.boxObject;
           popupbox.QueryInterface(Components.interfaces.nsIPopupBoxObject);
-          if (popupbox.popupState == "hiding" && !this._chooseClientApp()) {
-            // Select the (per-prefs) selected handler if no application was
-            // selected
-            this._setSelectedHandler(this._getFeedType());
+          if (popupbox.popupState == "hiding") {
+            this._chooseClientApp(function(aResult) {
+              if (!aResult) {
+                // Select the (per-prefs) selected handler if no application
+                // was selected
+                this._setSelectedHandler(this._getFeedType());
+              }
+            }.bind(this));
           }
           break;
         default:
           this._setAlwaysUseLabel();
       }
     }
   },
 
@@ -1205,80 +1216,87 @@ FeedWriter.prototype = {
 
     // Subscribe to the feed using the selected handler and save prefs
     var prefs = Cc["@mozilla.org/preferences-service;1"].
                 getService(Ci.nsIPrefBranch);
     var defaultHandler = "reader";
     var useAsDefault = this._getUIElement("alwaysUse").getAttribute("checked");
 
     var selectedItem = this._getSelectedItemFromMenulist(this._handlersMenuList);
+    let subscribeCallback = function() {
+      if (selectedItem.hasAttribute("webhandlerurl")) {
+        var webURI = selectedItem.getAttribute("webhandlerurl");
+        prefs.setCharPref(getPrefReaderForType(feedType), "web");
+
+        var supportsString = Cc["@mozilla.org/supports-string;1"].
+                             createInstance(Ci.nsISupportsString);
+        supportsString.data = webURI;
+        prefs.setComplexValue(getPrefWebForType(feedType), Ci.nsISupportsString,
+                              supportsString);
+
+        var wccr = Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"].
+                   getService(Ci.nsIWebContentConverterService);
+        var handler = wccr.getWebContentHandlerByURI(this._getMimeTypeForFeedType(feedType), webURI);
+        if (handler) {
+          if (useAsDefault) {
+            wccr.setAutoHandler(this._getMimeTypeForFeedType(feedType), handler);
+          }
+
+          this._window.location.href = handler.getHandlerURI(this._window.location.href);
+        }
+      } else {
+        switch (selectedItem.getAttribute("anonid")) {
+          case "selectedAppMenuItem":
+            prefs.setComplexValue(getPrefAppForType(feedType), Ci.nsILocalFile, 
+                                  this._selectedApp);
+            prefs.setCharPref(getPrefReaderForType(feedType), "client");
+            break;
+          case "defaultHandlerMenuItem":
+            prefs.setComplexValue(getPrefAppForType(feedType), Ci.nsILocalFile, 
+                                  this._defaultSystemReader);
+            prefs.setCharPref(getPrefReaderForType(feedType), "client");
+            break;
+          case "liveBookmarksMenuItem":
+            defaultHandler = "bookmarks";
+            prefs.setCharPref(getPrefReaderForType(feedType), "bookmarks");
+            break;
+        }
+        var feedService = Cc["@mozilla.org/browser/feeds/result-service;1"].
+                          getService(Ci.nsIFeedResultService);
+
+        // Pull the title and subtitle out of the document
+        var feedTitle = this._document.getElementById(TITLE_ID).textContent;
+        var feedSubtitle = this._document.getElementById(SUBTITLE_ID).textContent;
+        feedService.addToClientReader(this._window.location.href, feedTitle, feedSubtitle, feedType);
+      }
+
+      // If "Always use..." is checked, we should set PREF_*SELECTED_ACTION
+      // to either "reader" (If a web reader or if an application is selected),
+      // or to "bookmarks" (if the live bookmarks option is selected).
+      // Otherwise, we should set it to "ask"
+      if (useAsDefault) {
+        prefs.setCharPref(getPrefActionForType(feedType), defaultHandler);
+      } else {
+        prefs.setCharPref(getPrefActionForType(feedType), "ask");
+      }
+    }.bind(this);
 
     // Show the file picker before subscribing if the
     // choose application menuitem was chosen using the keyboard
     if (selectedItem.getAttribute("anonid") == "chooseApplicationMenuItem") {
-      if (!this._chooseClientApp())
-        return;
-      
-      selectedItem = this._getSelectedItemFromMenulist(this._handlersMenuList);
-    }
-
-    if (selectedItem.hasAttribute("webhandlerurl")) {
-      var webURI = selectedItem.getAttribute("webhandlerurl");
-      prefs.setCharPref(getPrefReaderForType(feedType), "web");
-
-      var supportsString = Cc["@mozilla.org/supports-string;1"].
-                           createInstance(Ci.nsISupportsString);
-      supportsString.data = webURI;
-      prefs.setComplexValue(getPrefWebForType(feedType), Ci.nsISupportsString,
-                            supportsString);
-
-      var wccr = Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"].
-                 getService(Ci.nsIWebContentConverterService);
-      var handler = wccr.getWebContentHandlerByURI(this._getMimeTypeForFeedType(feedType), webURI);
-      if (handler) {
-        if (useAsDefault)
-          wccr.setAutoHandler(this._getMimeTypeForFeedType(feedType), handler);
-
-        this._window.location.href = handler.getHandlerURI(this._window.location.href);
-      }
+      this._chooseClientApp(function(aResult) {
+        if (aResult) {
+          selectedItem =
+            this._getSelectedItemFromMenulist(this._handlersMenuList);
+          subscribeCallback();
+        }
+      }.bind(this));
+    } else {
+      subscribeCallback();
     }
-    else {
-      switch (selectedItem.getAttribute("anonid")) {
-        case "selectedAppMenuItem":
-          prefs.setComplexValue(getPrefAppForType(feedType), Ci.nsILocalFile, 
-                                this._selectedApp);
-          prefs.setCharPref(getPrefReaderForType(feedType), "client");
-          break;
-        case "defaultHandlerMenuItem":
-          prefs.setComplexValue(getPrefAppForType(feedType), Ci.nsILocalFile, 
-                                this._defaultSystemReader);
-          prefs.setCharPref(getPrefReaderForType(feedType), "client");
-          break;
-        case "liveBookmarksMenuItem":
-          defaultHandler = "bookmarks";
-          prefs.setCharPref(getPrefReaderForType(feedType), "bookmarks");
-          break;
-      }
-      var feedService = Cc["@mozilla.org/browser/feeds/result-service;1"].
-                        getService(Ci.nsIFeedResultService);
-
-      // Pull the title and subtitle out of the document
-      var feedTitle = this._document.getElementById(TITLE_ID).textContent;
-      var feedSubtitle = this._document.getElementById(SUBTITLE_ID).textContent;
-      feedService.addToClientReader(this._window.location.href, feedTitle, feedSubtitle, feedType);
-    }
-
-    // If "Always use..." is checked, we should set PREF_*SELECTED_ACTION
-    // to either "reader" (If a web reader or if an application is selected),
-    // or to "bookmarks" (if the live bookmarks option is selected).
-    // Otherwise, we should set it to "ask"
-    if (useAsDefault)
-      prefs.setCharPref(getPrefActionForType(feedType), defaultHandler);
-    else
-      prefs.setCharPref(getPrefActionForType(feedType), "ask");
   },
 
   // nsIObserver
   observe: function FW_observe(subject, topic, data) {
     if (!this._window) {
       // this._window is null unless this.init was called with a trusted
       // window object.
       return;
--- a/browser/components/places/content/places.js
+++ b/browser/components/places/content/places.js
@@ -349,44 +349,49 @@ var PlacesOrganizer = {
   importFromBrowser: function PO_importFromBrowser() {
     MigrationUtils.showMigrationWizard(window);
   },
 
   /**
    * Open a file-picker and import the selected file into the bookmarks store
    */
   importFromFile: function PO_importFromFile() {
-    var fp = Cc["@mozilla.org/filepicker;1"].
-             createInstance(Ci.nsIFilePicker);
+    let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
+    let fpCallback = function fpCallback_done(aResult) {
+      if (aResult != Ci.nsIFilePicker.returnCancel && fp.fileURL) {
+        Components.utils.import("resource://gre/modules/BookmarkHTMLUtils.jsm");
+        BookmarkHTMLUtils.importFromURL(fp.fileURL.spec, false);
+      }
+    };
+
     fp.init(window, PlacesUIUtils.getString("SelectImport"),
             Ci.nsIFilePicker.modeOpen);
     fp.appendFilters(Ci.nsIFilePicker.filterHTML);
-    if (fp.show() != Ci.nsIFilePicker.returnCancel) {
-      if (fp.fileURL) {
-        Components.utils.import("resource://gre/modules/BookmarkHTMLUtils.jsm");
-        BookmarkHTMLUtils.importFromURL(fp.fileURL.spec, false);
-      }
-    }
+    fp.open(fpCallback);
   },
 
   /**
    * Allows simple exporting of bookmarks.
    */
   exportBookmarks: function PO_exportBookmarks() {
-    var fp = Cc["@mozilla.org/filepicker;1"].
-             createInstance(Ci.nsIFilePicker);
+    let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
+    let fpCallback = function fpCallback_done(aResult) {
+      if (aResult != Ci.nsIFilePicker.returnCancel) {
+        let exporter =
+          Cc["@mozilla.org/browser/places/import-export-service;1"].
+            getService(Ci.nsIPlacesImportExportService);
+        exporter.exportHTMLToFile(fp.file);
+      }
+    };
+
     fp.init(window, PlacesUIUtils.getString("EnterExport"),
             Ci.nsIFilePicker.modeSave);
     fp.appendFilters(Ci.nsIFilePicker.filterHTML);
     fp.defaultString = "bookmarks.html";
-    if (fp.show() != Ci.nsIFilePicker.returnCancel) {
-      var exporter = Cc["@mozilla.org/browser/places/import-export-service;1"].
-                     getService(Ci.nsIPlacesImportExportService);
-      exporter.exportHTMLToFile(fp.file);
-    }
+    fp.open(fpCallback);
   },
 
   /**
    * Populates the restore menu with the dates of the backups available.
    */
   populateRestoreMenu: function PO_populateRestoreMenu() {
     let restorePopup = document.getElementById("fileRestorePopup");
 
@@ -436,30 +441,33 @@ var PlacesOrganizer = {
     }
   },
 
   /**
    * Called when 'Choose File...' is selected from the restore menu.
    * Prompts for a file and restores bookmarks to those in the file.
    */
   onRestoreBookmarksFromFile: function PO_onRestoreBookmarksFromFile() {
-    var fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
+    let dirSvc = Cc["@mozilla.org/file/directory_service;1"].
+                 getService(Ci.nsIProperties);
+    let backupsDir = dirSvc.get("Desk", Ci.nsILocalFile);
+    let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
+    let fpCallback = function fpCallback_done(aResult) {
+      if (aResult != Ci.nsIFilePicker.returnCancel) {
+        this.restoreBookmarksFromFile(fp.file);
+      }
+    }.bind(this);
+
     fp.init(window, PlacesUIUtils.getString("bookmarksRestoreTitle"),
             Ci.nsIFilePicker.modeOpen);
     fp.appendFilter(PlacesUIUtils.getString("bookmarksRestoreFilterName"),
                     PlacesUIUtils.getString("bookmarksRestoreFilterExtension"));
     fp.appendFilters(Ci.nsIFilePicker.filterAll);
-
-    var dirSvc = Cc["@mozilla.org/file/directory_service;1"].
-                 getService(Ci.nsIProperties);
-    var backupsDir = dirSvc.get("Desk", Ci.nsILocalFile);
     fp.displayDirectory = backupsDir;
-
-    if (fp.show() != Ci.nsIFilePicker.returnCancel)
-      this.restoreBookmarksFromFile(fp.file);
+    fp.open(fpCallback);
   },
 
   /**
    * Restores bookmarks from a JSON file.
    */
   restoreBookmarksFromFile: function PO_restoreBookmarksFromFile(aFile) {
     // check file extension
     if (!aFile.leafName.match(/\.json$/)) {
@@ -493,31 +501,33 @@ var PlacesOrganizer = {
   },
 
   /**
    * Backup bookmarks to desktop, auto-generate a filename with a date.
    * The file is a JSON serialization of bookmarks, tags and any annotations
    * of those items.
    */
   backupBookmarks: function PO_backupBookmarks() {
-    var fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
+    let dirSvc = Cc["@mozilla.org/file/directory_service;1"].
+                 getService(Ci.nsIProperties);
+    let backupsDir = dirSvc.get("Desk", Ci.nsILocalFile);
+    let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
+    let fpCallback = function fpCallback_done(aResult) {
+      if (aResult != Ci.nsIFilePicker.returnCancel) {
+        PlacesUtils.backups.saveBookmarksToJSONFile(fp.file);
+      }
+    };
+
     fp.init(window, PlacesUIUtils.getString("bookmarksBackupTitle"),
             Ci.nsIFilePicker.modeSave);
     fp.appendFilter(PlacesUIUtils.getString("bookmarksRestoreFilterName"),
                     PlacesUIUtils.getString("bookmarksRestoreFilterExtension"));
-
-    var dirSvc = Cc["@mozilla.org/file/directory_service;1"].
-                 getService(Ci.nsIProperties);
-    var backupsDir = dirSvc.get("Desk", Ci.nsILocalFile);
+    fp.defaultString = PlacesUtils.backups.getFilenameForDate();
     fp.displayDirectory = backupsDir;
-
-    fp.defaultString = PlacesUtils.backups.getFilenameForDate();
-
-    if (fp.show() != Ci.nsIFilePicker.returnCancel)
-      PlacesUtils.backups.saveBookmarksToJSONFile(fp.file);
+    fp.open(fpCallback);
   },
 
   _paneDisabled: false,
   _setDetailsFieldsDisabledState:
   function PO__setDetailsFieldsDisabledState(aDisabled) {
     if (aDisabled) {
       document.getElementById("paneElementsBroadcaster")
               .setAttribute("disabled", "true");
--- a/browser/components/preferences/applications.js
+++ b/browser/components/preferences/applications.js
@@ -1710,16 +1710,38 @@ var gApplicationsPane = {
   },
 
   chooseApp: function(aEvent) {
     // Don't let the normal "on select action" handler get this event,
     // as we handle it specially ourselves.
     aEvent.stopPropagation();
 
     var handlerApp;
+    let chooseAppCallback = function(aHandlerApp) {
+      // Rebuild the actions menu whether the user picked an app or canceled.
+      // If they picked an app, we want to add the app to the menu and select it.
+      // If they canceled, we want to go back to their previous selection.
+      this.rebuildActionsMenu();
+
+      // If the user picked a new app from the menu, select it.
+      if (aHandlerApp) {
+        let typeItem = this._list.selectedItem;
+        let actionsMenu =
+          document.getAnonymousElementByAttribute(typeItem, "class", "actionsMenu");
+        let menuItems = actionsMenu.menupopup.childNodes;
+        for (let i = 0; i < menuItems.length; i++) {
+          let menuItem = menuItems[i];
+          if (menuItem.handlerApp && menuItem.handlerApp.equals(aHandlerApp)) {
+            actionsMenu.selectedIndex = i;
+            this.onSelectAction(menuItem);
+            break;
+          }
+        }
+      }
+    }.bind(this);
 
 #ifdef XP_WIN
     var params = {};
     var handlerInfo = this._handledTypes[this._list.selectedItem.type];
 
     if (isFeedType(handlerInfo.type)) {
       // MIME info will be null, create a temp object.
       params.mimeInfo = this._mimeSvc.getFromTypeAndExtension(handlerInfo.type, 
@@ -1738,57 +1760,43 @@ var gApplicationsPane = {
                       params);
 
     if (this.isValidHandlerApp(params.handlerApp)) {
       handlerApp = params.handlerApp;
 
       // Add the app to the type's list of possible handlers.
       handlerInfo.addPossibleApplicationHandler(handlerApp);
     }
+
+    chooseAppCallback(handlerApp);
 #else
-    var fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
-    var winTitle = this._prefsBundle.getString("fpTitleChooseApp");
-    fp.init(window, winTitle, Ci.nsIFilePicker.modeOpen);
-    fp.appendFilters(Ci.nsIFilePicker.filterApps);
+    let winTitle = this._prefsBundle.getString("fpTitleChooseApp");
+    let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
+    let fpCallback = function fpCallback_done(aResult) {
+      if (aResult == Ci.nsIFilePicker.returnOK && fp.file &&
+          this._isValidHandlerExecutable(fp.file)) {
+        handlerApp = Cc["@mozilla.org/uriloader/local-handler-app;1"].
+                     createInstance(Ci.nsILocalHandlerApp);
+        handlerApp.name = getFileDisplayName(fp.file);
+        handlerApp.executable = fp.file;
+
+        // Add the app to the type's list of possible handlers.
+        let handlerInfo = this._handledTypes[this._list.selectedItem.type];
+        handlerInfo.addPossibleApplicationHandler(handlerApp);
+
+        chooseAppCallback(handlerApp);
+      }
+    }.bind(this);
 
     // Prompt the user to pick an app.  If they pick one, and it's a valid
     // selection, then add it to the list of possible handlers.
-    if (fp.show() == Ci.nsIFilePicker.returnOK && fp.file &&
-        this._isValidHandlerExecutable(fp.file)) {
-      handlerApp = Cc["@mozilla.org/uriloader/local-handler-app;1"].
-                   createInstance(Ci.nsILocalHandlerApp);
-      handlerApp.name = getFileDisplayName(fp.file);
-      handlerApp.executable = fp.file;
-
-      // Add the app to the type's list of possible handlers.
-      let handlerInfo = this._handledTypes[this._list.selectedItem.type];
-      handlerInfo.addPossibleApplicationHandler(handlerApp);
-    }
+    fp.init(window, winTitle, Ci.nsIFilePicker.modeOpen);
+    fp.appendFilters(Ci.nsIFilePicker.filterApps);
+    fp.open(fpCallback);
 #endif
-
-    // Rebuild the actions menu whether the user picked an app or canceled.
-    // If they picked an app, we want to add the app to the menu and select it.
-    // If they canceled, we want to go back to their previous selection.
-    this.rebuildActionsMenu();
-
-    // If the user picked a new app from the menu, select it.
-    if (handlerApp) {
-      let typeItem = this._list.selectedItem;
-      let actionsMenu =
-        document.getAnonymousElementByAttribute(typeItem, "class", "actionsMenu");
-      let menuItems = actionsMenu.menupopup.childNodes;
-      for (let i = 0; i < menuItems.length; i++) {
-        let menuItem = menuItems[i];
-        if (menuItem.handlerApp && menuItem.handlerApp.equals(handlerApp)) {
-          actionsMenu.selectedIndex = i;
-          this.onSelectAction(menuItem);
-          break;
-        }
-      }
-    }
   },
 
   // Mark which item in the list was last selected so we can reselect it
   // when we rebuild the list or when the user returns to the prefpane.
   onSelectionChanged: function() {
     if (this._list.selectedItem)
       this._list.setAttribute("lastSelectedType",
                               this._list.selectedItem.getAttribute("type"));
--- a/browser/components/preferences/in-content/applications.js
+++ b/browser/components/preferences/in-content/applications.js
@@ -1697,16 +1697,38 @@ var gApplicationsPane = {
   },
 
   chooseApp: function(aEvent) {
     // Don't let the normal "on select action" handler get this event,
     // as we handle it specially ourselves.
     aEvent.stopPropagation();
 
     var handlerApp;
+    let chooseAppCallback = function(aHandlerApp) {
+      // Rebuild the actions menu whether the user picked an app or canceled.
+      // If they picked an app, we want to add the app to the menu and select it.
+      // If they canceled, we want to go back to their previous selection.
+      this.rebuildActionsMenu();
+
+      // If the user picked a new app from the menu, select it.
+      if (aHandlerApp) {
+        let typeItem = this._list.selectedItem;
+        let actionsMenu =
+          document.getAnonymousElementByAttribute(typeItem, "class", "actionsMenu");
+        let menuItems = actionsMenu.menupopup.childNodes;
+        for (let i = 0; i < menuItems.length; i++) {
+          let menuItem = menuItems[i];
+          if (menuItem.handlerApp && menuItem.handlerApp.equals(aHandlerApp)) {
+            actionsMenu.selectedIndex = i;
+            this.onSelectAction(menuItem);
+            break;
+          }
+        }
+      }
+    }.bind(this);
 
 #ifdef XP_WIN
     var params = {};
     var handlerInfo = this._handledTypes[this._list.selectedItem.type];
 
     if (isFeedType(handlerInfo.type)) {
       // MIME info will be null, create a temp object.
       params.mimeInfo = this._mimeSvc.getFromTypeAndExtension(handlerInfo.type, 
@@ -1725,57 +1747,43 @@ var gApplicationsPane = {
                       params);
 
     if (this.isValidHandlerApp(params.handlerApp)) {
       handlerApp = params.handlerApp;
 
       // Add the app to the type's list of possible handlers.
       handlerInfo.addPossibleApplicationHandler(handlerApp);
     }
+
+    chooseAppCallback(handlerApp);
 #else
-    var fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
-    var winTitle = this._prefsBundle.getString("fpTitleChooseApp");
-    fp.init(window, winTitle, Ci.nsIFilePicker.modeOpen);
-    fp.appendFilters(Ci.nsIFilePicker.filterApps);
+    let winTitle = this._prefsBundle.getString("fpTitleChooseApp");
+    let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
+    let fpCallback = function fpCallback_done(aResult) {
+      if (aResult == Ci.nsIFilePicker.returnOK && fp.file &&
+          this._isValidHandlerExecutable(fp.file)) {
+        handlerApp = Cc["@mozilla.org/uriloader/local-handler-app;1"].
+                     createInstance(Ci.nsILocalHandlerApp);
+        handlerApp.name = getFileDisplayName(fp.file);
+        handlerApp.executable = fp.file;
+
+        // Add the app to the type's list of possible handlers.
+        let handlerInfo = this._handledTypes[this._list.selectedItem.type];
+        handlerInfo.addPossibleApplicationHandler(handlerApp);
+
+        chooseAppCallback(handlerApp);
+      }
+    }.bind(this);
 
     // Prompt the user to pick an app.  If they pick one, and it's a valid
     // selection, then add it to the list of possible handlers.
-    if (fp.show() == Ci.nsIFilePicker.returnOK && fp.file &&
-        this._isValidHandlerExecutable(fp.file)) {
-      handlerApp = Cc["@mozilla.org/uriloader/local-handler-app;1"].
-                   createInstance(Ci.nsILocalHandlerApp);
-      handlerApp.name = getFileDisplayName(fp.file);
-      handlerApp.executable = fp.file;
-
-      // Add the app to the type's list of possible handlers.
-      let handlerInfo = this._handledTypes[this._list.selectedItem.type];
-      handlerInfo.addPossibleApplicationHandler(handlerApp);
-    }
+    fp.init(window, winTitle, Ci.nsIFilePicker.modeOpen);
+    fp.appendFilters(Ci.nsIFilePicker.filterApps);
+    fp.open(fpCallback);
 #endif
-
-    // Rebuild the actions menu whether the user picked an app or canceled.
-    // If they picked an app, we want to add the app to the menu and select it.
-    // If they canceled, we want to go back to their previous selection.
-    this.rebuildActionsMenu();
-
-    // If the user picked a new app from the menu, select it.
-    if (handlerApp) {
-      let typeItem = this._list.selectedItem;
-      let actionsMenu =
-        document.getAnonymousElementByAttribute(typeItem, "class", "actionsMenu");
-      let menuItems = actionsMenu.menupopup.childNodes;
-      for (let i = 0; i < menuItems.length; i++) {
-        let menuItem = menuItems[i];
-        if (menuItem.handlerApp && menuItem.handlerApp.equals(handlerApp)) {
-          actionsMenu.selectedIndex = i;
-          this.onSelectAction(menuItem);
-          break;
-        }
-      }
-    }
   },
 
   // Mark which item in the list was last selected so we can reselect it
   // when we rebuild the list or when the user returns to the prefpane.
   onSelectionChanged: function() {
     if (this._list.selectedItem)
       this._list.setAttribute("lastSelectedType",
                               this._list.selectedItem.getAttribute("type"));
--- a/browser/components/preferences/in-content/main.js
+++ b/browser/components/preferences/in-content/main.js
@@ -253,49 +253,50 @@ var gMainPane = {
    * downloads are automatically saved, updating preferences and UI in
    * response to the choice, if one is made.
    */
   chooseFolder: function ()
   {
     const nsIFilePicker = Components.interfaces.nsIFilePicker;
     const nsILocalFile = Components.interfaces.nsILocalFile;
 
-    var fp = Components.classes["@mozilla.org/filepicker;1"]
-                       .createInstance(nsIFilePicker);
-    var bundlePreferences = document.getElementById("bundlePreferences");
-    var title = bundlePreferences.getString("chooseDownloadFolderTitle");
+    let bundlePreferences = document.getElementById("bundlePreferences");
+    let title = bundlePreferences.getString("chooseDownloadFolderTitle");
+    let folderListPref = document.getElementById("browser.download.folderList");
+    let currentDirPref = this._indexToFolder(folderListPref.value); // file
+    let defDownloads = this._indexToFolder(1); // file
+    let fp = Components.classes["@mozilla.org/filepicker;1"].
+             createInstance(nsIFilePicker);
+    let fpCallback = function fpCallback_done(aResult) {
+      if (aResult == nsIFilePicker.returnOK) {
+        let file = fp.file.QueryInterface(nsILocalFile);
+        let downloadDirPref = document.getElementById("browser.download.dir");
+
+        downloadDirPref.value = file;
+        folderListPref.value = this._folderToIndex(file);
+        // Note, the real prefs will not be updated yet, so dnld manager's
+        // userDownloadsDirectory may not return the right folder after
+        // this code executes. displayDownloadDirPref will be called on
+        // the assignment above to update the UI.
+      }
+    }.bind(this);
+
     fp.init(window, title, nsIFilePicker.modeGetFolder);
     fp.appendFilters(nsIFilePicker.filterAll);
-
-    var folderListPref = document.getElementById("browser.download.folderList");
-    var currentDirPref = this._indexToFolder(folderListPref.value); // file
-    var defDownloads = this._indexToFolder(1); // file
-
     // First try to open what's currently configured
     if (currentDirPref && currentDirPref.exists()) {
       fp.displayDirectory = currentDirPref;
     } // Try the system's download dir
     else if (defDownloads && defDownloads.exists()) {
       fp.displayDirectory = defDownloads;
     } // Fall back to Desktop
     else {
       fp.displayDirectory = this._indexToFolder(0);
     }
-
-    if (fp.show() == nsIFilePicker.returnOK) {
-      var file = fp.file.QueryInterface(nsILocalFile);
-      var currentDirPref = document.getElementById("browser.download.dir");
-      currentDirPref.value = file;
-      var folderListPref = document.getElementById("browser.download.folderList");
-      folderListPref.value = this._folderToIndex(file);
-      // Note, the real prefs will not be updated yet, so dnld manager's
-      // userDownloadsDirectory may not return the right folder after
-      // this code executes. displayDownloadDirPref will be called on
-      // the assignment above to update the UI.
-    }
+    fp.open(fpCallback);
   },
 
   /**
    * Initializes the download folder display settings based on the user's
    * preferences.
    */
   displayDownloadDirPref: function ()
   {
--- a/browser/components/preferences/main.js
+++ b/browser/components/preferences/main.js
@@ -250,49 +250,50 @@ var gMainPane = {
    * downloads are automatically saved, updating preferences and UI in
    * response to the choice, if one is made.
    */
   chooseFolder: function ()
   {
     const nsIFilePicker = Components.interfaces.nsIFilePicker;
     const nsILocalFile = Components.interfaces.nsILocalFile;
 
-    var fp = Components.classes["@mozilla.org/filepicker;1"]
-                       .createInstance(nsIFilePicker);
-    var bundlePreferences = document.getElementById("bundlePreferences");
-    var title = bundlePreferences.getString("chooseDownloadFolderTitle");
+    let bundlePreferences = document.getElementById("bundlePreferences");
+    let title = bundlePreferences.getString("chooseDownloadFolderTitle");
+    let folderListPref = document.getElementById("browser.download.folderList");
+    let currentDirPref = this._indexToFolder(folderListPref.value); // file
+    let defDownloads = this._indexToFolder(1); // file
+    let fp = Components.classes["@mozilla.org/filepicker;1"].
+             createInstance(nsIFilePicker);
+    let fpCallback = function fpCallback_done(aResult) {
+      if (aResult == nsIFilePicker.returnOK) {
+        let file = fp.file.QueryInterface(nsILocalFile);
+        let downloadDirPref = document.getElementById("browser.download.dir");
+
+        downloadDirPref.value = file;
+        folderListPref.value = this._folderToIndex(file);
+        // Note, the real prefs will not be updated yet, so dnld manager's
+        // userDownloadsDirectory may not return the right folder after
+        // this code executes. displayDownloadDirPref will be called on
+        // the assignment above to update the UI.
+      }
+    }.bind(this);
+
     fp.init(window, title, nsIFilePicker.modeGetFolder);
     fp.appendFilters(nsIFilePicker.filterAll);
-
-    var folderListPref = document.getElementById("browser.download.folderList");
-    var currentDirPref = this._indexToFolder(folderListPref.value); // file
-    var defDownloads = this._indexToFolder(1); // file
-
     // First try to open what's currently configured
     if (currentDirPref && currentDirPref.exists()) {
       fp.displayDirectory = currentDirPref;
     } // Try the system's download dir
     else if (defDownloads && defDownloads.exists()) {
       fp.displayDirectory = defDownloads;
     } // Fall back to Desktop
     else {
       fp.displayDirectory = this._indexToFolder(0);
     }
-
-    if (fp.show() == nsIFilePicker.returnOK) {
-      var file = fp.file.QueryInterface(nsILocalFile);
-      var currentDirPref = document.getElementById("browser.download.dir");
-      currentDirPref.value = file;
-      var folderListPref = document.getElementById("browser.download.folderList");
-      folderListPref.value = this._folderToIndex(file);
-      // Note, the real prefs will not be updated yet, so dnld manager's
-      // userDownloadsDirectory may not return the right folder after
-      // this code executes. displayDownloadDirPref will be called on
-      // the assignment above to update the UI.
-    }
+    fp.open(fpCallback);
   },
 
   /**
    * Initializes the download folder display settings based on the user's 
    * preferences.
    */
   displayDownloadDirPref: function ()
   {
--- a/browser/devtools/scratchpad/scratchpad.js
+++ b/browser/devtools/scratchpad/scratchpad.js
@@ -663,49 +663,57 @@ var Scratchpad = {
   /**
    * Open a file to edit in the Scratchpad.
    *
    * @param integer aIndex
    *        Optional integer: clicked menuitem in the 'Open Recent'-menu.
    */
   openFile: function SP_openFile(aIndex)
   {
-    let fp;
-    if (!aIndex && aIndex !== 0) {
-      fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
-      fp.init(window, this.strings.GetStringFromName("openFile.title"),
-              Ci.nsIFilePicker.modeOpen);
-      fp.defaultString = "";
-    }
-
-    if (aIndex > -1 || fp.show() != Ci.nsIFilePicker.returnCancel) {
+    let promptCallback = function(aFile) {
       this.promptSave(function(aCloseFile, aSaved, aStatus) {
         let shouldOpen = aCloseFile;
         if (aSaved && !Components.isSuccessCode(aStatus)) {
           shouldOpen = false;
         }
 
         if (shouldOpen) {
           this._skipClosePrompt = true;
 
           let file;
-          if (fp) {
-            file = fp.file;
+          if (aFile) {
+            file = aFile;
           } else {
             file = Components.classes["@mozilla.org/file/local;1"].
                    createInstance(Components.interfaces.nsILocalFile);
             let filePath = this.getRecentFiles()[aIndex];
             file.initWithPath(filePath);
           }
 
           this.setFilename(file.path);
           this.importFromFile(file, false);
           this.setRecentFile(file);
         }
       }.bind(this));
+    }.bind(this);
+
+    if (aIndex > -1) {
+      promptCallback();
+    } else {
+      let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
+      let fpCallback = function fpCallback_done(aResult) {
+        if (aResult != Ci.nsIFilePicker.returnCancel) {
+          promptCallback(fp.file);
+        }
+      };
+
+      fp.init(window, this.strings.GetStringFromName("openFile.title"),
+              Ci.nsIFilePicker.modeOpen);
+      fp.defaultString = "";
+      fp.open(fpCallback);
     }
   },
 
   /**
    * Get recent files.
    *
    * @return Array
    *         File paths.
@@ -889,32 +897,35 @@ var Scratchpad = {
    * Save the textbox content to a new file.
    *
    * @param function aCallback
    *        Optional function you want to call when file is saved
    */
   saveFileAs: function SP_saveFileAs(aCallback)
   {
     let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
+    let fpCallback = function fpCallback_done(aResult) {
+      if (aResult != Ci.nsIFilePicker.returnCancel) {
+        this.setFilename(fp.file.path);
+        this.exportToFile(fp.file, true, false, function(aStatus) {
+          if (Components.isSuccessCode(aStatus)) {
+            this.editor.dirty = false;
+            this.setRecentFile(fp.file);
+          }
+          if (aCallback) {
+            aCallback(aStatus);
+          }
+        });
+      }
+    }.bind(this);
+
     fp.init(window, this.strings.GetStringFromName("saveFileAs"),
             Ci.nsIFilePicker.modeSave);
     fp.defaultString = "scratchpad.js";
-    if (fp.show() != Ci.nsIFilePicker.returnCancel) {
-      this.setFilename(fp.file.path);
-
-      this.exportToFile(fp.file, true, false, function(aStatus) {
-        if (Components.isSuccessCode(aStatus)) {
-          this.editor.dirty = false;
-          this.setRecentFile(fp.file);
-        }
-        if (aCallback) {
-          aCallback(aStatus);
-        }
-      });
-    }
+    fp.open(fpCallback);
   },
 
   /**
    * Restore content from saved version of current file.
    *
    * @param function aCallback
    *        Optional function you want to call when file is saved
    */
--- a/browser/devtools/styleeditor/StyleEditor.jsm
+++ b/browser/devtools/styleeditor/StyleEditor.jsm
@@ -283,32 +283,34 @@ StyleEditor.prototype = {
    * @param mixed aFile
    *        Optional nsIFile or filename string.
    *        If not set a file picker will be shown.
    * @param nsIWindow aParentWindow
    *        Optional parent window for the file picker.
    */
   importFromFile: function SE_importFromFile(aFile, aParentWindow)
   {
-    aFile = this._showFilePicker(aFile, false, aParentWindow);
-    if (!aFile) {
-      return;
-    }
-    this._savedFile = aFile; // remember filename for next save if any
+    let callback = function(aFile) {
+      if (aFile) {
+        this._savedFile = aFile; // remember filename for next save if any
 
-    NetUtil.asyncFetch(aFile, function onAsyncFetch(aStream, aStatus) {
-      if (!Components.isSuccessCode(aStatus)) {
-        return this._signalError(LOAD_ERROR);
+        NetUtil.asyncFetch(aFile, function onAsyncFetch(aStream, aStatus) {
+          if (!Components.isSuccessCode(aStatus)) {
+            return this._signalError(LOAD_ERROR);
+          }
+          let source = NetUtil.readInputStreamToString(aStream, aStream.available());
+          aStream.close();
+    
+          this._appendNewStyleSheet(source);
+          this.clearFlag(StyleEditorFlags.ERROR);
+        }.bind(this));
       }
-      let source = NetUtil.readInputStreamToString(aStream, aStream.available());
-      aStream.close();
+    }.bind(this);
 
-      this._appendNewStyleSheet(source);
-      this.clearFlag(StyleEditorFlags.ERROR);
-    }.bind(this));
+    this._showFilePicker(aFile, false, aParentWindow, callback);
   },
 
   /**
     * Retrieve localized error message of last error condition, or null if none.
     * This is set when the editor has flag StyleEditorFlags.ERROR.
     *
     * @see addActionListener
     */
@@ -548,56 +550,58 @@ StyleEditor.prototype = {
    * @param function(nsIFile aFile) aCallback
    *        Optional callback called when the operation has finished.
    *        aFile has the nsIFile object for saved file or null if the operation
    *        has failed or has been canceled by the user.
    * @see savedFile
    */
   saveToFile: function SE_saveToFile(aFile, aCallback)
   {
-    aFile = this._showFilePicker(aFile || this._styleSheetFilePath, true);
-
-    if (!aFile) {
-      if (aCallback) {
-        aCallback(null);
-      }
-      return;
-    }
-
-    if (this._sourceEditor) {
-      this._state.text = this._sourceEditor.getText();
-    }
-
-    let ostream = FileUtils.openSafeFileOutputStream(aFile);
-    let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
-                      .createInstance(Ci.nsIScriptableUnicodeConverter);
-    converter.charset = "UTF-8";
-    let istream = converter.convertToInputStream(this._state.text);
-
-    NetUtil.asyncCopy(istream, ostream, function SE_onStreamCopied(status) {
-      if (!Components.isSuccessCode(status)) {
+    let callback = function(aReturnFile) {
+      if (!aReturnFile) {
         if (aCallback) {
           aCallback(null);
         }
-        this._signalError(SAVE_ERROR);
         return;
       }
-      FileUtils.closeSafeFileOutputStream(ostream);
+
+      if (this._sourceEditor) {
+        this._state.text = this._sourceEditor.getText();
+      }
 
-      // remember filename for next save if any
-      this._friendlyName = null;
-      this._savedFile = aFile;
-      this._persistExpando();
+      let ostream = FileUtils.openSafeFileOutputStream(aReturnFile);
+      let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
+                        .createInstance(Ci.nsIScriptableUnicodeConverter);
+      converter.charset = "UTF-8";
+      let istream = converter.convertToInputStream(this._state.text);
 
-      if (aCallback) {
-        aCallback(aFile);
-      }
-      this.clearFlag(StyleEditorFlags.UNSAVED);
-      this.clearFlag(StyleEditorFlags.ERROR);
-    }.bind(this));
+      NetUtil.asyncCopy(istream, ostream, function SE_onStreamCopied(status) {
+        if (!Components.isSuccessCode(status)) {
+          if (aCallback) {
+            aCallback(null);
+          }
+          this._signalError(SAVE_ERROR);
+          return;
+        }
+        FileUtils.closeSafeFileOutputStream(ostream);
+
+        // remember filename for next save if any
+        this._friendlyName = null;
+        this._savedFile = aReturnFile;
+        this._persistExpando();
+
+        if (aCallback) {
+          aCallback(aReturnFile);
+        }
+        this.clearFlag(StyleEditorFlags.UNSAVED);
+        this.clearFlag(StyleEditorFlags.ERROR);
+      }.bind(this));
+    }.bind(this);
+
+    this._showFilePicker(aFile || this._styleSheetFilePath, true, null, callback);
   },
 
   /**
    * Queue a throttled task to update the live style sheet.
    *
    * @param boolean aImmediate
    *        Optional. If true the update is performed immediately.
    */
@@ -685,56 +689,67 @@ StyleEditor.prototype = {
    *
    * @param mixed aFile
    *        Optional nsIFile or string representing the filename to auto-select.
    * @param boolean aSave
    *        If true, the user is selecting a filename to save.
    * @param nsIWindow aParentWindow
    *        Optional parent window. If null the parent window of the file picker
    *        will be the window of the attached input element.
-   * @return nsIFile
-   *         The selected file or null if the user did not pick one.
+   * @param aCallback
+   *        The callback method, which will be called passing in the selected
+   *        file or null if the user did not pick one.
    */
-  _showFilePicker: function SE__showFilePicker(aFile, aSave, aParentWindow)
+  _showFilePicker: function SE__showFilePicker(aFile, aSave, aParentWindow, aCallback)
   {
     if (typeof(aFile) == "string") {
       try {
         if (Services.io.extractScheme(aFile) == "file") {
           let uri = Services.io.newURI(aFile, null, null);
           let file = uri.QueryInterface(Ci.nsIFileURL).file;
-          return file;
+          aCallback(file);
+          return;
         }
       } catch (ex) {
       }
       try {
         let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
         file.initWithPath(aFile);
-        return file;
+        aCallback(file);
+        return;
       } catch (ex) {
         this._signalError(aSave ? SAVE_ERROR : LOAD_ERROR);
-        return null;
+        aCallback(null);
+        return;
       }
     }
     if (aFile) {
-      return aFile;
+      aCallback(aFile);
+      return;
     }
 
     let window = aParentWindow
                  ? aParentWindow
                  : this.inputElement.ownerDocument.defaultView;
     let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
     let mode = aSave ? fp.modeSave : fp.modeOpen;
     let key = aSave ? "saveStyleSheet" : "importStyleSheet";
+    let fpCallback = function fpCallback_done(aResult) {
+      if (aResult == Ci.nsIFilePicker.returnCancel) {
+        aCallback(null);
+      } else {
+        aCallback(fp.file);
+      }
+    };
 
     fp.init(window, _(key + ".title"), mode);
     fp.appendFilters(_(key + ".filter"), "*.css");
     fp.appendFilters(fp.filterAll);
-
-    let rv = fp.show();
-    return (rv == fp.returnCancel) ? null : fp.file;
+    fp.open(fpCallback);
+    return;
   },
 
   /**
    * Retrieve the style sheet source from the cache or from a local file.
    */
   _loadSource: function SE__loadSource()
   {
     if (!this.styleSheet.href) {
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -1532,20 +1532,32 @@ public:
 
   /**
    * Retrieve information about the viewport as a data structure.
    * This will return information in the viewport META data section
    * of the document. This can be used in lieu of ProcessViewportInfo(),
    * which places the viewport information in the document header instead
    * of returning it directly.
    *
+   * @param aDisplayWidth width of the on-screen display area for this
+   * document, in device pixels.
+   * @param aDisplayHeight height of the on-screen display area for this
+   * document, in device pixels.
+   *
    * NOTE: If the site is optimized for mobile (via the doctype), this
    * will return viewport information that specifies default information.
    */
-  static ViewportInfo GetViewportInfo(nsIDocument* aDocument);
+  static ViewportInfo GetViewportInfo(nsIDocument* aDocument,
+                                      uint32_t aDisplayWidth,
+                                      uint32_t aDisplayHeight);
+
+  /**
+   * The device-pixel-to-CSS-px ratio used to adjust meta viewport values.
+   */
+  static double GetDevicePixelsPerMetaViewportPixel(nsIWidget* aWidget);
 
   // Call EnterMicroTask when you're entering JS execution.
   // Usually the best way to do this is to use nsAutoMicroTask.
   static void EnterMicroTask() { ++sMicroTaskLevel; }
   static void LeaveMicroTask();
 
   static bool IsInMicroTask() { return sMicroTaskLevel != 0; }
   static uint32_t MicroTaskLevel() { return sMicroTaskLevel; }
--- a/content/base/public/nsIXMLHttpRequest.idl
+++ b/content/base/public/nsIXMLHttpRequest.idl
@@ -383,16 +383,17 @@ interface nsIXMLHttpRequest : nsISupport
    * If true, the same origin policy will not be enforced on the request.
    */
   readonly attribute boolean mozSystem;
 };
 
 [scriptable, uuid(840d0d00-e83e-4a29-b3c7-67e96e90a499)]
 interface nsIXHRSendable : nsISupports {
   void getSendInfo(out nsIInputStream body,
+                   out uint64_t contentLength,
                    out ACString contentType,
                    out ACString charset);
 };
 
 /**
  * @deprecated
  */
 [scriptable, uuid(8ae70a39-edf1-40b4-a992-472d23421c25)]
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -7,16 +7,18 @@
 /* A namespace class for static layout utilities. */
 
 #include "mozilla/Util.h"
 
 #include "jsapi.h"
 #include "jsdbgapi.h"
 #include "jsfriendapi.h"
 
+#include <math.h>
+
 #include "Layers.h"
 #include "nsJSUtils.h"
 #include "nsCOMPtr.h"
 #include "nsAString.h"
 #include "nsPrintfCString.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIScriptContext.h"
 #include "nsDOMCID.h"
@@ -5078,17 +5080,19 @@ static void ProcessViewportToken(nsIDocu
     aDocument->SetHeaderData(nsGkAtoms::viewport_user_scalable, value);
 }
 
 #define IS_SEPARATOR(c) ((c == '=') || (c == ',') || (c == ';') || \
                          (c == '\t') || (c == '\n') || (c == '\r'))
 
 /* static */
 ViewportInfo
-nsContentUtils::GetViewportInfo(nsIDocument *aDocument)
+nsContentUtils::GetViewportInfo(nsIDocument *aDocument,
+                                uint32_t aDisplayWidth,
+                                uint32_t aDisplayHeight)
 {
   ViewportInfo ret;
   ret.defaultZoom = 1.0;
   ret.autoSize = true;
   ret.allowZoom = true;
 
   nsAutoString viewport;
   aDocument->GetHeaderData(nsGkAtoms::viewport, viewport);
@@ -5167,82 +5171,77 @@ nsContentUtils::GetViewportInfo(nsIDocum
 
   if (widthStr.IsEmpty() &&
      (heightStr.EqualsLiteral("device-height") ||
           scaleFloat == 1.0))
   {
     autoSize = true;
   }
 
-  // XXXjwir3:
-  // See bug 706918, comment 23 for more information on this particular section
-  // of the code. We're using "screen size" in place of the size of the content
-  // area, because on mobile, these are close or equal. This will work for our
-  // purposes (bug 706198), but it will need to be changed in the future to be
-  // more correct when we bring the rest of the viewport code into platform.
-  // We actually want the size of the content area, in the event that we don't
-  // have any metadata about the width and/or height. On mobile, the screen size
-  // and the size of the content area are very close, or the same value.
-  // In XUL fennec, the content area is the size of the <browser> widget, but
-  // in native fennec, the content area is the size of the Gecko LayerView
-  // object.
-
-  // TODO:
-  // Once bug 716575 has been resolved, this code should be changed so that it
-  // does the right thing on all platforms.
-  nsresult result;
-  int32_t screenLeft, screenTop, screenWidth, screenHeight;
-  nsCOMPtr<nsIScreenManager> screenMgr =
-    do_GetService("@mozilla.org/gfx/screenmanager;1", &result);
-
-  nsCOMPtr<nsIScreen> screen;
-  screenMgr->GetPrimaryScreen(getter_AddRefs(screen));
-  screen->GetRect(&screenLeft, &screenTop, &screenWidth, &screenHeight);
-
-  uint32_t width = widthStr.ToInteger(&errorCode);
-  if (NS_FAILED(errorCode)) {
-    if (autoSize) {
-      width = screenWidth;
-    } else {
-      width = Preferences::GetInt("browser.viewport.desktopWidth", 0);
+  // Now convert the scale into device pixels per CSS pixel.
+  nsIWidget *widget = WidgetForDocument(aDocument);
+  double pixelRatio = widget ? GetDevicePixelsPerMetaViewportPixel(widget) : 1.0;
+  scaleFloat *= pixelRatio;
+  scaleMinFloat *= pixelRatio;
+  scaleMaxFloat *= pixelRatio;
+
+  uint32_t width, height;
+  if (autoSize) {
+    // aDisplayWidth and aDisplayHeight are in device pixels; convert them to
+    // CSS pixels for the viewport size.
+    width = aDisplayWidth / pixelRatio;
+    height = aDisplayHeight / pixelRatio;
+  } else {
+    nsresult widthErrorCode, heightErrorCode;
+    width = widthStr.ToInteger(&widthErrorCode);
+    height = heightStr.ToInteger(&heightErrorCode);
+
+    // If width or height has not been set to a valid number by this point,
+    // fall back to a default value.
+    bool validWidth = (!widthStr.IsEmpty() && NS_SUCCEEDED(widthErrorCode) && width > 0);
+    bool validHeight = (!heightStr.IsEmpty() && NS_SUCCEEDED(heightErrorCode) && height > 0);
+
+    if (!validWidth) {
+      if (validHeight && aDisplayWidth > 0 && aDisplayHeight > 0) {
+        width = uint32_t((height * aDisplayWidth) / aDisplayHeight);
+      } else {
+        width = Preferences::GetInt("browser.viewport.desktopWidth",
+                                    kViewportDefaultScreenWidth);
+      }
+    }
+
+    if (!validHeight) {
+      if (aDisplayWidth > 0 && aDisplayHeight > 0) {
+        height = uint32_t((width * aDisplayHeight) / aDisplayWidth);
+      } else {
+        height = width;
+      }
     }
   }
 
   width = NS_MIN(width, kViewportMaxWidth);
   width = NS_MAX(width, kViewportMinWidth);
 
   // Also recalculate the default zoom, if it wasn't specified in the metadata,
   // and the width is specified.
   if (scaleStr.IsEmpty() && !widthStr.IsEmpty()) {
-    scaleFloat = NS_MAX(scaleFloat, (float)(screenWidth/width));
-  }
-
-  uint32_t height = heightStr.ToInteger(&errorCode);
-
-  if (NS_FAILED(errorCode)) {
-    height = width * ((float)screenHeight / screenWidth);
-  }
-
-  // If height was provided by the user, but width wasn't, then we should
-  // calculate the width.
-  if (widthStr.IsEmpty() && !heightStr.IsEmpty()) {
-    width = (uint32_t) ((height * screenWidth) / screenHeight);
+    scaleFloat = NS_MAX(scaleFloat, float(aDisplayWidth) / float(width));
   }
 
   height = NS_MIN(height, kViewportMaxHeight);
   height = NS_MAX(height, kViewportMinHeight);
 
   // We need to perform a conversion, but only if the initial or maximum
   // scale were set explicitly by the user.
   if (!scaleStr.IsEmpty() && NS_SUCCEEDED(scaleErrorCode)) {
-    width = NS_MAX(width, (uint32_t)(screenWidth / scaleFloat));
-    height = NS_MAX(height, (uint32_t)(screenHeight / scaleFloat));
+    width = NS_MAX(width, (uint32_t)(aDisplayWidth / scaleFloat));
+    height = NS_MAX(height, (uint32_t)(aDisplayHeight / scaleFloat));
   } else if (!maxScaleStr.IsEmpty() && NS_SUCCEEDED(scaleMaxErrorCode)) {
-    width = NS_MAX(width, (uint32_t)(screenWidth / scaleMaxFloat));
-    height = NS_MAX(height, (uint32_t)(screenHeight / scaleMaxFloat));
+    width = NS_MAX(width, (uint32_t)(aDisplayWidth / scaleMaxFloat));
+    height = NS_MAX(height, (uint32_t)(aDisplayHeight / scaleMaxFloat));
   }
 
   bool allowZoom = true;
   nsAutoString userScalable;
   aDocument->GetHeaderData(nsGkAtoms::viewport_user_scalable, userScalable);
 
   if ((userScalable.EqualsLiteral("0")) ||
       (userScalable.EqualsLiteral("no")) ||
@@ -5256,16 +5255,38 @@ nsContentUtils::GetViewportInfo(nsIDocum
   ret.defaultZoom = scaleFloat;
   ret.minZoom = scaleMinFloat;
   ret.maxZoom = scaleMaxFloat;
   ret.autoSize = autoSize;
   return ret;
 }
 
 /* static */
+double
+nsContentUtils::GetDevicePixelsPerMetaViewportPixel(nsIWidget* aWidget)
+{
+  int32_t prefValue = Preferences::GetInt("browser.viewport.scaleRatio", 0);
+  if (prefValue > 0) {
+    return double(prefValue) / 100.0;
+  }
+
+  float dpi = aWidget->GetDPI();
+  if (dpi < 200.0) {
+    // Includes desktop displays, LDPI and MDPI Android devices
+    return 1.0;
+  }
+  if (dpi < 300.0) {
+    // Includes Nokia N900, and HDPI Android devices
+    return 1.5;
+  }
+  // For very high-density displays like the iPhone 4, use an integer ratio.
+  return floor(dpi / 150.0);
+}
+
+/* static */
 nsresult
 nsContentUtils::ProcessViewportInfo(nsIDocument *aDocument,
                                     const nsAString &viewportInfo) {
 
   /* We never fail. */
   nsresult rv = NS_OK;
 
   aDocument->SetHeaderData(nsGkAtoms::viewport, viewportInfo);
--- a/content/base/src/nsDOMFile.cpp
+++ b/content/base/src/nsDOMFile.cpp
@@ -369,25 +369,29 @@ nsDOMFileBase::GetFileInfo(indexedDB::Fi
     }
   }
 
   return nullptr;
 }
 
 NS_IMETHODIMP
 nsDOMFileBase::GetSendInfo(nsIInputStream** aBody,
+                           uint64_t* aContentLength,
                            nsACString& aContentType,
                            nsACString& aCharset)
 {
   nsresult rv;
 
   nsCOMPtr<nsIInputStream> stream;
   rv = this->GetInternalStream(getter_AddRefs(stream));
   NS_ENSURE_SUCCESS(rv, rv);
 
+  rv = this->GetSize(aContentLength);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   nsString contentType;
   rv = this->GetType(contentType);
   NS_ENSURE_SUCCESS(rv, rv);
 
   CopyUTF16toUTF8(contentType, aContentType);
 
   aCharset.Truncate();
 
--- a/content/base/src/nsFormData.cpp
+++ b/content/base/src/nsFormData.cpp
@@ -98,33 +98,34 @@ nsFormData::Append(const nsAString& aNam
 
   return AddNameValuePair(aName, valAsString);
 }
 
 // -------------------------------------------------------------------------
 // nsIXHRSendable
 
 NS_IMETHODIMP
-nsFormData::GetSendInfo(nsIInputStream** aBody, nsACString& aContentType,
-                        nsACString& aCharset)
+nsFormData::GetSendInfo(nsIInputStream** aBody, uint64_t* aContentLength,
+                        nsACString& aContentType, nsACString& aCharset)
 {
   nsFSMultipartFormData fs(NS_LITERAL_CSTRING("UTF-8"), nullptr);
   
   for (uint32_t i = 0; i < mFormData.Length(); ++i) {
     if (mFormData[i].valueIsFile) {
       fs.AddNameFilePair(mFormData[i].name, mFormData[i].fileValue);
     }
     else {
       fs.AddNameValuePair(mFormData[i].name, mFormData[i].stringValue);
     }
   }
 
   fs.GetContentType(aContentType);
   aCharset.Truncate();
-  NS_ADDREF(*aBody = fs.GetSubmissionBody());
+  *aContentLength = 0;
+  NS_ADDREF(*aBody = fs.GetSubmissionBody(aContentLength));
 
   return NS_OK;
 }
 
 
 // -------------------------------------------------------------------------
 // nsIJSNativeInitializer
 
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -2416,17 +2416,17 @@ nsXMLHttpRequest::SendAsBinary(const nsA
   if (aRv.Failed()) {
     return;
   }
 
   aRv = Send(variant);
 }
 
 static nsresult
-GetRequestBody(nsIDOMDocument* aDoc, nsIInputStream** aResult,
+GetRequestBody(nsIDOMDocument* aDoc, nsIInputStream** aResult, uint64* aContentLength,
                nsACString& aContentType, nsACString& aCharset)
 {
   aContentType.AssignLiteral("application/xml");
   nsAutoString inputEncoding;
   aDoc->GetInputEncoding(inputEncoding);
   if (!DOMStringIsNull(inputEncoding)) {
     CopyUTF16toUTF8(inputEncoding, aCharset);
   }
@@ -2450,70 +2450,81 @@ GetRequestBody(nsIDOMDocument* aDoc, nsI
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Make sure to use the encoding we'll send
   rv = serializer->SerializeToStream(aDoc, output, aCharset);
   NS_ENSURE_SUCCESS(rv, rv);
 
   output->Close();
 
+  uint32_t length;
+  rv = storStream->GetLength(&length);
+  NS_ENSURE_SUCCESS(rv, rv);
+  *aContentLength = length;
+
   return storStream->NewInputStream(0, aResult);
 }
 
 static nsresult
-GetRequestBody(const nsAString& aString, nsIInputStream** aResult,
+GetRequestBody(const nsAString& aString, nsIInputStream** aResult, uint64* aContentLength,
                nsACString& aContentType, nsACString& aCharset)
 {
   aContentType.AssignLiteral("text/plain");
   aCharset.AssignLiteral("UTF-8");
 
-  return NS_NewCStringInputStream(aResult, NS_ConvertUTF16toUTF8(aString));
+  nsCString converted = NS_ConvertUTF16toUTF8(aString);
+  *aContentLength = converted.Length();
+  return NS_NewCStringInputStream(aResult, converted);
 }
 
 static nsresult
-GetRequestBody(nsIInputStream* aStream, nsIInputStream** aResult,
+GetRequestBody(nsIInputStream* aStream, nsIInputStream** aResult, uint64* aContentLength,
                nsACString& aContentType, nsACString& aCharset)
 {
   aContentType.AssignLiteral("text/plain");
   aCharset.Truncate();
 
+  nsresult rv = aStream->Available(aContentLength);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   NS_ADDREF(*aResult = aStream);
 
   return NS_OK;
 }
 
 static nsresult
-GetRequestBody(nsIXHRSendable* aSendable, nsIInputStream** aResult,
+GetRequestBody(nsIXHRSendable* aSendable, nsIInputStream** aResult, uint64_t* aContentLength,
                nsACString& aContentType, nsACString& aCharset)
 {
-  return aSendable->GetSendInfo(aResult, aContentType, aCharset);
+  return aSendable->GetSendInfo(aResult, aContentLength, aContentType, aCharset);
 }
 
 static nsresult
-GetRequestBody(ArrayBuffer* aArrayBuffer, nsIInputStream** aResult,
+GetRequestBody(ArrayBuffer* aArrayBuffer, nsIInputStream** aResult, uint64_t* aContentLength,
                nsACString& aContentType, nsACString& aCharset)
 {
   aContentType.SetIsVoid(true);
   aCharset.Truncate();
 
   int32_t length = aArrayBuffer->Length();
+  *aContentLength = length;
   char* data = reinterpret_cast<char*>(aArrayBuffer->Data());
 
   nsCOMPtr<nsIInputStream> stream;
   nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream), data, length,
                                       NS_ASSIGNMENT_COPY);
   NS_ENSURE_SUCCESS(rv, rv);
 
   stream.forget(aResult);
 
   return NS_OK;
 }
 
 static nsresult
-GetRequestBody(nsIVariant* aBody, nsIInputStream** aResult,
+GetRequestBody(nsIVariant* aBody, nsIInputStream** aResult, uint64_t* aContentLength,
                nsACString& aContentType, nsACString& aCharset)
 {
   *aResult = nullptr;
 
   uint16_t dataType;
   nsresult rv = aBody->GetDataType(&dataType);
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -2524,38 +2535,38 @@ GetRequestBody(nsIVariant* aBody, nsIInp
     rv = aBody->GetAsInterface(&iid, getter_AddRefs(supports));
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsMemory::Free(iid);
 
     // document?
     nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(supports);
     if (doc) {
-      return GetRequestBody(doc, aResult, aContentType, aCharset);
+      return GetRequestBody(doc, aResult, aContentLength, aContentType, aCharset);
     }
 
     // nsISupportsString?
     nsCOMPtr<nsISupportsString> wstr = do_QueryInterface(supports);
     if (wstr) {
       nsAutoString string;
       wstr->GetData(string);
 
-      return GetRequestBody(string, aResult, aContentType, aCharset);
+      return GetRequestBody(string, aResult, aContentLength, aContentType, aCharset);
     }
 
     // nsIInputStream?
     nsCOMPtr<nsIInputStream> stream = do_QueryInterface(supports);
     if (stream) {
-      return GetRequestBody(stream, aResult, aContentType, aCharset);
+      return GetRequestBody(stream, aResult, aContentLength, aContentType, aCharset);
     }
 
     // nsIXHRSendable?
     nsCOMPtr<nsIXHRSendable> sendable = do_QueryInterface(supports);
     if (sendable) {
-      return GetRequestBody(sendable, aResult, aContentType, aCharset);
+      return GetRequestBody(sendable, aResult, aContentLength, aContentType, aCharset);
     }
 
     // ArrayBuffer?
     jsval realVal;
     nsCxPusher pusher;
     Maybe<JSAutoCompartment> ac;
 
     // If there's a context on the stack, we can just use it. Otherwise, we need
@@ -2570,86 +2581,90 @@ GetRequestBody(nsIVariant* aBody, nsIInp
     }
 
     nsresult rv = aBody->GetAsJSVal(&realVal);
     if (NS_SUCCEEDED(rv) && !JSVAL_IS_PRIMITIVE(realVal)) {
       JSObject *obj = JSVAL_TO_OBJECT(realVal);
       ac.construct(cx, obj);
       if (JS_IsArrayBufferObject(obj, cx)) {
           ArrayBuffer buf(cx, obj);
-          return GetRequestBody(&buf, aResult, aContentType, aCharset);
+          return GetRequestBody(&buf, aResult, aContentLength, aContentType, aCharset);
       }
     }
   }
   else if (dataType == nsIDataType::VTYPE_VOID ||
            dataType == nsIDataType::VTYPE_EMPTY) {
     // Makes us act as if !aBody, don't upload anything
     aContentType.AssignLiteral("text/plain");
     aCharset.AssignLiteral("UTF-8");
+    *aContentLength = 0;
 
     return NS_OK;
   }
 
   PRUnichar* data = nullptr;
   uint32_t len = 0;
   rv = aBody->GetAsWStringWithSize(&len, &data);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsString string;
   string.Adopt(data, len);
 
-  return GetRequestBody(string, aResult, aContentType, aCharset);
+  return GetRequestBody(string, aResult, aContentLength, aContentType, aCharset);
 }
 
 /* static */
 nsresult
 nsXMLHttpRequest::GetRequestBody(nsIVariant* aVariant,
                                  const Nullable<RequestBody>& aBody,
-                                 nsIInputStream** aResult,
+                                 nsIInputStream** aResult, uint64* aContentLength,
                                  nsACString& aContentType, nsACString& aCharset)
 {
   if (aVariant) {
-    return ::GetRequestBody(aVariant, aResult, aContentType, aCharset);
+    return ::GetRequestBody(aVariant, aResult, aContentLength, aContentType, aCharset);
   }
 
   const RequestBody& body = aBody.Value();
   RequestBody::Value value = body.GetValue();
   switch (body.GetType()) {
     case nsXMLHttpRequest::RequestBody::ArrayBuffer:
     {
-      return ::GetRequestBody(value.mArrayBuffer, aResult, aContentType, aCharset);
+      return ::GetRequestBody(value.mArrayBuffer, aResult, aContentLength,
+                              aContentType, aCharset);
     }
     case nsXMLHttpRequest::RequestBody::Blob:
     {
       nsresult rv;
       nsCOMPtr<nsIXHRSendable> sendable = do_QueryInterface(value.mBlob, &rv);
       NS_ENSURE_SUCCESS(rv, rv);
 
-      return ::GetRequestBody(sendable, aResult, aContentType, aCharset);
+      return ::GetRequestBody(sendable, aResult, aContentLength, aContentType, aCharset);
     }
     case nsXMLHttpRequest::RequestBody::Document:
     {
       nsCOMPtr<nsIDOMDocument> document = do_QueryInterface(value.mDocument);
-      return ::GetRequestBody(document, aResult, aContentType, aCharset);
+      return ::GetRequestBody(document, aResult, aContentLength, aContentType, aCharset);
     }
     case nsXMLHttpRequest::RequestBody::DOMString:
     {
-      return ::GetRequestBody(*value.mString, aResult, aContentType, aCharset);
+      return ::GetRequestBody(*value.mString, aResult, aContentLength,
+                              aContentType, aCharset);
     }
     case nsXMLHttpRequest::RequestBody::FormData:
     {
       nsresult rv;
       nsCOMPtr<nsIXHRSendable> sendable = do_QueryInterface(value.mFormData, &rv);
       NS_ENSURE_SUCCESS(rv, rv);
 
-      return ::GetRequestBody(sendable, aResult, aContentType, aCharset);
+      return ::GetRequestBody(sendable, aResult, aContentLength, aContentType, aCharset);
     }
     case nsXMLHttpRequest::RequestBody::InputStream:
     {
-      return ::GetRequestBody(value.mStream, aResult, aContentType, aCharset);
+      return ::GetRequestBody(value.mStream, aResult, aContentLength,
+                              aContentType, aCharset);
     }
     default:
     {
       return NS_ERROR_FAILURE;
     }
   }
 
   NS_NOTREACHED("Default cases exist for a reason");
@@ -2783,17 +2798,17 @@ nsXMLHttpRequest::Send(nsIVariant* aVari
   if ((aVariant || !aBody.IsNull()) && httpChannel &&
       !method.EqualsLiteral("GET")) {
 
     nsAutoCString charset;
     nsAutoCString defaultContentType;
     nsCOMPtr<nsIInputStream> postDataStream;
 
     rv = GetRequestBody(aVariant, aBody, getter_AddRefs(postDataStream),
-                        defaultContentType, charset);
+                        &mUploadTotal, defaultContentType, charset);
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (postDataStream) {
       // If no content type header was set by the client, we set it to
       // application/xml.
       nsAutoCString contentType;
       if (NS_FAILED(httpChannel->
                       GetRequestHeader(NS_LITERAL_CSTRING("Content-Type"),
@@ -2846,38 +2861,35 @@ nsXMLHttpRequest::Send(nsIVariant* aVari
                                        postDataStream, 
                                        4096);
         NS_ENSURE_SUCCESS(rv, rv);
 
         postDataStream = bufferedStream;
       }
 
       mUploadComplete = false;
-      uint64_t uploadTotal = 0;
-      postDataStream->Available(&uploadTotal);
-      mUploadTotal = uploadTotal;
 
       // We want to use a newer version of the upload channel that won't
       // ignore the necessary headers for an empty Content-Type.
       nsCOMPtr<nsIUploadChannel2> uploadChannel2(do_QueryInterface(httpChannel));
       // This assertion will fire if buggy extensions are installed
       NS_ASSERTION(uploadChannel2, "http must support nsIUploadChannel2");
       if (uploadChannel2) {
           uploadChannel2->ExplicitSetUploadStream(postDataStream, contentType,
-                                                 -1, method, false);
+                                                 mUploadTotal, method, false);
       }
       else {
         // http channel doesn't support the new nsIUploadChannel2. Emulate
         // as best we can using nsIUploadChannel
         if (contentType.IsEmpty()) {
           contentType.AssignLiteral("application/octet-stream");
         }
         nsCOMPtr<nsIUploadChannel> uploadChannel =
           do_QueryInterface(httpChannel);
-        uploadChannel->SetUploadStream(postDataStream, contentType, -1);
+        uploadChannel->SetUploadStream(postDataStream, contentType, mUploadTotal);
         // Reset the method to its original value
         httpChannel->SetRequestMethod(method);
       }
     }
   }
 
   if (httpChannel) {
     nsAutoCString contentTypeHeader;
--- a/content/base/src/nsXMLHttpRequest.h
+++ b/content/base/src/nsXMLHttpRequest.h
@@ -340,16 +340,17 @@ private:
   private:
     Type mType;
     Value mValue;
   };
 
   static nsresult GetRequestBody(nsIVariant* aVariant,
                                  const Nullable<RequestBody>& aBody,
                                  nsIInputStream** aResult,
+                                 uint64_t* aContentLength,
                                  nsACString& aContentType,
                                  nsACString& aCharset);
 
   nsresult Send(nsIVariant* aVariant, const Nullable<RequestBody>& aBody);
   nsresult Send(const Nullable<RequestBody>& aBody)
   {
     return Send(nullptr, aBody);
   }
--- a/content/base/test/Makefile.in
+++ b/content/base/test/Makefile.in
@@ -245,16 +245,23 @@ MOCHITEST_FILES_A = \
 		test_CrossSiteXHR_cache.html \
 		file_CrossSiteXHR_cache_server.sjs \
 		test_XHRDocURI.html \
 		file_XHRDocURI.xml \
 		file_XHRDocURI.xml^headers^ \
 		file_XHRDocURI.text \
 		file_XHRDocURI.text^headers^ \
 		test_DOMException.html \
+		test_meta_viewport0.html \
+		test_meta_viewport1.html \
+		test_meta_viewport2.html \
+		test_meta_viewport3.html \
+		test_meta_viewport4.html \
+		test_meta_viewport5.html \
+		test_meta_viewport6.html \
 		test_mutationobservers.html \
 		mutationobserver_dialog.html \
 		test_bug744830.html \
 		file_bug782342.txt \
 		test_bug782342.html \
 		$(NULL)
 
 MOCHITEST_FILES_B = \
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_meta_viewport0.html
@@ -0,0 +1,76 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>meta viewport test</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+  <p>No &lt;meta name="viewport"&gt; tag</p>
+  <script type="application/javascript;version=1.7">
+    "use strict";
+
+    SimpleTest.waitForExplicitFinish();
+
+    let tests = [];
+
+    tests.push(function test1() {
+      SpecialPowers.pushPrefEnv({"set": [["browser.viewport.scaleRatio", 100]]},
+        function() {
+          let info = getViewportInfo(800, 480);
+          is(info.defaultZoom, 0,       "initial scale is unspecified");
+          is(info.minZoom,     0,       "minumum scale defaults to the absolute minumum");
+          is(info.maxZoom,     10,      "maximum scale defaults to the absolute maximum");
+          is(info.width,       980,     "width is the default width");
+          is(info.height,      588,     "height is proportional to displayHeight");
+          is(info.autoSize,    false,   "autoSize is disabled by default");
+          is(info.allowZoom,   true,    "zooming is enabled by default");
+
+          info = getViewportInfo(490, 600);
+          is(info.width,       980,     "width is still the default width");
+          is(info.height,      1200,    "height is proportional to the new displayHeight");
+
+          nextTest();
+        });
+    });
+
+    tests.push(function test2() {
+      SpecialPowers.pushPrefEnv({"set": [["browser.viewport.scaleRatio", 150]]},
+        function() {
+          let info = getViewportInfo(800, 480);
+          is(info.width,       980,     "width is still the default width");
+          is(info.height,      588,     "height is still proportional to displayHeight");
+
+          nextTest();
+        });
+    });
+
+    function getViewportInfo(aDisplayWidth, aDisplayHeight) {
+      let defaultZoom = {}, allowZoom = {}, minZoom = {}, maxZoom = {},
+          width = {}, height = {}, autoSize = {};
+
+      let cwu = SpecialPowers.getDOMWindowUtils(window);
+      cwu.getViewportInfo(aDisplayWidth, aDisplayHeight, defaultZoom, allowZoom,
+                          minZoom, maxZoom, width, height, autoSize);
+      return {
+        defaultZoom: defaultZoom.value,
+        minZoom: minZoom.value,
+        maxZoom: maxZoom.value,
+        width: width.value,
+        height: height.value,
+        autoSize: autoSize.value,
+        allowZoom: allowZoom.value
+      };
+    }
+
+    function nextTest() {
+      if (tests.length)
+        (tests.shift())();
+      else
+        SimpleTest.finish();
+    }
+    addEventListener("load", nextTest);
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_meta_viewport1.html
@@ -0,0 +1,76 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>meta viewport test</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <meta name="viewport" content="width=device-width, initial-scale=1">
+</head>
+<body>
+  <p>width=device-width, initial-scale=1</p>
+  <script type="application/javascript;version=1.7">
+    "use strict";
+
+    SimpleTest.waitForExplicitFinish();
+
+    let tests = [];
+
+    tests.push(function test1() {
+      SpecialPowers.pushPrefEnv({"set": [["browser.viewport.scaleRatio", 100]]},
+        function() {
+          let info = getViewportInfo(800, 480);
+          is(info.defaultZoom, 1,    "initial zoom is 100%");
+          is(info.width,       800,  "width is the same as the displayWidth");
+          is(info.height,      480,  "height is the same as the displayHeight");
+          is(info.autoSize,    true, "width=device-width enables autoSize");
+          is(info.allowZoom,   true, "zooming is enabled by default");
+
+          info = getViewportInfo(900, 600);
+          is(info.width,       900,  "changing the displayWidth changes the width");
+          is(info.height,      600,  "changing the displayHeight changes the height");
+
+          nextTest();
+        });
+    });
+
+    tests.push(function test2() {
+      SpecialPowers.pushPrefEnv({"set": [["browser.viewport.scaleRatio", 150]]},
+        function() {
+          let info = getViewportInfo(900, 600);
+          is(info.defaultZoom, 1.5,  "initial zoom is 150%");
+          is(info.width,       600,  "width equals displayWidth/1.5");
+          is(info.height,      400,  "height equals displayHeight/1.5");
+
+          nextTest();
+        });
+    });
+
+    function getViewportInfo(aDisplayWidth, aDisplayHeight) {
+      let defaultZoom = {}, allowZoom = {}, minZoom = {}, maxZoom = {},
+          width = {}, height = {}, autoSize = {};
+
+      let cwu = SpecialPowers.getDOMWindowUtils(window);
+      cwu.getViewportInfo(aDisplayWidth, aDisplayHeight, defaultZoom, allowZoom,
+                          minZoom, maxZoom, width, height, autoSize);
+      return {
+        defaultZoom: defaultZoom.value,
+        minZoom: minZoom.value,
+        maxZoom: maxZoom.value,
+        width: width.value,
+        height: height.value,
+        autoSize: autoSize.value,
+        allowZoom: allowZoom.value
+      };
+    }
+
+    function nextTest() {
+      if (tests.length)
+        (tests.shift())();
+      else
+        SimpleTest.finish();
+    }
+    addEventListener("load", nextTest);
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_meta_viewport2.html
@@ -0,0 +1,76 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>meta viewport test</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <meta name="viewport" content="width=device-width">
+</head>
+<body>
+  <p>width=device-width</p>
+  <script type="application/javascript;version=1.7">
+    "use strict";
+
+    SimpleTest.waitForExplicitFinish();
+
+    let tests = [];
+
+    tests.push(function test1() {
+      SpecialPowers.pushPrefEnv({"set": [["browser.viewport.scaleRatio", 100]]},
+        function() {
+          let info = getViewportInfo(800, 480);
+          is(info.defaultZoom, 1,    "initial zoom is 100%");
+          is(info.width,       800,  "width is the same as the displayWidth");
+          is(info.height,      480,  "height is the same as the displayHeight");
+          is(info.autoSize,    true, "width=device-width enables autoSize");
+          is(info.allowZoom,   true, "zooming is enabled by default");
+
+          info = getViewportInfo(900, 600);
+          is(info.width,       900,  "changing the displayWidth changes the width");
+          is(info.height,      600,  "changing the displayHeight changes the height");
+
+          nextTest();
+        });
+    });
+
+    tests.push(function test2() {
+      SpecialPowers.pushPrefEnv({"set": [["browser.viewport.scaleRatio", 150]]},
+        function() {
+          let info = getViewportInfo(900, 600);
+          is(info.defaultZoom, 1.5,  "initial zoom is 150%");
+          is(info.width,       600,  "width equals displayWidth/1.5");
+          is(info.height,      400,  "height equals displayHeight/1.5");
+
+          nextTest();
+        });
+    });
+
+    function getViewportInfo(aDisplayWidth, aDisplayHeight) {
+      let defaultZoom = {}, allowZoom = {}, minZoom = {}, maxZoom = {},
+          width = {}, height = {}, autoSize = {};
+
+      let cwu = SpecialPowers.getDOMWindowUtils(window);
+      cwu.getViewportInfo(aDisplayWidth, aDisplayHeight, defaultZoom, allowZoom,
+                          minZoom, maxZoom, width, height, autoSize);
+      return {
+        defaultZoom: defaultZoom.value,
+        minZoom: minZoom.value,
+        maxZoom: maxZoom.value,
+        width: width.value,
+        height: height.value,
+        autoSize: autoSize.value,
+        allowZoom: allowZoom.value
+      };
+    }
+
+    function nextTest() {
+      if (tests.length)
+        (tests.shift())();
+      else
+        SimpleTest.finish();
+    }
+    addEventListener("load", nextTest);
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_meta_viewport3.html
@@ -0,0 +1,78 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>meta viewport test</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <meta name="viewport" content="width=320">
+</head>
+<body>
+  <p>width=320</p>
+  <script type="application/javascript;version=1.7">
+    "use strict";
+
+    SimpleTest.waitForExplicitFinish();
+
+    let tests = [];
+
+    tests.push(function test1() {
+      SpecialPowers.pushPrefEnv({"set": [["browser.viewport.scaleRatio", 100]]},
+        function() {
+          let info = getViewportInfo(800, 480);
+          is(info.defaultZoom, 2.5,   "initial zoom fits the displayWidth");
+          is(info.width,       320,   "width is set explicitly");
+          is(info.height,      223,   "height is at the absolute minimum");
+          is(info.autoSize,    false, "width=device-width enables autoSize");
+          is(info.allowZoom,   true,  "zooming is enabled by default");
+
+          info = getViewportInfo(480, 800);
+          is(info.defaultZoom, 1.5,   "initial zoom fits the new displayWidth");
+          is(info.width,       320,   "explicit width is unchanged");
+          is(info.height,      533,   "height changes proportional to displayHeight");
+
+          nextTest();
+        });
+    });
+
+    tests.push(function test2() {
+      SpecialPowers.pushPrefEnv({"set": [["browser.viewport.scaleRatio", 150]]},
+        function() {
+          // With an explicit width in CSS px, the scaleRatio has no effect.
+          let info = getViewportInfo(800, 480);
+          is(info.defaultZoom, 2.5,   "initial zoom still fits the displayWidth");
+          is(info.width,       320,   "width is still set explicitly");
+          is(info.height,      223,   "height is still minimum height");
+
+          nextTest();
+        });
+    });
+
+    function getViewportInfo(aDisplayWidth, aDisplayHeight) {
+      let defaultZoom = {}, allowZoom = {}, minZoom = {}, maxZoom = {},
+          width = {}, height = {}, autoSize = {};
+
+      let cwu = SpecialPowers.getDOMWindowUtils(window);
+      cwu.getViewportInfo(aDisplayWidth, aDisplayHeight, defaultZoom, allowZoom,
+                          minZoom, maxZoom, width, height, autoSize);
+      return {
+        defaultZoom: defaultZoom.value,
+        minZoom: minZoom.value,
+        maxZoom: maxZoom.value,
+        width: width.value,
+        height: height.value,
+        autoSize: autoSize.value,
+        allowZoom: allowZoom.value
+      };
+    }
+
+    function nextTest() {
+      if (tests.length)
+        (tests.shift())();
+      else
+        SimpleTest.finish();
+    }
+    addEventListener("load", nextTest);
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_meta_viewport4.html
@@ -0,0 +1,77 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>meta viewport test</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <meta name="viewport" content="initial-scale=1.0, user-scalable=no">
+</head>
+<body>
+  <p>initial-scale=1.0, user-scalable=no</p>
+  <script type="application/javascript;version=1.7">
+    "use strict";
+
+    SimpleTest.waitForExplicitFinish();
+
+    let tests = [];
+
+    tests.push(function test1() {
+      SpecialPowers.pushPrefEnv({"set": [["browser.viewport.scaleRatio", 100]]},
+        function() {
+          let info = getViewportInfo(800, 480);
+          is(info.defaultZoom, 1,     "initial zoom is set explicitly");
+          is(info.width,       800,   "width fits the initial zoom level");
+          is(info.height,      480,   "height fits the initial zoom level");
+          is(info.autoSize,    true,  "initial-scale=1 enables autoSize");
+          is(info.allowZoom,   false, "zooming is explicitly disabled");
+
+          info = getViewportInfo(480, 800);
+          is(info.defaultZoom, 1,     "initial zoom is still set explicitly");
+          is(info.width,       480,   "width changes to match the displayWidth");
+          is(info.height,      800,   "height changes to match the displayHeight");
+
+          nextTest();
+        });
+    });
+
+    tests.push(function test2() {
+      SpecialPowers.pushPrefEnv({"set": [["browser.viewport.scaleRatio", 150]]},
+        function() {
+          let info = getViewportInfo(800, 480);
+          is(info.defaultZoom, 1.5,   "initial zoom is adjusted for device pixel ratio");
+          is(info.width,       533,   "width fits the initial zoom");
+          is(info.height,      320,   "height fits the initial zoom");
+
+          nextTest();
+        });
+    });
+
+    function getViewportInfo(aDisplayWidth, aDisplayHeight) {
+      let defaultZoom = {}, allowZoom = {}, minZoom = {}, maxZoom = {},
+          width = {}, height = {}, autoSize = {};
+
+      let cwu = SpecialPowers.getDOMWindowUtils(window);
+      cwu.getViewportInfo(aDisplayWidth, aDisplayHeight, defaultZoom, allowZoom,
+                          minZoom, maxZoom, width, height, autoSize);
+      return {
+        defaultZoom: defaultZoom.value,
+        minZoom: minZoom.value,
+        maxZoom: maxZoom.value,
+        width: width.value,
+        height: height.value,
+        autoSize: autoSize.value,
+        allowZoom: allowZoom.value
+      };
+    }
+
+    function nextTest() {
+      if (tests.length)
+        (tests.shift())();
+      else
+        SimpleTest.finish();
+    }
+    addEventListener("load", nextTest);
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_meta_viewport5.html
@@ -0,0 +1,53 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>meta viewport test</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <meta name="viewport" content="user-scalable=NO">
+</head>
+<body>
+  <p>user-scalable=NO</p>
+  <script type="application/javascript;version=1.7">
+    "use strict";
+
+    SimpleTest.waitForExplicitFinish();
+
+    let tests = [];
+
+    tests.push(function test1() {
+      let info = getViewportInfo(800, 480);
+      is(info.allowZoom, true, "user-scalable values are case-sensitive; 'NO' is not valid");
+
+      nextTest();
+    });
+
+    function getViewportInfo(aDisplayWidth, aDisplayHeight) {
+      let defaultZoom = {}, allowZoom = {}, minZoom = {}, maxZoom = {},
+          width = {}, height = {}, autoSize = {};
+
+      let cwu = SpecialPowers.getDOMWindowUtils(window);
+      cwu.getViewportInfo(aDisplayWidth, aDisplayHeight, defaultZoom, allowZoom,
+                          minZoom, maxZoom, width, height, autoSize);
+      return {
+        defaultZoom: defaultZoom.value,
+        minZoom: minZoom.value,
+        maxZoom: maxZoom.value,
+        width: width.value,
+        height: height.value,
+        autoSize: autoSize.value,
+        allowZoom: allowZoom.value
+      };
+    }
+
+    function nextTest() {
+      if (tests.length)
+        (tests.shift())();
+      else
+        SimpleTest.finish();
+    }
+    addEventListener("load", nextTest);
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_meta_viewport6.html
@@ -0,0 +1,82 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>meta viewport test</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <meta name="viewport" content="width=2000, minimum-scale=0.75">
+</head>
+<body>
+  <p>width=2000, minimum-scale=0.75</p>
+  <script type="application/javascript;version=1.7">
+    "use strict";
+
+    SimpleTest.waitForExplicitFinish();
+
+    let tests = [];
+
+    tests.push(function test1() {
+      SpecialPowers.pushPrefEnv({"set": [["browser.viewport.scaleRatio", 100]]},
+        function() {
+          let info = getViewportInfo(800, 480);
+          is(info.minZoom,     0.75,    "minumum scale is set explicitly");
+          is(info.defaultZoom, 0.75,    "initial scale is bounded by the minimum scale");
+          is(info.maxZoom,     10,      "maximum scale defaults to the absolute maximum");
+          is(info.width,       2000,    "width is set explicitly");
+          is(info.height,      1200,    "height is proportional to displayHeight");
+          is(info.autoSize,    false,   "autoSize is disabled by default");
+          is(info.allowZoom,   true,    "zooming is enabled by default");
+
+          info = getViewportInfo(2000, 1000);
+          is(info.minZoom,     0.75,    "minumum scale is still set explicitly");
+          is(info.defaultZoom, 1,       "initial scale fits the width");
+          is(info.width,       2000,    "width is set explicitly");
+          is(info.height,      1000,    "height is proportional to the new displayHeight");
+
+          nextTest();
+        });
+    });
+
+    tests.push(function test2() {
+      SpecialPowers.pushPrefEnv({"set": [["browser.viewport.scaleRatio", 150]]},
+        function() {
+          let info = getViewportInfo(800, 480);
+          is(info.minZoom,     1.125,   "minumum scale is converted to device pixel scale");
+          is(info.defaultZoom, 1.125,   "initial scale is bounded by the minimum scale");
+          is(info.maxZoom,     15,      "maximum scale defaults to the absolute maximum");
+          is(info.width,       2000,    "width is still set explicitly");
+          is(info.height,      1200,    "height is still proportional to displayHeight");
+
+          nextTest();
+        });
+    });
+
+    function getViewportInfo(aDisplayWidth, aDisplayHeight) {
+      let defaultZoom = {}, allowZoom = {}, minZoom = {}, maxZoom = {},
+          width = {}, height = {}, autoSize = {};
+
+      let cwu = SpecialPowers.getDOMWindowUtils(window);
+      cwu.getViewportInfo(aDisplayWidth, aDisplayHeight, defaultZoom, allowZoom,
+                          minZoom, maxZoom, width, height, autoSize);
+      return {
+        defaultZoom: defaultZoom.value,
+        minZoom: minZoom.value,
+        maxZoom: maxZoom.value,
+        width: width.value,
+        height: height.value,
+        autoSize: autoSize.value,
+        allowZoom: allowZoom.value
+      };
+    }
+
+    function nextTest() {
+      if (tests.length)
+        (tests.shift())();
+      else
+        SimpleTest.finish();
+    }
+    addEventListener("load", nextTest);
+  </script>
+</body>
+</html>
--- a/content/html/content/public/nsFormSubmission.h
+++ b/content/html/content/public/nsFormSubmission.h
@@ -164,17 +164,17 @@ public:
                                         nsIInputStream** aPostDataStream);
 
   void GetContentType(nsACString& aContentType)
   {
     aContentType =
       NS_LITERAL_CSTRING("multipart/form-data; boundary=") + mBoundary;
   }
 
-  nsIInputStream* GetSubmissionBody();
+  nsIInputStream* GetSubmissionBody(uint64_t* aContentLength);
 
 protected:
 
   /**
    * Roll up the data we have so far and add it to the multiplexed data stream.
    */
   nsresult AddPostDataStream();
 
@@ -196,16 +196,21 @@ private:
   nsCString mPostDataChunk;
 
   /**
    * The boundary string to use after each "part" (the boundary that marks the
    * end of a value).  This is computed randomly and is different for each
    * submission.
    */
   nsCString mBoundary;
+
+  /**
+   * The total length in bytes of the streams that make up mPostDataStream
+   */
+  uint64_t mTotalLength;
 };
 
 /**
  * Get a submission object based on attributes in the form (ENCTYPE and METHOD)
  *
  * @param aForm the form to get a submission object based on
  * @param aOriginatingElement the originating element (can be null)
  * @param aFormSubmission the form submission object (out param)
--- a/content/html/content/src/nsFormSubmission.cpp
+++ b/content/html/content/src/nsFormSubmission.cpp
@@ -373,38 +373,40 @@ nsFSURLEncoded::URLEncode(const nsAStrin
 // --------------------------------------------------------------------------
 
 nsFSMultipartFormData::nsFSMultipartFormData(const nsACString& aCharset,
                                              nsIContent* aOriginatingElement)
     : nsEncodingFormSubmission(aCharset, aOriginatingElement)
 {
   mPostDataStream =
     do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1");
+  mTotalLength = 0;
 
   mBoundary.AssignLiteral("---------------------------");
   mBoundary.AppendInt(rand());
   mBoundary.AppendInt(rand());
   mBoundary.AppendInt(rand());
 }
 
 nsFSMultipartFormData::~nsFSMultipartFormData()
 {
   NS_ASSERTION(mPostDataChunk.IsEmpty(), "Left unsubmitted data");
 }
 
 nsIInputStream*
-nsFSMultipartFormData::GetSubmissionBody()
+nsFSMultipartFormData::GetSubmissionBody(uint64_t* aContentLength)
 {
   // Finish data
   mPostDataChunk += NS_LITERAL_CSTRING("--") + mBoundary
                   + NS_LITERAL_CSTRING("--" CRLF);
 
   // Add final data input stream
   AddPostDataStream();
 
+  *aContentLength = mTotalLength;
   return mPostDataStream;
 }
 
 nsresult
 nsFSMultipartFormData::AddNameValuePair(const nsAString& aName,
                                         const nsAString& aValue)
 {
   nsCString valueStr;
@@ -508,16 +510,21 @@ nsFSMultipartFormData::AddNameFilePair(c
 
   // Add the file to the stream
   if (fileStream) {
     // We need to dump the data up to this point into the POST data stream here,
     // since we're about to add the file input stream
     AddPostDataStream();
 
     mPostDataStream->AppendStream(fileStream);
+
+    uint64_t size;
+    nsresult rv = aBlob->GetSize(&size);
+    NS_ENSURE_SUCCESS(rv, rv);
+    mTotalLength += size;
   }
 
   // CRLF after file
   mPostDataChunk.AppendLiteral(CRLF);
 
   return NS_OK;
 }
 
@@ -531,17 +538,18 @@ nsFSMultipartFormData::GetEncodedSubmiss
   nsCOMPtr<nsIMIMEInputStream> mimeStream
     = do_CreateInstance("@mozilla.org/network/mime-input-stream;1", &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsAutoCString contentType;
   GetContentType(contentType);
   mimeStream->AddHeader("Content-Type", contentType.get());
   mimeStream->SetAddContentLength(true);
-  mimeStream->SetData(GetSubmissionBody());
+  uint64_t unused;
+  mimeStream->SetData(GetSubmissionBody(&unused));
 
   *aPostDataStream = mimeStream.forget().get();
 
   return NS_OK;
 }
 
 nsresult
 nsFSMultipartFormData::AddPostDataStream()
@@ -549,16 +557,17 @@ nsFSMultipartFormData::AddPostDataStream
   nsresult rv = NS_OK;
   
   nsCOMPtr<nsIInputStream> postDataChunkStream;
   rv = NS_NewCStringInputStream(getter_AddRefs(postDataChunkStream),
                                 mPostDataChunk);
   NS_ASSERTION(postDataChunkStream, "Could not open a stream for POST!");
   if (postDataChunkStream) {
     mPostDataStream->AppendStream(postDataChunkStream);
+    mTotalLength += mPostDataChunk.Length();
   }
 
   mPostDataChunk.Truncate();
 
   return rv;
 }
 
 // --------------------------------------------------------------------------
--- a/content/media/nsBuiltinDecoderStateMachine.cpp
+++ b/content/media/nsBuiltinDecoderStateMachine.cpp
@@ -1485,17 +1485,19 @@ void nsBuiltinDecoderStateMachine::Seek(
 
   // Bound the seek time to be inside the media range.
   NS_ASSERTION(mStartTime != -1, "Should know start time by now");
   NS_ASSERTION(mEndTime != -1, "Should know end time by now");
   mSeekTime = NS_MIN(mSeekTime, mEndTime);
   mSeekTime = NS_MAX(mStartTime, mSeekTime);
   LOG(PR_LOG_DEBUG, ("%p Changed state to SEEKING (to %f)", mDecoder.get(), aTime));
   mState = DECODER_STATE_SEEKING;
-  mDecoder->RecreateDecodedStream(mSeekTime - mStartTime);
+  if (mDecoder->GetDecodedStream()) {
+    mDecoder->RecreateDecodedStream(mSeekTime - mStartTime);
+  }
   ScheduleStateMachine();
 }
 
 void nsBuiltinDecoderStateMachine::StopDecodeThread()
 {
   NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread.");
   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
   if (mRequestedNewDecodeThread) {
@@ -2040,17 +2042,17 @@ nsresult nsBuiltinDecoderStateMachine::R
       // We will remain in the buffering state if we've not decoded enough
       // data to begin playback, or if we've not downloaded a reasonable
       // amount of data inside our buffering time.
       TimeDuration elapsed = now - mBufferingStart;
       bool isLiveStream = mDecoder->GetResource()->GetLength() == -1;
       if ((isLiveStream || !mDecoder->CanPlayThrough()) &&
             elapsed < TimeDuration::FromSeconds(mBufferingWait) &&
             (mQuickBuffering ? HasLowDecodedData(QUICK_BUFFERING_LOW_DATA_USECS)
-                            : (GetUndecodedData() < mBufferingWait * USECS_PER_S / 1000)) &&
+                            : (GetUndecodedData() < mBufferingWait * USECS_PER_S)) &&
             !resource->IsDataCachedToEndOfResource(mDecoder->mDecoderPosition) &&
             !resource->IsSuspended())
       {
         LOG(PR_LOG_DEBUG,
             ("%p Buffering: %.3lfs/%ds, timeout in %.3lfs %s",
               mDecoder.get(),
               GetUndecodedData() / static_cast<double>(USECS_PER_S),
               mBufferingWait,
@@ -2212,22 +2214,31 @@ void nsBuiltinDecoderStateMachine::Advan
     }
   }
 
   // Skip frames up to the frame at the playback position, and figure out
   // the time remaining until it's time to display the next frame.
   int64_t remainingTime = AUDIO_DURATION_USECS;
   NS_ASSERTION(clock_time >= mStartTime, "Should have positive clock time.");
   nsAutoPtr<VideoData> currentFrame;
+#ifdef PR_LOGGING
+  int32_t droppedFrames = 0;
+#endif
   if (mReader->VideoQueue().GetSize() > 0) {
     VideoData* frame = mReader->VideoQueue().PeekFront();
     while (mRealTime || clock_time >= frame->mTime) {
       mVideoFrameEndTime = frame->mEndTime;
       currentFrame = frame;
       LOG(PR_LOG_DEBUG, ("%p Decoder discarding video frame %lld", mDecoder.get(), frame->mTime));
+#ifdef PR_LOGGING
+      if (droppedFrames++) {
+        LOG(PR_LOG_DEBUG, ("%p Decoder discarding video frame %lld (%d so far)",
+              mDecoder.get(), frame->mTime, droppedFrames - 1));
+      }
+#endif
       mReader->VideoQueue().PopFront();
       // Notify the decode thread that the video queue's buffers may have
       // free'd up space for more frames.
       mDecoder->GetReentrantMonitor().NotifyAll();
       mDecoder->UpdatePlaybackOffset(frame->mOffset);
       if (mReader->VideoQueue().GetSize() == 0)
         break;
       frame = mReader->VideoQueue().PeekFront();
@@ -2383,17 +2394,17 @@ void nsBuiltinDecoderStateMachine::Updat
 
   NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
 }
 
 bool nsBuiltinDecoderStateMachine::JustExitedQuickBuffering()
 {
   return !mDecodeStartTime.IsNull() &&
     mQuickBuffering &&
-    (TimeStamp::Now() - mDecodeStartTime) < TimeDuration::FromSeconds(QUICK_BUFFER_THRESHOLD_USECS);
+    (TimeStamp::Now() - mDecodeStartTime) < TimeDuration::FromMicroseconds(QUICK_BUFFER_THRESHOLD_USECS);
 }
 
 void nsBuiltinDecoderStateMachine::StartBuffering()
 {
   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
 
   if (IsPlaying()) {
     StopPlayback();
--- a/dom/apps/src/Webapps.jsm
+++ b/dom/apps/src/Webapps.jsm
@@ -147,17 +147,21 @@ let DOMApplicationRegistry = {
     let runUpdate = Services.prefs.getBoolPref("dom.mozApps.runUpdate");
     Services.prefs.setBoolPref("dom.mozApps.runUpdate", false);
 
     // 1.
     this.loadCurrentRegistry((function() {
 #ifdef MOZ_WIDGET_GONK
     // if first run, merge the system apps.
     if (runUpdate) {
-      let file = FileUtils.getFile("coreAppsDir", ["webapps", "webapps.json"], false);
+      let file;
+      try {
+        file = FileUtils.getFile("coreAppsDir", ["webapps", "webapps.json"], false);
+      } catch(e) { }
+
       if (file && file.exists) {
         // 2.a
         this._loadJSONAsync(file, (function loadCoreRegistry(aData) {
           if (!aData) {
             this.registerAppsHandlers();
             return;
           }
 
@@ -189,16 +193,18 @@ let DOMApplicationRegistry = {
               }
             }
             // XXXX once bug 758269 is ready, revoke perms for this app
             // let localId = this.webapps[id].localId;
             // installPermissions(localId);
           }
           this.registerAppsHandlers();
         }).bind(this));
+      } else {
+        this.registerAppsHandlers();
       }
     } else {
       this.registerAppsHandlers();
     }
 #else
     this.registerAppsHandlers();
 #endif
     }).bind(this));
@@ -376,17 +382,18 @@ let DOMApplicationRegistry = {
           converter.charset = "UTF-8";
 
           data = JSON.parse(converter.ConvertToUnicode(NetUtil.readInputStreamToString(aStream,
                                                             aStream.available()) || ""));
           aStream.close();
           if (aCallback)
             aCallback(data);
         } catch (ex) {
-          Cu.reportError("DOMApplicationRegistry: Could not parse JSON: " + ex);
+          Cu.reportError("DOMApplicationRegistry: Could not parse JSON: " +
+                         aFile.path + " " + ex);
           if (aCallback)
             aCallback(null);
         }
       });
     } catch (ex) {
       Cu.reportError("DOMApplicationRegistry: Could not read from " +
                      aFile.path + " : " + ex);
       if (aCallback)
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -172,17 +172,17 @@ nsDOMWindowUtils::GetDocCharsetIsForced(
   *aIsForced = false;
 
   if (!IsUniversalXPConnectCapable()) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
   if (window) {
-    nsCOMPtr<nsIDocument> doc(do_QueryInterface(window->GetExtantDocument()));
+    nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
     *aIsForced = doc &&
       doc->GetDocumentCharacterSetSource() >= kCharsetFromParentForced;
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::GetDocumentMetadata(const nsAString& aName,
@@ -258,16 +258,41 @@ nsDOMWindowUtils::SetCSSViewport(float a
   nscoord width = nsPresContext::CSSPixelsToAppUnits(aWidthPx);
   nscoord height = nsPresContext::CSSPixelsToAppUnits(aHeightPx);
 
   presShell->ResizeReflowOverride(width, height);
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsDOMWindowUtils::GetViewportInfo(uint32_t aDisplayWidth,
+                                  uint32_t aDisplayHeight,
+                                  double *aDefaultZoom, bool *aAllowZoom,
+                                  double *aMinZoom, double *aMaxZoom,
+                                  uint32_t *aWidth, uint32_t *aHeight,
+                                  bool *aAutoSize)
+{
+  nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
+  NS_ENSURE_STATE(window);
+
+  nsCOMPtr<nsIDocument> doc(do_QueryInterface(window->GetExtantDocument()));
+  NS_ENSURE_STATE(doc);
+
+  ViewportInfo info = nsContentUtils::GetViewportInfo(doc, aDisplayWidth, aDisplayHeight);
+  *aDefaultZoom = info.defaultZoom;
+  *aAllowZoom = info.allowZoom;
+  *aMinZoom = info.minZoom;
+  *aMaxZoom = info.maxZoom;
+  *aWidth = info.width;
+  *aHeight = info.height;
+  *aAutoSize = info.autoSize;
+  return NS_OK;
+}
+
 static void DestroyNsRect(void* aObject, nsIAtom* aPropertyName,
                           void* aPropertyValue, void* aData)
 {
   nsRect* rect = static_cast<nsRect*>(aPropertyValue);
   delete rect;
 }
 
 static void
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -35,17 +35,17 @@ interface nsIQueryContentEventResult;
 interface nsIDOMWindow;
 interface nsIDOMBlob;
 interface nsIDOMFile;
 interface nsIFile;
 interface nsIDOMTouch;
 interface nsIDOMClientRect;
 interface nsIURI;
 
-[scriptable, uuid(6cf3e8f0-fb82-11e1-a21f-0800200c9a66)]
+[scriptable, uuid(90d8e97b-2c61-4c05-9f1c-e568d22f5bdc)]
 interface nsIDOMWindowUtils : nsISupports {
 
   /**
    * Image animation mode of the window. When this attribute's value
    * is changed, the implementation should set all images in the window
    * to the given value. That is, when set to kDontAnimMode, all images
    * will stop animating. The attribute's value must be one of the
    * animationMode values from imgIContainer.
@@ -99,16 +99,26 @@ interface nsIDOMWindowUtils : nsISupport
    * This will trigger reflow.
    *
    * The caller of this method must have UniversalXPConnect
    * privileges.
    */
   void setCSSViewport(in float aWidthPx, in float aHeightPx);
 
   /**
+   * Information retrieved from the <meta name="viewport"> tag.
+   * See nsContentUtils::GetViewportInfo for more information.
+   */
+  void getViewportInfo(in uint32_t aDisplayWidth, in uint32_t aDisplayHeight,
+                       out double aDefaultZoom, out boolean aAllowZoom,
+                       out double aMinZoom, out double aMaxZoom,
+                       out uint32_t aWidth, out uint32_t aHeight,
+                       out boolean aAutoSize);
+
+  /**
    * For any scrollable element, this allows you to override the
    * visible region and draw more than what is visible, which is
    * useful for asynchronous drawing. The "displayport" will be
    * <xPx, yPx, widthPx, heightPx> in units of CSS pixels,
    * regardless of the size of the enclosing container.  This
    * will *not* trigger reflow.
    *
    * For the root scroll area, pass in the root document element.
--- a/dom/ipc/Blob.cpp
+++ b/dom/ipc/Blob.cpp
@@ -26,34 +26,61 @@
 
 using namespace mozilla::dom;
 using namespace mozilla::dom::ipc;
 using namespace mozilla::ipc;
 
 namespace {
 
 class RemoteInputStream : public nsIInputStream,
-                          public nsISeekableStream
+                          public nsISeekableStream,
+                          public nsIIPCSerializableInputStream
 {
   mozilla::Monitor mMonitor;
   nsCOMPtr<nsIDOMBlob> mSourceBlob;
   nsCOMPtr<nsIInputStream> mStream;
   nsCOMPtr<nsISeekableStream> mSeekableStream;
+  ActorFlavorEnum mOrigin;
 
 public:
   NS_DECL_ISUPPORTS
 
-  RemoteInputStream(nsIDOMBlob* aSourceBlob)
-  : mMonitor("RemoteInputStream.mMonitor"), mSourceBlob(aSourceBlob)
+  RemoteInputStream(nsIDOMBlob* aSourceBlob, ActorFlavorEnum aOrigin)
+  : mMonitor("RemoteInputStream.mMonitor"), mSourceBlob(aSourceBlob),
+    mOrigin(aOrigin)
   {
     MOZ_ASSERT(NS_IsMainThread());
     MOZ_ASSERT(aSourceBlob);
   }
 
   void
+  Serialize(InputStreamParams& aParams)
+  {
+    nsCOMPtr<nsIRemoteBlob> remote = do_QueryInterface(mSourceBlob);
+    MOZ_ASSERT(remote);
+
+    if (mOrigin == Parent) {
+      aParams = RemoteInputStreamParams(
+        static_cast<PBlobParent*>(remote->GetPBlob()), nullptr);
+    } else {
+      aParams = RemoteInputStreamParams(
+        nullptr, static_cast<PBlobChild*>(remote->GetPBlob()));
+    }
+  }
+
+  bool
+  Deserialize(const InputStreamParams& aParams)
+  {
+    // See InputStreamUtils.cpp to see how deserialization of a
+    // RemoteInputStream is special-cased.
+    MOZ_NOT_REACHED("RemoteInputStream should never be deserialized");
+    return false;
+  }
+
+  void
   SetStream(nsIInputStream* aStream)
   {
     MOZ_ASSERT(NS_IsMainThread());
     MOZ_ASSERT(aStream);
 
     nsCOMPtr<nsIInputStream> stream = aStream;
     nsCOMPtr<nsISeekableStream> seekableStream = do_QueryInterface(aStream);
 
@@ -229,16 +256,17 @@ private:
   }
 };
 
 NS_IMPL_THREADSAFE_ADDREF(RemoteInputStream)
 NS_IMPL_THREADSAFE_RELEASE(RemoteInputStream)
 
 NS_INTERFACE_MAP_BEGIN(RemoteInputStream)
   NS_INTERFACE_MAP_ENTRY(nsIInputStream)
+  NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableInputStream)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISeekableStream, IsSeekableStream())
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
 NS_INTERFACE_MAP_END
 
 template <ActorFlavorEnum ActorFlavor>
 class InputStreamActor : public BlobTraits<ActorFlavor>::StreamType
 {
   nsRefPtr<RemoteInputStream> mRemoteStream;
@@ -416,17 +444,18 @@ private:
     void
     RunInternal(bool aNotify)
     {
       MOZ_ASSERT(NS_IsMainThread());
       MOZ_ASSERT(mActor);
       MOZ_ASSERT(!mInputStream);
       MOZ_ASSERT(!mDone);
 
-      nsRefPtr<RemoteInputStream> stream = new RemoteInputStream(mSourceBlob);
+      nsRefPtr<RemoteInputStream> stream = new RemoteInputStream(mSourceBlob,
+                                                                 ActorFlavor);
 
       StreamActorType* streamActor = new StreamActorType(stream);
       if (mActor->SendPBlobStreamConstructor(streamActor)) {
         stream.swap(mInputStream);
       }
 
       mActor = nullptr;
 
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -184,16 +184,19 @@ function RadioInterfaceLayer() {
 
   this.callWaitingStatus = null;
 
   // Read the 'ril.radio.disabled' setting in order to start with a known
   // value at boot time.
   let lock = gSettingsService.createLock();
   lock.get("ril.radio.disabled", this);
 
+  // Read preferred network type from the setting DB.
+  lock.get("ril.radio.preferredNetworkType", this);
+
   // Read the APN data form the setting DB.
   lock.get("ril.data.apn", this);
   lock.get("ril.data.user", this);
   lock.get("ril.data.passwd", this);
   lock.get("ril.data.httpProxyHost", this);
   lock.get("ril.data.httpProxyPort", this);
   lock.get("ril.data.roaming_enabled", this);
   lock.get("ril.data.enabled", this);
@@ -465,16 +468,19 @@ RadioInterfaceLayer.prototype = {
         this.handleCancelUSSD(message);
         break;
       case "stkcommand":
         this.handleStkProactiveCommand(message);
         break;
       case "stksessionend":
         ppmm.broadcastAsyncMessage("RIL:StkSessionEnd", null);
         break;
+      case "setPreferredNetworkType":
+        this.handleSetPreferredNetworkType(message);
+        break;
       default:
         throw new Error("Don't know about this message type: " +
                         message.rilMessageType);
     }
   },
 
   _messageManagerByRequest: null,
   saveRequestTarget: function saveRequestTarget(msg) {
@@ -638,16 +644,49 @@ RadioInterfaceLayer.prototype = {
     }
 
     if (!newInfo.batch) {
       ppmm.broadcastAsyncMessage("RIL:DataInfoChanged", dataInfo);
     }
     this.updateRILNetworkInterface();
   },
 
+  _preferredNetworkType: null,
+  setPreferredNetworkType: function setPreferredNetworkType(value) {
+    let networkType = RIL.RIL_PREFERRED_NETWORK_TYPE_TO_GECKO.indexOf(value);
+    if (networkType < 0) {
+      gSettingsService.createLock().set("ril.radio.preferredNetworkType",
+                                        this._preferredNetworkType || RIL.GECKO_PREFERRED_NETWORK_TYPE_DEFAULT,
+                                        false);
+      return;
+    }
+
+    if (networkType == this._preferredNetworkType) {
+      return;
+    }
+
+    this.worker.postMessage({rilMessageType: "setPreferredNetworkType",
+                             networkType: networkType});
+
+    this._ensureRadioState();
+  },
+
+  handleSetPreferredNetworkType: function handleSetPreferredNetworkType(message) {
+    if ((this._preferredNetworkType != null) && !message.success) {
+      gSettingsService.createLock().set("ril.radio.preferredNetworkType",
+                                        this._preferredNetworkType,
+                                        false);
+      return;
+    }
+
+    this._preferredNetworkType = message.networkType;
+    debug("_preferredNetworkType is now " +
+          RIL.RIL_PREFERRED_NETWORK_TYPE_TO_GECKO[this._preferredNetworkType]);
+  },
+
   handleSignalStrengthChange: function handleSignalStrengthChange(message) {
     let voiceInfo = this.rilContext.voice;
     // TODO CDMA, EVDO, LTE, etc. (see bug 726098)
     if (voiceInfo.signalStrength != message.gsmDBM ||
         voiceInfo.relSignalStrength != message.gsmRelative) {
       voiceInfo.signalStrength = message.gsmDBM;
       voiceInfo.relSignalStrength = message.gsmRelative;
       ppmm.broadcastAsyncMessage("RIL:VoiceInfoChanged", voiceInfo);
@@ -699,16 +738,21 @@ RadioInterfaceLayer.prototype = {
   _ensureRadioState: function _ensureRadioState() {
     debug("Reported radio state is " + this.rilContext.radioState +
           ", desired radio enabled state is " + this._radioEnabled);
     if (this._radioEnabled == null) {
       // We haven't read the initial value from the settings DB yet.
       // Wait for that.
       return;
     }
+    if (this._preferredNetworkType == null) {
+      // We haven't read the initial value from the settings DB yet.
+      // Wait for that.
+      return;
+    }
     if (!this._sysMsgListenerReady) {
       // The UI's system app isn't ready yet for us to receive any
       // events (e.g. incoming SMS, etc.). Wait for that.
       return;
     }
     if (this.rilContext.radioState == RIL.GECKO_RADIOSTATE_UNKNOWN) {
       // We haven't received a radio state notification from the RIL
       // yet. Wait for that.
@@ -1198,16 +1242,20 @@ RadioInterfaceLayer.prototype = {
 
   handle: function handle(aName, aResult) {
     switch(aName) {
       case "ril.radio.disabled":
         debug("'ril.radio.disabled' is now " + aResult);
         this._radioEnabled = !aResult;
         this._ensureRadioState();
         break;
+      case "ril.radio.preferredNetworkType":
+        debug("'ril.radio.preferredNetworkType' is now " + aResult);
+        this.setPreferredNetworkType(aResult);
+        break;
       case "ril.data.enabled":
         this._oldRilDataEnabledState = this.dataCallSettings["enabled"];
         // Fall through!
       case "ril.data.roaming_enabled":
       case "ril.data.apn":
       case "ril.data.user":
       case "ril.data.passwd":
       case "ril.data.httpProxyHost":
--- a/dom/system/gonk/ril_consts.js
+++ b/dom/system/gonk/ril_consts.js
@@ -330,28 +330,25 @@ const NETWORK_INFO_OPERATOR = "operator"
 const NETWORK_INFO_NETWORK_SELECTION_MODE = "networkSelectionMode";
 const NETWORK_INFO_MESSAGE_TYPES = [
   NETWORK_INFO_VOICE_REGISTRATION_STATE,
   NETWORK_INFO_DATA_REGISTRATION_STATE,
   NETWORK_INFO_OPERATOR,
   NETWORK_INFO_NETWORK_SELECTION_MODE
 ];
 
-const PREFERRED_NETWORK_TYPE_GSM_WCDMA = 0;
-const PREFERRED_NETWORK_TYPE_GSM_ONLY = 1;
-const PREFERRED_NETWORK_TYPE_WCDMA = 2;
-const PREFERRED_NETWORK_TYPE_GSM_WCDMA_AUTO = 3;
-const PREFERRED_NETWORK_TYPE_CDMA_EVDO_AUTO = 4;
-const PREFERRED_NETWORK_TYPE_CDMA_ONLY = 5;
-const PREFERRED_NETWORK_TYPE_EVDO_ONLY = 6;
-const PREFERRED_NETWORK_TYPE_GSM_WCDMA_CDMA_EVDO_AUTO = 7;
-const PREFERRED_NETWORK_TYPE_LTE_CDMA_EVDO = 8;
-const PREFERRED_NETWORK_TYPE_LTE_GSM_WCDMA = 9;
-const PREFERRED_NETWORK_TYPE_LTE_CMDA_EVDO_GSM_WCDMA = 10;
-const PREFERRED_NETWORK_TYPE_LTE_ONLY = 11;
+const GECKO_PREFERRED_NETWORK_TYPE_WCDMA_GSM = "wcdma/gsm";
+const GECKO_PREFERRED_NETWORK_TYPE_GSM_ONLY = "gsm";
+const GECKO_PREFERRED_NETWORK_TYPE_WCDMA_ONLY = "wcdma";
+const GECKO_PREFERRED_NETWORK_TYPE_DEFAULT = GECKO_PREFERRED_NETWORK_TYPE_WCDMA_GSM;
+const RIL_PREFERRED_NETWORK_TYPE_TO_GECKO = [
+  GECKO_PREFERRED_NETWORK_TYPE_WCDMA_GSM,
+  GECKO_PREFERRED_NETWORK_TYPE_GSM_ONLY,
+  GECKO_PREFERRED_NETWORK_TYPE_WCDMA_ONLY
+];
 
 // Network registration states. See TS 27.007 7.2
 const NETWORK_CREG_STATE_NOT_SEARCHING = 0;
 const NETWORK_CREG_STATE_REGISTERED_HOME = 1;
 const NETWORK_CREG_STATE_SEARCHING = 2;
 const NETWORK_CREG_STATE_DENIED = 3;
 const NETWORK_CREG_STATE_UNKNOWN = 4;
 const NETWORK_CREG_STATE_REGISTERED_ROAMING = 5;
--- a/dom/system/gonk/ril_worker.js
+++ b/dom/system/gonk/ril_worker.js
@@ -599,16 +599,22 @@ let RIL = {
    */
   _receivedSmsSegmentsMap: {},
 
   /**
    * Outgoing messages waiting for SMS-STATUS-REPORT.
    */
   _pendingSentSmsMap: {},
 
+  /**
+   * Index of the RIL_PREFERRED_NETWORK_TYPE_TO_GECKO. Its value should be
+   * preserved over rild reset.
+   */
+  preferredNetworkType: null,
+
   initRILState: function initRILState() {
     /**
      * One of the RADIO_STATE_* constants.
      */
     this.radioState = GECKO_RADIOSTATE_UNAVAILABLE;
     this._isInitialRadioState = true;
 
     /**
@@ -1676,26 +1682,43 @@ let RIL = {
 
   getOperator: function getOperator() {
     Buf.simpleRequest(REQUEST_OPERATOR);
   },
 
   /**
    * Set the preferred network type.
    *
-   * @param network_type
-   *        The network type. One of the PREFERRED_NETWORK_TYPE_* constants.
+   * @param options An object contains a valid index of
+   *                RIL_PREFERRED_NETWORK_TYPE_TO_GECKO as its `networkType`
+   *                attribute, or undefined to set current preferred network
+   *                type.
    */
-  setPreferredNetworkType: function setPreferredNetworkType(network_type) {
-    Buf.newParcel(REQUEST_SET_PREFERRED_NETWORK_TYPE);
-    Buf.writeUint32(network_type);
+  setPreferredNetworkType: function setPreferredNetworkType(options) {
+    if (options) {
+      this.preferredNetworkType = options.networkType;
+    }
+    if (this.preferredNetworkType == null) {
+      return;
+    }
+
+    Buf.newParcel(REQUEST_SET_PREFERRED_NETWORK_TYPE, options);
+    Buf.writeUint32(1);
+    Buf.writeUint32(this.preferredNetworkType);
     Buf.sendParcel();
   },
 
   /**
+   * Get the preferred network type.
+   */
+  getPreferredNetworkType: function getPreferredNetworkType() {
+    Buf.simpleRequest(REQUEST_GET_PREFERRED_NETWORK_TYPE);
+  },
+
+  /**
    * Request various states about the network.
    */
   requestNetworkInfo: function requestNetworkInfo() {
     if (this._processingNetworkInfo) {
       if (DEBUG) debug("Network info requested, but we're already requesting network info.");
       return;
     }
 
@@ -3994,18 +4017,43 @@ RIL[REQUEST_DELETE_SMS_ON_SIM] = null;
 RIL[REQUEST_SET_BAND_MODE] = null;
 RIL[REQUEST_QUERY_AVAILABLE_BAND_MODE] = null;
 RIL[REQUEST_STK_GET_PROFILE] = null;
 RIL[REQUEST_STK_SET_PROFILE] = null;
 RIL[REQUEST_STK_SEND_ENVELOPE_COMMAND] = null;
 RIL[REQUEST_STK_SEND_TERMINAL_RESPONSE] = null;
 RIL[REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM] = null;
 RIL[REQUEST_EXPLICIT_CALL_TRANSFER] = null;
-RIL[REQUEST_SET_PREFERRED_NETWORK_TYPE] = null;
-RIL[REQUEST_GET_PREFERRED_NETWORK_TYPE] = null;
+RIL[REQUEST_SET_PREFERRED_NETWORK_TYPE] = function REQUEST_SET_PREFERRED_NETWORK_TYPE(length, options) {
+  if (options.networkType == null) {
+    // The request was made by ril_worker itself automatically. Don't report.
+    return;
+  }
+
+  this.sendDOMMessage({
+    rilMessageType: "setPreferredNetworkType",
+    networkType: options.networkType,
+    success: options.rilRequestError == ERROR_SUCCESS
+  });
+};
+RIL[REQUEST_GET_PREFERRED_NETWORK_TYPE] = function REQUEST_GET_PREFERRED_NETWORK_TYPE(length, options) {
+  let networkType;
+  if (!options.rilRequestError) {
+    networkType = RIL_PREFERRED_NETWORK_TYPE_TO_GECKO.indexOf(GECKO_PREFERRED_NETWORK_TYPE_DEFAULT);
+    if (Buf.readUint32()) {
+      this.preferredNetworkType = networkType = Buf.readUint32();
+    }
+  }
+
+  this.sendDOMMessage({
+    rilMessageType: "getPreferredNetworkType",
+    networkType: networkType,
+    success: options.rilRequestError == ERROR_SUCCESS
+  });
+};
 RIL[REQUEST_GET_NEIGHBORING_CELL_IDS] = null;
 RIL[REQUEST_SET_LOCATION_UPDATES] = null;
 RIL[REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE] = null;
 RIL[REQUEST_CDMA_SET_ROAMING_PREFERENCE] = null;
 RIL[REQUEST_CDMA_QUERY_ROAMING_PREFERENCE] = null;
 RIL[REQUEST_SET_TTY_MODE] = null;
 RIL[REQUEST_QUERY_TTY_MODE] = null;
 RIL[REQUEST_CDMA_SET_PREFERRED_VOICE_PRIVACY_MODE] = null;
@@ -4228,16 +4276,18 @@ RIL[UNSOLICITED_RIL_CONNECTED] = functio
   let version = Buf.readUint32List()[0];
   RILQUIRKS_V5_LEGACY = (version < 5);
   if (DEBUG) {
     debug("Detected RIL version " + version);
     debug("RILQUIRKS_V5_LEGACY is " + RILQUIRKS_V5_LEGACY);
   }
 
   this.initRILState();
+
+  this.setPreferredNetworkType();
 };
 
 /**
  * This object exposes the functionality to parse and serialize PDU strings
  *
  * A PDU is a string containing a series of hexadecimally encoded octets
  * or nibble-swapped binary-coded decimals (BCDs). It contains not only the
  * message text but information about the sender, the SMS service center,
--- a/hal/Makefile.in
+++ b/hal/Makefile.in
@@ -78,17 +78,17 @@ CPPSRCS += \
   WindowsSensor.cpp \
   FallbackVibration.cpp \
   FallbackScreenConfiguration.cpp \
   FallbackPower.cpp \
   FallbackAlarm.cpp \
   $(NULL)
 else ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 CPPSRCS += \
-  FallbackBattery.cpp \
+  CocoaBattery.cpp \
   FallbackVibration.cpp \
   FallbackPower.cpp \
   FallbackScreenConfiguration.cpp \
   FallbackAlarm.cpp \
   $(NULL)
 CMMSRCS += \
   CocoaSensor.mm \
   smslib.mm \
new file mode 100644
--- /dev/null
+++ b/hal/cocoa/CocoaBattery.cpp
@@ -0,0 +1,291 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : */
+/* 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/. */
+
+#import <CoreFoundation/CoreFoundation.h>
+#import <IOKit/ps/IOPowerSources.h>
+#import <IOKit/ps/IOPSKeys.h>
+
+#include <mozilla/Hal.h>
+#include <mozilla/dom/battery/Constants.h>
+#include <mozilla/Services.h>
+
+#include <nsObserverService.h>
+
+#include <dlfcn.h>
+
+#define IOKIT_FRAMEWORK_PATH "/System/Library/Frameworks/IOKit.framework/IOKit"
+
+#ifndef kIOPSTimeRemainingUnknown
+  #define kIOPSTimeRemainingUnknown ((CFTimeInterval)-1.0)
+#endif
+#ifndef kIOPSTimeRemainingUnlimited
+  #define kIOPSTimeRemainingUnlimited ((CFTimeInterval)-2.0)
+#endif
+
+using namespace mozilla::dom::battery;
+
+namespace mozilla {
+namespace hal_impl {
+
+typedef CFTimeInterval (*IOPSGetTimeRemainingEstimateFunc)(void);
+
+class MacPowerInformationService
+{
+public:
+  static MacPowerInformationService* GetInstance();
+  static void Shutdown();
+
+  void BeginListening();
+  void StopListening();
+
+  static void HandleChange(void *aContext);
+
+  ~MacPowerInformationService();
+
+private:
+  MacPowerInformationService();
+
+  // The reference to the runloop that is notified of power changes.
+  CFRunLoopSourceRef mRunLoopSource;
+
+  double mLevel;
+  bool mCharging;
+  double mRemainingTime;
+  bool mShouldNotify;
+
+  friend void GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo);
+
+  static MacPowerInformationService* sInstance;
+
+  static void* sIOKitFramework;
+  static IOPSGetTimeRemainingEstimateFunc sIOPSGetTimeRemainingEstimate;
+};
+
+void* MacPowerInformationService::sIOKitFramework;
+IOPSGetTimeRemainingEstimateFunc MacPowerInformationService::sIOPSGetTimeRemainingEstimate;
+
+/*
+ * Implementation of mozilla::hal_impl::EnableBatteryNotifications,
+ *                   mozilla::hal_impl::DisableBatteryNotifications,
+ *               and mozilla::hal_impl::GetCurrentBatteryInformation.
+ */
+
+void
+EnableBatteryNotifications()
+{
+  MacPowerInformationService::GetInstance()->BeginListening();
+}
+
+void
+DisableBatteryNotifications()
+{
+  MacPowerInformationService::GetInstance()->StopListening();
+}
+
+void
+GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo)
+{
+  MacPowerInformationService* powerService = MacPowerInformationService::GetInstance();
+
+  aBatteryInfo->level() = powerService->mLevel;
+  aBatteryInfo->charging() = powerService->mCharging;
+  aBatteryInfo->remainingTime() = powerService->mRemainingTime;
+}
+
+/*
+ * Following is the implementation of MacPowerInformationService.
+ */
+
+MacPowerInformationService* MacPowerInformationService::sInstance = nullptr;
+
+namespace {
+struct SingletonDestroyer MOZ_FINAL : public nsIObserver
+{
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+};
+
+NS_IMPL_ISUPPORTS1(SingletonDestroyer, nsIObserver)
+
+NS_IMETHODIMP
+SingletonDestroyer::Observe(nsISupports*, const char* aTopic, const PRUnichar*)
+{
+  MOZ_ASSERT(!strcmp(aTopic, "xpcom-shutdown"));
+  MacPowerInformationService::Shutdown();
+  return NS_OK;
+}
+} // anonymous namespace
+
+/* static */ MacPowerInformationService*
+MacPowerInformationService::GetInstance()
+{
+  if (sInstance) {
+    return sInstance;
+  }
+
+  sInstance = new MacPowerInformationService();
+
+  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+  if (obs) {
+    obs->AddObserver(new SingletonDestroyer(), "xpcom-shutdown", false);
+  }
+
+  return sInstance;
+}
+
+void
+MacPowerInformationService::Shutdown()
+{
+  delete sInstance;
+  sInstance = nullptr;
+}
+
+MacPowerInformationService::MacPowerInformationService()
+  : mRunLoopSource(nullptr)
+  , mLevel(kDefaultLevel)
+  , mCharging(kDefaultCharging)
+  , mRemainingTime(kDefaultRemainingTime)
+  , mShouldNotify(false)
+{
+  // IOPSGetTimeRemainingEstimate (and the related constants) are only available
+  // on 10.7, so we test for their presence at runtime.
+  sIOKitFramework = dlopen(IOKIT_FRAMEWORK_PATH, RTLD_LAZY | RTLD_LOCAL);
+  if (sIOKitFramework) {
+    sIOPSGetTimeRemainingEstimate =
+      (IOPSGetTimeRemainingEstimateFunc)dlsym(sIOKitFramework, "IOPSGetTimeRemainingEstimate");
+  } else {
+    sIOPSGetTimeRemainingEstimate = nullptr;
+  }
+}
+
+MacPowerInformationService::~MacPowerInformationService()
+{
+  MOZ_ASSERT(!mRunLoopSource,
+               "The observers have not been correctly removed! "
+               "(StopListening should have been called)");
+
+  if (sIOKitFramework) {
+    dlclose(sIOKitFramework);
+  }
+}
+
+void
+MacPowerInformationService::BeginListening()
+{
+  // Set ourselves up to be notified about changes.
+  MOZ_ASSERT(!mRunLoopSource, "IOPS Notification Loop Source already set up. "
+                              "(StopListening should have been called)");
+
+  mRunLoopSource = ::IOPSNotificationCreateRunLoopSource(HandleChange, this);
+  if (mRunLoopSource) {
+    ::CFRunLoopAddSource(::CFRunLoopGetCurrent(), mRunLoopSource,
+                         kCFRunLoopDefaultMode);
+
+    // Invoke our callback now so we have data if GetCurrentBatteryInformation is
+    // called before a change happens.
+    HandleChange(this);
+    mShouldNotify = true;
+  }
+}
+
+void
+MacPowerInformationService::StopListening()
+{
+  MOZ_ASSERT(mRunLoopSource, "IOPS Notification Loop Source not set up. "
+                             "(StopListening without BeginListening)");
+
+  ::CFRunLoopRemoveSource(::CFRunLoopGetCurrent(), mRunLoopSource,
+                          kCFRunLoopDefaultMode);
+  mRunLoopSource = nullptr;
+}
+
+void
+MacPowerInformationService::HandleChange(void* aContext) {
+  MacPowerInformationService* power =
+    static_cast<MacPowerInformationService*>(aContext);
+
+  CFTypeRef data = ::IOPSCopyPowerSourcesInfo();
+  if (!data) {
+    ::CFRelease(data);
+    return;
+  }
+
+  // Get the list of power sources.
+  CFArrayRef list = ::IOPSCopyPowerSourcesList(data);
+  if (!list) {
+    ::CFRelease(list);
+    return;
+  }
+
+  // Default values. These will be used if there are 0 sources or we can't find
+  // better information.
+  double level = kDefaultLevel;
+  double charging = kDefaultCharging;
+  double remainingTime = kDefaultRemainingTime;
+
+  // Look for the first battery power source to give us the information we need.
+  // Usually there's only 1 available, depending on current power source.
+  for (CFIndex i = 0; i < ::CFArrayGetCount(list); ++i) {
+    CFTypeRef source = ::CFArrayGetValueAtIndex(list, i);
+    CFDictionaryRef currPowerSourceDesc = ::IOPSGetPowerSourceDescription(data, source);
+    if (!currPowerSourceDesc) {
+      continue;
+    }
+
+    if (sIOPSGetTimeRemainingEstimate) {
+      // See if we can get a time estimate.
+      CFTimeInterval estimate = sIOPSGetTimeRemainingEstimate();
+      if (estimate == kIOPSTimeRemainingUnlimited || estimate == kIOPSTimeRemainingUnknown) {
+        remainingTime = kUnknownRemainingTime;
+      } else {
+        remainingTime = estimate;
+      }
+    }
+
+    // Get a battery level estimate. This key is required.
+    int currentCapacity = 0;
+    const void* cfRef = ::CFDictionaryGetValue(currPowerSourceDesc, CFSTR(kIOPSCurrentCapacityKey));
+    ::CFNumberGetValue((CFNumberRef)cfRef, kCFNumberSInt32Type, &currentCapacity);
+
+    // This key is also required.
+    int maxCapacity = 0;
+    cfRef = ::CFDictionaryGetValue(currPowerSourceDesc, CFSTR(kIOPSMaxCapacityKey));
+    ::CFNumberGetValue((CFNumberRef)cfRef, kCFNumberSInt32Type, &maxCapacity);
+
+    if (maxCapacity > 0) {
+      level = static_cast<double>(currentCapacity)/static_cast<double>(maxCapacity);
+    }
+
+    // Find out if we're charging.
+    // This key is optional, we fallback to kDefaultCharging if the current power
+    // source doesn't have that info.
+    if(::CFDictionaryGetValueIfPresent(currPowerSourceDesc, CFSTR(kIOPSIsChargingKey), &cfRef)) {
+      charging = ::CFBooleanGetValue((CFBooleanRef)cfRef);
+    }
+
+    break;
+  }
+
+  bool isNewData = level != power->mLevel || charging != power->mCharging ||
+                   remainingTime != power->mRemainingTime;
+
+  power->mRemainingTime = remainingTime;
+  power->mCharging = charging;
+  power->mLevel = level;
+
+  // Notify the observers if stuff changed.
+  if (power->mShouldNotify && isNewData) {
+    hal::NotifyBatteryChange(hal::BatteryInformation(power->mLevel,
+                                                     power->mCharging,
+                                                     power->mRemainingTime));
+  }
+
+  ::CFRelease(data);
+  ::CFRelease(list);
+}
+
+} // namespace hal_impl
+} // namespace mozilla
--- a/image/decoders/nsGIFDecoder2.cpp
+++ b/image/decoders/nsGIFDecoder2.cpp
@@ -73,16 +73,17 @@ namespace image {
 //////////////////////////////////////////////////////////////////////
 // GIF Decoder Implementation
 
 nsGIFDecoder2::nsGIFDecoder2(RasterImage &aImage, imgIDecoderObserver* aObserver)
   : Decoder(aImage, aObserver)
   , mCurrentRow(-1)
   , mLastFlushedRow(-1)
   , mImageData(nullptr)
+  , mColormap(nullptr)
   , mOldColor(0)
   , mCurrentFrame(-1)
   , mCurrentPass(0)
   , mLastFlushedPass(0)
   , mGIFOpen(false)
   , mSawTransparency(false)
 {
   // Clear out the structure, excluding the arrays
@@ -182,27 +183,37 @@ nsresult nsGIFDecoder2::BeginImageFrame(
   // and include transparency to allow for optimization of opaque images
   if (mGIFStruct.images_decoded) {
     // Image data is stored with original depth and palette
     rv = mImage.EnsureFrame(mGIFStruct.images_decoded,
                             mGIFStruct.x_offset, mGIFStruct.y_offset,
                             mGIFStruct.width, mGIFStruct.height,
                             format, aDepth, &mImageData, &imageDataLength,
                             &mColormap, &mColormapSize);
+
+    // While EnsureFrame can reuse frames, we unconditionally increment
+    // mGIFStruct.images_decoded when we're done with a frame, so we both can
+    // and need to zero out the colormap and image data after every call to
+    // EnsureFrame.
+    if (NS_SUCCEEDED(rv) && mColormap) {
+      memset(mColormap, 0, mColormapSize);
+    }
   } else {
     // Regardless of depth of input, image is decoded into 24bit RGB
     rv = mImage.EnsureFrame(mGIFStruct.images_decoded,
                             mGIFStruct.x_offset, mGIFStruct.y_offset,
                             mGIFStruct.width, mGIFStruct.height,
                             format, &mImageData, &imageDataLength);
   }
 
   if (NS_FAILED(rv))
     return rv;
 
+  memset(mImageData, 0, imageDataLength);
+
   mImage.SetFrameDisposalMethod(mGIFStruct.images_decoded,
                                 mGIFStruct.disposal_method);
 
   // Tell the superclass we're starting a frame
   PostFrameStart();
 
   if (!mGIFStruct.images_decoded) {
     // Send a onetime invalidation for the first frame if it has a y-axis offset. 
@@ -757,31 +768,55 @@ nsGIFDecoder2::WriteInternal(const char 
           } else {
             /* No images decoded, there is nothing to display. */
             mGIFStruct.state = gif_error;
           }
       }
       break;
 
     case gif_extension:
+      // Comment taken directly from WebKit's GIFImageReader.cpp.
+      //
+      // The GIF spec mandates lengths for three of the extensions below.
+      // However, it's possible for GIFs in the wild to deviate. For example,
+      // some GIFs that embed ICC color profiles using gif_application_extension
+      // violate the spec and treat this extension block like a sort of
+      // "extension + data" block, giving a size greater than 11 and filling the
+      // remaining bytes with data (then following with more data blocks as
+      // needed), instead of placing a true data block just after the 11 byte
+      // extension block.
+      //
+      // Accordingly, if the specified length is larger than the required value,
+      // we use it. If it's smaller, then we enforce the spec value, because the
+      // parsers for these extensions expect to have the specified number of
+      // bytes available, and if we don't ensure that, they could read off the
+      // end of the heap buffer. (In this case, it's likely the GIF is corrupt
+      // and we'll soon fail to decode anyway.)
       mGIFStruct.bytes_to_consume = q[1];
       if (mGIFStruct.bytes_to_consume) {
         switch (*q) {
         case GIF_GRAPHIC_CONTROL_LABEL:
           mGIFStruct.state = gif_control_extension;
+          mGIFStruct.bytes_to_consume = NS_MAX(mGIFStruct.bytes_to_consume, 4u);
           break;
-  
+
         case GIF_APPLICATION_EXTENSION_LABEL:
           mGIFStruct.state = gif_application_extension;
+          mGIFStruct.bytes_to_consume = NS_MAX(mGIFStruct.bytes_to_consume, 11u);
           break;
-  
+
+        case GIF_PLAIN_TEXT_LABEL:
+          mGIFStruct.state = gif_skip_block;
+          mGIFStruct.bytes_to_consume = NS_MAX(mGIFStruct.bytes_to_consume, 12u);
+          break;
+
         case GIF_COMMENT_LABEL:
           mGIFStruct.state = gif_consume_comment;
           break;
-  
+
         default:
           mGIFStruct.state = gif_skip_block;
         }
       } else {
         GETN(1, gif_image_start);
       }
       break;
 
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -1588,17 +1588,17 @@ RasterImage::SourceDataComplete()
   if (CanDiscard()) {
     nsresult rv = DiscardTracker::Reset(&mDiscardTrackerNode);
     CONTAINER_ENSURE_SUCCESS(rv);
   }
   return NS_OK;
 }
 
 nsresult
-RasterImage::NewSourceData(const char* aMimeType)
+RasterImage::NewSourceData()
 {
   nsresult rv;
 
   if (mError)
     return NS_ERROR_FAILURE;
 
   // The source data should be complete before calling this
   NS_ABORT_IF_FALSE(mHasSourceData,
@@ -1623,18 +1623,16 @@ RasterImage::NewSourceData(const char* a
 
   // The decoder was shut down and we didn't flag an error, so we should be decoded
   NS_ABORT_IF_FALSE(mDecoded, "Should be decoded in NewSourceData");
 
   // Reset some flags
   mDecoded = false;
   mHasSourceData = false;
 
-  mSourceDataMimeType.Assign(aMimeType);
-
   // We're decode-on-load here. Open up a new decoder just like what happens when
   // we call Init() for decode-on-load images.
   rv = InitDecoder(/* aDoSizeDecode = */ false);
   CONTAINER_ENSURE_SUCCESS(rv);
 
   return NS_OK;
 }
 
--- a/image/src/RasterImage.h
+++ b/image/src/RasterImage.h
@@ -264,17 +264,17 @@ public:
    * value. Should this just return void?
    */
   nsresult AddSourceData(const char *aBuffer, uint32_t aCount);
 
   /* Called after the all the source data has been added with addSourceData. */
   nsresult SourceDataComplete();
 
   /* Called for multipart images when there's a new source image to add. */
-  nsresult NewSourceData(const char *aMimeType);
+  nsresult NewSourceData();
 
   /**
    * A hint of the number of bytes of source data that the image contains. If
    * called early on, this can help reduce copying and reallocations by
    * appropriately preallocating the source data buffer.
    *
    * We take this approach rather than having the source data management code do
    * something more complicated (like chunklisting) because HTTP is by far the
--- a/image/src/imgRequest.cpp
+++ b/image/src/imgRequest.cpp
@@ -74,21 +74,28 @@ PRLogModuleInfo *gImgLog = PR_NewLogModu
 NS_IMPL_ISUPPORTS8(imgRequest,
                    imgIDecoderObserver, imgIContainerObserver,
                    nsIStreamListener, nsIRequestObserver,
                    nsISupportsWeakReference,
                    nsIChannelEventSink,
                    nsIInterfaceRequestor,
                    nsIAsyncVerifyRedirectCallback)
 
-imgRequest::imgRequest(imgLoader* aLoader) :
-  mLoader(aLoader), mValidator(nullptr), mImageSniffers("image-sniffing-services"),
-  mInnerWindowId(0), mCORSMode(imgIRequest::CORS_NONE),
-  mDecodeRequested(false), mIsMultiPartChannel(false), mGotData(false),
-  mIsInCache(false), mBlockingOnload(false)
+imgRequest::imgRequest(imgLoader* aLoader)
+ : mLoader(aLoader)
+ , mValidator(nullptr)
+ , mImageSniffers("image-sniffing-services")
+ , mInnerWindowId(0)
+ , mCORSMode(imgIRequest::CORS_NONE)
+ , mDecodeRequested(false)
+ , mIsMultiPartChannel(false)
+ , mGotData(false)
+ , mIsInCache(false)
+ , mBlockingOnload(false)
+ , mResniffMimeType(false)
 {
   // Register our pref observers if we haven't yet.
   if (NS_UNLIKELY(!gInitializedPrefCaches)) {
     InitPrefCaches();
   }
 }
 
 imgRequest::~imgRequest()
@@ -782,32 +789,28 @@ NS_IMETHODIMP imgRequest::OnStartRequest
   nsCOMPtr<nsIMultiPartChannel> mpchan(do_QueryInterface(aRequest));
   if (mpchan)
       mIsMultiPartChannel = true;
 
   // If we're not multipart, we shouldn't have an image yet
   NS_ABORT_IF_FALSE(mIsMultiPartChannel || !mImage,
                     "Already have an image for non-multipart request");
 
-  // If we're multipart, and our image is initialized, fix things up for another round
+  // If we're multipart and about to load another image, signal so we can
+  // detect the mime type in OnDataAvailable.
   if (mIsMultiPartChannel && mImage) {
-    // Update the content type for this new part
-    nsCOMPtr<nsIChannel> partChan(do_QueryInterface(aRequest));
-    partChan->GetContentType(mContentType);
-    if (mContentType.EqualsLiteral(SVG_MIMETYPE) ||
-        mImage->GetType() == imgIContainer::TYPE_VECTOR) {
-      // mImage won't be reusable due to format change or a new SVG part
-      // Reset the tracker and forget that we have data for OnDataAvailable to
-      // treat its next call as a fresh image.
-      mStatusTracker = new imgStatusTracker(nullptr);
-      mGotData = false;
-    } else if (mImage->GetType() == imgIContainer::TYPE_RASTER) {
-      // Inform the RasterImage that we have new source data
-      static_cast<RasterImage*>(mImage.get())->NewSourceData(mContentType.get());
-    }
+    mResniffMimeType = true;
+    if (mImage->GetType() == imgIContainer::TYPE_RASTER) {
+        // Tell the RasterImage to reinitialize itself. We have to do this in
+        // OnStartRequest so that its state machine is always in a consistent
+        // state.
+        // Note that if our MIME type changes, mImage will be replaced with a
+        // new object.
+        static_cast<RasterImage*>(mImage.get())->NewSourceData();
+      }
   }
 
   /*
    * If mRequest is null here, then we need to set it so that we'll be able to
    * cancel it if our Cancel() method is called.  Note that this can only
    * happen for multipart channels.  We could simply not null out mRequest for
    * non-last parts, if GetIsLastPart() were reliable, but it's not.  See
    * https://bugzilla.mozilla.org/show_bug.cgi?id=339610
@@ -930,16 +933,22 @@ NS_IMETHODIMP imgRequest::OnStopRequest(
   while (srIter.HasMore()) {
     statusTracker.SendStopRequest(srIter.GetNext(), lastPart, status);
   }
 
   mTimedChannel = nullptr;
   return NS_OK;
 }
 
+struct mimetype_closure
+{
+  imgRequest* request;
+  nsACString* newType;
+};
+
 /* prototype for these defined below */
 static NS_METHOD sniff_mimetype_callback(nsIInputStream* in, void* closure, const char* fromRawSegment,
                                          uint32_t toOffset, uint32_t count, uint32_t *writeCount);
 
 /** nsIStreamListener methods **/
 
 /* void onDataAvailable (in nsIRequest request, in nsISupports ctxt, in nsIInputStream inStr, in unsigned long long sourceOffset, in unsigned long count); */
 NS_IMETHODIMP
@@ -948,251 +957,275 @@ imgRequest::OnDataAvailable(nsIRequest *
                             uint32_t count)
 {
   LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequest::OnDataAvailable", "count", count);
 
   NS_ASSERTION(aRequest, "imgRequest::OnDataAvailable -- no request!");
 
   nsresult rv;
 
-  uint16_t imageType;
-  if (mGotData) {
-    imageType = mImage->GetType();
-  } else {
+  if (!mGotData || mResniffMimeType) {
     LOG_SCOPE(gImgLog, "imgRequest::OnDataAvailable |First time through... finding mimetype|");
 
     mGotData = true;
 
+    mimetype_closure closure;
+    nsAutoCString newType;
+    closure.request = this;
+    closure.newType = &newType;
+
     /* look at the first few bytes and see if we can tell what the data is from that
      * since servers tend to lie. :(
      */
     uint32_t out;
-    inStr->ReadSegments(sniff_mimetype_callback, this, count, &out);
+    inStr->ReadSegments(sniff_mimetype_callback, &closure, count, &out);
 
 #ifdef DEBUG
     /* NS_WARNING if the content type from the channel isn't the same if the sniffing */
 #endif
 
     nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
-    if (mContentType.IsEmpty()) {
+    if (newType.IsEmpty()) {
       LOG_SCOPE(gImgLog, "imgRequest::OnDataAvailable |sniffing of mimetype failed|");
 
       rv = NS_ERROR_FAILURE;
       if (chan) {
-        rv = chan->GetContentType(mContentType);
+        rv = chan->GetContentType(newType);
       }
 
       if (NS_FAILED(rv)) {
         PR_LOG(gImgLog, PR_LOG_ERROR,
                ("[this=%p] imgRequest::OnDataAvailable -- Content type unavailable from the channel\n",
                 this));
 
         this->Cancel(NS_IMAGELIB_ERROR_FAILURE);
 
         return NS_BINDING_ABORTED;
       }
 
       LOG_MSG(gImgLog, "imgRequest::OnDataAvailable", "Got content type from the channel");
     }
 
-    /* now we have mimetype, so we can infer the image type that we want */
-    if (mContentType.EqualsLiteral(SVG_MIMETYPE)) {
-      mImage = new VectorImage(mStatusTracker.forget());
-    } else {
-      mImage = new RasterImage(mStatusTracker.forget());
-    }
-    mImage->SetInnerWindowID(mInnerWindowId);
-    imageType = mImage->GetType();
+    // If we're a regular image and this is the first call to OnDataAvailable,
+    // this will always be true. If we've resniffed our MIME type (i.e. we're a
+    // multipart/x-mixed-replace image), we have to be able to switch our image
+    // type and decoder.
+    // We always reinitialize for SVGs, because they have no way of
+    // reinitializing themselves.
+    if (mContentType != newType || newType.EqualsLiteral(SVG_MIMETYPE)) {
+      mContentType = newType;
 
-    // Notify any imgRequestProxys that are observing us that we have an Image.
-    nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
-    while (iter.HasMore()) {
-      iter.GetNext()->SetImage(mImage);
-    }
+      // If we've resniffed our MIME type and it changed, we need to create a
+      // new status tracker to give to the image, because we don't have one of
+      // our own any more.
+      if (mResniffMimeType) {
+        NS_ABORT_IF_FALSE(mIsMultiPartChannel, "Resniffing a non-multipart image");
+        mStatusTracker = new imgStatusTracker(nullptr);
+      }
 
-    /* set our mimetype as a property */
-    nsCOMPtr<nsISupportsCString> contentType(do_CreateInstance("@mozilla.org/supports-cstring;1"));
-    if (contentType) {
-      contentType->SetData(mContentType);
-      mProperties->Set("type", contentType);
-    }
+      mResniffMimeType = false;
+
+      /* now we have mimetype, so we can infer the image type that we want */
+      if (mContentType.EqualsLiteral(SVG_MIMETYPE)) {
+        mImage = new VectorImage(mStatusTracker.forget());
+      } else {
+        mImage = new RasterImage(mStatusTracker.forget());
+      }
+      mImage->SetInnerWindowID(mInnerWindowId);
 
-    /* set our content disposition as a property */
-    nsAutoCString disposition;
-    if (chan) {
-      chan->GetContentDispositionHeader(disposition);
-    }
-    if (!disposition.IsEmpty()) {
-      nsCOMPtr<nsISupportsCString> contentDisposition(do_CreateInstance("@mozilla.org/supports-cstring;1"));
-      if (contentDisposition) {
-        contentDisposition->SetData(disposition);
-        mProperties->Set("content-disposition", contentDisposition);
+      // Notify any imgRequestProxys that are observing us that we have an Image.
+      nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
+      while (iter.HasMore()) {
+        iter.GetNext()->SetImage(mImage);
       }
-    }
 
-    LOG_MSG_WITH_PARAM(gImgLog, "imgRequest::OnDataAvailable", "content type", mContentType.get());
+      /* set our mimetype as a property */
+      nsCOMPtr<nsISupportsCString> contentType(do_CreateInstance("@mozilla.org/supports-cstring;1"));
+      if (contentType) {
+        contentType->SetData(mContentType);
+        mProperties->Set("type", contentType);
+      }
 
-    //
-    // Figure out our Image initialization flags
-    //
+      /* set our content disposition as a property */
+      nsAutoCString disposition;
+      if (chan) {
+        chan->GetContentDispositionHeader(disposition);
+      }
+      if (!disposition.IsEmpty()) {
+        nsCOMPtr<nsISupportsCString> contentDisposition(do_CreateInstance("@mozilla.org/supports-cstring;1"));
+        if (contentDisposition) {
+          contentDisposition->SetData(disposition);
+          mProperties->Set("content-disposition", contentDisposition);
+        }
+      }
 
-    // We default to the static globals
-    bool isDiscardable = gDiscardable;
-    bool doDecodeOnDraw = gDecodeOnDraw;
+      LOG_MSG_WITH_PARAM(gImgLog, "imgRequest::OnDataAvailable", "content type", mContentType.get());
 
-    // We want UI to be as snappy as possible and not to flicker. Disable discarding
-    // and decode-on-draw for chrome URLS
-    bool isChrome = false;
-    rv = mURI->SchemeIs("chrome", &isChrome);
-    if (NS_SUCCEEDED(rv) && isChrome)
-      isDiscardable = doDecodeOnDraw = false;
+      //
+      // Figure out our Image initialization flags
+      //
+
+      // We default to the static globals
+      bool isDiscardable = gDiscardable;
+      bool doDecodeOnDraw = gDecodeOnDraw;
 
-    // We don't want resources like the "loading" icon to be discardable or
-    // decode-on-draw either.
-    bool isResource = false;
-    rv = mURI->SchemeIs("resource", &isResource);
-    if (NS_SUCCEEDED(rv) && isResource)
-      isDiscardable = doDecodeOnDraw = false;
+      // We want UI to be as snappy as possible and not to flicker. Disable discarding
+      // and decode-on-draw for chrome URLS
+      bool isChrome = false;
+      rv = mURI->SchemeIs("chrome", &isChrome);
+      if (NS_SUCCEEDED(rv) && isChrome)
+        isDiscardable = doDecodeOnDraw = false;
 
-    // For multipart/x-mixed-replace, we basically want a direct channel to the
-    // decoder. Disable both for this case as well.
-    if (mIsMultiPartChannel)
-      isDiscardable = doDecodeOnDraw = false;
+      // We don't want resources like the "loading" icon to be discardable or
+      // decode-on-draw either.
+      bool isResource = false;
+      rv = mURI->SchemeIs("resource", &isResource);
+      if (NS_SUCCEEDED(rv) && isResource)
+        isDiscardable = doDecodeOnDraw = false;
 
-    // We have all the information we need
-    uint32_t imageFlags = Image::INIT_FLAG_NONE;
-    if (isDiscardable)
-      imageFlags |= Image::INIT_FLAG_DISCARDABLE;
-    if (doDecodeOnDraw)
-      imageFlags |= Image::INIT_FLAG_DECODE_ON_DRAW;
-    if (mIsMultiPartChannel)
-      imageFlags |= Image::INIT_FLAG_MULTIPART;
+      // For multipart/x-mixed-replace, we basically want a direct channel to the
+      // decoder. Disable both for this case as well.
+      if (mIsMultiPartChannel)
+        isDiscardable = doDecodeOnDraw = false;
 
-    // Get our URI string
-    nsAutoCString uriString;
-    rv = mURI->GetSpec(uriString);
-    if (NS_FAILED(rv))
-      uriString.Assign("<unknown image URI>");
+      // We have all the information we need
+      uint32_t imageFlags = Image::INIT_FLAG_NONE;
+      if (isDiscardable)
+        imageFlags |= Image::INIT_FLAG_DISCARDABLE;
+      if (doDecodeOnDraw)
+        imageFlags |= Image::INIT_FLAG_DECODE_ON_DRAW;
+      if (mIsMultiPartChannel)
+        imageFlags |= Image::INIT_FLAG_MULTIPART;
+
+      // Get our URI string
+      nsAutoCString uriString;
+      rv = mURI->GetSpec(uriString);
+      if (NS_FAILED(rv))
+        uriString.Assign("<unknown image URI>");
 
-    // Initialize the image that we created above. For RasterImages, this
-    // instantiates a decoder behind the scenes, so if we don't have a decoder
-    // for this mimetype we'll find out about it here.
-    rv = mImage->Init(this, mContentType.get(), uriString.get(), imageFlags);
-    if (NS_FAILED(rv)) { // Probably bad mimetype
+      // Initialize the image that we created above. For RasterImages, this
+      // instantiates a decoder behind the scenes, so if we don't have a decoder
+      // for this mimetype we'll find out about it here.
+      rv = mImage->Init(this, mContentType.get(), uriString.get(), imageFlags);
 
-      this->Cancel(rv);
-      return NS_BINDING_ABORTED;
-    }
+      // We allow multipart images to fail to initialize without cancelling the
+      // load because subsequent images might be fine.
+      if (NS_FAILED(rv) && !mIsMultiPartChannel) { // Probably bad mimetype
+
+        this->Cancel(rv);
+        return NS_BINDING_ABORTED;
+      }
 
-    if (imageType == imgIContainer::TYPE_RASTER) {
-      /* Use content-length as a size hint for http channels. */
-      nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
-      if (httpChannel) {
-        nsAutoCString contentLength;
-        rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("content-length"),
-                                            contentLength);
-        if (NS_SUCCEEDED(rv)) {
-          int32_t len = contentLength.ToInteger(&rv);
+      if (mImage->GetType() == imgIContainer::TYPE_RASTER) {
+        /* Use content-length as a size hint for http channels. */
+        nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
+        if (httpChannel) {
+          nsAutoCString contentLength;
+          rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("content-length"),
+                                              contentLength);
+          if (NS_SUCCEEDED(rv)) {
+            int32_t len = contentLength.ToInteger(&rv);
 
-          // Pass anything usable on so that the RasterImage can preallocate
-          // its source buffer
-          if (len > 0) {
-            uint32_t sizeHint = (uint32_t) len;
-            sizeHint = NS_MIN<uint32_t>(sizeHint, 20000000); /* Bound by something reasonable */
-            RasterImage* rasterImage = static_cast<RasterImage*>(mImage.get());
-            rv = rasterImage->SetSourceSizeHint(sizeHint);
-            if (NS_FAILED(rv)) {
-              // Flush memory, try to get some back, and try again
-              rv = nsMemory::HeapMinimize(true);
-              nsresult rv2 = rasterImage->SetSourceSizeHint(sizeHint);
-              // If we've still failed at this point, things are going downhill
-              if (NS_FAILED(rv) || NS_FAILED(rv2)) {
-                NS_WARNING("About to hit OOM in imagelib!");
+            // Pass anything usable on so that the RasterImage can preallocate
+            // its source buffer
+            if (len > 0) {
+              uint32_t sizeHint = (uint32_t) len;
+              sizeHint = NS_MIN<uint32_t>(sizeHint, 20000000); /* Bound by something reasonable */
+              RasterImage* rasterImage = static_cast<RasterImage*>(mImage.get());
+              rv = rasterImage->SetSourceSizeHint(sizeHint);
+              if (NS_FAILED(rv)) {
+                // Flush memory, try to get some back, and try again
+                rv = nsMemory::HeapMinimize(true);
+                nsresult rv2 = rasterImage->SetSourceSizeHint(sizeHint);
+                // If we've still failed at this point, things are going downhill
+                if (NS_FAILED(rv) || NS_FAILED(rv2)) {
+                  NS_WARNING("About to hit OOM in imagelib!");
+                }
               }
             }
           }
         }
       }
-    }
 
-    if (imageType == imgIContainer::TYPE_RASTER) {
-      // If we were waiting on the image to do something, now's our chance.
-      if (mDecodeRequested) {
-        mImage->RequestDecode();
+      if (mImage->GetType() == imgIContainer::TYPE_RASTER) {
+        // If we were waiting on the image to do something, now's our chance.
+        if (mDecodeRequested) {
+          mImage->RequestDecode();
+        }
+      } else { // mImage->GetType() == imgIContainer::TYPE_VECTOR
+        nsCOMPtr<nsIStreamListener> imageAsStream = do_QueryInterface(mImage);
+        NS_ABORT_IF_FALSE(imageAsStream,
+                          "SVG-typed Image failed QI to nsIStreamListener");
+        imageAsStream->OnStartRequest(aRequest, nullptr);
       }
-    } else { // imageType == imgIContainer::TYPE_VECTOR
-      nsCOMPtr<nsIStreamListener> imageAsStream = do_QueryInterface(mImage);
-      NS_ABORT_IF_FALSE(imageAsStream,
-                        "SVG-typed Image failed QI to nsIStreamListener");
-      imageAsStream->OnStartRequest(aRequest, nullptr);
     }
   }
 
-  if (imageType == imgIContainer::TYPE_RASTER) {
+  if (mImage->GetType() == imgIContainer::TYPE_RASTER) {
     // WriteToRasterImage always consumes everything it gets
     // if it doesn't run out of memory
     uint32_t bytesRead;
     rv = inStr->ReadSegments(RasterImage::WriteToRasterImage,
                              static_cast<void*>(mImage),
                              count, &bytesRead);
     NS_ABORT_IF_FALSE(bytesRead == count || mImage->HasError(),
   "WriteToRasterImage should consume everything or the image must be in error!");
-  } else { // imageType == imgIContainer::TYPE_VECTOR
+  } else { // mImage->GetType() == imgIContainer::TYPE_VECTOR
     nsCOMPtr<nsIStreamListener> imageAsStream = do_QueryInterface(mImage);
     rv = imageAsStream->OnDataAvailable(aRequest, ctxt, inStr,
                                         sourceOffset, count);
   }
   if (NS_FAILED(rv)) {
     PR_LOG(gImgLog, PR_LOG_WARNING,
            ("[this=%p] imgRequest::OnDataAvailable -- "
             "copy to RasterImage failed\n", this));
     this->Cancel(NS_IMAGELIB_ERROR_FAILURE);
     return NS_BINDING_ABORTED;
   }
 
   return NS_OK;
 }
 
 static NS_METHOD sniff_mimetype_callback(nsIInputStream* in,
-                                         void* closure,
+                                         void* data,
                                          const char* fromRawSegment,
                                          uint32_t toOffset,
                                          uint32_t count,
                                          uint32_t *writeCount)
 {
-  imgRequest *request = static_cast<imgRequest*>(closure);
+  mimetype_closure* closure = static_cast<mimetype_closure*>(data);
 
-  NS_ASSERTION(request, "request is null!");
+  NS_ASSERTION(closure, "closure is null!");
 
   if (count > 0)
-    request->SniffMimeType(fromRawSegment, count);
+    closure->request->SniffMimeType(fromRawSegment, count, *closure->newType);
 
   *writeCount = 0;
   return NS_ERROR_FAILURE;
 }
 
 void
-imgRequest::SniffMimeType(const char *buf, uint32_t len)
+imgRequest::SniffMimeType(const char *buf, uint32_t len, nsACString& newType)
 {
-  imgLoader::GetMimeTypeFromContent(buf, len, mContentType);
+  imgLoader::GetMimeTypeFromContent(buf, len, newType);
 
   // The vast majority of the time, imgLoader will find a gif/jpeg/png image
-  // and fill mContentType with the sniffed MIME type.
-  if (!mContentType.IsEmpty())
+  // and fill newType with the sniffed MIME type.
+  if (!newType.IsEmpty())
     return;
 
   // When our sniffing fails, we want to query registered image decoders
   // to see if they can identify the image. If we always trusted the server
   // to send the right MIME, images sent as text/plain would not be rendered.
   const nsCOMArray<nsIContentSniffer>& sniffers = mImageSniffers.GetEntries();
   uint32_t length = sniffers.Count();
   for (uint32_t i = 0; i < length; ++i) {
     nsresult rv =
-      sniffers[i]->GetMIMETypeFromContent(nullptr, (const uint8_t *) buf, len, mContentType);
-    if (NS_SUCCEEDED(rv) && !mContentType.IsEmpty()) {
+      sniffers[i]->GetMIMETypeFromContent(nullptr, (const uint8_t *) buf, len, newType);
+    if (NS_SUCCEEDED(rv) && !newType.IsEmpty()) {
       return;
     }
   }
 }
 
 
 /** nsIInterfaceRequestor methods **/
 
--- a/image/src/imgRequest.h
+++ b/image/src/imgRequest.h
@@ -66,17 +66,17 @@ public:
                 int32_t aCORSMode);
 
   // Callers must call imgRequestProxy::Notify later.
   nsresult AddProxy(imgRequestProxy *proxy);
 
   // aNotify==false still sends OnStopRequest.
   nsresult RemoveProxy(imgRequestProxy *proxy, nsresult aStatus, bool aNotify);
 
-  void SniffMimeType(const char *buf, uint32_t len);
+  void SniffMimeType(const char *buf, uint32_t len, nsACString& newType);
 
   // Cancel, but also ensure that all work done in Init() is undone. Call this
   // only when the channel has failed to open, and so calling Cancel() on it
   // won't be sufficient.
   void CancelAndAbort(nsresult aStatus);
 
   // Methods that get forwarded to the Image, or deferred until it's
   // instantiated.
@@ -229,11 +229,12 @@ private:
   // Sometimes consumers want to do things before the image is ready. Let them,
   // and apply the action when the image becomes available.
   bool mDecodeRequested : 1;
 
   bool mIsMultiPartChannel : 1;
   bool mGotData : 1;
   bool mIsInCache : 1;
   bool mBlockingOnload : 1;
+  bool mResniffMimeType : 1;
 };
 
 #endif
--- a/image/test/mochitest/Makefile.in
+++ b/image/test/mochitest/Makefile.in
@@ -50,20 +50,22 @@ MOCHITEST_FILES =   imgutils.js \
                 test_bug89419-2.html \
                 test_bug552605-1.html \
                 test_bug552605-2.html \
                 bug552605.sjs \
                 bug671906-iframe.html \
                 bug671906.sjs \
                 test_bug671906.html \
                 test_bug733553.html \
-                bug733553-iframe.html \
                 bug733553.sjs \
                 bug733553-informant.sjs \
                 animated-gif2.gif \
+                invalid.jpg \
+                bad.jpg \
+                rillybad.jpg \
                 test_bug767779.html \
                 bug767779.sjs \
                 animated-gif_trailing-garbage.gif \
 		test_error_events.html \
 		error-early.png \
 		test_drawDiscardedImage.html \
                 $(NULL)
 
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..555a416d7d8e6c0f835a756994a8efca17c55cd8
GIT binary patch
literal 2477
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
G1pWd#D4b{j
deleted file mode 100644
--- a/image/test/mochitest/bug733553-iframe.html
+++ /dev/null
@@ -1,7 +0,0 @@
-<html>
-<head>
-<title>Bug 733553 iframe</title>
-<body>
-<img src="bug733553.sjs" id="image1" />
-</body>
-</html>
--- a/image/test/mochitest/bug733553.sjs
+++ b/image/test/mochitest/bug733553.sjs
@@ -1,21 +1,30 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
-var bodyPartIndex = 0;
+var bodyPartIndex = -1;
 var bodyParts = [
   ["red.png", "image/png"],
   ["animated-gif2.gif", "image/gif"],
   ["red.png", "image/png"],
   ["lime100x100.svg", "image/svg+xml"],
+  ["lime100x100.svg", "image/svg+xml"],
   ["animated-gif2.gif", "image/gif"],
   ["red.png", "image/png"],
-  ["lime100x100.svg", "image/svg+xml"]
+  ["damon.jpg", "image/jpeg"],
+  ["damon.jpg", "application/octet-stream"],
+  ["damon.jpg", "image/jpeg"],
+  ["rillybad.jpg", "application/x-unknown-content-type"],
+  ["damon.jpg", "image/jpeg"],
+  ["bad.jpg", "image/jpeg"],
+  ["red.png", "image/png"],
+  ["invalid.jpg", "image/jpeg"],
+  ["animated-gif2.gif", "image/gif"]
 ];
 var timer = Components.classes["@mozilla.org/timer;1"];
 var partTimer = timer.createInstance(Components.interfaces.nsITimer);
 
 function getFileAsInputStream(aFilename) {
   var file = Components.classes["@mozilla.org/file/directory_service;1"]
              .getService(Components.interfaces.nsIProperties)
              .get("CurWorkD", Components.interfaces.nsIFile);
@@ -29,36 +38,35 @@ function getFileAsInputStream(aFilename)
   var fileStream = Components.classes['@mozilla.org/network/file-input-stream;1']
                    .createInstance(Components.interfaces.nsIFileInputStream);
   fileStream.init(file, 1, 0, false);
   return fileStream;
 }
 
 function handleRequest(request, response)
 {
+  setSharedState("next-part", "-1");
   response.setHeader("Content-Type",
                      "multipart/x-mixed-replace;boundary=BOUNDARYOMG", false);
   response.setHeader("Cache-Control", "no-cache", false);
   response.setStatusLine(request.httpVersion, 200, "OK");
   // We're sending parts off in a delayed fashion, to let the tests occur.
   response.processAsync();
   response.write("--BOUNDARYOMG\r\n");
   sendParts(response);
 }
 
 function sendParts(response) {
   let wait = false;
   let nextPart = parseInt(getSharedState("next-part"), 10);
-  if (nextPart) {
-    if (nextPart == bodyPartIndex) {
-      // Haven't been signaled yet, remain in holding pattern
-      wait = true;
-    } else {
-      bodyPartIndex = nextPart;
-    }
+  if (nextPart == bodyPartIndex) {
+    // Haven't been signaled yet, remain in holding pattern
+    wait = true;
+  } else {
+    bodyPartIndex = nextPart;
   }
   if (bodyParts.length > bodyPartIndex) {
     let callback;
     if (!wait) {
       callback = getSendNextPart(response);
     } else {
       callback = function () { sendParts(response); };
     }
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..e2fb1d303f244a1825636b6360ac4f7e5fc0601a
GIT binary patch
literal 11142
zc$}Sj1yEegw(cOw;0_sdfWa-eJHa6YcYes=GRQ!14<SJYCqR${he3jSf)5fT3>GZ7
z2lpT^|9N%JyZ6?)ukL%R>Z{tj*XrK=Rj<{(dUq?i!@TWa-n<6>9(Iz9PHr|1c2Au>
z>>LR3l-yx3JE*slJ4}*M$<-Zh_ov7P>S)LNN8#<|?kdUXX5+{E$6kh?hY{w^3-@;S
zvirvq^lvjBMmHb0H?No7D;rlQTO02`1s?8y3jU~|E`L|?@Ur{o3nAVMFB=Cp8_9pw
z{>SJ4(J?JMn1i>YB%=Vogs3nf9wFYt?87pEM8Qa10Prt)m<K2U@PI%Z;A1=-92|T+
zJbWS$2@xS75j`apDF^~)Wnl(0GqG`tJ!Rt*=44{#R}c`Ekdl>^W#xOOt|YA{CL=5L
zcOw{h`1nMGM06x1bW$A598&+!^3Vw&#Q_{)$YWtJ129Q3ut+f;dI4Yn0OJwHKjfcX
z#=ykFe)JfKgNuj%$Dn}(fQf;Hg^7*z=n*#dA8qg-Jph~Z5gC(!!eeq>8z8eMg<yEn
zCme`kO&6u!_ahdemtGOLcvMe7)HJMY>>Qk2!Xlz#;u4Zd&y-bE)zmfg4GfKpO-#+8
zws!Uoj!w>SZ=YAbe*OWGucO{XzkL^zoRXTB{yrl!E5D$ysJNuGth~0azM-+Hxuvzc
zr?;;kH840dfu5Y2o|*kIx3apnzOlKry|a6Ka(Z@tad~xp^EWRH02anS<=^rm{lg0r
z8ygE7_%|;MOy9o^NU<L=2|OlK&;{Cfk~0g2<4`CjeX8lgg$U^#QNHy0jz`5Jyuy0?
zH?@B-6A=~t9}tW9|6=wpV*iKN9Do1|<Imt>kpkoZC#@X$VR-)zoQ9hGhS<p-0D2dr
znVuPP4*x<E1!^p6_g;OCW!vwTfTPCHjnfAJQ|S3l@|BTi0^6q8`4ZmR$%w3x6v)76
z_NeIk0g&GK0JublMzMT(^5qRMbr`0|`4UU?IdI}VH$JG|C_99H%0Au}f#&KY3F=!1
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
--- a/image/test/mochitest/test_bug733553.html
+++ b/image/test/mochitest/test_bug733553.html
@@ -4,67 +4,80 @@
 https://bugzilla.mozilla.org/show_bug.cgi?id=733553
 -->
 <head>
   <title>Test for Bug 733553</title>
   <script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
-<body>
+<body onload="initializeOnload()">
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=733553">Mozilla Bug 733553</a>
 <p id="display"></p>
 <pre id="test">
 <script type="application/javascript">
 
 SimpleTest.waitForExplicitFinish();
 
-var testIndex = 0;
+var testIndex = -1;
 var testParts = [
   [1, "red.png"],
   [40, "animated-gif2.gif"],
   [1, "red.png"],
   [100, "lime100x100.svg"],
+  [100, "lime100x100.svg"],
   [40, "animated-gif2.gif"],
-  [1, "red.png"]
+  [1, "red.png"],
+  [80, "damon.jpg"],
+  [80, "damon.jpg"],
+  [80, "damon.jpg"],
+  // An invalid image (from bug 787899) that is further delivered with a
+  // "special" bad MIME type that indicates that the necko
+  // multipart/x-mixed-replace parser wasn't able to parse it.
+  [0, "rillybad.jpg"],
+  // Bad.jpg is bad such that it sniffs as a JPEG, and therefore doesn't change
+  // what's been decoded (since the RasterImage isn't deleted), so its width
+  // needs to be the same as the previous image.
+  [80, "damon.jpg"],
+  [80, "bad.jpg"],
+  [1, "red.png"],
+  [0, "invalid.jpg"],
+  [40, "animated-gif2.gif"]
 ];
 
 // We'll append the part number to this, and tell the informant
 const BASE_URL = "bug733553-informant.sjs?";
 
 function initializeOnload() {
-  var iframeelem = document.getElementById('test-iframe');
-  var firstimg = iframeelem.contentDocument.getElementById('image1');
+  var firstimg = document.createElement('img');
   firstimg.addEventListener("load", imageLoad, false);
+  firstimg.addEventListener("error", imageLoad, false);
+  firstimg.src = "bug733553.sjs";
+  document.getElementById('content').appendChild(firstimg);
+
+  // Really ready for first, but who's counting
+  readyForNext();
 }
 
 function readyForNext() {
   var loader = document.getElementById("loader");
-  if (loader) {
-    testIndex++;
-    loader.src = BASE_URL + testIndex;
-  }
+  loader.src = BASE_URL + ++testIndex;
 }
 
 function imageLoad(aEvent) {
-  if (testParts.length > testIndex) {
-    var [width, fileName] = testParts[testIndex];
-    if (aEvent.target.width == width) {
-      is(aEvent.target.width, width,
-         "Test " + testIndex + " " + fileName + " width correct");
-      readyForNext();
-    }
+  var [width, fileName] = testParts[testIndex];
+  is(aEvent.target.width, width,
+     "Test " + testIndex + " " + fileName + " width correct");
+
+  if ((testParts.length - 1) == testIndex) {
+    SimpleTest.finish();
   } else {
-    aEvent.target.removeEventListener("load", imageLoad, false);
-    var loader = document.getElementById("loader");
     readyForNext();
-    SimpleTest.finish();
   }
 }
 
 </script>
 </pre>
 <div id="content"> <!-- style="display: none" -->
-<iframe id="test-iframe" src="http://mochi.test:8888/tests/image/test/mochitest/bug733553-iframe.html" onload="initializeOnload()"></iframe>
 <iframe id="loader"></iframe>
 </div>
 </body>
 </html>
--- a/ipc/glue/InputStreamParams.ipdlh
+++ b/ipc/glue/InputStreamParams.ipdlh
@@ -1,15 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include "ipc/IPCMessageUtils.h";
 
 using mozilla::void_t;
+include protocol PBlob;
 
 namespace mozilla {
 namespace ipc {
 
 struct StringInputStreamParams
 {
   nsCString data;
 };
@@ -31,24 +32,30 @@ struct PartialFileInputStreamParams
 struct MultiplexInputStreamParams
 {
   InputStreamParams[] streams;
   uint32_t currentStream;
   nsresult status;
   bool startedReadingCurrent;
 };
 
+struct RemoteInputStreamParams
+{
+  PBlob remoteBlob;
+};
+
 union InputStreamParams
 {
   StringInputStreamParams;
   FileInputStreamParams;
   PartialFileInputStreamParams;
   BufferedInputStreamParams;
   MIMEInputStreamParams;
   MultiplexInputStreamParams;
+  RemoteInputStreamParams;
 };
 
 union OptionalInputStreamParams
 {
   void_t;
   InputStreamParams;
 };
 
--- a/ipc/glue/InputStreamUtils.cpp
+++ b/ipc/glue/InputStreamUtils.cpp
@@ -2,26 +2,32 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "InputStreamUtils.h"
 
 #include "nsIIPCSerializableInputStream.h"
 
 #include "mozilla/Assertions.h"
+#include "mozilla/dom/ipc/Blob.h"
 #include "nsComponentManagerUtils.h"
 #include "nsDebug.h"
 #include "nsID.h"
+#include "nsIDOMFile.h"
+#include "nsIXULRuntime.h"
 #include "nsMIMEInputStream.h"
 #include "nsMultiplexInputStream.h"
 #include "nsNetCID.h"
 #include "nsStringStream.h"
 #include "nsThreadUtils.h"
+#include "nsXULAppAPI.h"
 
 using namespace mozilla::ipc;
+using mozilla::dom::BlobChild;
+using mozilla::dom::BlobParent;
 
 namespace {
 
 NS_DEFINE_CID(kStringInputStreamCID, NS_STRINGINPUTSTREAM_CID);
 NS_DEFINE_CID(kFileInputStreamCID, NS_LOCALFILEINPUTSTREAM_CID);
 NS_DEFINE_CID(kPartialFileInputStreamCID, NS_PARTIALLOCALFILEINPUTSTREAM_CID);
 NS_DEFINE_CID(kBufferedInputStreamCID, NS_BUFFEREDINPUTSTREAM_CID);
 NS_DEFINE_CID(kMIMEInputStreamCID, NS_MIMEINPUTSTREAM_CID);
@@ -95,16 +101,39 @@ DeserializeInputStream(const InputStream
     case InputStreamParams::TMIMEInputStreamParams:
       serializable = do_CreateInstance(kMIMEInputStreamCID);
       break;
 
     case InputStreamParams::TMultiplexInputStreamParams:
       serializable = do_CreateInstance(kMultiplexInputStreamCID);
       break;
 
+    // When the input stream already exists in this process, all we need to do
+    // is retrieve the original instead of sending any data over the wire.
+    case InputStreamParams::TRemoteInputStreamParams: {
+      nsCOMPtr<nsIDOMBlob> domBlob;
+      const RemoteInputStreamParams& params =
+          aParams.get_RemoteInputStreamParams();
+
+      domBlob = params.remoteBlobParent() ?
+          static_cast<BlobParent*>(params.remoteBlobParent())->GetBlob() :
+          static_cast<BlobChild*>(params.remoteBlobChild())->GetBlob();
+
+      MOZ_ASSERT(domBlob, "Invalid blob contents");
+
+      // If fetching the internal stream fails, we ignore it and return a
+      // null stream.
+      nsCOMPtr<nsIInputStream> stream;
+      nsresult rv = domBlob->GetInternalStream(getter_AddRefs(stream));
+      if (NS_FAILED(rv) || !stream) {
+        NS_WARNING("Couldn't obtain a valid stream from the blob");
+      }
+      return stream.forget();
+    }
+
     default:
       MOZ_ASSERT(false, "Unknown params!");
       return nullptr;
   }
 
   MOZ_ASSERT(serializable);
 
   if (!serializable->Deserialize(aParams)) {
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -126,16 +126,17 @@ CPPSRCS		= \
 		NameFunctions.cpp \
 		ParallelArray.cpp \
 		ParseMaps.cpp \
 		ParseNode.cpp \
 		Parser.cpp \
 		SPSProfiler.cpp \
 		TokenStream.cpp \
 		TestingFunctions.cpp \
+		Profilers.cpp \
 		LifoAlloc.cpp \
 		Eval.cpp \
 		MapObject.cpp \
 		RegExpObject.cpp \
 		RegExpStatics.cpp \
 		RegExp.cpp \
 		Marking.cpp \
 		Memory.cpp \
--- a/js/src/builtin/ParallelArray.cpp
+++ b/js/src/builtin/ParallelArray.cpp
@@ -221,16 +221,23 @@ ArrayLikeToIndexVector(JSContext *cx, Ha
         {
             return false;
         }
     }
 
     return true;
 }
 
+static inline bool
+IdIsInBoundsIndex(JSContext *cx, HandleObject obj, HandleId id)
+{
+    uint32_t i;
+    return js_IdIsIndex(id, &i) && i < ParallelArrayObject::as(obj)->outermostDimension();
+}
+
 template <bool impl(JSContext *, CallArgs)>
 static inline
 JSBool NonGenericMethod(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod(cx, ParallelArrayObject::is, impl, args);
 }
 
@@ -815,17 +822,17 @@ Class ParallelArrayObject::class_ = {
         lookupSpecial,
         defineGeneric,
         defineProperty,
         defineElement,
         defineSpecial,
         getGeneric,
         getProperty,
         getElement,
-        NULL,                // getElementIfPresent
+        getElementIfPresent,
         getSpecial,
         setGeneric,
         setProperty,
         setElement,
         setSpecial,
         getGenericAttributes,
         getPropertyAttributes,
         getElementAttributes,
@@ -1029,16 +1036,25 @@ ParallelArrayObject::create(JSContext *c
                                                Int32Value(static_cast<int32_t>(dims[i])));
 
     result->setSlot(SLOT_DIMENSIONS, ObjectValue(*dimArray));
 
     // Store the buffer and offset.
     result->setSlot(SLOT_BUFFER, ObjectValue(*buffer));
     result->setSlot(SLOT_BUFFER_OFFSET, Int32Value(static_cast<int32_t>(offset)));
 
+    // ParallelArray objects are frozen, so mark it as non-extensible here.
+    Shape *empty = EmptyShape::getInitialShape(cx, &class_,
+                                               result->getProto(), result->getParent(),
+                                               result->getAllocKind(),
+                                               BaseShape::NOT_EXTENSIBLE);
+    if (!empty)
+        return false;
+    result->setLastPropertyInfallible(empty);
+
     // This is usually args.rval() from build or construct.
     vp.setObject(*result);
 
     return true;
 }
 
 JSBool
 ParallelArrayObject::construct(JSContext *cx, unsigned argc, Value *vp)
@@ -1425,17 +1441,24 @@ ParallelArrayObject::get(JSContext *cx, 
         return ReportBadArg(cx, ".prototype.get");
 
     return obj->getParallelArrayElement(cx, iv, args.rval());
 }
 
 bool
 ParallelArrayObject::dimensionsGetter(JSContext *cx, CallArgs args)
 {
-    args.rval().setObject(*(as(&args.thisv().toObject())->dimensionArray()));
+    RootedObject dimArray(cx, as(&args.thisv().toObject())->dimensionArray());
+    RootedObject copy(cx, NewDenseCopiedArray(cx, dimArray->getDenseArrayInitializedLength(),
+                                              dimArray->getDenseArrayElements()));
+    if (!copy)
+        return false;
+    // Reuse the existing dimension array's type.
+    copy->setType(dimArray->type());
+    args.rval().setObject(*copy);
     return true;
 }
 
 bool
 ParallelArrayObject::lengthGetter(JSContext *cx, CallArgs args)
 {
     args.rval().setNumber(as(&args.thisv().toObject())->outermostDimension());
     return true;
@@ -1502,17 +1525,17 @@ ParallelArrayObject::toStringBufferImpl(
         JS_ASSERT(!elem->isMagic(JS_ARRAY_HOLE));
 
         if (!JS_CHECK_OPERATION_LIMIT(cx))
             return false;
 
         if (!elem->isNullOrUndefined()) {
             if (useLocale) {
                 tmp = *elem;
-                JSObject *robj = ToObject(cx, tmp);
+                RootedObject robj(cx, ToObject(cx, tmp));
                 if (!robj)
                     return false;
 
                 id = NameToId(cx->names().toLocaleString);
                 if (!robj->callMethod(cx, id, 0, NULL, &localeElem) ||
                     !ValueToStringBuffer(cx, localeElem, sb))
                 {
                     return false;
@@ -1584,22 +1607,16 @@ ParallelArrayObject::mark(JSTracer *trc,
 JSBool
 ParallelArrayObject::lookupGeneric(JSContext *cx, HandleObject obj, HandleId id,
                                    MutableHandleObject objp, MutableHandleShape propp)
 {
     uint32_t i;
     if (js_IdIsIndex(id, &i))
         return lookupElement(cx, obj, i, objp, propp);
 
-    if (JSID_IS_ATOM(id, cx->names().length)) {
-        MarkNonNativePropertyFound(obj, propp);
-        objp.set(obj);
-        return true;
-    }
-
     RootedObject proto(cx, obj->getProto());
     if (proto)
         return JSObject::lookupGeneric(cx, proto, id, objp, propp);
 
     objp.set(NULL);
     propp.set(NULL);
     return true;
 }
@@ -1632,80 +1649,97 @@ JSBool
 ParallelArrayObject::lookupSpecial(JSContext *cx, HandleObject obj, HandleSpecialId sid,
                                    MutableHandleObject objp, MutableHandleShape propp)
 {
     RootedId id(cx, SPECIALID_TO_JSID(sid));
     return lookupGeneric(cx, obj, id, objp, propp);
 }
 
 JSBool
-ParallelArrayObject::defineGeneric(JSContext *cx, HandleObject obj, HandleId id, HandleValue Value,
+ParallelArrayObject::defineGeneric(JSContext *cx, HandleObject obj, HandleId id, HandleValue value,
                                    JSPropertyOp getter, StrictPropertyOp setter, unsigned attrs)
 {
-    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_IMMUTABLE);
-    return false;
+    uint32_t i;
+    if (js_IdIsIndex(id, &i) && i < as(obj)->outermostDimension()) {
+        RootedValue existingValue(cx);
+        if (!as(obj)->getParallelArrayElement(cx, i, &existingValue))
+            return false;
+
+        bool same;
+        if (!SameValue(cx, value, existingValue, &same))
+            return false;
+        if (!same)
+            return Throw(cx, id, JSMSG_CANT_REDEFINE_PROP);
+    } else {
+        RootedValue tmp(cx, value);
+        if (!setGeneric(cx, obj, id, &tmp, true))
+            return false;
+    }
+
+    return setGenericAttributes(cx, obj, id, &attrs);
 }
 
 JSBool
 ParallelArrayObject::defineProperty(JSContext *cx, HandleObject obj,
                                     HandlePropertyName name, HandleValue value,
                                     JSPropertyOp getter, StrictPropertyOp setter, unsigned attrs)
 {
-    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_IMMUTABLE);
-    return false;
+    RootedId id(cx, NameToId(name));
+    return defineGeneric(cx, obj, id, value, getter, setter, attrs);
 }
 
 JSBool
 ParallelArrayObject::defineElement(JSContext *cx, HandleObject obj,
                                    uint32_t index, HandleValue value,
                                    PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
 {
-    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_IMMUTABLE);
-    return false;
+    RootedId id(cx);
+    if (!IndexToId(cx, index, id.address()))
+        return false;
+    return defineGeneric(cx, obj, id, value, getter, setter, attrs);
 }
 
 JSBool
 ParallelArrayObject::defineSpecial(JSContext *cx, HandleObject obj,
                                    HandleSpecialId sid, HandleValue value,
                                    PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
 {
-    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_IMMUTABLE);
-    return false;
+    RootedId id(cx, SPECIALID_TO_JSID(sid));
+    return defineGeneric(cx, obj, id, value, getter, setter, attrs);
 }
 
 JSBool
 ParallelArrayObject::getGeneric(JSContext *cx, HandleObject obj, HandleObject receiver,
                                 HandleId id, MutableHandleValue vp)
 {
-    Value idval = IdToValue(id);
+    RootedValue idval(cx, IdToValue(id));
 
     uint32_t index;
     if (IsDefinitelyIndex(idval, &index))
         return getElement(cx, obj, receiver, index, vp);
 
+    Rooted<SpecialId> sid(cx);
+    if (ValueIsSpecial(obj, &idval, sid.address(), cx))
+        return getSpecial(cx, obj, receiver, sid, vp);
+
     JSAtom *atom = ToAtom(cx, idval);
     if (!atom)
         return false;
 
     if (atom->isIndex(&index))
         return getElement(cx, obj, receiver, index, vp);
 
     Rooted<PropertyName*> name(cx, atom->asPropertyName());
     return getProperty(cx, obj, receiver, name, vp);
 }
 
 JSBool
 ParallelArrayObject::getProperty(JSContext *cx, HandleObject obj, HandleObject receiver,
                                  HandlePropertyName name, MutableHandleValue vp)
 {
-    if (name == cx->names().length) {
-        vp.setNumber(as(obj)->outermostDimension());
-        return true;
-    }
-
     RootedObject proto(cx, obj->getProto());
     if (proto)
         return JSObject::getProperty(cx, proto, receiver, name, vp);
 
     vp.setUndefined();
     return true;
 }
 
@@ -1714,78 +1748,108 @@ ParallelArrayObject::getElement(JSContex
                                 uint32_t index, MutableHandleValue vp)
 {
     // Unlike normal arrays, [] for ParallelArray does not walk the prototype
     // chain and just returns undefined.
     return as(obj)->getParallelArrayElement(cx, index, vp);
 }
 
 JSBool
+ParallelArrayObject::getElementIfPresent(JSContext *cx, HandleObject obj, HandleObject receiver,
+                                         uint32_t index, MutableHandleValue vp, bool *present)
+{
+    RootedParallelArrayObject source(cx, as(obj));
+    if (index < source->outermostDimension()) {
+        if (!source->getParallelArrayElement(cx, index, vp))
+            return false;
+        *present = true;
+        return true;
+    }
+
+    *present = false;
+    vp.setUndefined();
+    return true;
+}
+
+JSBool
 ParallelArrayObject::getSpecial(JSContext *cx, HandleObject obj, HandleObject receiver,
                                 HandleSpecialId sid, MutableHandleValue vp)
 {
     if (!obj->getProto()) {
         vp.setUndefined();
         return true;
     }
 
     RootedId id(cx, SPECIALID_TO_JSID(sid));
     return baseops::GetProperty(cx, obj, receiver, id, vp);
 }
 
 JSBool
 ParallelArrayObject::setGeneric(JSContext *cx, HandleObject obj, HandleId id,
                                 MutableHandleValue vp, JSBool strict)
 {
-    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_IMMUTABLE);
-    return false;
+    JS_ASSERT(!obj->isExtensible());
+
+    if (IdIsInBoundsIndex(cx, obj, id)) {
+        if (strict)
+            return JSObject::reportReadOnly(cx, id);
+        if (cx->hasStrictOption())
+            return JSObject::reportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING);
+    } else {
+        if (strict)
+            return obj->reportNotExtensible(cx);
+        if (cx->hasStrictOption())
+            return obj->reportNotExtensible(cx, JSREPORT_STRICT | JSREPORT_WARNING);
+    }
+
+    return true;
 }
 
 JSBool
 ParallelArrayObject::setProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
                                  MutableHandleValue vp, JSBool strict)
 {
-    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_IMMUTABLE);
-    return false;
+    RootedId id(cx, NameToId(name));
+    return setGeneric(cx, obj, id, vp, strict);
 }
 
 JSBool
 ParallelArrayObject::setElement(JSContext *cx, HandleObject obj, uint32_t index,
                                 MutableHandleValue vp, JSBool strict)
 {
-    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_IMMUTABLE);
-    return false;
+    RootedId id(cx);
+    if (!IndexToId(cx, index, id.address()))
+        return false;
+    return setGeneric(cx, obj, id, vp, strict);
 }
 
 JSBool
 ParallelArrayObject::setSpecial(JSContext *cx, HandleObject obj, HandleSpecialId sid,
                                 MutableHandleValue vp, JSBool strict)
 {
-    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_IMMUTABLE);
-    return false;
+    RootedId id(cx, SPECIALID_TO_JSID(sid));
+    return setGeneric(cx, obj, id, vp, strict);
 }
 
 JSBool
 ParallelArrayObject::getGenericAttributes(JSContext *cx, HandleObject obj, HandleId id,
                                           unsigned *attrsp)
 {
-    if (JSID_IS_ATOM(id, cx->names().length))
-        *attrsp = JSPROP_PERMANENT | JSPROP_READONLY;
-    else
-        *attrsp = JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_ENUMERATE;
-
+    *attrsp = JSPROP_PERMANENT | JSPROP_READONLY;
+    uint32_t i;
+    if (js_IdIsIndex(id, &i))
+        *attrsp |= JSPROP_ENUMERATE;
     return true;
 }
 
 JSBool
 ParallelArrayObject::getPropertyAttributes(JSContext *cx, HandleObject obj, HandlePropertyName name,
                                            unsigned *attrsp)
 {
-    if (name == cx->names().length)
-        *attrsp = JSPROP_PERMANENT | JSPROP_READONLY;
+    *attrsp = JSPROP_PERMANENT | JSPROP_READONLY;
     return true;
 }
 
 JSBool
 ParallelArrayObject::getElementAttributes(JSContext *cx, HandleObject obj, uint32_t index,
                                           unsigned *attrsp)
 {
     *attrsp = JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_ENUMERATE;
@@ -1799,89 +1863,111 @@ ParallelArrayObject::getSpecialAttribute
     *attrsp = JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_ENUMERATE;
     return true;
 }
 
 JSBool
 ParallelArrayObject::setGenericAttributes(JSContext *cx, HandleObject obj, HandleId id,
                                           unsigned *attrsp)
 {
-    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_IMMUTABLE);
-    return false;
+    if (IdIsInBoundsIndex(cx, obj, id)) {
+        unsigned attrs;
+        if (!getGenericAttributes(cx, obj, id, &attrs))
+            return false;
+        if (*attrsp != attrs)
+            return Throw(cx, id, JSMSG_CANT_REDEFINE_PROP);
+    }
+
+    return obj->reportNotExtensible(cx);
 }
 
 JSBool
 ParallelArrayObject::setPropertyAttributes(JSContext *cx, HandleObject obj, HandlePropertyName name,
                                            unsigned *attrsp)
 {
-    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_IMMUTABLE);
-    return false;
+    RootedId id(cx, NameToId(name));
+    return setGenericAttributes(cx, obj, id, attrsp);
 }
 
 JSBool
 ParallelArrayObject::setElementAttributes(JSContext *cx, HandleObject obj, uint32_t index,
                                           unsigned *attrsp)
 {
-    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_IMMUTABLE);
-    return false;
+    RootedId id(cx);
+    if (!IndexToId(cx, index, id.address()))
+        return false;
+    return setGenericAttributes(cx, obj, id, attrsp);
 }
 
 JSBool
 ParallelArrayObject::setSpecialAttributes(JSContext *cx, HandleObject obj, HandleSpecialId sid,
                                           unsigned *attrsp)
 {
-    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_IMMUTABLE);
-    return false;
+    RootedId id(cx, SPECIALID_TO_JSID(sid));
+    return setGenericAttributes(cx, obj, id, attrsp);
 }
 
 JSBool
 ParallelArrayObject::deleteGeneric(JSContext *cx, HandleObject obj, HandleId id,
                                    MutableHandleValue rval, JSBool strict)
 {
-    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_IMMUTABLE);
-    return false;
+    if (IdIsInBoundsIndex(cx, obj, id)) {
+        if (strict)
+            return obj->reportNotConfigurable(cx, id);
+        if (cx->hasStrictOption()) {
+            if (!obj->reportNotConfigurable(cx, id, JSREPORT_STRICT | JSREPORT_WARNING))
+                return false;
+        }
+
+        rval.setBoolean(false);
+        return true;
+    }
+
+    rval.setBoolean(true);
+    return true;
 }
 
 JSBool
 ParallelArrayObject::deleteProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
                                     MutableHandleValue rval, JSBool strict)
 {
-    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_IMMUTABLE);
-    return false;
+    RootedId id(cx, NameToId(name));
+    return deleteGeneric(cx, obj, id, rval, strict);
 }
 
 JSBool
 ParallelArrayObject::deleteElement(JSContext *cx, HandleObject obj, uint32_t index,
                                    MutableHandleValue rval, JSBool strict)
 {
-    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_IMMUTABLE);
-    return false;
+    RootedId id(cx);
+    if (!IndexToId(cx, index, id.address()))
+        return false;
+    return deleteGeneric(cx, obj, id, rval, strict);
 }
 
 JSBool
 ParallelArrayObject::deleteSpecial(JSContext *cx, HandleObject obj, HandleSpecialId sid,
                                    MutableHandleValue rval, JSBool strict)
 {
-    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_IMMUTABLE);
-    return false;
+    RootedId id(cx, SPECIALID_TO_JSID(sid));
+    return deleteGeneric(cx, obj, id, rval, strict);
 }
 
 bool
 ParallelArrayObject::enumerate(JSContext *cx, HandleObject obj, unsigned flags,
                                AutoIdVector *props)
 {
     RootedParallelArrayObject source(cx, as(obj));
 
-    if (flags & JSITER_HIDDEN && !props->append(NameToId(cx->names().length)))
-        return false;
-
     // ParallelArray objects have no holes.
     if (source->outermostDimension() > 0) {
-        for (uint32_t i = 0; i < source->outermostDimension(); i++)
-            props->append(INT_TO_JSID(i));
+        for (uint32_t i = 0; i < source->outermostDimension(); i++) {
+            if (!props->append(INT_TO_JSID(i)))
+                return false;
+        }
     }
 
     if (flags & JSITER_OWNONLY)
         return true;
 
     RootedObject proto(cx, obj->getProto());
     if (proto) {
         AutoIdVector protoProps(cx);
--- a/js/src/builtin/ParallelArray.h
+++ b/js/src/builtin/ParallelArray.h
@@ -374,16 +374,18 @@ class ParallelArrayObject : public JSObj
     static JSBool getSpecial(JSContext *cx, HandleObject obj, HandleObject receiver,
                              HandleSpecialId sid, MutableHandleValue vp);
     static JSBool setGeneric(JSContext *cx, HandleObject obj, HandleId id,
                              MutableHandleValue vp, JSBool strict);
     static JSBool setProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
                               MutableHandleValue vp, JSBool strict);
     static JSBool setElement(JSContext *cx, HandleObject obj, uint32_t index,
                              MutableHandleValue vp, JSBool strict);
+    static JSBool getElementIfPresent(JSContext *cx, HandleObject obj, HandleObject receiver,
+                                      uint32_t index, MutableHandleValue vp, bool *present);
     static JSBool setSpecial(JSContext *cx, HandleObject obj, HandleSpecialId sid,
                              MutableHandleValue vp, JSBool strict);
     static JSBool getGenericAttributes(JSContext *cx, HandleObject obj, HandleId id,
                                        unsigned *attrsp);
     static JSBool getPropertyAttributes(JSContext *cx, HandleObject obj, HandlePropertyName name,
                                         unsigned *attrsp);
     static JSBool getElementAttributes(JSContext *cx, HandleObject obj, uint32_t index,
                                        unsigned *attrsp);
new file mode 100644
--- /dev/null
+++ b/js/src/builtin/Profilers.cpp
@@ -0,0 +1,502 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/* Profiling-related API */
+
+#include <stdarg.h>
+
+#include "Profilers.h"
+#include "jsapi.h"
+#include "jscntxt.h"
+#include "jsprobes.h"
+
+#include "jscntxtinlines.h"
+#include "vm/Stack-inl.h"
+
+#ifdef MOZ_CALLGRIND
+#include <valgrind/callgrind.h>
+#endif
+
+#ifdef __APPLE__
+#include "devtools/sharkctl.h"
+#endif
+
+using namespace js;
+
+/* Thread-unsafe error management */
+
+static char gLastError[2000];
+
+static void
+#ifdef __GNUC__
+__attribute__((unused,format(printf,1,2)))
+#endif
+UnsafeError(const char *format, ...)
+{
+    va_list args;
+    va_start(args, format);
+    (void) vsnprintf(gLastError, sizeof(gLastError), format, args);
+    va_end(args);
+
+    gLastError[sizeof(gLastError) - 1] = '\0';
+}
+
+JS_PUBLIC_API(const char *)
+JS_UnsafeGetLastProfilingError()
+{
+    return gLastError;
+}
+
+JS_PUBLIC_API(JSBool)
+JS_StartProfiling(const char *profileName)
+{
+    JSBool ok = JS_TRUE;
+#if defined(MOZ_SHARK) && defined(__APPLE__)
+    if (!Shark::Start()) {
+        UnsafeError("Failed to start Shark for %s", profileName);
+        ok = JS_FALSE;
+    }
+#endif
+#ifdef __linux__
+    if (!js_StartPerf())
+        ok = JS_FALSE;
+#endif
+    return ok;
+}
+
+JS_PUBLIC_API(JSBool)
+JS_StopProfiling(const char *profileName)
+{
+    JSBool ok = JS_TRUE;
+#if defined(MOZ_SHARK) && defined(__APPLE__)
+    Shark::Stop();
+#endif
+#ifdef __linux__
+    if (!js_StopPerf())
+        ok = JS_FALSE;
+#endif
+    return ok;
+}
+
+/*
+ * Start or stop whatever platform- and configuration-specific profiling
+ * backends are available.
+ */
+static JSBool
+ControlProfilers(bool toState)
+{
+    JSBool ok = JS_TRUE;
+
+    if (! Probes::ProfilingActive && toState) {
+#if defined(MOZ_SHARK) && defined(__APPLE__)
+        if (!Shark::Start()) {
+            UnsafeError("Failed to start Shark");
+            ok = JS_FALSE;
+        }
+#endif
+#ifdef MOZ_CALLGRIND
+        if (! js_StartCallgrind()) {
+            UnsafeError("Failed to start Callgrind");
+            ok = JS_FALSE;
+        }
+#endif
+    } else if (Probes::ProfilingActive && ! toState) {
+#if defined(MOZ_SHARK) && defined(__APPLE__)
+        Shark::Stop();
+#endif
+#ifdef MOZ_CALLGRIND
+        if (! js_StopCallgrind()) {
+            UnsafeError("failed to stop Callgrind");
+            ok = JS_FALSE;
+        }
+#endif
+    }
+
+    Probes::ProfilingActive = toState;
+
+    return ok;
+}
+
+/*
+ * Pause/resume whatever profiling mechanism is currently compiled
+ * in, if applicable. This will not affect things like dtrace.
+ *
+ * Do not mix calls to these APIs with calls to the individual
+ * profilers' pause/resume functions, because only overall state is
+ * tracked, not the state of each profiler.
+ */
+JS_PUBLIC_API(JSBool)
+JS_PauseProfilers(const char *profileName)
+{
+    return ControlProfilers(false);
+}
+
+JS_PUBLIC_API(JSBool)
+JS_ResumeProfilers(const char *profileName)
+{
+    return ControlProfilers(true);
+}
+
+JS_PUBLIC_API(JSBool)
+JS_DumpProfile(const char *outfile, const char *profileName)
+{
+    JSBool ok = JS_TRUE;
+#ifdef MOZ_CALLGRIND
+    js_DumpCallgrind(outfile);
+#endif
+    return ok;
+}
+
+#ifdef MOZ_PROFILING
+
+struct RequiredStringArg {
+    JSContext *mCx;
+    char *mBytes;
+    RequiredStringArg(JSContext *cx, unsigned argc, jsval *vp, size_t argi, const char *caller)
+        : mCx(cx), mBytes(NULL)
+    {
+        if (argc <= argi) {
+            JS_ReportError(cx, "%s: not enough arguments", caller);
+        } else if (!JSVAL_IS_STRING(JS_ARGV(cx, vp)[argi])) {
+            JS_ReportError(cx, "%s: invalid arguments (string expected)", caller);
+        } else {
+            mBytes = JS_EncodeString(cx, JSVAL_TO_STRING(JS_ARGV(cx, vp)[argi]));
+        }
+    }
+    operator void*() {
+        return (void*) mBytes;
+    }
+    ~RequiredStringArg() {
+        if (mBytes)
+            js_free(mBytes);
+    }
+};
+
+static JSBool
+StartProfiling(JSContext *cx, unsigned argc, jsval *vp)
+{
+    if (argc == 0) {
+        JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StartProfiling(NULL)));
+        return JS_TRUE;
+    }
+
+    RequiredStringArg profileName(cx, argc, vp, 0, "startProfiling");
+    if (!profileName)
+        return JS_FALSE;
+    JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StartProfiling(profileName.mBytes)));
+    return JS_TRUE;
+}
+
+static JSBool
+StopProfiling(JSContext *cx, unsigned argc, jsval *vp)
+{
+    if (argc == 0) {
+        JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StopProfiling(NULL)));
+        return JS_TRUE;
+    }
+
+    RequiredStringArg profileName(cx, argc, vp, 0, "stopProfiling");
+    if (!profileName)
+        return JS_FALSE;
+    JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StopProfiling(profileName.mBytes)));
+    return JS_TRUE;
+}
+
+static JSBool
+PauseProfilers(JSContext *cx, unsigned argc, jsval *vp)
+{
+    if (argc == 0) {
+        JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_PauseProfilers(NULL)));
+        return JS_TRUE;
+    }
+
+    RequiredStringArg profileName(cx, argc, vp, 0, "pauseProfiling");
+    if (!profileName)
+        return JS_FALSE;
+    JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_PauseProfilers(profileName.mBytes)));
+    return JS_TRUE;
+}
+
+static JSBool
+ResumeProfilers(JSContext *cx, unsigned argc, jsval *vp)
+{
+    if (argc == 0) {
+        JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_ResumeProfilers(NULL)));
+        return JS_TRUE;
+    }
+
+    RequiredStringArg profileName(cx, argc, vp, 0, "resumeProfiling");
+    if (!profileName)
+        return JS_FALSE;
+    JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_ResumeProfilers(profileName.mBytes)));
+    return JS_TRUE;
+}
+
+/* Usage: DumpProfile([filename[, profileName]]) */
+static JSBool
+DumpProfile(JSContext *cx, unsigned argc, jsval *vp)
+{
+    bool ret;
+    if (argc == 0) {
+        ret = JS_DumpProfile(NULL, NULL);
+    } else {
+        RequiredStringArg filename(cx, argc, vp, 0, "dumpProfile");
+        if (!filename)
+            return JS_FALSE;
+
+        if (argc == 1) {
+            ret = JS_DumpProfile(filename.mBytes, NULL);
+        } else {
+            RequiredStringArg profileName(cx, argc, vp, 1, "dumpProfile");
+            if (!profileName)
+                return JS_FALSE;
+
+            ret = JS_DumpProfile(filename.mBytes, profileName.mBytes);
+        }
+    }
+
+    JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(ret));
+    return true;
+}
+
+#ifdef MOZ_SHARK
+
+static JSBool
+IgnoreAndReturnTrue(JSContext *cx, unsigned argc, jsval *vp)
+{
+    JS_SET_RVAL(cx, vp, JSVAL_TRUE);
+    return true;
+}
+
+#endif
+
+#ifdef MOZ_CALLGRIND
+static JSBool
+StartCallgrind(JSContext *cx, unsigned argc, jsval *vp)
+{
+    JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_StartCallgrind()));
+    return JS_TRUE;
+}
+
+static JSBool
+StopCallgrind(JSContext *cx, unsigned argc, jsval *vp)
+{
+    JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_StopCallgrind()));
+    return JS_TRUE;
+}
+
+static JSBool
+DumpCallgrind(JSContext *cx, unsigned argc, jsval *vp)
+{
+    if (argc == 0) {
+        JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_DumpCallgrind(NULL)));
+        return JS_TRUE;
+    }
+
+    RequiredStringArg outFile(cx, argc, vp, 0, "dumpCallgrind");
+    if (!outFile)
+        return JS_FALSE;
+
+    JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_DumpCallgrind(outFile.mBytes)));
+    return JS_TRUE;
+}
+#endif
+
+static JSFunctionSpec profiling_functions[] = {
+    JS_FN("startProfiling",  StartProfiling,      1,0),
+    JS_FN("stopProfiling",   StopProfiling,       1,0),
+    JS_FN("pauseProfilers",  PauseProfilers,      1,0),
+    JS_FN("resumeProfilers", ResumeProfilers,     1,0),
+    JS_FN("dumpProfile",     DumpProfile,         2,0),
+#ifdef MOZ_SHARK
+    /* Keep users of the old shark API happy. */
+    JS_FN("connectShark",    IgnoreAndReturnTrue, 0,0),
+    JS_FN("disconnectShark", IgnoreAndReturnTrue, 0,0),
+    JS_FN("startShark",      StartProfiling,      0,0),
+    JS_FN("stopShark",       StopProfiling,       0,0),
+#endif
+#ifdef MOZ_CALLGRIND
+    JS_FN("startCallgrind", StartCallgrind,       0,0),
+    JS_FN("stopCallgrind",  StopCallgrind,        0,0),
+    JS_FN("dumpCallgrind",  DumpCallgrind,        1,0),
+#endif
+    JS_FS_END
+};
+
+#endif
+
+JS_PUBLIC_API(JSBool)
+JS_DefineProfilingFunctions(JSContext *cx, JSObject *objArg)
+{
+    RootedObject obj(cx, objArg);
+
+    assertSameCompartment(cx, obj);
+#ifdef MOZ_PROFILING
+    return JS_DefineFunctions(cx, obj, profiling_functions);
+#else
+    return true;
+#endif
+}
+
+#ifdef MOZ_CALLGRIND
+
+JS_FRIEND_API(JSBool)
+js_StartCallgrind()
+{
+    JS_SILENCE_UNUSED_VALUE_IN_EXPR(CALLGRIND_START_INSTRUMENTATION);
+    JS_SILENCE_UNUSED_VALUE_IN_EXPR(CALLGRIND_ZERO_STATS);
+    return true;
+}
+
+JS_FRIEND_API(JSBool)
+js_StopCallgrind()
+{
+    JS_SILENCE_UNUSED_VALUE_IN_EXPR(CALLGRIND_STOP_INSTRUMENTATION);
+    return true;
+}
+
+JS_FRIEND_API(JSBool)
+js_DumpCallgrind(const char *outfile)
+{
+    if (outfile) {
+        JS_SILENCE_UNUSED_VALUE_IN_EXPR(CALLGRIND_DUMP_STATS_AT(outfile));
+    } else {
+        JS_SILENCE_UNUSED_VALUE_IN_EXPR(CALLGRIND_DUMP_STATS);
+    }
+
+    return true;
+}
+
+#endif /* MOZ_CALLGRIND */
+
+#ifdef __linux__
+
+/*
+ * Code for starting and stopping |perf|, the Linux profiler.
+ *
+ * Output from profiling is written to mozperf.data in your cwd.
+ *
+ * To enable, set MOZ_PROFILE_WITH_PERF=1 in your environment.
+ *
+ * To pass additional parameters to |perf record|, provide them in the
+ * MOZ_PROFILE_PERF_FLAGS environment variable.  If this variable does not
+ * exist, we default it to "--call-graph".  (If you don't want --call-graph but
+ * don't want to pass any other args, define MOZ_PROFILE_PERF_FLAGS to the empty
+ * string.)
+ *
+ * If you include --pid or --output in MOZ_PROFILE_PERF_FLAGS, you're just
+ * asking for trouble.
+ *
+ * Our split-on-spaces logic is lame, so don't expect MOZ_PROFILE_PERF_FLAGS to
+ * work if you pass an argument which includes a space (e.g.
+ * MOZ_PROFILE_PERF_FLAGS="-e 'foo bar'").
+ */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <signal.h>
+
+static bool perfInitialized = false;
+static pid_t perfPid = 0;
+
+JSBool js_StartPerf()
+{
+    const char *outfile = "mozperf.data";
+
+    if (perfPid != 0) {
+        UnsafeError("js_StartPerf: called while perf was already running!\n");
+        return false;
+    }
+
+    // Bail if MOZ_PROFILE_WITH_PERF is empty or undefined.
+    if (!getenv("MOZ_PROFILE_WITH_PERF") ||
+        !strlen(getenv("MOZ_PROFILE_WITH_PERF"))) {
+        return true;
+    }
+
+    /*
+     * Delete mozperf.data the first time through -- we're going to append to it
+     * later on, so we want it to be clean when we start out.
+     */
+    if (!perfInitialized) {
+        perfInitialized = true;
+        unlink(outfile);
+        char cwd[4096];
+        printf("Writing perf profiling data to %s/%s\n",
+               getcwd(cwd, sizeof(cwd)), outfile);
+    }
+
+    pid_t mainPid = getpid();
+
+    pid_t childPid = fork();
+    if (childPid == 0) {
+        /* perf record --append --pid $mainPID --output=$outfile $MOZ_PROFILE_PERF_FLAGS */
+
+        char mainPidStr[16];
+        snprintf(mainPidStr, sizeof(mainPidStr), "%d", mainPid);
+        const char *defaultArgs[] = {"perf", "record", "--append",
+                                     "--pid", mainPidStr, "--output", outfile};
+
+        Vector<const char*, 0, SystemAllocPolicy> args;
+        args.append(defaultArgs, ArrayLength(defaultArgs));
+
+        const char *flags = getenv("MOZ_PROFILE_PERF_FLAGS");
+        if (!flags) {
+            flags = "--call-graph";
+        }
+
+        // Split |flags| on spaces.  (Don't bother to free it -- we're going to
+        // exec anyway.)
+        char *toksave;
+        char *tok = strtok_r(strdup(flags), " ", &toksave);
+        while (tok) {
+            args.append(tok);
+            tok = strtok_r(NULL, " ", &toksave);
+        }
+
+        args.append((char*) NULL);
+
+        execvp("perf", const_cast<char**>(args.begin()));
+
+        /* Reached only if execlp fails. */
+        fprintf(stderr, "Unable to start perf.\n");
+        exit(1);
+    }
+    else if (childPid > 0) {
+        perfPid = childPid;
+
+        /* Give perf a chance to warm up. */
+        usleep(500 * 1000);
+        return true;
+    }
+    else {
+        UnsafeError("js_StartPerf: fork() failed\n");
+        return false;
+    }
+}
+
+JSBool js_StopPerf()
+{
+    if (perfPid == 0) {
+        UnsafeError("js_StopPerf: perf is not running.\n");
+        return true;
+    }
+
+    if (kill(perfPid, SIGINT)) {
+        UnsafeError("js_StopPerf: kill failed\n");
+
+        // Try to reap the process anyway.
+        waitpid(perfPid, NULL, WNOHANG);
+    }
+    else {
+        waitpid(perfPid, NULL, 0);
+    }
+
+    perfPid = 0;
+    return true;
+}
+
+#endif /* __linux__ */
new file mode 100644
--- /dev/null
+++ b/js/src/builtin/Profilers.h
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sw=4 et tw=99:
+ *
+ * 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/. */
+
+/*
+ * Functions for controlling profilers from within JS: Valgrind, Perf,
+ * Shark, etc.
+ */
+#ifndef Profilers_h___
+#define Profilers_h___
+
+#include "jsapi.h"
+
+/**
+ * Start any profilers that are available and have been configured on for this
+ * platform. This is NOT thread safe.
+ *
+ * The profileName is used by some profilers to describe the current profiling
+ * run. It may be used for part of the filename of the output, but the
+ * specifics depend on the profiler. Many profilers will ignore it. Passing in
+ * NULL is legal; some profilers may use it to output to stdout or similar.
+ *
+ * Returns true if no profilers fail to start.
+ */
+extern JS_PUBLIC_API(JSBool)
+JS_StartProfiling(const char *profileName);
+
+/**
+ * Stop any profilers that were previously started with JS_StartProfiling.
+ * Returns true if no profilers fail to stop.
+ */
+extern JS_PUBLIC_API(JSBool)
+JS_StopProfiling(const char *profileName);
+
+/**
+ * Write the current profile data to the given file, if applicable to whatever
+ * profiler is being used.
+ */
+extern JS_PUBLIC_API(JSBool)
+JS_DumpProfile(const char *outfile, const char *profileName);
+
+/**
+ * Pause currently active profilers (only supported by some profilers). Returns
+ * whether any profilers failed to pause. (Profilers that do not support
+ * pause/resume do not count.)
+ */
+extern JS_PUBLIC_API(JSBool)
+JS_PauseProfilers(const char *profileName);
+
+/**
+ * Resume suspended profilers
+ */
+extern JS_PUBLIC_API(JSBool)
+JS_ResumeProfilers(const char *profileName);
+
+/**
+ * The profiling API calls are not able to report errors, so they use a
+ * thread-unsafe global memory buffer to hold the last error encountered. This
+ * should only be called after something returns false.
+ */
+JS_PUBLIC_API(const char *)
+JS_UnsafeGetLastProfilingError();
+
+#ifdef MOZ_CALLGRIND
+
+extern JS_FRIEND_API(JSBool)
+js_StopCallgrind();
+
+extern JS_FRIEND_API(JSBool)
+js_StartCallgrind();
+
+extern JS_FRIEND_API(JSBool)
+js_DumpCallgrind(const char *outfile);
+
+#endif /* MOZ_CALLGRIND */
+
+#ifdef __linux__
+
+extern JS_FRIEND_API(JSBool)
+js_StartPerf();
+
+extern JS_FRIEND_API(JSBool)
+js_StopPerf();
+
+#endif /* __linux__ */
+
+#endif /* Profilers_h___ */
--- a/js/src/ion/Ion.cpp
+++ b/js/src/ion/Ion.cpp
@@ -1042,17 +1042,17 @@ IonCompile(JSContext *cx, JSScript *scri
     return true;
 }
 
 bool
 TestIonCompile(JSContext *cx, JSScript *script, JSFunction *fun, jsbytecode *osrPc, bool constructing)
 {
     if (!IonCompile<TestCompiler>(cx, script, fun, osrPc, constructing)) {
         if (!cx->isExceptionPending())
-            ForbidCompilation(script);
+            ForbidCompilation(cx, script);
         return false;
     }
     return true;
 }
 
 static bool
 CheckFrame(StackFrame *fp)
 {
@@ -1197,26 +1197,26 @@ ion::CanEnterAtBranch(JSContext *cx, Han
         return Method_Skipped;
 
     // Optionally ignore on user request.
     if (!js_IonOptions.osr)
         return Method_Skipped;
 
     // Mark as forbidden if frame can't be handled.
     if (!CheckFrame(fp)) {
-        ForbidCompilation(script);
+        ForbidCompilation(cx, script);
         return Method_CantCompile;
     }
 
     // Attempt compilation. Returns Method_Compiled if already compiled.
     JSFunction *fun = fp->isFunctionFrame() ? fp->fun() : NULL;
     MethodStatus status = Compile<TestCompiler>(cx, script, fun, pc, false);
     if (status != Method_Compiled) {
         if (status == Method_CantCompile)
-            ForbidCompilation(script);
+            ForbidCompilation(cx, script);
         return status;
     }
 
     if (script->ion->osrPc() != pc)
         return Method_Skipped;
 
     // This can GC, so afterward, script->ion is not guaranteed to be valid.
     if (!cx->compartment->ionCompartment()->enterJIT(cx))
@@ -1253,26 +1253,26 @@ ion::CanEnter(JSContext *cx, HandleScrip
         RootedObject obj(cx, js_CreateThisForFunction(cx, callee, newType));
         if (!obj)
             return Method_Skipped;
         fp->functionThis().setObject(*obj);
     }
 
     // Mark as forbidden if frame can't be handled.
     if (!CheckFrame(fp)) {
-        ForbidCompilation(script);
+        ForbidCompilation(cx, script);
         return Method_CantCompile;
     }
 
     // Attempt compilation. Returns Method_Compiled if already compiled.
     JSFunction *fun = fp->isFunctionFrame() ? fp->fun() : NULL;
     MethodStatus status = Compile<TestCompiler>(cx, script, fun, NULL, fp->isConstructing());
     if (status != Method_Compiled) {
         if (status == Method_CantCompile)
-            ForbidCompilation(script);
+            ForbidCompilation(cx, script);
         return status;
     }
 
     // This can GC, so afterward, script->ion is not guaranteed to be valid.
     if (!cx->compartment->ionCompartment()->enterJIT(cx))
         return Method_Error;
 
     if (!script->ion)
@@ -1629,17 +1629,19 @@ ion::Invalidate(JSContext *cx, const Vec
 }
 
 bool
 ion::Invalidate(JSContext *cx, JSScript *script, bool resetUses)
 {
     JS_ASSERT(script->hasIonScript());
 
     Vector<types::RecompileInfo> scripts(cx);
-    scripts.append(script->ionScript()->recompileInfo());
+    if (!scripts.append(script->ionScript()->recompileInfo()))
+        return false;
+
     Invalidate(cx, scripts, resetUses);
     return true;
 }
 
 void
 ion::FinishInvalidation(FreeOp *fop, JSScript *script)
 {
     if (!script->hasIonScript())
@@ -1662,27 +1664,29 @@ ion::FinishInvalidation(FreeOp *fop, JSS
 
 void
 ion::MarkFromIon(JSCompartment *comp, Value *vp)
 {
     gc::MarkValueUnbarriered(comp->barrierTracer(), vp, "write barrier");
 }
 
 void
-ion::ForbidCompilation(JSScript *script)
+ion::ForbidCompilation(JSContext *cx, JSScript *script)
 {
     IonSpew(IonSpew_Abort, "Disabling Ion compilation of script %s:%d",
             script->filename, script->lineno);
 
-    if (script->hasIonScript() && script->compartment()->needsBarrier()) {
-        // We're about to remove edges from the JSScript to gcthings
-        // embedded in the IonScript. Perform one final trace of the
-        // IonScript for the incremental GC, as it must know about
-        // those edges.
-        IonScript::Trace(script->compartment()->barrierTracer(), script->ion);
+    if (script->hasIonScript()) {
+        // It is only safe to modify script->ion if the script is not currently
+        // running, because IonFrameIterator needs to tell what ionScript to
+        // use (either the one on the JSScript, or the one hidden in the
+        // breadcrumbs Invalidation() leaves). Therefore, if invalidation
+        // fails, we cannot disable the script.
+        if (!Invalidate(cx, script, false))
+            return;
     }
 
     script->ion = ION_DISABLED_SCRIPT;
 }
 
 uint32_t
 ion::UsesBeforeIonRecompile(JSScript *script, jsbytecode *pc)
 {
--- a/js/src/ion/Ion.h
+++ b/js/src/ion/Ion.h
@@ -247,16 +247,16 @@ void AttachFinishedCompilations(JSContex
 void FinishOffThreadBuilder(IonBuilder *builder);
 bool TestIonCompile(JSContext *cx, JSScript *script, JSFunction *fun, jsbytecode *osrPc, bool constructing);
 
 static inline bool IsEnabled(JSContext *cx)
 {
     return cx->hasRunOption(JSOPTION_ION) && cx->typeInferenceEnabled();
 }
 
-void ForbidCompilation(JSScript *script);
+void ForbidCompilation(JSContext *cx, JSScript *script);
 uint32_t UsesBeforeIonRecompile(JSScript *script, jsbytecode *pc);
 
 } // namespace ion
 } // namespace js
 
 #endif // jsion_ion_h__
 
--- a/js/src/ion/VMFunctions.cpp
+++ b/js/src/ion/VMFunctions.cpp
@@ -34,24 +34,23 @@ InvokeFunction(JSContext *cx, JSFunction
 
     // In order to prevent massive bouncing between Ion and JM, see if we keep
     // hitting functions that are uncompilable.
     
     if (fun->isInterpreted() && !fun->script()->canIonCompile()) {
         JSScript *script = GetTopIonJSScript(cx);
         if (script->hasIonScript() && ++script->ion->slowCallCount >= js_IonOptions.slowCallLimit) {
             AutoFlushCache afc("InvokeFunction");
-            Invalidate(cx, script, false);
 
-            // Finally, poison the script so we don't try to run it again
-            ForbidCompilation(script);
+            // Poison the script so we don't try to run it again. This will
+            // trigger invalidation.
+            ForbidCompilation(cx, script);
         }
     }
 
-
     // TI will return false for monitorReturnTypes, meaning there is no
     // TypeBarrier or Monitor instruction following this. However, we need to
     // explicitly monitor if the callee has not been analyzed yet. We special
     // case this to avoid the cost of ion::GetPcScript if we must take this
     // path frequently.
     bool needsMonitor = ShouldMonitorReturnType(fun);
 
     // Data in the argument vector is arranged for a JIT -> JIT call.
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/evaluate-restore-options.js
@@ -0,0 +1,11 @@
+// Bug 791157: the shell 'evaluate' function should properly restore the
+// context's options.
+
+try {
+    evaluate('%', {noScriptRval: true});
+} catch(e) {}
+new Function("");
+
+try {
+  evaluate('new Function("");', {noScriptRval: true});
+} catch (e) {}
--- a/js/src/jit-test/tests/parallelarray/length-3.js
+++ b/js/src/jit-test/tests/parallelarray/length-3.js
@@ -1,11 +1,9 @@
 function testLength() {
-  // Test length attributes
+  // Test length immutability.
   var p = new ParallelArray([1,2,3,4]);
-  var desc = Object.getOwnPropertyDescriptor(p, "length");
-  assertEq(desc.enumerable, false);
-  assertEq(desc.configurable, false);
-  assertEq(desc.writable, false);
-  assertEq(desc.value, 4);
+  p.length = 0;
+  assertEq(p[0], 1);
+  assertEq(p.length, 4);
 }
 
 testLength();
--- a/js/src/jit-test/tests/parallelarray/shape-2.js
+++ b/js/src/jit-test/tests/parallelarray/shape-2.js
@@ -2,15 +2,14 @@ load(libdir + "eqArrayHelper.js");
 
 function testShape() {
   // Test higher dimension shape up to 8D
   var shape = [];
   for (var i = 0; i < 8; i++) {
     shape.push(i+1);
     var p = new ParallelArray(shape, function () { return 0; });
     // Test shape identity and shape
-    assertEq(p.shape, p.shape);
+    assertEqArray(p.shape, shape);
     assertEq(p.shape !== shape, true);
-    assertEqArray(p.shape, shape);
   }
 }
 
 testShape();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/parallelarray/shape-4.js
@@ -0,0 +1,12 @@
+load(libdir + "eqArrayHelper.js");
+
+function testShape() {
+  // Test shape immutability.
+  var p = new ParallelArray([1,2,3,4]);
+  p.shape[0] = 0;
+  p.shape[1] = 42;
+  assertEq(p[0], 1);
+  assertEqArray(p.shape, [4]);
+}
+
+testShape();
--- a/js/src/jit-test/tests/parallelarray/surfaces-1.js
+++ b/js/src/jit-test/tests/parallelarray/surfaces-1.js
@@ -1,8 +1,10 @@
+load(libdir + "eqArrayHelper.js");
+
 // ParallelArray surfaces
 
 var desc = Object.getOwnPropertyDescriptor(this, "ParallelArray");
 assertEq(desc.enumerable, false);
 assertEq(desc.configurable, true);
 assertEq(desc.writable, true);
 
 assertEq(typeof ParallelArray, 'function');
@@ -30,8 +32,19 @@ function checkMethod(name, arity) {
 checkMethod("map", 1);
 checkMethod("reduce", 1);
 checkMethod("scan", 1);
 checkMethod("scatter", 1);
 checkMethod("filter", 1);
 checkMethod("flatten", 0);
 checkMethod("partition", 1);
 checkMethod("get", 1);
+
+function checkAccessor(name) {
+  var desc = Object.getOwnPropertyDescriptor(ParallelArray.prototype, name);
+  assertEq(desc.enumerable, false);
+  assertEq(desc.configurable, false);
+  assertEq(typeof desc.get, 'function');
+  assertEq(desc.set, undefined);
+}
+
+checkAccessor("length");
+checkAccessor("shape");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/parallelarray/surfaces-3.js
@@ -0,0 +1,3 @@
+// ParallelArray objects are frozen.
+
+assertEq(Object.isFrozen(new ParallelArray), true);
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -349,30 +349,29 @@ MSG_DEF(JSMSG_PARAMETER_AFTER_REST,   29
 MSG_DEF(JSMSG_NO_REST_NAME,           296, 0, JSEXN_SYNTAXERR, "no parameter name after ...")
 MSG_DEF(JSMSG_ARGUMENTS_AND_REST,     297, 0, JSEXN_SYNTAXERR, "'arguments' object may not be used in conjunction with a rest parameter")
 MSG_DEF(JSMSG_FUNCTION_ARGUMENTS_AND_REST, 298, 0, JSEXN_ERR, "the 'arguments' property of a function with a rest parameter may not be used")
 MSG_DEF(JSMSG_REST_WITH_DEFAULT,      299, 0, JSEXN_SYNTAXERR, "rest parameter may not have a default")
 MSG_DEF(JSMSG_NONDEFAULT_FORMAL_AFTER_DEFAULT, 300, 0, JSEXN_SYNTAXERR, "parameter(s) with default followed by parameter without default")
 MSG_DEF(JSMSG_YIELD_IN_DEFAULT,       301, 0, JSEXN_SYNTAXERR, "yield in default expression")
 MSG_DEF(JSMSG_INTRINSIC_NOT_DEFINED,  302, 1, JSEXN_REFERENCEERR, "no intrinsic function {0}")
 MSG_DEF(JSMSG_ALREADY_HAS_SOURCEMAP,  303, 1, JSEXN_ERR,      "{0} is being assigned a source map, yet already has one")
-MSG_DEF(JSMSG_PAR_ARRAY_IMMUTABLE,    304, 0, JSEXN_TYPEERR, "ParallelArray objects are immutable")
-MSG_DEF(JSMSG_PAR_ARRAY_BAD_ARG,      305, 1, JSEXN_TYPEERR, "invalid ParallelArray{0} argument")
-MSG_DEF(JSMSG_PAR_ARRAY_BAD_PARTITION, 306, 0, JSEXN_ERR, "argument must be divisible by outermost dimension")
-MSG_DEF(JSMSG_PAR_ARRAY_REDUCE_EMPTY, 307, 0, JSEXN_ERR, "cannot reduce empty ParallelArray object")
-MSG_DEF(JSMSG_PAR_ARRAY_ALREADY_FLAT, 308, 0, JSEXN_ERR, "cannot flatten 1-dimensional ParallelArray object")
-MSG_DEF(JSMSG_PAR_ARRAY_SCATTER_CONFLICT, 309, 0, JSEXN_ERR, "no conflict resolution function provided")
-MSG_DEF(JSMSG_PAR_ARRAY_SCATTER_BOUNDS, 310, 0, JSEXN_ERR, "index in scatter vector out of bounds")
-MSG_DEF(JSMSG_CANT_REPORT_NC_AS_NE,   311, 0, JSEXN_TYPEERR, "proxy can't report a non-configurable own property as non-existent")
-MSG_DEF(JSMSG_CANT_REPORT_E_AS_NE,    312, 0, JSEXN_TYPEERR, "proxy can't report an existing own property as non-existent on a non-extensible object")
-MSG_DEF(JSMSG_CANT_REPORT_NEW,        313, 0, JSEXN_TYPEERR, "proxy can't report a new property on a non-extensible object")
-MSG_DEF(JSMSG_CANT_REPORT_INVALID,    314, 0, JSEXN_TYPEERR, "proxy can't report an incompatible property descriptor")
-MSG_DEF(JSMSG_CANT_REPORT_NE_AS_NC,   315, 0, JSEXN_TYPEERR, "proxy can't report a non-existent property as non-configurable")
-MSG_DEF(JSMSG_CANT_DEFINE_NEW,        316, 0, JSEXN_TYPEERR, "proxy can't define a new property on a non-extensible object")
-MSG_DEF(JSMSG_CANT_DEFINE_INVALID,    317, 0, JSEXN_TYPEERR, "proxy can't define an incompatible property descriptor")
-MSG_DEF(JSMSG_CANT_DEFINE_NE_AS_NC,   318, 0, JSEXN_TYPEERR, "proxy can't define a non-existent property as non-configurable")
-MSG_DEF(JSMSG_INVALID_TRAP_RESULT,    319, 2, JSEXN_TYPEERR, "trap {1} for {0} returned an invalid result")
-MSG_DEF(JSMSG_CANT_SKIP_NC,           320, 0, JSEXN_TYPEERR, "proxy can't skip a non-configurable property")
-MSG_DEF(JSMSG_MUST_REPORT_SAME_VALUE, 321, 0, JSEXN_TYPEERR, "proxy must report the same value for a non-writable, non-configurable property")
-MSG_DEF(JSMSG_MUST_REPORT_UNDEFINED,  322, 0, JSEXN_TYPEERR, "proxy must report undefined for a non-configurable accessor property without a getter")
-MSG_DEF(JSMSG_CANT_SET_NW_NC,         323, 0, JSEXN_TYPEERR, "proxy can't successfully set a non-writable, non-configurable property")
-MSG_DEF(JSMSG_CANT_SET_WO_SETTER,     324, 0, JSEXN_TYPEERR, "proxy can't succesfully set an accessor property without a setter")
-MSG_DEF(JSMSG_DEBUG_BAD_REFERENT,     325, 2, JSEXN_TYPEERR, "{0} does not refer to {1}")
+MSG_DEF(JSMSG_PAR_ARRAY_BAD_ARG,      304, 1, JSEXN_TYPEERR, "invalid ParallelArray{0} argument")
+MSG_DEF(JSMSG_PAR_ARRAY_BAD_PARTITION, 305, 0, JSEXN_ERR, "argument must be divisible by outermost dimension")
+MSG_DEF(JSMSG_PAR_ARRAY_REDUCE_EMPTY, 306, 0, JSEXN_ERR, "cannot reduce empty ParallelArray object")
+MSG_DEF(JSMSG_PAR_ARRAY_ALREADY_FLAT, 307, 0, JSEXN_ERR, "cannot flatten 1-dimensional ParallelArray object")
+MSG_DEF(JSMSG_PAR_ARRAY_SCATTER_CONFLICT, 308, 0, JSEXN_ERR, "no conflict resolution function provided")
+MSG_DEF(JSMSG_PAR_ARRAY_SCATTER_BOUNDS, 309, 0, JSEXN_ERR, "index in scatter vector out of bounds")
+MSG_DEF(JSMSG_CANT_REPORT_NC_AS_NE,   310, 0, JSEXN_TYPEERR, "proxy can't report a non-configurable own property as non-existent")
+MSG_DEF(JSMSG_CANT_REPORT_E_AS_NE,    311, 0, JSEXN_TYPEERR, "proxy can't report an existing own property as non-existent on a non-extensible object")
+MSG_DEF(JSMSG_CANT_REPORT_NEW,        312, 0, JSEXN_TYPEERR, "proxy can't report a new property on a non-extensible object")
+MSG_DEF(JSMSG_CANT_REPORT_INVALID,    313, 0, JSEXN_TYPEERR, "proxy can't report an incompatible property descriptor")
+MSG_DEF(JSMSG_CANT_REPORT_NE_AS_NC,   314, 0, JSEXN_TYPEERR, "proxy can't report a non-existent property as non-configurable")
+MSG_DEF(JSMSG_CANT_DEFINE_NEW,        315, 0, JSEXN_TYPEERR, "proxy can't define a new property on a non-extensible object")
+MSG_DEF(JSMSG_CANT_DEFINE_INVALID,    316, 0, JSEXN_TYPEERR, "proxy can't define an incompatible property descriptor")
+MSG_DEF(JSMSG_CANT_DEFINE_NE_AS_NC,   317, 0, JSEXN_TYPEERR, "proxy can't define a non-existent property as non-configurable")
+MSG_DEF(JSMSG_INVALID_TRAP_RESULT,    318, 2, JSEXN_TYPEERR, "trap {1} for {0} returned an invalid result")
+MSG_DEF(JSMSG_CANT_SKIP_NC,           319, 0, JSEXN_TYPEERR, "proxy can't skip a non-configurable property")
+MSG_DEF(JSMSG_MUST_REPORT_SAME_VALUE, 320, 0, JSEXN_TYPEERR, "proxy must report the same value for a non-writable, non-configurable property")
+MSG_DEF(JSMSG_MUST_REPORT_UNDEFINED,  321, 0, JSEXN_TYPEERR, "proxy must report undefined for a non-configurable accessor property without a getter")
+MSG_DEF(JSMSG_CANT_SET_NW_NC,         322, 0, JSEXN_TYPEERR, "proxy can't successfully set a non-writable, non-configurable property")
+MSG_DEF(JSMSG_CANT_SET_WO_SETTER,     323, 0, JSEXN_TYPEERR, "proxy can't succesfully set an accessor property without a setter")
+MSG_DEF(JSMSG_DEBUG_BAD_REFERENT,     324, 2, JSEXN_TYPEERR, "{0} does not refer to {1}")
--- a/js/src/jsclone.cpp
+++ b/js/src/jsclone.cpp
@@ -444,33 +444,37 @@ bool
 JSStructuredCloneWriter::writeArrayBuffer(JSHandleObject obj)
 {
     ArrayBufferObject &buffer = obj->asArrayBuffer();
     return out.writePair(SCTAG_ARRAY_BUFFER_OBJECT, buffer.byteLength()) &&
            out.writeBytes(buffer.dataPointer(), buffer.byteLength());
 }
 
 bool
-JSStructuredCloneWriter::startObject(JSHandleObject obj)
+JSStructuredCloneWriter::startObject(JSHandleObject obj, bool *backref)
 {
-    JS_ASSERT(obj->isArray() || obj->isObject());
-
     /* Handle cycles in the object graph. */
     CloneMemory::AddPtr p = memory.lookupForAdd(obj);
-    if (p)
+    if ((*backref = p))
         return out.writePair(SCTAG_BACK_REFERENCE_OBJECT, p->value);
     if (!memory.add(p, obj, memory.count()))
         return false;
 
     if (memory.count() == UINT32_MAX) {
         JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL,
                              JSMSG_NEED_DIET, "object graph to serialize");
         return false;
     }
 
+    return true;
+}
+
+bool
+JSStructuredCloneWriter::traverseObject(JSHandleObject obj)
+{
     /*
      * Get enumerable property ids and put them in reverse order so that they
      * will come off the stack in forward order.
      */
     size_t initialLength = ids.length();
     if (!GetPropertyNames(context(), obj, JSITER_OWNONLY, &ids))
         return false;
     jsid *begin = ids.begin() + initialLength, *end = ids.end();
@@ -506,29 +510,36 @@ JSStructuredCloneWriter::startWrite(cons
 
         // The object might be a security wrapper. See if we can clone what's
         // behind it. If we can, unwrap the object.
         obj = UnwrapObjectChecked(context(), obj);
         if (!obj)
             return false;
 
         AutoCompartment ac(context(), obj);
+
+        bool backref;
+        if (!startObject(obj, &backref))
+            return false;
+        if (backref)
+            return true;
+
         if (obj->isRegExp()) {
             RegExpObject &reobj = obj->asRegExp();
             return out.writePair(SCTAG_REGEXP_OBJECT, reobj.getFlags()) &&
                    writeString(SCTAG_STRING, reobj.getSource());
         } else if (obj->isDate()) {
             double d = js_DateGetMsecSinceEpoch(context(), obj);
             return out.writePair(SCTAG_DATE_OBJECT, 0) && out.writeDouble(d);
-        } else if (obj->isObject() || obj->isArray()) {
-            return startObject(obj);
         } else if (obj->isTypedArray()) {
             return writeTypedArray(obj);
         } else if (obj->isArrayBuffer() && obj->asArrayBuffer().hasData()) {
             return writeArrayBuffer(obj);
+        } else if (obj->isObject() || obj->isArray()) {
+            return traverseObject(obj);
         } else if (obj->isBoolean()) {
             return out.writePair(SCTAG_BOOLEAN_OBJECT, obj->asBoolean().unbox());
         } else if (obj->isNumber()) {
             return out.writePair(SCTAG_NUMBER_OBJECT, 0) &&
                    out.writeDouble(obj->asNumber().unbox());
         } else if (obj->isString()) {
             return writeString(SCTAG_STRING_OBJECT, obj->asString().unbox());
         }
@@ -816,59 +827,68 @@ JSStructuredCloneReader::startRead(Value
         break;
       }
 
       case SCTAG_ARRAY_OBJECT:
       case SCTAG_OBJECT_OBJECT: {
         JSObject *obj = (tag == SCTAG_ARRAY_OBJECT)
                         ? NewDenseEmptyArray(context())
                         : NewBuiltinClassInstance(context(), &ObjectClass);
-        if (!obj || !objs.append(ObjectValue(*obj)) ||
-            !allObjs.append(ObjectValue(*obj)))
+        if (!obj || !objs.append(ObjectValue(*obj)))
             return false;
         vp->setObject(*obj);
         break;
       }
 
       case SCTAG_BACK_REFERENCE_OBJECT: {
         if (data >= allObjs.length()) {
             JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL,
                                  JSMSG_SC_BAD_SERIALIZED_DATA,
-                                 "invalid input");
+                                 "invalid back reference in input");
+            return false;
         }
         *vp = allObjs[data];
-        break;
+        return true;
       }
 
       case SCTAG_ARRAY_BUFFER_OBJECT:
-        return readArrayBuffer(data, vp);
+        if (!readArrayBuffer(data, vp))
+            return false;
+        break;
 
       default: {
         if (tag <= SCTAG_FLOAT_MAX) {
             double d = ReinterpretPairAsDouble(tag, data);
             if (!checkDouble(d))
                 return false;
             vp->setNumber(d);
             break;
         }
 
-        if (SCTAG_TYPED_ARRAY_MIN <= tag && tag <= SCTAG_TYPED_ARRAY_MAX)
-            return readTypedArray(tag, data, vp);
+        if (SCTAG_TYPED_ARRAY_MIN <= tag && tag <= SCTAG_TYPED_ARRAY_MAX) {
+            if (!readTypedArray(tag, data, vp))
+                return false;
+            break;
+        }
 
         if (!callbacks || !callbacks->read) {
             JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA,
                                  "unsupported type");
             return false;
         }
         JSObject *obj = callbacks->read(context(), this, tag, data, closure);
         if (!obj)
             return false;
         vp->setObject(*obj);
       }
     }
+
+    if (vp->isObject() && !allObjs.append(*vp))
+        return false;
+
     return true;
 }
 
 bool
 JSStructuredCloneReader::readId(jsid *idp)
 {
     uint32_t tag, data;
     if (!in.readPair(&tag, &data))
--- a/js/src/jsclone.h
+++ b/js/src/jsclone.h
@@ -129,18 +129,19 @@ struct JSStructuredCloneWriter {
 
   private:
     JSContext *context() { return out.context(); }
 
     bool writeString(uint32_t tag, JSString *str);
     bool writeId(jsid id);
     bool writeArrayBuffer(JSHandleObject obj);
     bool writeTypedArray(JSHandleObject obj);
-    bool startObject(JSHandleObject obj);
+    bool startObject(JSHandleObject obj, bool *backref);
     bool startWrite(const js::Value &v);
+    bool traverseObject(JSHandleObject obj);
 
     inline void checkStack();
 
     js::SCOutput &out;
 
     // Vector of objects with properties remaining to be written.
     //
     // NB: These can span multiple compartments, so the compartment must be
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -4,17 +4,16 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*
  * JS debugging API.
  */
 #include <string.h>
-#include <stdarg.h>
 #include "jsprvtd.h"
 #include "jstypes.h"
 #include "jsutil.h"
 #include "jsclist.h"
 #include "jsapi.h"
 #include "jscntxt.h"
 #include "jsversion.h"
 #include "jsdbgapi.h"
@@ -43,20 +42,16 @@
 #include "jsscopeinlines.h"
 #include "jsscriptinlines.h"
 
 #include "vm/Stack-inl.h"
 
 #include "jsautooplen.h"
 #include "mozilla/Util.h"
 
-#ifdef __APPLE__
-#include "devtools/sharkctl.h"
-#endif
-
 using namespace js;
 using namespace js::gc;
 using namespace mozilla;
 
 JS_PUBLIC_API(JSBool)
 JS_GetDebugMode(JSContext *cx)
 {
     return cx->compartment->debugMode();
@@ -1059,662 +1054,16 @@ js_RevertVersion(JSContext *cx)
 JS_PUBLIC_API(const JSDebugHooks *)
 JS_GetGlobalDebugHooks(JSRuntime *rt)
 {
     return &rt->debugHooks;
 }
 
 /************************************************************************/
 
-/* Profiling-related API */
-
-/* Thread-unsafe error management */
-
-static char gLastError[2000];
-
-static void
-#ifdef __GNUC__
-__attribute__((unused,format(printf,1,2)))
-#endif
-UnsafeError(const char *format, ...)
-{
-    va_list args;
-    va_start(args, format);
-    (void) vsnprintf(gLastError, sizeof(gLastError), format, args);
-    va_end(args);
-
-    gLastError[sizeof(gLastError) - 1] = '\0';
-}
-
-JS_PUBLIC_API(const char *)
-JS_UnsafeGetLastProfilingError()
-{
-    return gLastError;
-}
-
-JS_PUBLIC_API(JSBool)
-JS_StartProfiling(const char *profileName)
-{
-    JSBool ok = JS_TRUE;
-#if defined(MOZ_SHARK) && defined(__APPLE__)
-    if (!Shark::Start()) {
-        UnsafeError("Failed to start Shark for %s", profileName);
-        ok = JS_FALSE;
-    }
-#endif
-#if 0 //def MOZ_VTUNE
-    if (!js_StartVtune(profileName))
-        ok = JS_FALSE;
-#endif
-#ifdef __linux__
-    if (!js_StartPerf())
-        ok = JS_FALSE;
-#endif
-    return ok;
-}
-
-JS_PUBLIC_API(JSBool)
-JS_StopProfiling(const char *profileName)
-{
-    JSBool ok = JS_TRUE;
-#if defined(MOZ_SHARK) && defined(__APPLE__)
-    Shark::Stop();
-#endif
-#if 0 //def MOZ_VTUNE
-    if (!js_StopVtune())
-        ok = JS_FALSE;
-#endif
-#ifdef __linux__
-    if (!js_StopPerf())
-        ok = JS_FALSE;
-#endif
-    return ok;
-}
-
-/*
- * Start or stop whatever platform- and configuration-specific profiling
- * backends are available.
- */
-static JSBool
-ControlProfilers(bool toState)
-{
-    JSBool ok = JS_TRUE;
-
-    if (! Probes::ProfilingActive && toState) {
-#if defined(MOZ_SHARK) && defined(__APPLE__)
-        if (!Shark::Start()) {
-            UnsafeError("Failed to start Shark");
-            ok = JS_FALSE;
-        }
-#endif
-#ifdef MOZ_CALLGRIND
-        if (! js_StartCallgrind()) {
-            UnsafeError("Failed to start Callgrind");
-            ok = JS_FALSE;
-        }
-#endif
-#if 0 //def MOZ_VTUNE
-        if (! js_ResumeVtune())
-            ok = JS_FALSE;
-#endif
-    } else if (Probes::ProfilingActive && ! toState) {
-#if defined(MOZ_SHARK) && defined(__APPLE__)
-        Shark::Stop();
-#endif
-#ifdef MOZ_CALLGRIND
-        if (! js_StopCallgrind()) {
-            UnsafeError("failed to stop Callgrind");
-            ok = JS_FALSE;
-        }
-#endif
-#if 0 //def MOZ_VTUNE
-        if (! js_PauseVtune())
-            ok = JS_FALSE;
-#endif
-    }
-
-    Probes::ProfilingActive = toState;
-
-    return ok;
-}
-
-/*
- * Pause/resume whatever profiling mechanism is currently compiled
- * in, if applicable. This will not affect things like dtrace.
- *
- * Do not mix calls to these APIs with calls to the individual
- * profilers' pause/resume functions, because only overall state is
- * tracked, not the state of each profiler.
- */
-JS_PUBLIC_API(JSBool)
-JS_PauseProfilers(const char *profileName)
-{
-    return ControlProfilers(false);
-}
-
-JS_PUBLIC_API(JSBool)
-JS_ResumeProfilers(const char *profileName)
-{
-    return ControlProfilers(true);
-}
-
-JS_PUBLIC_API(JSBool)
-JS_DumpProfile(const char *outfile, const char *profileName)
-{
-    JSBool ok = JS_TRUE;
-#ifdef MOZ_CALLGRIND
-    js_DumpCallgrind(outfile);
-#endif
-    return ok;
-}
-
-#ifdef MOZ_PROFILING
-
-struct RequiredStringArg {
-    JSContext *mCx;
-    char *mBytes;
-    RequiredStringArg(JSContext *cx, unsigned argc, jsval *vp, size_t argi, const char *caller)
-        : mCx(cx), mBytes(NULL)
-    {
-        if (argc <= argi) {
-            JS_ReportError(cx, "%s: not enough arguments", caller);
-        } else if (!JSVAL_IS_STRING(JS_ARGV(cx, vp)[argi])) {
-            JS_ReportError(cx, "%s: invalid arguments (string expected)", caller);
-        } else {
-            mBytes = JS_EncodeString(cx, JSVAL_TO_STRING(JS_ARGV(cx, vp)[argi]));
-        }
-    }
-    operator void*() {
-        return (void*) mBytes;
-    }
-    ~RequiredStringArg() {
-        if (mBytes)
-            js_free(mBytes);
-    }
-};
-
-static JSBool
-StartProfiling(JSContext *cx, unsigned argc, jsval *vp)
-{
-    if (argc == 0) {
-        JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StartProfiling(NULL)));
-        return JS_TRUE;
-    }
-
-    RequiredStringArg profileName(cx, argc, vp, 0, "startProfiling");
-    if (!profileName)
-        return JS_FALSE;
-    JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StartProfiling(profileName.mBytes)));
-    return JS_TRUE;
-}
-
-static JSBool
-StopProfiling(JSContext *cx, unsigned argc, jsval *vp)
-{
-    if (argc == 0) {
-        JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StopProfiling(NULL)));
-        return JS_TRUE;
-    }
-
-    RequiredStringArg profileName(cx, argc, vp, 0, "stopProfiling");
-    if (!profileName)
-        return JS_FALSE;
-    JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StopProfiling(profileName.mBytes)));
-    return JS_TRUE;
-}
-
-static JSBool
-PauseProfilers(JSContext *cx, unsigned argc, jsval *vp)
-{
-    if (argc == 0) {
-        JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_PauseProfilers(NULL)));
-        return JS_TRUE;
-    }
-
-    RequiredStringArg profileName(cx, argc, vp, 0, "pauseProfiling");
-    if (!profileName)
-        return JS_FALSE;
-    JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_PauseProfilers(profileName.mBytes)));
-    return JS_TRUE;
-}
-
-static JSBool
-ResumeProfilers(JSContext *cx, unsigned argc, jsval *vp)
-{
-    if (argc == 0) {
-        JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_ResumeProfilers(NULL)));
-        return JS_TRUE;
-    }
-
-    RequiredStringArg profileName(cx, argc, vp, 0, "resumeProfiling");
-    if (!profileName)
-        return JS_FALSE;
-    JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_ResumeProfilers(profileName.mBytes)));
-    return JS_TRUE;
-}
-
-/* Usage: DumpProfile([filename[, profileName]]) */
-static JSBool
-DumpProfile(JSContext *cx, unsigned argc, jsval *vp)
-{
-    bool ret;
-    if (argc == 0) {
-        ret = JS_DumpProfile(NULL, NULL);
-    } else {
-        RequiredStringArg filename(cx, argc, vp, 0, "dumpProfile");
-        if (!filename)
-            return JS_FALSE;
-
-        if (argc == 1) {
-            ret = JS_DumpProfile(filename.mBytes, NULL);
-        } else {
-            RequiredStringArg profileName(cx, argc, vp, 1, "dumpProfile");
-            if (!profileName)
-                return JS_FALSE;
-
-            ret = JS_DumpProfile(filename.mBytes, profileName.mBytes);
-        }
-    }
-
-    JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(ret));
-    return true;
-}
-
-#ifdef MOZ_SHARK
-
-static JSBool
-IgnoreAndReturnTrue(JSContext *cx, unsigned argc, jsval *vp)
-{
-    JS_SET_RVAL(cx, vp, JSVAL_TRUE);
-    return true;
-}
-
-#endif
-
-#ifdef MOZ_CALLGRIND
-static JSBool
-StartCallgrind(JSContext *cx, unsigned argc, jsval *vp)
-{
-    JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_StartCallgrind()));
-    return JS_TRUE;
-}
-
-static JSBool
-StopCallgrind(JSContext *cx, unsigned argc, jsval *vp)
-{
-    JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_StopCallgrind()));
-    return JS_TRUE;
-}
-
-static JSBool
-DumpCallgrind(JSContext *cx, unsigned argc, jsval *vp)
-{
-    if (argc == 0) {
-        JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_DumpCallgrind(NULL)));
-        return JS_TRUE;
-    }
-
-    RequiredStringArg outFile(cx, argc, vp, 0, "dumpCallgrind");
-    if (!outFile)
-        return JS_FALSE;
-
-    JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_DumpCallgrind(outFile.mBytes)));
-    return JS_TRUE;
-}
-#endif
-
-#ifdef MOZ_VTUNE
-static JSBool
-StartVtune(JSContext *cx, unsigned argc, jsval *vp)
-{
-    RequiredStringArg profileName(cx, argc, vp, 0, "startVtune");
-    if (!profileName)
-        return JS_FALSE;
-    JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_StartVtune(profileName.mBytes)));
-    return JS_TRUE;
-}
-
-static JSBool
-StopVtune(JSContext *cx, unsigned argc, jsval *vp)
-{
-    JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_StopVtune()));
-    return JS_TRUE;
-}
-
-static JSBool
-PauseVtune(JSContext *cx, unsigned argc, jsval *vp)
-{
-    JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_PauseVtune()));
-    return JS_TRUE;
-}
-
-static JSBool
-ResumeVtune(JSContext *cx, unsigned argc, jsval *vp)
-{
-    JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_ResumeVtune()));
-    return JS_TRUE;
-}
-#endif
-
-static JSFunctionSpec profiling_functions[] = {
-    JS_FN("startProfiling",  StartProfiling,      1,0),
-    JS_FN("stopProfiling",   StopProfiling,       1,0),
-    JS_FN("pauseProfilers",  PauseProfilers,      1,0),
-    JS_FN("resumeProfilers", ResumeProfilers,     1,0),
-    JS_FN("dumpProfile",     DumpProfile,         2,0),
-#ifdef MOZ_SHARK
-    /* Keep users of the old shark API happy. */
-    JS_FN("connectShark",    IgnoreAndReturnTrue, 0,0),
-    JS_FN("disconnectShark", IgnoreAndReturnTrue, 0,0),
-    JS_FN("startShark",      StartProfiling,      0,0),
-    JS_FN("stopShark",       StopProfiling,       0,0),
-#endif
-#ifdef MOZ_CALLGRIND
-    JS_FN("startCallgrind", StartCallgrind,       0,0),
-    JS_FN("stopCallgrind",  StopCallgrind,        0,0),
-    JS_FN("dumpCallgrind",  DumpCallgrind,        1,0),
-#endif
-#if 0 //ef MOZ_VTUNE
-    JS_FN("startVtune",     js_StartVtune,        1,0),
-    JS_FN("stopVtune",      js_StopVtune,         0,0),
-    JS_FN("pauseVtune",     js_PauseVtune,        0,0),
-    JS_FN("resumeVtune",    js_ResumeVtune,       0,0),
-#endif
-    JS_FS_END
-};
-
-#endif
-
-JS_PUBLIC_API(JSBool)
-JS_DefineProfilingFunctions(JSContext *cx, JSObject *objArg)
-{
-    RootedObject obj(cx, objArg);
-
-    assertSameCompartment(cx, obj);
-#ifdef MOZ_PROFILING
-    return JS_DefineFunctions(cx, obj, profiling_functions);
-#else
-    return true;
-#endif
-}
-
-#ifdef MOZ_CALLGRIND
-
-#include <valgrind/callgrind.h>
-
-JS_FRIEND_API(JSBool)
-js_StartCallgrind()
-{
-    JS_SILENCE_UNUSED_VALUE_IN_EXPR(CALLGRIND_START_INSTRUMENTATION);
-    JS_SILENCE_UNUSED_VALUE_IN_EXPR(CALLGRIND_ZERO_STATS);
-    return true;
-}
-
-JS_FRIEND_API(JSBool)
-js_StopCallgrind()
-{
-    JS_SILENCE_UNUSED_VALUE_IN_EXPR(CALLGRIND_STOP_INSTRUMENTATION);
-    return true;
-}
-
-JS_FRIEND_API(JSBool)
-js_DumpCallgrind(const char *outfile)
-{
-    if (outfile) {
-        JS_SILENCE_UNUSED_VALUE_IN_EXPR(CALLGRIND_DUMP_STATS_AT(outfile));
-    } else {
-        JS_SILENCE_UNUSED_VALUE_IN_EXPR(CALLGRIND_DUMP_STATS);
-    }
-
-    return true;
-}
-
-#endif /* MOZ_CALLGRIND */
-
-#if 0 //def MOZ_VTUNE
-#include <VTuneApi.h>
-
-static const char *vtuneErrorMessages[] = {
-  "unknown, error #0",
-  "invalid 'max samples' field",
-  "invalid 'samples per buffer' field",
-  "invalid 'sample interval' field",
-  "invalid path",
-  "sample file in use",
-  "invalid 'number of events' field",
-  "unknown, error #7",
-  "internal error",
-  "bad event name",
-  "VTStopSampling called without calling VTStartSampling",
-  "no events selected for event-based sampling",
-  "events selected cannot be run together",
-  "no sampling parameters",
-  "sample database already exists",
-  "sampling already started",
-  "time-based sampling not supported",
-  "invalid 'sampling parameters size' field",
-  "invalid 'event size' field",
-  "sampling file already bound",
-  "invalid event path",
-  "invalid license",
-  "invalid 'global options' field",
-
-};
-
-bool
-js_StartVtune(const char *profileName)
-{
-    VTUNE_EVENT events[] = {
-        { 1000000, 0, 0, 0, "CPU_CLK_UNHALTED.CORE" },
-        { 1000000, 0, 0, 0, "INST_RETIRED.ANY" },
-    };
-
-    U32 n_events = sizeof(events) / sizeof(VTUNE_EVENT);
-    char *default_filename = "mozilla-vtune.tb5";
-    JSString *str;
-    U32 status;
-
-    VTUNE_SAMPLING_PARAMS params = {
-        sizeof(VTUNE_SAMPLING_PARAMS),
-        sizeof(VTUNE_EVENT),
-        0, 0, /* Reserved fields */
-        1,    /* Initialize in "paused" state */
-        0,    /* Max samples, or 0 for "continuous" */
-        4096, /* Samples per buffer */
-        0.1,  /* Sampling interval in ms */
-        1,    /* 1 for event-based sampling, 0 for time-based */
-
-        n_events,
-        events,
-        default_filename,
-    };
-
-    if (profileName) {
-        char filename[strlen(profileName) + strlen("-vtune.tb5") + 1];
-        snprintf(filename, sizeof(filename), "%s-vtune.tb5", profileName);
-        params.tb5Filename = filename;
-    }
-
-    status = VTStartSampling(&params);
-
-    if (params.tb5Filename != default_filename)
-        js_free(params.tb5Filename);
-
-    if (status != 0) {
-        if (status == VTAPI_MULTIPLE_RUNS)
-            VTStopSampling(0);
-        if (status < sizeof(vtuneErrorMessages))
-            UnsafeError("Vtune setup error: %s", vtuneErrorMessages[status]);
-        else
-            UnsafeError("Vtune setup error: %d", status);
-        return false;
-    }
-    return true;
-}
-
-bool
-js_StopVtune()
-{
-    U32 status = VTStopSampling(1);
-    if (status) {
-        if (status < sizeof(vtuneErrorMessages))
-            UnsafeError("Vtune shutdown error: %s", vtuneErrorMessages[status]);
-        else
-            UnsafeError("Vtune shutdown error: %d", status);
-        return false;
-    }
-    return true;
-}
-
-bool
-js_PauseVtune()
-{
-    VTPause();
-    return true;
-}
-
-bool
-js_ResumeVtune()
-{
-    VTResume();
-    return true;
-}
-
-#endif /* MOZ_VTUNE */
-
-#ifdef __linux__
-
-/*
- * Code for starting and stopping |perf|, the Linux profiler.
- *
- * Output from profiling is written to mozperf.data in your cwd.
- *
- * To enable, set MOZ_PROFILE_WITH_PERF=1 in your environment.
- *
- * To pass additional parameters to |perf record|, provide them in the
- * MOZ_PROFILE_PERF_FLAGS environment variable.  If this variable does not
- * exist, we default it to "--call-graph".  (If you don't want --call-graph but
- * don't want to pass any other args, define MOZ_PROFILE_PERF_FLAGS to the empty
- * string.)
- *
- * If you include --pid or --output in MOZ_PROFILE_PERF_FLAGS, you're just
- * asking for trouble.
- *
- * Our split-on-spaces logic is lame, so don't expect MOZ_PROFILE_PERF_FLAGS to
- * work if you pass an argument which includes a space (e.g.
- * MOZ_PROFILE_PERF_FLAGS="-e 'foo bar'").
- */
-
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <unistd.h>
-#include <signal.h>
-
-static bool perfInitialized = false;
-static pid_t perfPid = 0;
-
-JSBool js_StartPerf()
-{
-    const char *outfile = "mozperf.data";
-
-    if (perfPid != 0) {
-        UnsafeError("js_StartPerf: called while perf was already running!\n");
-        return false;
-    }
-
-    // Bail if MOZ_PROFILE_WITH_PERF is empty or undefined.
-    if (!getenv("MOZ_PROFILE_WITH_PERF") ||
-        !strlen(getenv("MOZ_PROFILE_WITH_PERF"))) {
-        return true;
-    }
-
-    /*
-     * Delete mozperf.data the first time through -- we're going to append to it
-     * later on, so we want it to be clean when we start out.
-     */
-    if (!perfInitialized) {
-        perfInitialized = true;
-        unlink(outfile);
-        char cwd[4096];
-        printf("Writing perf profiling data to %s/%s\n",
-               getcwd(cwd, sizeof(cwd)), outfile);
-    }
-
-    pid_t mainPid = getpid();
-
-    pid_t childPid = fork();
-    if (childPid == 0) {
-        /* perf record --append --pid $mainPID --output=$outfile $MOZ_PROFILE_PERF_FLAGS */
-
-        char mainPidStr[16];
-        snprintf(mainPidStr, sizeof(mainPidStr), "%d", mainPid);
-        const char *defaultArgs[] = {"perf", "record", "--append",
-                                     "--pid", mainPidStr, "--output", outfile};
-
-        Vector<const char*, 0, SystemAllocPolicy> args;
-        args.append(defaultArgs, ArrayLength(defaultArgs));
-
-        const char *flags = getenv("MOZ_PROFILE_PERF_FLAGS");
-        if (!flags) {
-            flags = "--call-graph";
-        }
-
-        // Split |flags| on spaces.  (Don't bother to free it -- we're going to
-        // exec anyway.)
-        char *toksave;
-        char *tok = strtok_r(strdup(flags), " ", &toksave);
-        while (tok) {
-            args.append(tok);
-            tok = strtok_r(NULL, " ", &toksave);
-        }
-
-        args.append((char*) NULL);
-
-        execvp("perf", const_cast<char**>(args.begin()));
-
-        /* Reached only if execlp fails. */
-        fprintf(stderr, "Unable to start perf.\n");
-        exit(1);
-    }
-    else if (childPid > 0) {
-        perfPid = childPid;
-
-        /* Give perf a chance to warm up. */
-        usleep(500 * 1000);
-        return true;
-    }
-    else {
-        UnsafeError("js_StartPerf: fork() failed\n");
-        return false;
-    }
-}
-
-JSBool js_StopPerf()
-{
-    if (perfPid == 0) {
-        UnsafeError("js_StopPerf: perf is not running.\n");
-        return true;
-    }
-
-    if (kill(perfPid, SIGINT)) {
-        UnsafeError("js_StopPerf: kill failed\n");
-
-        // Try to reap the process anyway.
-        waitpid(perfPid, NULL, WNOHANG);
-    }
-    else {
-        waitpid(perfPid, NULL, 0);
-    }
-
-    perfPid = 0;
-    return true;
-}
-
-#endif /* __linux__ */
-
 JS_PUBLIC_API(void)
 JS_DumpBytecode(JSContext *cx, JSScript *scriptArg)
 {
 #if defined(DEBUG)
     Rooted<JSScript*> script(cx, scriptArg);
 
     Sprinter sprinter(cx);
     if (!sprinter.init())
--- a/js/src/jsdbgapi.h
+++ b/js/src/jsdbgapi.h
@@ -390,114 +390,25 @@ JS_GetScriptTotalSize(JSContext *cx, JSS
 
 extern JS_FRIEND_API(void)
 js_RevertVersion(JSContext *cx);
 
 extern JS_PUBLIC_API(const JSDebugHooks *)
 JS_GetGlobalDebugHooks(JSRuntime *rt);
 
 /**
- * Start any profilers that are available and have been configured on for this
- * platform. This is NOT thread safe.
- *
- * The profileName is used by some profilers to describe the current profiling
- * run. It may be used for part of the filename of the output, but the
- * specifics depend on the profiler. Many profilers will ignore it. Passing in
- * NULL is legal; some profilers may use it to output to stdout or similar.
- *
- * Returns true if no profilers fail to start.
- */
-extern JS_PUBLIC_API(JSBool)
-JS_StartProfiling(const char *profileName);
-
-/**
- * Stop any profilers that were previously started with JS_StartProfiling.
- * Returns true if no profilers fail to stop.
- */
-extern JS_PUBLIC_API(JSBool)
-JS_StopProfiling(const char *profileName);
-
-/**
- * Write the current profile data to the given file, if applicable to whatever
- * profiler is being used.
- */
-extern JS_PUBLIC_API(JSBool)
-JS_DumpProfile(const char *outfile, const char *profileName);
-
-/**
- * Pause currently active profilers (only supported by some profilers). Returns
- * whether any profilers failed to pause. (Profilers that do not support
- * pause/resume do not count.)
- */
-extern JS_PUBLIC_API(JSBool)
-JS_PauseProfilers(const char *profileName);
-
-/**
- * Resume suspended profilers
- */
-extern JS_PUBLIC_API(JSBool)
-JS_ResumeProfilers(const char *profileName);
-
-/**
  * Add various profiling-related functions as properties of the given object.
  */
 extern JS_PUBLIC_API(JSBool)
 JS_DefineProfilingFunctions(JSContext *cx, JSObject *obj);
 
 /* Defined in vm/Debugger.cpp. */
 extern JS_PUBLIC_API(JSBool)
 JS_DefineDebuggerObject(JSContext *cx, JSObject *obj);
 
-/**
- * The profiling API calls are not able to report errors, so they use a
- * thread-unsafe global memory buffer to hold the last error encountered. This
- * should only be called after something returns false.
- */
-JS_PUBLIC_API(const char *)
-JS_UnsafeGetLastProfilingError();
-
-#ifdef MOZ_CALLGRIND
-
-extern JS_FRIEND_API(JSBool)
-js_StopCallgrind();
-
-extern JS_FRIEND_API(JSBool)
-js_StartCallgrind();
-
-extern JS_FRIEND_API(JSBool)
-js_DumpCallgrind(const char *outfile);
-
-#endif /* MOZ_CALLGRIND */
-
-#ifdef MOZ_VTUNE
-
-extern JS_FRIEND_API(bool)
-js_StartVtune(const char *profileName);
-
-extern JS_FRIEND_API(bool)
-js_StopVtune();
-
-extern JS_FRIEND_API(bool)
-js_PauseVtune();
-
-extern JS_FRIEND_API(bool)
-js_ResumeVtune();
-
-#endif /* MOZ_VTUNE */
-
-#ifdef __linux__
-
-extern JS_FRIEND_API(JSBool)
-js_StartPerf();
-
-extern JS_FRIEND_API(JSBool)
-js_StopPerf();
-
-#endif /* __linux__ */
-
 extern JS_PUBLIC_API(void)
 JS_DumpBytecode(JSContext *cx, JSScript *script);
 
 extern JS_PUBLIC_API(void)
 JS_DumpCompartmentBytecode(JSContext *cx);
 
 extern JS_PUBLIC_API(void)
 JS_DumpPCCounts(JSContext *cx, JSScript *script);
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -126,17 +126,17 @@ fun_getProperty(JSContext *cx, HandleObj
 
 #ifdef JS_ION
         // If this script hasn't been compiled yet, make sure it will never
         // be compiled. IonMonkey does not guarantee |f.arguments| can be
         // fully recovered, so we try to mitigate observing this behavior by
         // detecting its use early.
         JSScript *script = iter.script();
         if (!script->hasIonScript())
-            ion::ForbidCompilation(script);
+            ion::ForbidCompilation(cx, script);
 #endif
 
         vp.setObject(*argsobj);
         return true;
     }
 
 #ifdef JS_METHODJIT
     if (iter.isScript() && iter.isIon())
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -897,26 +897,27 @@ Evaluate(JSContext *cx, unsigned argc, j
     {
         JSAutoCompartment ac(cx, global);
         uint32_t saved = JS_GetOptions(cx);
         uint32_t options = saved & ~(JSOPTION_COMPILE_N_GO | JSOPTION_NO_SCRIPT_RVAL);
         if (compileAndGo)
             options |= JSOPTION_COMPILE_N_GO;
         if (noScriptRval)
             options |= JSOPTION_NO_SCRIPT_RVAL;
+
         JS_SetOptions(cx, options);
-
         JSScript *script = JS_CompileUCScript(cx, global, codeChars, codeLength, fileName, lineNumber);
+        JS_SetOptions(cx, saved);
         if (!script)
             return false;
+
         if (sourceMapURL && !script->scriptSource()->hasSourceMap()) {
             if (!script->scriptSource()->setSourceMap(cx, sourceMapURL, script->filename))
                 return false;
         }
-        JS_SetOptions(cx, saved);
         if (!JS_ExecuteScript(cx, global, script, vp))
             return false;
     }
 
     return JS_WrapValue(cx, vp);
 }
 
 static JSString *
--- a/js/src/tests/js1_8_5/extensions/clone-complex-object.js
+++ b/js/src/tests/js1_8_5/extensions/clone-complex-object.js
@@ -1,77 +1,168 @@
 // |reftest| skip-if(!xulRuntime.shell)
 // -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 // Any copyright is dedicated to the Public Domain.
 // http://creativecommons.org/licenses/publicdomain/
 
+// Set of properties on a cloned object that are legitimately non-enumerable,
+// grouped by object type.
+var non_enumerable = { 'Array': [ 'length' ],
+                       'String': [ 'length' ] };
+
+// Set of properties on a cloned object that are legitimately non-configurable,
+// grouped by object type. The property name '0' stands in for any indexed
+// property.
+var non_configurable = { 'String': [ 0 ],
+                         '(typed array)': [ 0 ] };
+
+// Set of properties on a cloned object that are legitimately non-writable,
+// grouped by object type. The property name '0' stands in for any indexed
+// property.
+var non_writable = { 'String': [ 0 ] };
+
+function classOf(obj) {
+    var classString = Object.prototype.toString.call(obj);
+    var [ all, classname ] = classString.match(/\[object (\w+)/);
+    return classname;
+}
+
+function isIndex(p) {
+    var u = p >>> 0;
+    return ("" + u == p && u != 0xffffffff);
+}
+
+function notIndex(p) {
+    return !isIndex(p);
+}
+
+function tableContains(table, cls, prop) {
+    if (isIndex(prop))
+        prop = 0;
+    if (cls.match(/\wArray$/))
+        cls = "(typed array)";
+    var exceptionalProps = table[cls] || [];
+    return exceptionalProps.indexOf(prop) != -1;
+}
+
+function shouldBeConfigurable(cls, prop) {
+    return !tableContains(non_configurable, cls, prop);
+}
+
+function shouldBeWritable(cls, prop) {
+    return !tableContains(non_writable, cls, prop);
+}
+
+function ownProperties(obj) {
+    return Object.getOwnPropertyNames(obj).
+        map(function (p) { return [p, Object.getOwnPropertyDescriptor(obj, p)]; });
+}
+
+function isCloneable(pair) {
+    return typeof pair[0] === 'string' && pair[1].enumerable;
+}
+
+function compareProperties(a, b, stack, path) {
+    var ca = classOf(a);
+
+    // 'b', the original object, may have non-enumerable or XMLName properties;
+    // ignore them. 'a', the clone, should not have any non-enumerable
+    // properties (except .length, if it's an Array or String) or XMLName
+    // properties.
+    var pb = ownProperties(b).filter(isCloneable);
+    var pa = ownProperties(a);
+    for (var i = 0; i < pa.length; i++) {
+        var propname = pa[i][0];
+        assertEq(typeof propname, "string", "clone should not have E4X properties " + path);
+        if (!pa[i][1].enumerable) {
+            if (tableContains(non_enumerable, ca, propname)) {
+                // remove it so that the comparisons below will work
+                pa.splice(i, 1);
+                i--;
+            } else {
+                throw new Error("non-enumerable clone property " + uneval(pa[i][0]) + " " + path);
+            }
+        }
+    }
+
+    // Check that, apart from properties whose names are array indexes,
+    // the enumerable properties appear in the same order.
+    var aNames = pa.map(function (pair) { return pair[1]; }).filter(notIndex);
+    var bNames = pa.map(function (pair) { return pair[1]; }).filter(notIndex);
+    assertEq(aNames.join(","), bNames.join(","), path);
+
+    // Check that the lists are the same when including array indexes.
+    function byName(a, b) { a = a[0]; b = b[0]; return a < b ? -1 : a === b ? 0 : 1; }
+    pa.sort(byName);
+    pb.sort(byName);
+    assertEq(pa.length, pb.length, "should see the same number of properties " + path);
+    for (var i = 0; i < pa.length; i++) {
+        var aName = pa[i][0];
+        var bName = pb[i][0];
+        assertEq(aName, bName, path);
+
+        var path2 = isIndex(aName) ? path + "[" + aName + "]" : path + "." + aName;
+        var da = pa[i][1];
+        var db = pb[i][1];
+        assertEq(da.configurable, shouldBeConfigurable(ca, aName), path2);
+        assertEq(da.writable, shouldBeWritable(ca, aName), path2);
+        assertEq("value" in da, true, path2);
+        var va = da.value;
+        var vb = b[pb[i][0]];
+        stack.push([va, vb, path2]);
+    }
+}
+
 function isClone(a, b) {
-    var stack = [[a, b]];
+    var stack = [[a, b, 'obj']];
     var memory = new WeakMap();
     var rmemory = new WeakMap();
 
     while (stack.length > 0) {
         var pair = stack.pop();
-        var x = pair[0], y = pair[1];
+        var x = pair[0], y = pair[1], path = pair[2];
         if (typeof x !== "object" || x === null) {
             // x is primitive.
-            if (x !== y)
-                return false;
+            assertEq(x, y, "equal primitives");
         } else if (x instanceof Date) {
-            if (x.getTime() !== y.getTime())
-                return false;
+            assertEq(x.getTime(), y.getTime(), "equal times for cloned Dates");
         } else if (memory.has(x)) {
             // x is an object we have seen before in a.
-            if (y !== memory.get(x))
-                return false;
-            assertEq(rmemory.get(y), x);
+            assertEq(y, memory.get(x), "repeated object the same");
+            assertEq(rmemory.get(y), x, "repeated object's clone already seen");
         } else {
             // x is an object we have not seen before.
-	          // Check that we have not seen y before either.
-            if (rmemory.has(y))
-                return false;
-
-            // x and y must be of the same [[Class]].
-            var xcls = Object.prototype.toString.call(x);
-            var ycls = Object.prototype.toString.call(y);
-            if (xcls !== ycls)
-                return false;
+	    // Check that we have not seen y before either.
+            assertEq(rmemory.has(y), false);
 
-            // This function is only designed to check Objects and Arrays.
-            assertEq(xcls === "[object Object]" || xcls === "[object Array]",
-                     true);
+            var xcls = classOf(x);
+            var ycls = classOf(y);
+            assertEq(xcls, ycls, "same [[Class]]");
 
-            // Compare objects.
-            var xk = Object.keys(x), yk = Object.keys(y);
-            if (xk.length !== yk.length)
-                return false;
-            for (var i = 0; i < xk.length; i++) {
-                // We must see the same property names in the same order.
-                if (xk[i] !== yk[i])
-                    return false;
+            // clone objects should have the default prototype of the class
+            assertEq(Object.getPrototypeOf(x), this[xcls].prototype);
 
-                // Put the property values on the stack to compare later.
-                stack.push([x[xk[i]], y[yk[i]]]);
-            }
+            compareProperties(x, y, stack, path);
 
             // Record that we have seen this pair of objects.
             memory.set(x, y);
             rmemory.set(y, x);
         }
     }
     return true;
 }
 
-function check(a) {
-    assertEq(isClone(a, deserialize(serialize(a))), true);
+function check(val) {
+    var clone = deserialize(serialize(val));
+    assertEq(isClone(val, clone), true);
+    return clone;
 }
 
-// Various recursive objects, i.e. those which the structured cloning
-// algorithm wants us to reject due to "memory".
-//
+// Various recursive objects
+
 // Recursive array.
 var a = [];
 a[0] = a;
 check(a);
 
 // Recursive Object.
 var b = {};
 b.next = b;
@@ -98,9 +189,125 @@ for (var i = 0; i < 10000; i++) {
     b[0] = {};
     b[1] = [];
     b = b[1];
 }
 b[0] = {owner: a};
 b[1] = [];
 check(a);
 
+// Date objects should not be identical even if representing the same date
+var ar = [ new Date(1000), new Date(1000) ];
+var clone = check(ar);
+assertEq(clone[0] === clone[1], false);
+
+// Identity preservation for various types of objects
+
+function checkSimpleIdentity(v)
+{
+    a = check([ v, v ]);
+    assertEq(a[0] === a[1], true);
+    return a;
+}
+
+var v = new Boolean(true);
+checkSimpleIdentity(v);
+
+v = new Number(17);
+checkSimpleIdentity(v);
+
+v = new String("yo");
+checkSimpleIdentity(v);
+
+v = "fish";
+checkSimpleIdentity(v);
+
+v = new Int8Array([ 10, 20 ]);
+checkSimpleIdentity(v);
+
+v = new ArrayBuffer(7);
+checkSimpleIdentity(v);
+
+v = new Date(1000);
+b = [ v, v, { 'date': v } ];
+clone = check(b);
+assertEq(clone[0] === clone[1], true);
+assertEq(clone[0], clone[2]['date']);
+assertEq(clone[0] === v, false);
+
+// Reduced and modified from postMessage_structured_clone test
+let foo = { };
+let baz = { };
+let obj = { 'foo': foo,
+            'bar': { 'foo': foo },
+            'expando': { 'expando': baz },
+            'baz': baz };
+check(obj);
+
+for (var obj of new getTestContent)
+    check(obj);
+
+// Stolen wholesale from postMessage_structured_clone_helper.js
+function getTestContent()
+{
+  yield "hello";
+  yield 2+3;
+  yield 12;
+  yield null;
+  yield "complex" + "string";
+  yield new Object();
+  yield new Date(1306113544);
+  yield [1, 2, 3, 4, 5];
+  let obj = new Object();
+  obj.foo = 3;
+  obj.bar = "hi";
+  obj.baz = new Date(1306113544);
+  obj.boo = obj;
+  yield obj;
+
+  let recursiveobj = new Object();
+  recursiveobj.a = recursiveobj;
+  recursiveobj.foo = new Object();
+  recursiveobj.foo.bar = "bar";
+  recursiveobj.foo.backref = recursiveobj;
+  recursiveobj.foo.baz = 84;
+  recursiveobj.foo.backref2 = recursiveobj;
+  recursiveobj.bar = new Object();
+  recursiveobj.bar.foo = "foo";
+  recursiveobj.bar.backref = recursiveobj;
+  recursiveobj.bar.baz = new Date(1306113544);
+  recursiveobj.bar.backref2 = recursiveobj;
+  recursiveobj.expando = recursiveobj;
+  yield recursiveobj;
+
+  let obj = new Object();
+  obj.expando1 = 1;
+  obj.foo = new Object();
+  obj.foo.bar = 2;
+  obj.bar = new Object();
+  obj.bar.foo = obj.foo;
+  obj.expando = new Object();
+  obj.expando.expando = new Object();
+  obj.expando.expando.obj = obj;
+  obj.expando2 = 4;
+  obj.baz = obj.expando.expando;
+  obj.blah = obj.bar;
+  obj.foo.baz = obj.blah;
+  obj.foo.blah = obj.blah;
+  yield obj;
+
+  let diamond = new Object();
+  let obj = new Object();
+  obj.foo = "foo";
+  obj.bar = 92;
+  obj.backref = diamond;
+  diamond.ref1 = obj;
+  diamond.ref2 = obj;
+  yield diamond;
+
+  let doubleref = new Object();
+  let obj = new Object();
+  doubleref.ref1 = obj;
+  doubleref.ref2 = obj;
+  yield doubleref;
+}
+
 reportCompare(0, 0, 'ok');
--- a/js/src/tests/lib/results.py
+++ b/js/src/tests/lib/results.py
@@ -1,11 +1,15 @@
 import re
-from subprocess import list2cmdline
 from progressbar import NullProgressBar, ProgressBar
+import pipes
+
+# subprocess.list2cmdline does not properly escape for sh-like shells
+def escape_cmdline(args):
+    return ' '.join([ pipes.quote(a) for a in args ])
 
 class TestOutput:
     """Output from a test run."""
     def __init__(self, test, cmd, out, err, rc, dt, timed_out):
         self.test = test   # Test
         self.cmd = cmd     # str:   command line of test
         self.out = out     # str:   stdout
         self.err = err     # str:   stderr
@@ -102,17 +106,17 @@ class ResultsSink:
             self.counts['TIMEOUT'] += 1
         if isinstance(output, NullTestOutput):
             if self.options.tinderbox:
                 self.print_tinderbox_result('TEST-KNOWN-FAIL', output.test.path, time=output.dt, skip=True)
             self.counts['SKIP'] += 1
             self.n += 1
         else:
             if self.options.show_cmd:
-                print >> self.fp, list2cmdline(output.cmd)
+                print >> self.fp, escape_cmdline(output.cmd)
 
             if self.options.show_output:
                 print >> self.fp, '    rc = %d, run time = %f' % (output.rc, output.dt)
                 self.fp.write(output.out)
                 self.fp.write(output.err)
 
             result = TestResult.from_output(output)
             tup = (result.result, result.test.expect, result.test.random)
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -5061,17 +5061,46 @@ nsLayoutUtils::FontSizeInflationEnabled(
 
   if (!presShell ||
       (presShell->FontSizeInflationEmPerLine() == 0 &&
        presShell->FontSizeInflationMinTwips() == 0) ||
        aPresContext->IsChrome()) {
     return false;
   }
 
-  ViewportInfo vInf =
-    nsContentUtils::GetViewportInfo(aPresContext->PresShell()->GetDocument());
-
-  if (vInf.defaultZoom >= 1.0 || vInf.autoSize) {
-    return false;
+  // XXXjwir3:
+  // See bug 706918, comment 23 for more information on this particular section
+  // of the code. We're using "screen size" in place of the size of the content
+  // area, because on mobile, these are close or equal. This will work for our
+  // purposes (bug 706198), but it will need to be changed in the future to be
+  // more correct when we bring the rest of the viewport code into platform.
+  // We actually want the size of the content area, in the event that we don't
+  // have any metadata about the width and/or height. On mobile, the screen size
+  // and the size of the content area are very close, or the same value.
+  // In XUL fennec, the content area is the size of the <browser> widget, but
+  // in native fennec, the content area is the size of the Gecko LayerView
+  // object.
+
+  // TODO:
+  // Once bug 716575 has been resolved, this code should be changed so that it
+  // does the right thing on all platforms.
+  nsresult rv;
+  nsCOMPtr<nsIScreenManager> screenMgr =
+    do_GetService("@mozilla.org/gfx/screenmanager;1", &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIScreen> screen;
+  screenMgr->GetPrimaryScreen(getter_AddRefs(screen));
+  if (screen) {
+    int32_t screenLeft, screenTop, screenWidth, screenHeight;
+    screen->GetRect(&screenLeft, &screenTop, &screenWidth, &screenHeight);
+
+    ViewportInfo vInf =
+      nsContentUtils::GetViewportInfo(aPresContext->PresShell()->GetDocument(),
+                                      screenWidth, screenHeight);
+
+    if (vInf.defaultZoom >= 1.0 || vInf.autoSize) {
+      return false;
+    }
   }
 
   return true;
 }
--- a/media/omx-plugin/OmxPlugin.cpp
+++ b/media/omx-plugin/OmxPlugin.cpp
@@ -19,18 +19,16 @@
 
 #include "android/log.h"
 
 #undef LOG
 #define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "OmxPlugin" , ## args)
 
 using namespace MPAPI;
 
-const int OMX_QCOM_COLOR_FormatYVU420PackedSemiPlanar32m4ka = 0x7FA30C01;
-
 namespace android {
 
 // MediaStreamSource is a DataSource that reads from a MPAPI media stream.
 
 class MediaStreamSource : public DataSource {
   PluginHost *mPluginHost;
 public:
   MediaStreamSource(PluginHost *aPluginHost, Decoder *aDecoder);
--- a/netwerk/protocol/ftp/PFTPChannel.ipdl
+++ b/netwerk/protocol/ftp/PFTPChannel.ipdl
@@ -4,16 +4,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PNecko;
 include InputStreamParams;
 include URIParams;
 
+include protocol PBlob; //FIXME: bug #792908
+
 include "SerializedLoadContext.h";
 
 using IPC::SerializedLoadContext;
 using PRTime;
 
 namespace mozilla {
 namespace net {
 
--- a/netwerk/protocol/http/PHttpChannel.ipdl
+++ b/netwerk/protocol/http/PHttpChannel.ipdl
@@ -4,16 +4,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PNecko;
 include InputStreamParams;
 include URIParams;
 
+include protocol PBlob; //FIXME: bug #792908
+
 include "mozilla/net/PHttpChannelParams.h";
 include "mozilla/net/NeckoMessageUtils.h";
 include "prio.h";
 include "SerializedLoadContext.h";
 
 using RequestHeaderTuples;
 using nsHttpHeaderArray;
 using nsHttpResponseHead;
--- a/netwerk/protocol/websocket/PWebSocket.ipdl
+++ b/netwerk/protocol/websocket/PWebSocket.ipdl
@@ -5,16 +5,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PNecko;
 include protocol PBrowser;
 include InputStreamParams;
 include URIParams;
 
+include protocol PBlob; //FIXME: bug #792908
+
 include "SerializedLoadContext.h";
 using IPC::SerializedLoadContext;
 
 namespace mozilla {
 namespace net {
 
 async protocol PWebSocket
 {
--- a/toolkit/components/osfile/osfile_shared_front.jsm
+++ b/toolkit/components/osfile/osfile_shared_front.jsm
@@ -186,16 +186,68 @@ AbstractFile.normalizeToPointer = functi
   }
   if (offset != 0) {
     ptr = exports.OS.Shared.offsetBy(ptr, offset);
   }
   return {ptr: ptr, bytes: bytes};
 };
 
 /**
+ * Code shared by iterators.
+ */
+AbstractFile.AbstractIterator = function AbstractIterator() {
+};
+AbstractFile.AbstractIterator.prototype = {
+  /**
+   * Allow iterating with |for|
+   */
+  __iterator__: function __iterator__() {
+    return this;
+  },
+  /**
+   * Apply a function to all elements of the directory sequentially.
+   *
+   * @param {Function} cb This function will be applied to all entries
+   * of the directory. It receives as arguments
+   *  - the OS.File.Entry corresponding to the entry;
+   *  - the index of the entry in the enumeration;
+   *  - the iterator itself - calling |close| on the iterator stops
+   *   the loop.
+   */
+  forEach: function forEach(cb) {
+    let index = 0;
+    for (let entry in this) {
+      cb(entry, index++, this);
+    }
+  },
+  /**
+   * Return several entries at once.
+   *
+   * Entries are returned in the same order as a walk with |forEach| or
+   * |for(...)|.
+   *
+   * @param {number=} length If specified, the number of entries
+   * to return. If unspecified, return all remaining entries.
+   * @return {Array} An array containing the next |length| entries, or
+   * less if the iteration contains less than |length| entries left.
+   */
+  nextBatch: function nextBatch(length) {
+    let array = [];
+    let i = 0;
+    for (let entry in this) {
+      array.push(entry);
+      if (++i >= length) {
+        return array;
+      }
+    }
+    return array;
+  }
+};
+
+/**
  * Utility function shared by implementations of |OS.File.open|:
  * extract read/write/trunc/create/existing flags from a |mode|
  * object.
  *
  * @param {*=} mode An object that may contain fields |read|,
  * |write|, |truncate|, |create|, |existing|. These fields
  * are interpreted only if true-ish.
  * @return {{read:bool, write:bool, trunc:bool, create:bool,
--- a/toolkit/components/osfile/osfile_unix_front.jsm
+++ b/toolkit/components/osfile/osfile_unix_front.jsm
@@ -578,63 +578,61 @@
       * @param {string} path The directory upon which to iterate.
       * @param {*=} options Ignored in this implementation.
       *
       * @throws {File.Error} If |path| does not represent a directory or
       * if the directory cannot be iterated.
       * @constructor
       */
      File.DirectoryIterator = function DirectoryIterator(path, options) {
+       exports.OS.Shared.AbstractFile.AbstractIterator.call(this);
        let dir = throw_on_null("DirectoryIterator", UnixFile.opendir(path));
        this._dir = dir;
        this._path = path;
      };
-     File.DirectoryIterator.prototype = {
-       __iterator__: function __iterator__() {
-         return this;
-       },
-       /**
-        * Return the next entry in the directory, if any such entry is
-        * available.
-        *
-        * Skip special directories "." and "..".
-        *
-        * @return {File.Entry} The next entry in the directory.
-        * @throws {StopIteration} Once all files in the directory have been
-        * encountered.
-        */
-       next: function next() {
-         if (!this._dir) {
-           throw StopIteration;
+     File.DirectoryIterator.prototype = Object.create(exports.OS.Shared.AbstractFile.AbstractIterator.prototype);
+
+     /**
+      * Return the next entry in the directory, if any such entry is
+      * available.
+      *
+      * Skip special directories "." and "..".
+      *
+      * @return {File.Entry} The next entry in the directory.
+      * @throws {StopIteration} Once all files in the directory have been
+      * encountered.
+      */
+     File.DirectoryIterator.prototype.next = function next() {
+       if (!this._dir) {
+         throw StopIteration;
+       }
+       for (let entry = UnixFile.readdir(this._dir);
+            entry != null && !entry.isNull();
+            entry = UnixFile.readdir(this._dir)) {
+         let contents = entry.contents;
+         if (contents.d_type == OS.Constants.libc.DT_DIR) {
+           let name = contents.d_name.readString();
+           if (name == "." || name == "..") {
+             continue;
+           }
          }
-         for (let entry = UnixFile.readdir(this._dir);
-              entry != null && !entry.isNull();
-              entry = UnixFile.readdir(this._dir)) {
-           let contents = entry.contents;
-           if (contents.d_type == OS.Constants.libc.DT_DIR) {
-             let name = contents.d_name.readString();
-             if (name == "." || name == "..") {
-               continue;
-             }
-           }
-           return new File.DirectoryIterator.Entry(contents, this._path);
-         }
-         this.close();
-         throw StopIteration;
-       },
+         return new File.DirectoryIterator.Entry(contents, this._path);
+       }
+       this.close();
+       throw StopIteration;
+     };
 
-       /**
-        * Close the iterator and recover all resources.
-        * You should call this once you have finished iterating on a directory.
-        */
-       close: function close() {
-         if (!this._dir) return;
-         UnixFile.closedir(this._dir);
-         this._dir = null;
-       }
+     /**
+      * Close the iterator and recover all resources.
+      * You should call this once you have finished iterating on a directory.
+      */
+     File.DirectoryIterator.prototype.close = function close() {
+       if (!this._dir) return;
+       UnixFile.closedir(this._dir);
+       this._dir = null;
      };
 
      /**
       * An entry in a directory.
       */
      File.DirectoryIterator.Entry = function Entry(unix_entry, parent) {
        // Copy the relevant part of |unix_entry| to ensure that
        // our data is not overwritten prematurely.
--- a/toolkit/components/osfile/osfile_win_front.jsm
+++ b/toolkit/components/osfile/osfile_win_front.jsm
@@ -475,36 +475,41 @@
       * @param {string} path The directory upon which to iterate.
       * @param {*=} options Ignored in this implementation.
       *
       * @throws {File.Error} If |path| does not represent a directory or
       * if the directory cannot be iterated.
       * @constructor
       */
      File.DirectoryIterator = function DirectoryIterator(path, options) {
+       exports.OS.Shared.AbstractFile.AbstractIterator.call(this);
        if (options && options.winPattern) {
          this._pattern = path + "\\" + options.winPattern;
        } else {
          this._pattern = path + "\\*";
        }
        this._handle = null;
        this._path = path;
        this._started = false;
+       this._closed = false;
      };
-     File.DirectoryIterator.prototype = {
-       __iterator__: function __iterator__() {
-         return this;
-       },
+     File.DirectoryIterator.prototype = Object.create(exports.OS.Shared.AbstractFile.AbstractIterator.prototype);
 
        /**
         * Fetch the next entry in the directory.
         *
         * @return null If we have reached the end of the directory.
         */
-       _next: function _next() {
+     File.DirectoryIterator.prototype._next = function _next() {
+        // Bailout if the iterator is closed. Note that this may
+        // happen even before it is fully initialized.
+        if (this._closed) {
+          return null;
+        }
+
          // Iterator is not fully initialized yet. Finish
          // initialization.
          if (!this._started) {
             this._started = true;
             this._handle = WinFile.FindFirstFile(this._pattern, gFindDataPtr);
             if (this._handle == null) {
               let error = ctypes.winLastError;
               if (error == Const.ERROR_FILE_NOT_FOUND) {
@@ -512,21 +517,16 @@
                 return null;
               } else {
                 throw new File.Error("iter (FindFirstFile)", error);
               }
             }
             return gFindData;
          }
 
-         // We have closed this iterator already.
-         if (!this._handle) {
-           return null;
-         }
-
          if (WinFile.FindNextFile(this._handle, gFindDataPtr)) {
            return gFindData;
          } else {
            let error = ctypes.winLastError;
            this.close();
            if (error == Const.ERROR_NO_MORE_FILES) {
               return null;
            } else {
@@ -539,37 +539,44 @@
         * available.
         *
         * Skip special directories "." and "..".
         *
         * @return {File.Entry} The next entry in the directory.
         * @throws {StopIteration} Once all files in the directory have been
         * encountered.
         */
-       next: function next() {
+     File.DirectoryIterator.prototype.next = function next() {
          // FIXME: If we start supporting "\\?\"-prefixed paths, do not forget
          // that "." and ".." are absolutely normal file names if _path starts
          // with such prefix
          for (let entry = this._next(); entry != null; entry = this._next()) {
            let name = entry.cFileName.readString();
            if (name == "." || name == "..") {
              continue;
            }
            return new File.DirectoryIterator.Entry(entry, this._path);
          }
          throw StopIteration;
-       },
-       close: function close() {
-         if (!this._handle) {
-           return;
-         }
-         WinFile.FindClose(this._handle);
+     };
+
+     File.DirectoryIterator.prototype.close = function close() {
+       if (this._closed) {
+         return;
+       }
+       this._closed = true;
+       if (this._handle) {
+         // We might not have a handle if the iterator is closed
+         // before being used.
+         throw_on_zero("FindClose",
+           WinFile.FindClose(this._handle));
          this._handle = null;
        }
      };
+
      File.DirectoryIterator.Entry = function Entry(win_entry, parent) {
        // Copy the relevant part of |win_entry| to ensure that
        // our data is not overwritten prematurely.
        if (!win_entry.dwFileAttributes) {
          throw new TypeError();
        }
        this._dwFileAttributes = win_entry.dwFileAttributes;
        this._name = win_entry.cFileName.readString();
--- a/toolkit/components/osfile/tests/mochi/worker_test_osfile_front.js
+++ b/toolkit/components/osfile/tests/mochi/worker_test_osfile_front.js
@@ -444,16 +444,73 @@ function test_iter_dir()
       ok(lastAccess.getFullYear() >= year - 1 && lastAccess.getFullYear() <= year, "test_iter_dir: consistent lastAccess date");
     }
 
   }
   ok(encountered_tmp_file, "test_iter_dir: We have found the temporary file");
 
   ok(true, "test_iter_dir: Cleaning up");
   iterator.close();
+
+  // Testing nextBatch()
+  iterator = new OS.File.DirectoryIterator(parent);
+  let allentries = [x for(x in iterator)];
+  iterator.close();
+
+  ok(allentries.length >= 14, "test_iter_dir: Meta-check: the test directory should contain at least 14 items");
+
+  iterator = new OS.File.DirectoryIterator(parent);
+  let firstten = iterator.nextBatch(10);
+  is(firstten.length, 10, "test_iter_dir: nextBatch(10) returns 10 items");
+  for (let i = 0; i < firstten.length; ++i) {
+    is(allentries[i].path, firstten[i].path, "test_iter_dir: Checking that batch returns the correct entries");
+  }
+  let nextthree = iterator.nextBatch(3);
+  is(nextthree.length, 3, "test_iter_dir: nextBatch(3) returns 3 items");
+  for (let i = 0; i < nextthree.length; ++i) {
+    is(allentries[i + firstten.length].path, nextthree[i].path, "test_iter_dir: Checking that batch 2 returns the correct entries");
+  }
+  let everythingelse = iterator.nextBatch();
+  ok(everythingelse.length >= 1, "test_iter_dir: nextBatch() returns at least one item");
+  for (let i = 0; i < everythingelse.length; ++i) {
+    is(allentries[i + firstten.length + nextthree.length].path, everythingelse[i].path, "test_iter_dir: Checking that batch 3 returns the correct entries");
+  }
+  is(iterator.nextBatch().length, 0, "test_iter_dir: Once there is nothing left, nextBatch returns an empty array");
+  iterator.close();
+
+  iterator = new OS.File.DirectoryIterator(parent);
+  iterator.close();
+  is(iterator.nextBatch().length, 0, "test_iter_dir: nextBatch on closed iterator returns an empty array");
+
+  iterator = new OS.File.DirectoryIterator(parent);
+  let allentries2 = iterator.nextBatch();
+  is(allentries.length, allentries2.length, "test_iter_dir: Checking that getBatch(null) returns the right number of entries");
+  for (let i = 0; i < allentries.length; ++i) {
+    is(allentries[i].path, allentries2[i].path, "test_iter_dir: Checking that getBatch(null) returns everything in the right order");
+  }
+  iterator.close();
+
+  // Test forEach
+  iterator = new OS.File.DirectoryIterator(parent);
+  let index = 0;
+  iterator.forEach(
+    function cb(entry, aIndex, aIterator) {
+      is(index, aIndex, "test_iter_dir: Checking that forEach index is correct");
+      ok(iterator == aIterator, "test_iter_dir: Checking that right iterator is passed");
+      if (index < 10) {
+        is(allentries[index].path, entry.path, "test_iter_dir: Checking that forEach entry is correct");
+      } else if (index == 10) {
+        iterator.close();
+      } else {
+        ok(false, "test_iter_dir: Checking that forEach can be stopped early");
+      }
+      ++index;
+  });
+  iterator.close();
+
   ok(true, "test_iter_dir: Complete");
 }
 
 function test_position() {
   ok(true, "test_position: Starting");
 
   ok("POS_START" in OS.File, "test_position: POS_START exists");
   ok("POS_CURRENT" in OS.File, "test_position: POS_CURRENT exists");
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -1916,16 +1916,22 @@
     "description": "Firefox: Time taken by the tab opening animation in milliseconds"
   },
   "FX_TAB_ANIM_CLOSE_MS": {
     "kind": "exponential",
     "high": "3000",
     "n_buckets": 10,
     "description": "Firefox: Time taken by the tab closing animation in milliseconds"
   },
+  "FX_TAB_SWITCH_UPDATE_MS": {
+    "kind": "exponential",
+    "high": "1000",
+    "n_buckets": 20,
+    "description": "Firefox: Time in ms spent updating UI in response to a tab switch"
+  },
   "FX_KEYWORD_URL_USERSET": {
     "kind": "boolean",
     "description": "Firefox: keyword.URL has a user-set value"
   },
   "FX_IDENTITY_POPUP_OPEN_MS": {
     "kind": "exponential",
     "high": "1000",
     "n_buckets": 10,
--- a/toolkit/content/Troubleshoot.jsm
+++ b/toolkit/content/Troubleshoot.jsm
@@ -30,16 +30,17 @@ const PREFS_WHITELIST = [
   "browser.search.param",
   "browser.search.searchEnginesURL",
   "browser.search.suggest.enabled",
   "browser.search.update",
   "browser.search.useDBForOrder",
   "browser.sessionstore.",
   "browser.startup.homepage",
   "browser.tabs.",
+  "browser.urlbar.",
   "browser.zoom.",
   "dom.",
   "extensions.checkCompatibility",
   "extensions.lastAppVersion",
   "font.",
   "general.autoScroll",
   "general.useragent.",
   "gfx.",
--- a/toolkit/content/tests/browser/browser_Troubleshoot.js
+++ b/toolkit/content/tests/browser/browser_Troubleshoot.js
@@ -26,18 +26,23 @@ registerCleanupFunction(function () {
   // If it's not deleted, it outlives the test and is reported as a leak.
   delete window.Troubleshoot;
 });
 
 let tests = [
 
   function snapshotSchema(done) {
     Troubleshoot.snapshot(function (snapshot) {
-      validateObject(snapshot, SNAPSHOT_SCHEMA);
-      ok(true, "The snapshot should conform to the schema.");
+      try {
+        validateObject(snapshot, SNAPSHOT_SCHEMA);
+        ok(true, "The snapshot should conform to the schema.");
+      }
+      catch (err) {
+        ok(false, err);
+      }
       done();
     });
   },
 
   function modifiedPreferences(done) {
     let prefs = [
       "javascript.troubleshoot",
       "troubleshoot.foo",
--- a/toolkit/content/widgets/textbox.xml
+++ b/toolkit/content/widgets/textbox.xml
@@ -87,22 +87,19 @@
                                          if (val) this.setAttribute('readonly', 'true');
                                          else this.removeAttribute('readonly'); return val;"
                                   onget="return this.inputField.readOnly;"/>
       <property name="clickSelectsAll"
                 onget="return this.getAttribute('clickSelectsAll') == 'true';"
                 onset="if (val) this.setAttribute('clickSelectsAll', 'true');
                        else this.removeAttribute('clickSelectsAll'); return val;" />
 
-      <property name="editor" readonly="true">
-        <getter><![CDATA[
-          const nsIDOMNSEditableElement = Components.interfaces.nsIDOMNSEditableElement;
-          return this.inputField.QueryInterface(nsIDOMNSEditableElement).editor;
-        ]]></getter>
-      </property>
+      <field name="editor" readonly="true">
+        this.inputField.QueryInterface(Components.interfaces.nsIDOMNSEditableElement).editor;
+      </field>
 
       <method name="reset">
         <body><![CDATA[
           this.value = this.defaultValue;
           try {
             this.editor.transactionManager.clear();
             return true;
           }
@@ -212,17 +209,17 @@
               return;
           }
           this.setAttribute("focused", "true");
         ]]>
       </handler>
 
       <handler event="blur" phase="capturing">
         <![CDATA[
-          this.removeAttribute('focused');
+          this.removeAttribute("focused");
 
           // don't trigger clickSelectsAll when switching application windows
           if (window == window.top &&
               window.constructor == ChromeWindow &&
               document.activeElement == this.inputField)
             this.mIgnoreFocus = true;
         ]]>
       </handler>
--- a/widget/nsIFilePicker.idl
+++ b/widget/nsIFilePicker.idl
@@ -6,17 +6,17 @@
 
 #include "nsISupports.idl"
 
 interface nsIFile;
 interface nsIURI;
 interface nsIDOMWindow;
 interface nsISimpleEnumerator;
 
-[scriptable, uuid(0d79adad-b244-49A5-9997-2a8cad93fc44)]
+[scriptable, function, uuid(0d79adad-b244-49A5-9997-2a8cad93fc44)]
 interface nsIFilePickerShownCallback : nsISupports
 {
  /**
   * Callback which is called when a filepicker is shown and a result
   * is returned.
   *
   * @param aResult One of returnOK, returnCancel, or returnReplace
   */
--- a/widget/windows/GfxInfo.cpp
+++ b/widget/windows/GfxInfo.cpp
@@ -727,42 +727,65 @@ WindowsVersionToOperatingSystem(int32_t 
     };
 }
 
 const nsTArray<GfxDriverInfo>&
 GfxInfo::GetGfxDriverInfo()
 {
   if (!mDriverInfo->Length()) {
     /*
+     * It should be noted here that more specialized rules on certain features
+     * should be inserted -before- more generalized restriction. As the first
+     * match for feature/OS/device found in the list will be used for the final
+     * blacklisting call.
+     */
+    
+    /*
      * NVIDIA entries
      */
+    APPEND_TO_DRIVER_BLOCKLIST( DRIVER_OS_WINDOWS_VISTA,
+      (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorNVIDIA), GfxDriverInfo::allDevices,
+      nsIGfxInfo::FEATURE_DIRECT2D, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
+      DRIVER_LESS_THAN, V(8,17,12,5721), "257.21" );
+    APPEND_TO_DRIVER_BLOCKLIST( DRIVER_OS_WINDOWS_7,
+      (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorNVIDIA), GfxDriverInfo::allDevices,
+      nsIGfxInfo::FEATURE_DIRECT2D, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
+      DRIVER_LESS_THAN, V(8,17,12,5721), "257.21" );
     APPEND_TO_DRIVER_BLOCKLIST( DRIVER_OS_WINDOWS_XP,
       (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorNVIDIA), GfxDriverInfo::allDevices,
       GfxDriverInfo::allFeatures, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
-      DRIVER_LESS_THAN, V(6,14,12,5721), "257.21" );
+      DRIVER_LESS_THAN, V(6,14,11,8265), "182.65" );
     APPEND_TO_DRIVER_BLOCKLIST( DRIVER_OS_WINDOWS_VISTA,
       (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorNVIDIA), GfxDriverInfo::allDevices,
       GfxDriverInfo::allFeatures, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
-      DRIVER_LESS_THAN, V(8,17,12,5721), "257.21" );
+      DRIVER_LESS_THAN, V(8,17,11,8265), "182.65" );
     APPEND_TO_DRIVER_BLOCKLIST( DRIVER_OS_WINDOWS_7,
       (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorNVIDIA), GfxDriverInfo::allDevices,
       GfxDriverInfo::allFeatures, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
-      DRIVER_LESS_THAN, V(8,17,12,5721), "257.21" );
+      DRIVER_LESS_THAN, V(8,17,11,8265), "182.65" );
 
     /*
      * AMD/ATI entries
      */
     APPEND_TO_DRIVER_BLOCKLIST( DRIVER_OS_ALL,
       (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorATI), GfxDriverInfo::allDevices,
+      nsIGfxInfo::FEATURE_DIRECT2D, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
+       DRIVER_LESS_THAN, V(8,741,0,0), "10.6" );
+    APPEND_TO_DRIVER_BLOCKLIST( DRIVER_OS_ALL,
+      (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorAMD), GfxDriverInfo::allDevices,
+      nsIGfxInfo::FEATURE_DIRECT2D, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
+      DRIVER_LESS_THAN, V(8,741,0,0), "10.6" );
+    APPEND_TO_DRIVER_BLOCKLIST( DRIVER_OS_ALL,
+      (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorATI), GfxDriverInfo::allDevices,
       GfxDriverInfo::allFeatures, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
-      DRIVER_LESS_THAN, V(8,741,0,0), "10.6" );
+      DRIVER_LESS_THAN, V(8,62,0,0), "9.6" );
     APPEND_TO_DRIVER_BLOCKLIST( DRIVER_OS_ALL,
       (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorAMD), GfxDriverInfo::allDevices,
       GfxDriverInfo::allFeatures, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
-      DRIVER_LESS_THAN, V(8,741,0,0), "10.6" );
+      DRIVER_LESS_THAN, V(8,62,0,0), "9.6" );
 
     /*
      * Bug 783517 - crashes in AMD driver on Windows 8
      */
     APPEND_TO_DRIVER_BLOCKLIST( DRIVER_OS_WINDOWS_8,
       (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorATI), GfxDriverInfo::allDevices,
       GfxDriverInfo::allFeatures, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,
       DRIVER_LESS_THAN_OR_EQUAL, V(8,982,0,0), "> 12.8" );
@@ -802,37 +825,56 @@ GfxInfo::GetGfxDriverInfo()
      * Block all features on any drivers before this, as there's a crash when a MS Hotfix is installed.
      * The crash itself is Direct2D-related, but for safety we block all features.
      */
     #define IMPLEMENT_INTEL_DRIVER_BLOCKLIST(winVer, devFamily, driverVer)                                                      \
       APPEND_TO_DRIVER_BLOCKLIST2( winVer,                                                                                      \
         (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorIntel), (GfxDeviceFamily*) GfxDriverInfo::GetDeviceFamily(devFamily), \
         GfxDriverInfo::allFeatures, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,                                                 \
         DRIVER_LESS_THAN, driverVer )
+    #define IMPLEMENT_INTEL_DRIVER_BLOCKLIST_D2D(winVer, devFamily, driverVer)                                                      \
+      APPEND_TO_DRIVER_BLOCKLIST2( winVer,                                                                                      \
+        (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorIntel), (GfxDeviceFamily*) GfxDriverInfo::GetDeviceFamily(devFamily), \
+        nsIGfxInfo::FEATURE_DIRECT2D, nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION,                                                 \
+        DRIVER_LESS_THAN, driverVer )
 
-    IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_XP, IntelGMA500,   V(6,14,11,1018));
+    IMPLEMENT_INTEL_DRIVER_BLOCKLIST_D2D(DRIVER_OS_WINDOWS_VISTA, IntelGMA500,   V(7,14,10,1006));
+    IMPLEMENT_INTEL_DRIVER_BLOCKLIST_D2D(DRIVER_OS_WINDOWS_VISTA, IntelGMA900,   GfxDriverInfo::allDriverVersions);
+    IMPLEMENT_INTEL_DRIVER_BLOCKLIST_D2D(DRIVER_OS_WINDOWS_VISTA, IntelGMA950,   V(7,14,10,1504));
+    IMPLEMENT_INTEL_DRIVER_BLOCKLIST_D2D(DRIVER_OS_WINDOWS_VISTA, IntelGMA3150,  V(7,14,10,2124));
+    IMPLEMENT_INTEL_DRIVER_BLOCKLIST_D2D(DRIVER_OS_WINDOWS_VISTA, IntelGMAX3000, V(7,15,10,1666));
+    IMPLEMENT_INTEL_DRIVER_BLOCKLIST_D2D(DRIVER_OS_WINDOWS_VISTA, IntelGMAX4500HD, V(8,15,10,2202));
+
+    IMPLEMENT_INTEL_DRIVER_BLOCKLIST_D2D(DRIVER_OS_WINDOWS_7, IntelGMA500,   V(5,0,0,2026));
+    IMPLEMENT_INTEL_DRIVER_BLOCKLIST_D2D(DRIVER_OS_WINDOWS_7, IntelGMA900,   GfxDriverInfo::allDriverVersions);
+    IMPLEMENT_INTEL_DRIVER_BLOCKLIST_D2D(DRIVER_OS_WINDOWS_7, IntelGMA950,   V(8,15,10,1930));
+    IMPLEMENT_INTEL_DRIVER_BLOCKLIST_D2D(DRIVER_OS_WINDOWS_7, IntelGMA3150,  V(8,14,10,2117));
+    IMPLEMENT_INTEL_DRIVER_BLOCKLIST_D2D(DRIVER_OS_WINDOWS_7, IntelGMAX3000, V(8,15,10,1930));
+    IMPLEMENT_INTEL_DRIVER_BLOCKLIST_D2D(DRIVER_OS_WINDOWS_7, IntelGMAX4500HD, V(8,15,10,2202));
+
+    IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_XP, IntelGMA500,   V(3,0,20,3200));
     IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_XP, IntelGMA900,   V(6,14,10,4764));
     IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_XP, IntelGMA950,   V(6,14,10,4926));
-    IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_XP, IntelGMA3150,  V(6,14,10,5260));
+    IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_XP, IntelGMA3150,  V(6,14,10,5134));
     IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_XP, IntelGMAX3000, V(6,14,10,5218));
     IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_XP, IntelGMAX4500HD, V(6,14,10,5284));
 
-    IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_VISTA, IntelGMA500,   V(7,14,10,1006));
+    IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_VISTA, IntelGMA500,   V(3,0,20,3200));
     IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_VISTA, IntelGMA900,   GfxDriverInfo::allDriverVersions);
     IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_VISTA, IntelGMA950,   V(7,14,10,1504));
-    IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_VISTA, IntelGMA3150,  V(7,14,10,2124));
+    IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_VISTA, IntelGMA3150,  V(7,14,10,1910));
     IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_VISTA, IntelGMAX3000, V(7,15,10,1666));
-    IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_VISTA, IntelGMAX4500HD, V(8,15,10,2202));
+    IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_VISTA, IntelGMAX4500HD, V(8,15,10,1855));
 
     IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_7, IntelGMA500,   V(5,0,0,2026));
     IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_7, IntelGMA900,   GfxDriverInfo::allDriverVersions);
     IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_7, IntelGMA950,   V(8,15,10,1930));
-    IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_7, IntelGMA3150,  V(8,14,10,2117));
+    IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_7, IntelGMA3150,  V(8,14,10,1972));
     IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_7, IntelGMAX3000, V(8,15,10,1930));
-    IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_7, IntelGMAX4500HD, V(8,15,10,2202));
+    IMPLEMENT_INTEL_DRIVER_BLOCKLIST(DRIVER_OS_WINDOWS_7, IntelGMAX4500HD, V(8,15,10,1855));
 
     /* OpenGL on any Intel hardware is discouraged */
     APPEND_TO_DRIVER_BLOCKLIST2( DRIVER_OS_ALL,
       (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorIntel), GfxDriverInfo::allDevices,
       nsIGfxInfo::FEATURE_OPENGL_LAYERS, nsIGfxInfo::FEATURE_DISCOURAGED,
       DRIVER_LESS_THAN, GfxDriverInfo::allDriverVersions );
     APPEND_TO_DRIVER_BLOCKLIST2( DRIVER_OS_ALL,
       (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorIntel), GfxDriverInfo::allDevices,
--- a/xpcom/tests/TestHarness.h
+++ b/xpcom/tests/TestHarness.h
@@ -75,67 +75,16 @@ void passed(const char* msg, ...)
   va_start(ap, msg);
   vprintf(msg, ap);
   va_end(ap);
 
   putchar('\n');
 }
 
 //-----------------------------------------------------------------------------
-// Code profiling
-//
-static const char* gCurrentProfile;
-
-/**
- * If the build has been configured properly, start the best code profiler
- * available on this platform.
- *
- * This is NOT thread safe.
- *
- * @precondition Profiling is not started
- * @param profileName A descriptive name for this profiling run.  Every 
- *                    attempt is made to name the profile data according
- *                    to this name, but check your platform's profiler
- *                    documentation for what this means.
- * @return true if profiling was available and successfully started.
- * @see StopProfiling
- */
-inline bool
-StartProfiling(const char* profileName)
-{
-    NS_ASSERTION(profileName, "need a name for this profile");
-    NS_PRECONDITION(!gCurrentProfile, "started a new profile before stopping another");
-
-    JSBool ok = JS_StartProfiling(profileName);
-    gCurrentProfile = profileName;
-    return ok ? true : false;
-}
-
-/**
- * Stop the platform's profiler.  For what this means, what happens after
- * stopping, and how the profile data can be accessed, check the 
- * documentation of your platform's profiler.
- *
- * This is NOT thread safe.
- *
- * @precondition Profiling was started
- * @return true if profiling was successfully stopped.
- * @see StartProfiling
- */
-inline bool
-StopProfiling()
-{
-    NS_PRECONDITION(gCurrentProfile, "tried to stop profile before starting one");
-
-    const char* profileName = gCurrentProfile;
-    gCurrentProfile = 0;
-    return JS_StopProfiling(profileName) ? true : false;
-}
-
-//-----------------------------------------------------------------------------
 
 class ScopedLogging
 {
 public:
     ScopedLogging()
     {
         NS_LogInit();
     }