Merge inbound to m-c.
authorRyan VanderMeulen <ryanvm@gmail.com>
Sat, 22 Sep 2012 08:28:28 -0400
changeset 107917 b461a7cd250e813aa920ebe6cbefb8112e554e10
parent 107875 9cfb80a82883725b5fbdba4adb271d9f2d8f2e41 (current diff)
parent 107916 a06ea10ddb048c494401d42b40fbc159c71e826c (diff)
child 107918 156f00b9ac9238a087a504371438e1c497af9e17
push id82
push usershu@rfrn.org
push dateFri, 05 Oct 2012 13:20:22 +0000
milestone18.0a1
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();
     }