Merge mozilla-central to tracemonkey.
authorRobert Sayre <sayrer@gmail.com>
Sun, 26 Sep 2010 12:49:11 -0400
changeset 54722 660c0c8a0d34a334fccfdfcdad11ee1dc1dda8ce
parent 54721 3e7fbdbd0b2f8464f66ed2f2fd21359f8428ebbb (current diff)
parent 54656 49cc66b9f097b4b36c900eb81f9c24d738842c37 (diff)
child 54723 63066ec9dd8d8fa7d7c5e0ee55ebc15dd29b908d
push id16011
push userrsayre@mozilla.com
push dateWed, 29 Sep 2010 06:01:57 +0000
treeherdermozilla-central@d7e659b4f80c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone2.0b7pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to tracemonkey.
configure.in
js/src/jsxml.cpp
toolkit/crashreporter/google-breakpad/src/common/linux/memory.h
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -83,16 +83,21 @@ pref("browser.dictionaries.download.url"
 //           default=10 minutes
 pref("app.update.timer", 600000);
 
 // App-specific update preferences
 
 // The interval to check for updates (app.update.interval) is defined in
 // firefox-branding.js
 
+// Alternative windowtype for an application update user interface window. When
+// a window with this windowtype is open the application update service won't
+// open the normal application update user interface window.
+pref("app.update.altwindowtype", "Browser:About");
+
 // Enables some extra Application Update Logging (can reduce performance)
 pref("app.update.log", false);
 
 // The number of general background check failures to allow before notifying the
 // user of the failure. User initiated update checks always notify the user of
 // the failure.
 pref("app.update.backgroundMaxErrors", 10);
 
--- a/browser/base/content/aboutDialog.css
+++ b/browser/base/content/aboutDialog.css
@@ -47,23 +47,44 @@
 #distribution,
 #distributionId {
   font-weight: bold;
   display: none;
   margin-top: 0;
   margin-bottom: 0;
 }
 
-#checkForUpdatesButton,
 .text-blurb {
   margin-bottom: 10px;
   -moz-margin-start: 0;
   -moz-padding-start: 0;
 }
 
+#updateBox {
+  margin-bottom: 10px;
+}
+
+#updateButton,
+#updateDeck > hbox > label {
+  -moz-margin-start: 0;
+  -moz-padding-start: 0;
+}
+
+#updateDeck > hbox > label:not([class="text-link"]) {
+  color: #909090;
+  font-style:italic;
+}
+
+.update-throbber {
+  width: 16px;
+  min-height: 16px;
+  -moz-margin-end: 3px;
+  list-style-image: url("chrome://global/skin/icons/loading_16.png");
+}
+
 .trademark-label,
 .text-link,
 .text-link:focus {
   margin: 0px;
   padding: 0px;
 }
 
 .bottom-link,
--- a/browser/base/content/aboutDialog.js
+++ b/browser/base/content/aboutDialog.js
@@ -16,16 +16,17 @@
 # The Initial Developer of the Original Code is
 # Blake Ross (blaker@netscape.com).
 # Portions created by the Initial Developer are Copyright (C) 2002
 # the Initial Developer. All Rights Reserved.
 #
 # Contributor(s):
 #   Ehsan Akhgari <ehsan.akhgari@gmail.com>
 #   Margaret Leibovic <margaret.leibovic@gmail.com>
+#   Robert Strong <robert.bugzilla@gmail.com>
 #
 # Alternatively, the contents of this file may be used under the terms of
 # either the GNU General Public License Version 2 or later (the "GPL"), or
 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 # in which case the provisions of the GPL or the LGPL are applicable instead
 # of those above. If you wish to allow use of your version of this file only
 # under the terms of either the GPL or the LGPL, and not to allow others to
 # use your version of this file under the terms of the MPL, indicate your
@@ -60,30 +61,479 @@ function init(aEvent)
       distroIdField.style.display = "block";
     }
   }
   catch (e) {
     // Pref is unset
   }
 
 #ifdef MOZ_UPDATER
-  initUpdates();
+  gAppUpdater = new appUpdater();
 #endif
 
 #ifdef XP_MACOSX
   // it may not be sized at this point, and we need its width to calculate its position
   window.sizeToContent();
   window.moveTo((screen.availWidth / 2) - (window.outerWidth / 2), screen.availHeight / 5);
 #endif
 }
 
 #ifdef MOZ_UPDATER
-/**
- * Sets up "Check for Updates..." button.
- */
-function initUpdates()
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+Components.utils.import("resource://gre/modules/DownloadUtils.jsm");
+Components.utils.import("resource://gre/modules/AddonManager.jsm");
+
+var gAppUpdater;
+
+function onUnload(aEvent) {
+  if (gAppUpdater.isChecking)
+    gAppUpdater.checker.stopChecking(Components.interfaces.nsIUpdateChecker.CURRENT_CHECK);
+  // Safe to call even when there isn't a download in progress.
+  gAppUpdater.removeDownloadListener();
+  gAppUpdater = null;
+}
+
+
+function appUpdater()
+{
+  this.updateDeck = document.getElementById("updateDeck");
+
+  // Hide the update deck when there is already an update window open to avoid
+  // syncing issues between them.
+  if (Services.wm.getMostRecentWindow("Update:Wizard")) {
+    this.updateDeck.hidden = true;
+    return;
+  }
+
+  XPCOMUtils.defineLazyServiceGetter(this, "aus",
+                                     "@mozilla.org/updates/update-service;1",
+                                     "nsIApplicationUpdateService");
+  XPCOMUtils.defineLazyServiceGetter(this, "checker",
+                                     "@mozilla.org/updates/update-checker;1",
+                                     "nsIUpdateChecker");
+  XPCOMUtils.defineLazyServiceGetter(this, "um",
+                                     "@mozilla.org/updates/update-manager;1",
+                                     "nsIUpdateManager");
+  XPCOMUtils.defineLazyServiceGetter(this, "bs",
+                                     "@mozilla.org/extensions/blocklist;1",
+                                     "nsIBlocklistService");
+
+  this.bundle = Services.strings.
+                createBundle("chrome://browser/locale/browser.properties");
+
+  this.updateBtn = document.getElementById("updateButton");
+
+  // The button label value must be set so its height is correct.
+  this.setupUpdateButton("update.checkInsideButton");
+
+  let manualURL = Services.urlFormatter.formatURLPref("app.update.url.manual");
+  let manualLink = document.getElementById("manualLink");
+  manualLink.value = manualURL;
+  manualLink.href = manualURL;
+  document.getElementById("failedLink").href = manualURL;
+
+  if (this.updateDisabledAndLocked) {
+    this.selectPanel("adminDisabled");
+    return;
+  }
+
+  if (this.isPending) {
+    this.setupUpdateButton("update.restart." +
+                           (this.isMajor ? "upgradeButton" : "applyButton"));
+    return;
+  }
+
+  if (this.isDownloading) {
+    this.startDownload();
+    return;
+  }
+
+  if (this.updateEnabled && this.updateAuto) {
+    this.selectPanel("checkingForUpdates");
+    this.isChecking = true;
+    this.checker.checkForUpdates(this.updateCheckListener, true);
+    return;
+  }
+}
+
+appUpdater.prototype =
 {
-  var browserBundle = Services.strings.
-                      createBundle("chrome://browser/locale/browser.properties");
-  var checkForUpdates = document.getElementById("checkForUpdatesButton");
-  setupCheckForUpdates(checkForUpdates, browserBundle);
-}
+  // true when there is an update check in progress.
+  isChecking: false,
+
+  // true when there is an update already staged / ready to be applied.
+  get isPending() {
+    if (this.update)
+      return this.update.state == "pending";
+    return this.um.activeUpdate && this.um.activeUpdate.state == "pending";
+  },
+
+  // true when there is an update download in progress.
+  get isDownloading() {
+    if (this.update)
+      return this.update.state == "downloading";
+    return this.um.activeUpdate &&
+           this.um.activeUpdate.state == "downloading";
+  },
+
+  // true when the update type is major.
+  get isMajor() {
+    if (this.update)
+      return this.update.type == "major";
+    return this.um.activeUpdate.type == "major";
+  },
+
+  // true when updating is disabled by an administrator.
+  get updateDisabledAndLocked() {
+    return !this.updateEnabled &&
+           Services.prefs.prefIsLocked("app.update.enabled");
+  },
+
+  // true when updating is enabled.
+  get updateEnabled() {
+    try {
+      return Services.prefs.getBoolPref("app.update.enabled");
+    }
+    catch (e) { }
+    return true; // Firefox default is true
+  },
+
+  // true when updating is automatic.
+  get updateAuto() {
+    try {
+      return Services.prefs.getBoolPref("app.update.auto");
+    }
+    catch (e) { }
+    return true; // Firefox default is true
+  },
+
+  /**
+   * Sets the deck's selected panel.
+   *
+   * @param  aChildID
+   *         The id of the deck's child to select.
+   */
+  selectPanel: function(aChildID) {
+    this.updateDeck.selectedPanel = document.getElementById(aChildID);
+    this.updateBtn.disabled = (aChildID != "updateButtonBox");
+  },
+
+  /**
+   * Sets the update button's label and accesskey.
+   *
+   * @param  aKeyPrefix
+   *         The prefix for the properties file entry to use for setting the
+   *         label and accesskey.
+   */
+  setupUpdateButton: function(aKeyPrefix) {
+    this.updateBtn.label = this.bundle.GetStringFromName(aKeyPrefix + ".label");
+    this.updateBtn.accessKey = this.bundle.GetStringFromName(aKeyPrefix + ".accesskey");
+    if (!document.commandDispatcher.focusedElement ||
+        document.commandDispatcher.focusedElement.isSameNode(this.updateBtn))
+      this.updateBtn.focus();
+  },
+
+  /**
+   * Handles oncommand for the update button.
+   */
+  buttonOnCommand: function() {
+    if (this.isPending) {
+      // Notify all windows that an application quit has been requested.
+      let cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"].
+                       createInstance(Components.interfaces.nsISupportsPRBool);
+      Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
+
+      // Something aborted the quit process.
+      if (cancelQuit.data)
+        return;
+
+      // If already in safe mode restart in safe mode (bug 327119)
+      if (Services.appinfo.inSafeMode) {
+        let env = Components.classes["@mozilla.org/process/environment;1"].
+                  getService(Components.interfaces.nsIEnvironment);
+        env.set("MOZ_SAFE_MODE_RESTART", "1");
+      }
+
+      Components.classes["@mozilla.org/toolkit/app-startup;1"].
+      getService(Components.interfaces.nsIAppStartup).
+      quit(Components.interfaces.nsIAppStartup.eAttemptQuit |
+           Components.interfaces.nsIAppStartup.eRestart);
+      return;
+    }
+
+    const URI_UPDATE_PROMPT_DIALOG = "chrome://mozapps/content/update/updates.xul";
+    // Firefox no longer displays a license for updates and the licenseURL check
+    // is just in case a distibution does.
+    if (this.update && (this.update.billboardURL || this.update.licenseURL ||
+        this.addons.length != 0)) {
+      var ary = null;
+      ary = Components.classes["@mozilla.org/supports-array;1"].
+            createInstance(Components.interfaces.nsISupportsArray);
+      ary.AppendElement(this.update);
+      var openFeatures = "chrome,centerscreen,dialog=no,resizable=no,titlebar,toolbar=no";
+      Services.ww.openWindow(null, URI_UPDATE_PROMPT_DIALOG, "", openFeatures, ary);
+      window.close();
+      return;
+    }
+
+    this.selectPanel("checkingForUpdates");
+    this.isChecking = true;
+    this.checker.checkForUpdates(this.updateCheckListener, true);
+  },
+
+  /**
+   * Implements nsIUpdateCheckListener. The methods implemented by
+   * nsIUpdateCheckListener have to be in a different scope from
+   * nsIIncrementalDownload because both nsIUpdateCheckListener and
+   * nsIIncrementalDownload implement onProgress.
+   */
+  updateCheckListener: {
+    /**
+     * See nsIUpdateService.idl
+     */
+    onProgress: function(aRequest, aPosition, aTotalSize) {
+    },
+
+    /**
+     * See nsIUpdateService.idl
+     */
+    onCheckComplete: function(aRequest, aUpdates, aUpdateCount) {
+      gAppUpdater.isChecking = false;
+      gAppUpdater.update = gAppUpdater.aus.
+                           selectUpdate(aUpdates, aUpdates.length);
+      if (!gAppUpdater.update) {
+        gAppUpdater.selectPanel("noUpdatesFound");
+        return;
+      }
+
+      if (!gAppUpdater.aus.canApplyUpdates) {
+        gAppUpdater.selectPanel("manualUpdate");
+        return;
+      }
+
+      // Firefox no longer displays a license for updates and the licenseURL
+      // check is just in case a distibution does.
+      if (gAppUpdater.update.billboardURL || gAppUpdater.update.licenseURL) {
+        gAppUpdater.selectPanel("updateButtonBox");
+        gAppUpdater.setupUpdateButton("update.openUpdateUI." +
+                                      (this.isMajor ? "upgradeButton"
+                                                    : "applyButton"));
+        return;
+      }
+
+      if (!gAppUpdater.update.appVersion ||
+          Services.vc.compare(gAppUpdater.update.appVersion,
+                              Services.appinfo.version) == 0) {
+        gAppUpdater.startDownload();
+        return;
+      }
+
+      gAppUpdater.checkAddonCompatibility();
+    },
+
+    /**
+     * See nsIUpdateService.idl
+     */
+    onError: function(aRequest, aUpdate) {
+      // Errors in the update check are treated as no updates found. If the
+      // update check fails repeatedly without a success the user will be
+      // notified with the normal app update user interface so this is safe.
+      gAppUpdater.isChecking = false;
+      gAppUpdater.selectPanel("noUpdatesFound");
+      return;
+    },
+
+    /**
+     * See nsISupports.idl
+     */
+    QueryInterface: function(aIID) {
+      if (!aIID.equals(Components.interfaces.nsIUpdateCheckListener) &&
+          !aIID.equals(Components.interfaces.nsISupports))
+        throw Components.results.NS_ERROR_NO_INTERFACE;
+      return this;
+    }
+  },
+
+  /**
+   * Checks the compatibility of add-ons for the application update.
+   */
+  checkAddonCompatibility: function() {
+    var self = this;
+    AddonManager.getAllAddons(function(aAddons) {
+      self.addons = [];
+      self.addonsCheckedCount = 0;
+      aAddons.forEach(function(aAddon) {
+        // If an add-on isn't appDisabled and isn't userDisabled then it is
+        // either active now or the user expects it to be active after the
+        // restart. If that is the case and the add-on is not installed by the
+        // application and is not compatible with the new application version
+        // then the user should be warned that the add-on will become
+        // incompatible. If an addon's type equals plugin it is skipped since
+        // checking plugins compatibility information isn't supported and
+        // getting the scope property of a plugin breaks in some environments
+        // (see bug 566787).
+        if (aAddon.type != "plugin" &&
+            !aAddon.appDisabled && !aAddon.userDisabled &&
+            aAddon.scope != AddonManager.SCOPE_APPLICATION &&
+            aAddon.isCompatible &&
+            !aAddon.isCompatibleWith(self.update.appVersion,
+                                     self.update.platformVersion))
+          self.addons.push(aAddon);
+      });
+      self.addonsTotalCount = self.addons.length;
+      if (self.addonsTotalCount == 0) {
+        self.startDownload();
+        return;
+      }
+
+      self.checkAddonsForUpdates();
+    });
+  },
+
+  /**
+   * Checks if there are updates for add-ons that are incompatible with the
+   * application update.
+   */
+  checkAddonsForUpdates: function() {
+    this.addons.forEach(function(aAddon) {
+      aAddon.findUpdates(this, AddonManager.UPDATE_WHEN_NEW_APP_DETECTED,
+                         this.update.appVersion,
+                         this.update.platformVersion);
+    }, this);
+  },
+
+  /**
+   * See XPIProvider.jsm
+   */
+  onCompatibilityUpdateAvailable: function(aAddon) {
+    for (var i = 0; i < this.addons.length; ++i) {
+      if (this.addons[i].id == aAddon.id) {
+        this.addons.splice(i, 1);
+        break;
+      }
+    }
+  },
+
+  /**
+   * See XPIProvider.jsm
+   */
+  onUpdateAvailable: function(aAddon, aInstall) {
+    if (!this.bs.isAddonBlocklisted(aAddon.id, aInstall.version,
+                                    this.update.appVersion,
+                                    this.update.platformVersion)) {
+      // Compatibility or new version updates mean the same thing here.
+      this.onCompatibilityUpdateAvailable(aAddon);
+    }
+  },
+
+  /**
+   * See XPIProvider.jsm
+   */
+  onUpdateFinished: function(aAddon) {
+    ++this.addonsCheckedCount;
+
+    if (this.addonsCheckedCount < this.addonsTotalCount)
+      return;
+
+    if (this.addons.length == 0) {
+      // Compatibility updates or new version updates were found for all add-ons
+      this.startDownload();
+      return;
+    }
+
+    this.selectPanel("updateButtonBox");
+    this.setupUpdateButton("update.openUpdateUI." +
+                           (this.isMajor ? "upgradeButton" : "applyButton"));
+  },
+
+  /**
+   * Starts the download of an update mar.
+   */
+  startDownload: function() {
+    if (!this.update)
+      this.update = this.um.activeUpdate;
+    this.update.QueryInterface(Components.interfaces.nsIWritablePropertyBag);
+    this.update.setProperty("foregroundDownload", "true");
+
+    this.aus.pauseDownload();
+    let state = this.aus.downloadUpdate(this.update, false);
+    if (state == "failed") {
+      this.selectPanel("downloadFailed");      
+      return;
+    }
+
+    this.downloadStatus = document.getElementById("downloadStatus");
+    this.downloadStatus.value =
+      DownloadUtils.getTransferTotal(0, this.update.selectedPatch.size);
+    this.selectPanel("downloading");
+    this.aus.addDownloadListener(this);
+  },
+
+  removeDownloadListener: function() {
+    this.aus.removeDownloadListener(this);
+  },
+
+  /**
+   * See nsIRequestObserver.idl
+   */
+  onStartRequest: function(aRequest, aContext) {
+  },
+
+  /**
+   * See nsIRequestObserver.idl
+   */
+  onStopRequest: function(aRequest, aContext, aStatusCode) {
+    switch (aStatusCode) {
+    case Components.results.NS_ERROR_UNEXPECTED:
+      if (this.update.selectedPatch.state == "download-failed" &&
+          (this.update.isCompleteUpdate || this.update.patchCount != 2)) {
+        // Verification error of complete patch, informational text is held in
+        // the update object.
+        this.removeDownloadListener();
+        this.selectPanel("downloadFailed");
+        break;
+      }
+      // Verification failed for a partial patch, complete patch is now
+      // downloading so return early and do NOT remove the download listener!
+      break;
+    case Components.results.NS_BINDING_ABORTED:
+      // Do not remove UI listener since the user may resume downloading again.
+      break;
+    case Components.results.NS_OK:
+      this.removeDownloadListener();
+      this.selectPanel("updateButtonBox");
+      this.setupUpdateButton("update.restart." +
+                             (this.isMajor ? "upgradeButton" : "applyButton"));
+      break;
+    default:
+      this.removeDownloadListener();
+      this.selectPanel("downloadFailed");
+      break;
+    }
+
+  },
+
+  /**
+   * See nsIProgressEventSink.idl
+   */
+  onStatus: function(aRequest, aContext, aStatus, aStatusArg) {
+  },
+
+  /**
+   * See nsIProgressEventSink.idl
+   */
+  onProgress: function(aRequest, aContext, aProgress, aProgressMax) {
+    this.downloadStatus.value =
+      DownloadUtils.getTransferTotal(aProgress, aProgressMax);
+  },
+
+  /**
+   * See nsISupports.idl
+   */
+  QueryInterface: function(aIID) {
+    if (!aIID.equals(Components.interfaces.nsIProgressEventSink) &&
+        !aIID.equals(Components.interfaces.nsIRequestObserver) &&
+        !aIID.equals(Components.interfaces.nsISupports))
+      throw Components.results.NS_ERROR_NO_INTERFACE;
+    return this;
+  }
+};
 #endif
--- a/browser/base/content/aboutDialog.xul
+++ b/browser/base/content/aboutDialog.xul
@@ -18,16 +18,17 @@
 # The Initial Developer of the Original Code is
 # Blake Ross (blaker@netscape.com).
 # Portions created by the Initial Developer are Copyright (C) 2002
 # the Initial Developer. All Rights Reserved.
 #
 # Contributor(s):
 #   Ehsan Akhgari <ehsan.akhgari@gmail.com>
 #   Margaret Leibovic <margaret.leibovic@gmail.com>
+#   Robert Strong <robert.bugzilla@gmail.com>
 #
 # Alternatively, the contents of this file may be used under the terms of
 # either the GNU General Public License Version 2 or later (the "GPL"), or
 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 # in which case the provisions of the GPL or the LGPL are applicable instead
 # of those above. If you wish to allow use of your version of this file only
 # under the terms of either the GPL or the LGPL, and not to allow others to
 # use your version of this file under the terms of the MPL, indicate your
@@ -52,39 +53,67 @@
 <?xul-overlay href="chrome://browser/content/macBrowserOverlay.xul"?>
 #endif
 
 <window xmlns:html="http://www.w3.org/1999/xhtml"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         id="aboutDialog"
         windowtype="Browser:About"
         onload="init(event);"
+#ifdef MOZ_UPDATER
+        onunload="onUnload(event);"
+#endif
 #ifdef XP_MACOSX
         inwindowmenu="false"
 #else
         title="&aboutDialog.title;"
 #endif
         >
 
-  <script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/>
   <script type="application/javascript" src="chrome://browser/content/aboutDialog.js"/>
 
   <vbox>
     <hbox id="clientBox">
       <vbox id="leftBox" flex="1"/>
       <vbox id="rightBox" flex="1">
 #expand <label id="version" value="__MOZ_APP_VERSION__"/>
         <label id="distribution" class="text-blurb"/>
         <label id="distributionId" class="text-blurb"/>
+        <vbox id="updateBox">
 #ifdef MOZ_UPDATER
-        <hbox>
-          <button id="checkForUpdatesButton" oncommand="checkForUpdates();" align="start"/>
-          <spacer flex="1"/>
-        </hbox>
+          <deck id="updateDeck" orient="vertical">
+            <hbox id="updateButtonBox" align="center">
+              <button id="updateButton" align="start"
+                      oncommand="gAppUpdater.buttonOnCommand();"/>
+              <spacer flex="1"/>
+            </hbox>
+            <hbox id="checkingForUpdates" align="center">
+              <image class="update-throbber"/><label>&update.checkingForUpdates;</label>
+            </hbox>
+            <hbox id="checkingAddonCompat" align="center">
+              <image class="update-throbber"/><label>&update.checkingAddonCompat;</label>
+            </hbox>
+            <hbox id="downloading" align="center">
+              <image class="update-throbber"/><label>&update.downloading.start;</label><label id="downloadStatus"/><label>&update.downloading.end;</label>
+            </hbox>
+            <hbox id="downloadFailed" align="center">
+              <label>&update.failed.start;</label><label id="failedLink" class="text-link">&update.failed.linkText;</label><label>&update.failed.end;</label>
+            </hbox>
+            <hbox id="adminDisabled" align="center">
+              <label>&update.adminDisabled;</label>
+            </hbox>
+            <hbox id="noUpdatesFound" align="center">
+              <label>&update.noUpdatesFound;</label>
+            </hbox>
+            <hbox id="manualUpdate" align="center">
+              <label>&update.manual.start;</label><label id="manualLink" class="text-link"/><label>&update.manual.end;</label>
+            </hbox>
+          </deck>
 #endif
+        </vbox>
         <description class="text-blurb">
           &community.start2;<label class="text-link" href="http://www.mozilla.org/">&community.mozillaLink;</label>&community.middle2;<label class="text-link" href="about:credits">&community.creditsLink;</label>&community.end2;
         </description>
         <description class="text-blurb">
           &contribute.start;<label class="text-link" href="http://www.mozilla.org/contribute/">&contribute.getInvolvedLink;</label>&contribute.end;
         </description>
       </vbox>
     </hbox>
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -132,16 +132,20 @@ toolbar[mode="icons"] > #reload-button[d
 }
 
 .menuitem-iconic-tooltip,
 .menuitem-tooltip[type="checkbox"],
 .menuitem-tooltip[type="radio"] {
   -moz-binding: url("chrome://browser/content/urlbarBindings.xml#menuitem-iconic-tooltip");
 }
 
+#appmenu_offlineModeRecovery:not([checked=true]) {
+  display: none;
+}
+
 /* ::::: location bar ::::: */
 #urlbar {
   -moz-binding: url(chrome://browser/content/urlbarBindings.xml#urlbar);
 }
 
 #urlbar-progress {
   -moz-binding: url("chrome://global/content/bindings/progressmeter.xml#progressmeter");
 }
@@ -416,8 +420,19 @@ window[chromehidden~="toolbar"] toolbar:
 
 #invalid-form-popup {
   max-width: 280px;
 }
 
 #geolocation-notification {
   -moz-binding: url("chrome://browser/content/urlbarBindings.xml#geolocation-notification");
 }
+
+/* override hidden="true" for the status bar compatibility shim
+   in case it was persisted for the real status bar */
+#status-bar {
+  display: -moz-box;
+}
+
+/* Remove the resizer from the statusbar compatibility shim */
+#status-bar > .statusbar-resizerpanel {
+  display: none;
+}
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -484,16 +484,21 @@
           </hbox>
           <menuitem id="appmenu_privateBrowsing"
                     class="menuitem-iconic menuitem-iconic-tooltip"
                     label="&privateBrowsingCmd.start.label;"
                     startlabel="&privateBrowsingCmd.start.label;"
                     stoplabel="&privateBrowsingCmd.stop.label;"
                     command="Tools:PrivateBrowsing"
                     key="key_privatebrowsing"/>
+          <menuitem label="&goOfflineCmd.label;"
+                    id="appmenu_offlineModeRecovery"
+                    type="checkbox"
+                    observes="workOfflineMenuitemState"
+                    oncommand="BrowserOffline.toggleOfflineStatus();"/>
           <menuseparator class="appmenu-menuseparator"/>
           <hbox>
             <menuitem id="appmenu-edit-label"
                       label="&appMenuEdit.label;"
                       disabled="true"/>
             <toolbarbutton id="appmenu-cut"
                            class="appmenu-edit-button"
                            command="cmd_cut"
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -429,17 +429,16 @@
                 if (aWebProgress.DOMWindow == this.mBrowser.contentWindow)
                   this.mBrowser.userTypedClear += 2;
 
                 if (!this.mBlank) {
                   if (!(aStateFlags & nsIWebProgressListener.STATE_RESTORING)) {
                     this.mTab.setAttribute("busy", "true");
                     this.mTab.setAttribute("progresspercent", "0");
                     this._startStalledTimer();
-                    this.mTabBrowser.updateIcon(this.mTab);
                     this.mTabBrowser.setTabTitleLoading(this.mTab);
                   }
 
                   if (this.mTab.selected)
                     this.mTabBrowser.mIsBusy = true;
                 }
               }
               else if (aStateFlags & nsIWebProgressListener.STATE_STOP &&
@@ -458,17 +457,16 @@
 
                 if (this.mBlank)
                   this.mBlank = false;
 
                 this.mTab.removeAttribute("busy");
                 this.mTab.removeAttribute("progresspercent");
                 this.mTab.removeAttribute("stalled");
                 this._cancelStalledTimer();
-                this.mTabBrowser.updateIcon(this.mTab);
 
                 var location = aRequest.QueryInterface(nsIChannel).URI;
 
                 // For keyword URIs clear the user typed value since they will be changed into real URIs
                 if (location.scheme == "keyword")
                   this.mBrowser.userTypedValue = null;
 
                 if (this.mTab.label == this.mTabBrowser.mStringBundle.getString("tabs.loading"))
@@ -597,47 +595,39 @@
 
             if (aURI && this.mFaviconService) {
               if (!(aURI instanceof Ci.nsIURI))
                 aURI = makeURI(aURI);
               this.mFaviconService.setAndLoadFaviconForPage(browser.currentURI,
                                                             aURI, false);
             }
 
-            this.updateIcon(aTab);
+            if ((browser.mIconURL || "") != aTab.getAttribute("image")) {
+              if (browser.mIconURL)
+                aTab.setAttribute("image", browser.mIconURL);
+              else
+                aTab.removeAttribute("image");
+              this._tabAttrModified(aTab);
+            }
 
             this._callProgressListeners(browser, "onLinkIconAvailable", [browser.mIconURL]);
           ]]>
         </body>
       </method>
 
       <method name="getIcon">
         <parameter name="aTab"/>
         <body>
           <![CDATA[
             let browser = aTab ? this.getBrowserForTab(aTab) : this.selectedBrowser;
             return browser.mIconURL;
           ]]>
         </body>
       </method>
 
-      <method name="updateIcon">
-        <parameter name="aTab"/>
-        <body>
-          <![CDATA[
-            var browser = this.getBrowserForTab(aTab);
-            if (!aTab.hasAttribute("busy") && browser.mIconURL)
-              aTab.setAttribute("image", browser.mIconURL);
-            else
-              aTab.removeAttribute("image");
-            this._tabAttrModified(aTab);
-          ]]>
-        </body>
-      </method>
-
       <method name="shouldLoadFavIcon">
         <parameter name="aURI"/>
         <body>
           <![CDATA[
             return (aURI &&
                     Services.prefs.getBoolPref("browser.chrome.site_icons") &&
                     Services.prefs.getBoolPref("browser.chrome.favicons") &&
                     ("schemeIs" in aURI) && (aURI.schemeIs("http") || aURI.schemeIs("https")));
@@ -645,41 +635,39 @@
         </body>
       </method>
 
       <method name="useDefaultIcon">
         <parameter name="aTab"/>
         <body>
           <![CDATA[
             var browser = this.getBrowserForTab(aTab);
-            var docURIObject = browser.contentDocument.documentURIObject; 
+            var docURIObject = browser.contentDocument.documentURIObject;
+            var icon = null;
             if (browser.contentDocument instanceof ImageDocument) {
               if (Services.prefs.getBoolPref("browser.chrome.site_icons")) {
+                let sz = Services.prefs.getIntPref("browser.chrome.image_icons.max_size");
                 try {
-                  let sz = Services.prefs.getIntPref("browser.chrome.image_icons.max_size");
-                  if (!sz)
-                    return;
-
-                  var req = browser.contentDocument.imageRequest;
-                  if (!req || !req.image ||
-                      req.image.width > sz ||
-                      req.image.height > sz)
-                    return;
-
-                  this.setIcon(aTab, browser.currentURI);
+                  let req = browser.contentDocument.imageRequest;
+                  if (req &&
+                      req.image &&
+                      req.image.width <= sz &&
+                      req.image.height <= sz)
+                    icon = browser.currentURI;
                 } catch (e) { }
               }
             }
             // Use documentURIObject in the check for shouldLoadFavIcon so that we
             // do the right thing with about:-style error pages.  Bug 453442
             else if (this.shouldLoadFavIcon(docURIObject)) {
-              var url = docURIObject.prePath + "/favicon.ico";
+              let url = docURIObject.prePath + "/favicon.ico";
               if (!this.isFailedIcon(url))
-                this.setIcon(aTab, url);
+                icon = url;
             }
+            this.setIcon(aTab, icon);
           ]]>
         </body>
       </method>
 
       <method name="isFailedIcon">
         <parameter name="aURI"/>
         <body>
           <![CDATA[
@@ -969,17 +957,16 @@
               return;
 
             this.mTabbedMode = true; // Welcome to multi-tabbed mode.
 
             if (XULBrowserWindow.isBusy) {
               this.mCurrentTab.setAttribute("busy", "true");
               this.mIsBusy = true;
               this.setTabTitleLoading(this.mCurrentTab);
-              this.updateIcon(this.mCurrentTab);
             } else {
               this.setIcon(this.mCurrentTab, this.mCurrentBrowser.mIconURL);
             }
 
             var filter;
             if (this.mTabFilters.length > 0) {
               // Use the filter hooked up in our addProgressListener
               filter = this.mTabFilters[0];
@@ -1720,17 +1707,16 @@
 
             ourBrowser.webProgress.addProgressListener(filter,
               Components.interfaces.nsIWebProgress.NOTIFY_ALL);
 
             if (isBusy)
               this.setTabTitleLoading(aOurTab);
             else
               this.setTabTitle(aOurTab);
-            this.updateIcon(aOurTab);
 
             // If the tab was already selected (this happpens in the scenario
             // of replaceTabWithWindow), notify onLocationChange, etc.
             if (aOurTab == this.selectedTab)
               this.updateCurrentBrowser(true);
           ]]>
         </body>
       </method>
--- a/browser/base/content/test/browser_bug521216.js
+++ b/browser/base/content/test/browser_bug521216.js
@@ -34,13 +34,14 @@ var progressListener = {
   onLocationChange: function onLocationChange(aBrowser) {
     if (aBrowser == tab.linkedBrowser)
       record(arguments.callee.name);
   },
   onStateChange: function onStateChange(aBrowser) {
     if (aBrowser == tab.linkedBrowser)
       record(arguments.callee.name);
   },
-  onLinkIconAvailable: function onLinkIconAvailable(aBrowser) {
-    if (aBrowser == tab.linkedBrowser)
+  onLinkIconAvailable: function onLinkIconAvailable(aBrowser, aIconURL) {
+    if (aBrowser == tab.linkedBrowser &&
+        aIconURL == "about:logo")
       record(arguments.callee.name);
   }
 };
--- a/browser/components/sessionstore/src/nsSessionStore.js
+++ b/browser/components/sessionstore/src/nsSessionStore.js
@@ -848,25 +848,17 @@ SessionStoreService.prototype = {
     browser.removeEventListener("change", this, true);
     browser.removeEventListener("input", this, true);
     browser.removeEventListener("DOMAutoComplete", this, true);
 
     delete browser.__SS_data;
 
     // If this tab was in the middle of restoring, we want to restore the next
     // tab. If the tab hasn't been restored, we want to remove it from the array.
-    if (browser.__SS_restoring) {
-      this.restoreNextTab(true);
-    }
-    else if (browser.__SS_needsRestore) {
-      if (aTab.hidden)
-        this._tabsToRestore.hidden.splice(this._tabsToRestore.hidden.indexOf(aTab));
-      else
-        this._tabsToRestore.visible.splice(this._tabsToRestore.visible.indexOf(aTab));
-    }
+    this._resetTabRestoringState(aTab, true);
 
     if (!aNoNotification) {
       this.saveStateDelayed(aWindow);
     }
   },
 
   /**
    * When a tab closes, collect its properties
@@ -2197,16 +2189,34 @@ SessionStoreService.prototype = {
 
       if (winData.tabs[t].pinned)
         tabbrowser.pinTab(tabs[t]);
       else
         tabbrowser.unpinTab(tabs[t]);
       tabs[t].hidden = winData.tabs[t].hidden;
     }
 
+    // If overwriting tabs, we want to remove __SS_restoring from the browser.
+    if (aOverwriteTabs) {
+      for (let i = 0; i < tabbrowser.tabs.length; i++)
+        this._resetTabRestoringState(tabbrowser.tabs[i], false);
+    }
+
+    // We want to set up a counter on the window that indicates how many tabs
+    // in this window are unrestored. This will be used in restoreNextTab to
+    // determine if gRestoreTabsProgressListener should be removed from the window.
+    // If we aren't overwriting existing tabs, then we want to add to the existing
+    // count in case there are still tabs restoring.
+    if (!aWindow.__SS_tabsToRestore)
+      aWindow.__SS_tabsToRestore = 0;
+    if (aOverwriteTabs)
+      aWindow.__SS_tabsToRestore = newTabCount;
+    else
+      aWindow.__SS_tabsToRestore += newTabCount;
+
     // We want to correlate the window with data from the last session, so
     // assign another id if we have one. Otherwise clear so we don't do
     // anything with it.
     delete aWindow.__SS_lastSessionWindowID;
     if (winData.__lastSessionWindowID)
       aWindow.__SS_lastSessionWindowID = winData.__lastSessionWindowID;
 
     // when overwriting tabs, remove all superflous ones
@@ -2282,71 +2292,22 @@ SessionStoreService.prototype = {
           var restoreHistoryFunc = function(self) {
             self.restoreHistoryPrecursor(aWindow, aTabs, aTabData, aSelectTab, aIx, aCount + 1);
           }
           aWindow.setTimeout(restoreHistoryFunc, 100, this);
           return;
         }
       }
     }
-    
-    // mark the tabs as loading
-    for (t = 0; t < aTabs.length; t++) {
-      let tab = aTabs[t];
-      let browser = tabbrowser.getBrowserForTab(tab);
-      let tabData = aTabData[t];
-
-      if (tabData.pinned)
-        tabbrowser.pinTab(tab);
-      else
-        tabbrowser.unpinTab(tab);
-      tab.hidden = tabData.hidden;
-
-      tabData._tabStillLoading = true;
-
-      // keep the data around to prevent dataloss in case
-      // a tab gets closed before it's been properly restored
-      browser.__SS_data = tabData;
-      browser.__SS_needsRestore = true;
-
-      if (!tabData.entries || tabData.entries.length == 0) {
-        // make sure to blank out this tab's content
-        // (just purging the tab's history won't be enough)
-        browser.contentDocument.location = "about:blank";
-        continue;
-      }
-      
-      browser.stop(); // in case about:blank isn't done yet
-      
-      tab.setAttribute("busy", "true");
-      tabbrowser.updateIcon(tab);
-      
-      // wall-paper fix for bug 439675: make sure that the URL to be loaded
-      // is always visible in the address bar
-      let activeIndex = (tabData.index || tabData.entries.length) - 1;
-      let activePageData = tabData.entries[activeIndex] || null;
-      browser.userTypedValue = activePageData ? activePageData.url || null : null;
-
-      // If the page has a title, set it.
-      if (activePageData) {
-        if (activePageData.title) {
-          tab.label = activePageData.title;
-          tab.crop = "end";
-        } else if (activePageData.url != "about:blank") {
-          tab.label = activePageData.url;
-          tab.crop = "center";
-        }
-      }
-    }
-    
+
     if (aTabs.length > 0) {
       // Load hidden tabs last, by pushing them to the end of the list
       let unhiddenTabs = aTabs.length;
       for (let t = 0; t < unhiddenTabs; ) {
-        if (aTabs[t].hidden) {
+        if (aTabData[t].hidden) {
           aTabs = aTabs.concat(aTabs.splice(t, 1));
           aTabData = aTabData.concat(aTabData.splice(t, 1));
           if (aSelectTab > t)
             --aSelectTab;
           --unhiddenTabs;
           continue;
         }
         ++t;
@@ -2374,16 +2335,65 @@ SessionStoreService.prototype = {
       // make sure to restore the selected tab first (if any)
       if (aSelectTab-- && aTabs[aSelectTab]) {
         aTabs.unshift(aTabs.splice(aSelectTab, 1)[0]);
         aTabData.unshift(aTabData.splice(aSelectTab, 1)[0]);
         tabbrowser.selectedTab = aTabs[0];
       }
     }
 
+    // Prepare the tabs so that they can be properly restored. We'll pin/unpin
+    // and show/hide tabs as necessary. We'll also set the labels, user typed
+    // value, and attach a copy of the tab's data in case we close it before
+    // it's been restored.
+    for (t = 0; t < aTabs.length; t++) {
+      let tab = aTabs[t];
+      let browser = tabbrowser.getBrowserForTab(tab);
+      let tabData = aTabData[t];
+
+      if (tabData.pinned)
+        tabbrowser.pinTab(tab);
+      else
+        tabbrowser.unpinTab(tab);
+      tab.hidden = tabData.hidden;
+
+      tabData._tabStillLoading = true;
+
+      // keep the data around to prevent dataloss in case
+      // a tab gets closed before it's been properly restored
+      browser.__SS_data = tabData;
+      browser.__SS_needsRestore = true;
+
+      if (!tabData.entries || tabData.entries.length == 0) {
+        // make sure to blank out this tab's content
+        // (just purging the tab's history won't be enough)
+        browser.contentDocument.location = "about:blank";
+        continue;
+      }
+
+      browser.stop(); // in case about:blank isn't done yet
+
+      // wall-paper fix for bug 439675: make sure that the URL to be loaded
+      // is always visible in the address bar
+      let activeIndex = (tabData.index || tabData.entries.length) - 1;
+      let activePageData = tabData.entries[activeIndex] || null;
+      browser.userTypedValue = activePageData ? activePageData.url || null : null;
+
+      // If the page has a title, set it.
+      if (activePageData) {
+        if (activePageData.title) {
+          tab.label = activePageData.title;
+          tab.crop = "end";
+        } else if (activePageData.url != "about:blank") {
+          tab.label = activePageData.url;
+          tab.crop = "center";
+        }
+      }
+    }
+
     if (!this._isWindowLoaded(aWindow)) {
       // from now on, the data will come from the actual window
       delete this._statesToRestore[aWindow.__SS_restoreID];
       delete aWindow.__SS_restoreID;
       delete this._windows[aWindow.__SSi]._restoring;
     }
     
     // helper hashes for ensuring unique frame IDs and unique document
@@ -2480,27 +2490,31 @@ SessionStoreService.prototype = {
         this._tabsToRestore.hidden.push(tab);
       else
         this._tabsToRestore.visible.push(tab);
       this.restoreNextTab();
     }
   },
 
   restoreTab: function(aTab) {
+    let window = aTab.ownerDocument.defaultView;
     let browser = aTab.linkedBrowser;
     let tabData = browser.__SS_data;
 
     // There are cases within where we haven't actually started a load and so we
     // should call restoreNextTab. We don't want to do it immediately though
     // since we might not set userTypedValue in a timely fashion.
     let shouldRestoreNextTab = false;
 
     // Increase our internal count.
     this._tabsRestoringCount++;
 
+    // Decrement the number of tabs this window needs to restore
+    window.__SS_tabsToRestore--;
+
     delete browser.__SS_needsRestore;
 
     let activeIndex = (tabData.index || tabData.entries.length) - 1;
     if (activeIndex >= tabData.entries.length)
       activeIndex = tabData.entries.length - 1;
 
     // Attach data that will be restored on "load" event, after tab is restored.
     if (activeIndex > -1) {
@@ -2571,19 +2585,21 @@ SessionStoreService.prototype = {
 
     if (nextTabArray) {
       let tab = nextTabArray.shift();
       this.restoreTab(tab);
     }
     else {
       // Remove the progress listener from windows. It will get re-added as needed.
       this._forEachBrowserWindow(function(aWindow) {
-        // This won't fail since removeTabsProgressListener just filters. It
-        // doesn't attempt to splice.
-        aWindow.gBrowser.removeTabsProgressListener(gRestoreTabsProgressListener)
+        if (!aWindow.__SS_tabsToRestore) {
+          // This won't fail since removeTabsProgressListener just filters. It
+          // doesn't attempt to splice.
+          aWindow.gBrowser.removeTabsProgressListener(gRestoreTabsProgressListener);
+        }
       });
     }
   },
 
   /**
    * expands serialized history data into a session-history-entry instance
    * @param aEntry
    *        Object containing serialized history data for a URL
@@ -3493,21 +3509,46 @@ SessionStoreService.prototype = {
 #endif
     this._closedWindows.splice(spliceTo);
   },
 
   /**
    * Reset state to prepare for a new session state to be restored.
    */
   _resetRestoringState: function sss__initRestoringState() {
-    //
-    this._tasToRestore = { visible: [], hidden: [] };
+    this._tabsToRestore = { visible: [], hidden: [] };
     this._tabsRestoringCount = 0;
   },
 
+  _resetTabRestoringState: function sss__resetTabRestoringState(aTab, aRestoreNextTab) {
+    let browser = aTab.linkedBrowser;
+
+    if (browser.__SS_restoring) {
+      delete browser.__SS_restoring;
+      if (aRestoreNextTab) {
+        // this._tabsRestoringCount is decremented in restoreNextTab.
+        this.restoreNextTab(true);
+      }
+      else {
+        // Even if we aren't restoring the next tab, we still need to decrement
+        // the restoring count. Normally it gets done within restoreNextTab.
+        this._tabsRestoringCount--;
+      }
+    }
+    else if (browser.__SS_needsRestore) {
+      let window = aTab.ownerDocument.defaultView;
+      window.__SS_tabsToRestore--;
+      delete browser.__SS_needsRestore;
+      if (aTab.hidden)
+        this._tabsToRestore.hidden.splice(this._tabsToRestore.hidden.indexOf(aTab));
+      else
+        this._tabsToRestore.visible.splice(this._tabsToRestore.visible.indexOf(aTab));
+    }
+  },
+
 /* ........ Storage API .............. */
 
   /**
    * write file to disk
    * @param aFile
    *        nsIFile
    * @param aData
    *        String data
--- a/browser/components/sessionstore/test/browser/browser_586068-cascaded_restore.js
+++ b/browser/components/sessionstore/test/browser/browser_586068-cascaded_restore.js
@@ -43,39 +43,46 @@ let stateBackup = ss.getBrowserState();
 
 
 function test() {
   /** Test for Bug 586068 - Cascade page loads when restoring **/
   waitForExplicitFinish();
   runNextTest();
 }
 
-let tests = [test_cascade, test_select];
+let tests = [test_cascade, test_select, test_multiWindowState,
+             test_setWindowStateNoOverwrite, test_setWindowStateOverwrite,
+             test_setBrowserStateInterrupted];
 function runNextTest() {
+  // Reset the pref
+  try {
+    Services.prefs.clearUserPref("browser.sessionstore.max_concurrent_tabs");
+  } catch (e) {}
+
+  // set an empty state & run the next test, or finish
   if (tests.length) {
-    ss.setWindowState(window,
-                      JSON.stringify({ windows: [{ tabs: [{ url: 'about:blank' }], }] }),
-                      true);
+    ss.setBrowserState(JSON.stringify({ windows: [{ tabs: [{ url: 'about:blank' }], }] }));
     executeSoon(tests.shift());
   }
   else {
     ss.setBrowserState(stateBackup);
     executeSoon(finish);
   }
 }
 
 
 function test_cascade() {
   // Set the pref to 1 so we know exactly how many tabs should be restoring at any given time
   Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 1);
 
   // We have our own progress listener for this test, which we'll attach before our state is set
   let progressListener = {
     onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
-      if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+      if (aBrowser.__SS_restoring &&
+          aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
         test_cascade_progressCallback();
     }
   }
 
   let state = { windows: [{ tabs: [
     { entries: [{ url: "http://example.com" }], extData: { "uniq": r() } },
@@ -97,128 +104,438 @@ function test_cascade() {
     [3, 1, 2],
     [2, 1, 3],
     [1, 1, 4],
     [0, 1, 5]
   ];
 
   function test_cascade_progressCallback() {
     loadCount++;
-    // We'll get the first <length of windows[0].tabs> load events here, even
-    // though they are ignored by sessionstore. Those are due to explicit
-    // "stop" events before any restoring action takes place. We can safely
-    // ignore these events.
-    if (loadCount <= state.windows[0].tabs.length)
-      return;
-
     let counts = countTabs();
-    let expected = expectedCounts[loadCount - state.windows[0].tabs.length - 1];
+    let expected = expectedCounts[loadCount - 1];
 
     is(counts[0], expected[0], "test_cascade: load " + loadCount + " - # tabs that need to be restored");
     is(counts[1], expected[1], "test_cascade: load " + loadCount + " - # tabs that are restoring");
     is(counts[2], expected[2], "test_cascade: load " + loadCount + " - # tabs that has been restored");
 
-    if (loadCount == state.windows[0].tabs.length * 2) {
-      window.gBrowser.removeTabsProgressListener(progressListener);
-      // Reset the pref
-      try {
-        Services.prefs.clearUserPref("browser.sessionstore.max_concurrent_tabs");
-      } catch (e) {}
-      runNextTest();
-    }
+    if (loadCount < state.windows[0].tabs.length)
+      return;
+
+    window.gBrowser.removeTabsProgressListener(progressListener);
+    runNextTest();
   }
 
   // This progress listener will get attached before the listener in session store.
   window.gBrowser.addTabsProgressListener(progressListener);
   ss.setBrowserState(JSON.stringify(state));
 }
 
 
 function test_select() {
   // Set the pref to 0 so we know exactly how many tabs should be restoring at
   // any given time. This guarantees that a finishing load won't start another.
   Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 0);
 
   // We have our own progress listener for this test, which we'll attach before our state is set
   let progressListener = {
     onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
-      if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+      if (aBrowser.__SS_restoring &&
+          aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
           aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
         test_select_progressCallback(aBrowser);
     }
   }
 
   let state = { windows: [{ tabs: [
     { entries: [{ url: "http://example.org" }], extData: { "uniq": r() } },
     { entries: [{ url: "http://example.org" }], extData: { "uniq": r() } },
     { entries: [{ url: "http://example.org" }], extData: { "uniq": r() } },
     { entries: [{ url: "http://example.org" }], extData: { "uniq": r() } },
     { entries: [{ url: "http://example.org" }], extData: { "uniq": r() } },
     { entries: [{ url: "http://example.org" }], extData: { "uniq": r() } }
-  ], selectedIndex: 1 }] };
+  ], selected: 1 }] };
 
   let loadCount = 0;
   // expectedCounts looks a little wierd for the test case, but it works. See
   // comment in test_cascade for an explanation
   let expectedCounts = [
     [5, 1, 0],
     [4, 1, 1],
     [3, 1, 2],
     [2, 1, 3],
     [1, 1, 4],
     [0, 1, 5]
   ];
   let tabOrder = [0, 5, 1, 4, 3, 2];
 
   function test_select_progressCallback(aBrowser) {
     loadCount++;
-    // We'll get the first <length of windows[0].tabs> load events here, even
-    // though they are ignored by sessionstore. Those are due to explicit
-    // "stop" events before any restoring action takes place. We can safely
-    // ignore these events.
-    if (loadCount <= state.windows[0].tabs.length)
-      return;
 
-    let loadIndex = loadCount - state.windows[0].tabs.length - 1;
     let counts = countTabs();
-    let expected = expectedCounts[loadIndex];
+    let expected = expectedCounts[loadCount - 1];
 
     is(counts[0], expected[0], "test_select: load " + loadCount + " - # tabs that need to be restored");
     is(counts[1], expected[1], "test_select: load " + loadCount + " - # tabs that are restoring");
     is(counts[2], expected[2], "test_select: load " + loadCount + " - # tabs that has been restored");
 
-    if (loadCount == state.windows[0].tabs.length * 2) {
-      window.gBrowser.removeTabsProgressListener(progressListener);
-      // Reset the pref
-      try {
-        Services.prefs.clearUserPref("browser.sessionstore.max_concurrent_tabs");
-      } catch (e) {}
-      runNextTest();
-    }
-    else {
+    if (loadCount < state.windows[0].tabs.length) {
       // double check that this tab was the right one
-      let expectedData = state.windows[0].tabs[tabOrder[loadIndex]].extData.uniq;
+      let expectedData = state.windows[0].tabs[tabOrder[loadCount - 1]].extData.uniq;
       let tab;
       for (let i = 0; i < window.gBrowser.tabs.length; i++) {
         if (!tab && window.gBrowser.tabs[i].linkedBrowser == aBrowser)
           tab = window.gBrowser.tabs[i];
       }
       is(ss.getTabValue(tab, "uniq"), expectedData, "test_select: load " + loadCount + " - correct tab was restored");
 
       // select the next tab
-      window.gBrowser.selectTabAtIndex(tabOrder[loadIndex + 1]);
+      window.gBrowser.selectTabAtIndex(tabOrder[loadCount]);
+      return;
     }
+
+    window.gBrowser.removeTabsProgressListener(progressListener);
+    runNextTest();
   }
 
   window.gBrowser.addTabsProgressListener(progressListener);
   ss.setBrowserState(JSON.stringify(state));
 }
 
 
+function test_multiWindowState() {
+  // We have our own progress listener for this test, which we'll attach before our state is set
+  let progressListener = {
+    onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
+      // We only care about load events when the tab still has __SS_restoring on it.
+      // Since our listener is attached before the sessionstore one, this works out.
+      if (aBrowser.__SS_restoring &&
+          aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+          aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
+          aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
+        test_multiWindowState_progressCallback(aBrowser);
+    }
+  }
+
+  // The first window will be put into the already open window and the second
+  // window will be opened with _openWindowWithState, which is the source of the problem.
+  let state = { windows: [
+    {
+      tabs: [
+        { entries: [{ url: "http://example.org#0" }], extData: { "uniq": r() } }
+      ],
+      selected: 1
+    },
+    {
+      tabs: [
+        { entries: [{ url: "http://example.com#1" }], extData: { "uniq": r() } },
+        { entries: [{ url: "http://example.com#2" }], extData: { "uniq": r() } },
+        { entries: [{ url: "http://example.com#3" }], extData: { "uniq": r() } },
+        { entries: [{ url: "http://example.com#4" }], extData: { "uniq": r() } },
+        { entries: [{ url: "http://example.com#5" }], extData: { "uniq": r() } },
+        { entries: [{ url: "http://example.com#6" }], extData: { "uniq": r() } }
+      ],
+      selected: 4
+    }
+  ] };
+  let numTabs = state.windows[0].tabs.length + state.windows[1].tabs.length;
+
+  let loadCount = 0;
+  function test_multiWindowState_progressCallback(aBrowser) {
+    loadCount++;
+
+    if (loadCount < numTabs)
+      return;
+
+    // We don't actually care about load order in this test, just that they all
+    // do load.
+    is(loadCount, numTabs, "test_multiWindowState: all tabs were restored");
+    let count = countTabs();
+    is(count[0], 0,
+       "test_multiWindowState: there are no tabs left needing restore");
+
+    // Remove the progress listener from this window, it will be removed from
+    // theWin when that window is closed (in setBrowserState).
+    window.gBrowser.removeTabsProgressListener(progressListener);
+    runNextTest();
+  }
+
+  // We also want to catch the 2nd window, so we need to observe domwindowopened
+  function windowObserver(aSubject, aTopic, aData) {
+    let theWin = aSubject.QueryInterface(Ci.nsIDOMWindow);
+    if (aTopic == "domwindowopened") {
+      theWin.addEventListener("load", function() {
+        theWin.removeEventListener("load", arguments.callee, false);
+
+        Services.ww.unregisterNotification(windowObserver);
+        theWin.gBrowser.addTabsProgressListener(progressListener);
+      }, false);
+    }
+  }
+  Services.ww.registerNotification(windowObserver);
+
+  window.gBrowser.addTabsProgressListener(progressListener);
+  ss.setBrowserState(JSON.stringify(state));
+}
+
+
+function test_setWindowStateNoOverwrite() {
+  // Set the pref to 1 so we know exactly how many tabs should be restoring at any given time
+  Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 1);
+
+  // We have our own progress listener for this test, which we'll attach before our state is set
+  let progressListener = {
+    onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
+      // We only care about load events when the tab still has __SS_restoring on it.
+      // Since our listener is attached before the sessionstore one, this works out.
+      if (aBrowser.__SS_restoring &&
+          aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+          aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
+          aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
+        test_setWindowStateNoOverwrite_progressCallback(aBrowser);
+    }
+  }
+
+  // We'll use 2 states so that we can make sure calling setWindowState doesn't
+  // wipe out currently restoring data.
+  let state1 = { windows: [{ tabs: [
+    { entries: [{ url: "http://example.com#1" }] },
+    { entries: [{ url: "http://example.com#2" }] },
+    { entries: [{ url: "http://example.com#3" }] },
+    { entries: [{ url: "http://example.com#4" }] },
+    { entries: [{ url: "http://example.com#5" }] },
+  ] }] };
+  let state2 = { windows: [{ tabs: [
+    { entries: [{ url: "http://example.org#1" }] },
+    { entries: [{ url: "http://example.org#2" }] },
+    { entries: [{ url: "http://example.org#3" }] },
+    { entries: [{ url: "http://example.org#4" }] },
+    { entries: [{ url: "http://example.org#5" }] }
+  ] }] };
+
+  let numTabs = state1.windows[0].tabs.length + state2.windows[0].tabs.length;
+
+  let loadCount = 0;
+  function test_setWindowStateNoOverwrite_progressCallback(aBrowser) {
+    loadCount++;
+
+    // When loadCount == 2, we'll also restore state2 into the window
+    if (loadCount == 2)
+      ss.setWindowState(window, JSON.stringify(state2), false);
+
+    if (loadCount < numTabs)
+      return;
+
+    // We don't actually care about load order in this test, just that they all
+    // do load.
+    is(loadCount, numTabs, "test_setWindowStateNoOverwrite: all tabs were restored");
+    is(window.__SS_tabsToRestore, 0,
+       "test_setWindowStateNoOverwrite: window doesn't think there are more tabs to restore");
+    let count = countTabs();
+    is(count[0], 0,
+       "test_setWindowStateNoOverwrite: there are no tabs left needing restore");
+
+    // Remove the progress listener from this window, it will be removed from
+    // theWin when that window is closed (in setBrowserState).
+    window.gBrowser.removeTabsProgressListener(progressListener);
+
+    runNextTest();
+  }
+
+  window.gBrowser.addTabsProgressListener(progressListener);
+  ss.setWindowState(window, JSON.stringify(state1), true);
+}
+
+
+function test_setWindowStateOverwrite() {
+  // Set the pref to 1 so we know exactly how many tabs should be restoring at any given time
+  Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 1);
+
+  // We have our own progress listener for this test, which we'll attach before our state is set
+  let progressListener = {
+    onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
+      // We only care about load events when the tab still has __SS_restoring on it.
+      // Since our listener is attached before the sessionstore one, this works out.
+      if (aBrowser.__SS_restoring &&
+          aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+          aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
+          aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
+        test_setWindowStateOverwrite_progressCallback(aBrowser);
+    }
+  }
+
+  // We'll use 2 states so that we can make sure calling setWindowState doesn't
+  // wipe out currently restoring data.
+  let state1 = { windows: [{ tabs: [
+    { entries: [{ url: "http://example.com#1" }] },
+    { entries: [{ url: "http://example.com#2" }] },
+    { entries: [{ url: "http://example.com#3" }] },
+    { entries: [{ url: "http://example.com#4" }] },
+    { entries: [{ url: "http://example.com#5" }] },
+  ] }] };
+  let state2 = { windows: [{ tabs: [
+    { entries: [{ url: "http://example.org#1" }] },
+    { entries: [{ url: "http://example.org#2" }] },
+    { entries: [{ url: "http://example.org#3" }] },
+    { entries: [{ url: "http://example.org#4" }] },
+    { entries: [{ url: "http://example.org#5" }] }
+  ] }] };
+
+  let numTabs = 2 + state2.windows[0].tabs.length;
+
+  let loadCount = 0;
+  function test_setWindowStateOverwrite_progressCallback(aBrowser) {
+    loadCount++;
+
+    // When loadCount == 2, we'll also restore state2 into the window
+    if (loadCount == 2)
+      ss.setWindowState(window, JSON.stringify(state2), true);
+
+    if (loadCount < numTabs)
+      return;
+
+    // We don't actually care about load order in this test, just that they all
+    // do load.
+    is(loadCount, numTabs, "test_setWindowStateOverwrite: all tabs were restored");
+    is(window.__SS_tabsToRestore, 0,
+       "test_setWindowStateOverwrite: window doesn't think there are more tabs to restore");
+    let count = countTabs();
+    is(count[0], 0,
+       "test_setWindowStateOverwrite: there are no tabs left needing restore");
+
+    // Remove the progress listener from this window, it will be removed from
+    // theWin when that window is closed (in setBrowserState).
+    window.gBrowser.removeTabsProgressListener(progressListener);
+
+    runNextTest();
+  }
+
+  window.gBrowser.addTabsProgressListener(progressListener);
+  ss.setWindowState(window, JSON.stringify(state1), true);
+}
+
+
+function test_setBrowserStateInterrupted() {
+  // Set the pref to 1 so we know exactly how many tabs should be restoring at any given time
+  Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 1);
+
+  // We have our own progress listener for this test, which we'll attach before our state is set
+  let progressListener = {
+    onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
+      // We only care about load events when the tab still has __SS_restoring on it.
+      // Since our listener is attached before the sessionstore one, this works out.
+      if (aBrowser.__SS_restoring &&
+          aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+          aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
+          aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
+        test_setBrowserStateInterrupted_progressCallback(aBrowser);
+    }
+  }
+
+  // The first state will be loaded using setBrowserState, followed by the 2nd
+  // state also being loaded using setBrowserState, interrupting the first restore.
+  let state1 = { windows: [
+    {
+      tabs: [
+        { entries: [{ url: "http://example.org#1" }], extData: { "uniq": r() } },
+        { entries: [{ url: "http://example.org#2" }], extData: { "uniq": r() } },
+        { entries: [{ url: "http://example.org#3" }], extData: { "uniq": r() } },
+        { entries: [{ url: "http://example.org#4" }], extData: { "uniq": r() } }
+      ],
+      selected: 1
+    },
+    {
+      tabs: [
+        { entries: [{ url: "http://example.com#1" }], extData: { "uniq": r() } },
+        { entries: [{ url: "http://example.com#2" }], extData: { "uniq": r() } },
+        { entries: [{ url: "http://example.com#3" }], extData: { "uniq": r() } },
+        { entries: [{ url: "http://example.com#4" }], extData: { "uniq": r() } },
+      ],
+      selected: 3
+    }
+  ] };
+  let state2 = { windows: [
+    {
+      tabs: [
+        { entries: [{ url: "http://example.org#5" }], extData: { "uniq": r() } },
+        { entries: [{ url: "http://example.org#6" }], extData: { "uniq": r() } },
+        { entries: [{ url: "http://example.org#7" }], extData: { "uniq": r() } },
+        { entries: [{ url: "http://example.org#8" }], extData: { "uniq": r() } }
+      ],
+      selected: 3
+    },
+    {
+      tabs: [
+        { entries: [{ url: "http://example.com#5" }], extData: { "uniq": r() } },
+        { entries: [{ url: "http://example.com#6" }], extData: { "uniq": r() } },
+        { entries: [{ url: "http://example.com#7" }], extData: { "uniq": r() } },
+        { entries: [{ url: "http://example.com#8" }], extData: { "uniq": r() } },
+      ],
+      selected: 1
+    }
+  ] };
+
+  // interruptedAfter will be set after the selected tab from each window have loaded.
+  let interruptedAfter = 0;
+  let loadedWindow1 = false;
+  let loadedWindow2 = false;
+  let numTabs = state2.windows[0].tabs.length + state2.windows[1].tabs.length;
+
+  let loadCount = 0;
+  function test_setBrowserStateInterrupted_progressCallback(aBrowser) {
+    loadCount++;
+
+    if (aBrowser.currentURI.spec == state1.windows[0].tabs[2].entries[0].url)
+      loadedWindow1 = true;
+    if (aBrowser.currentURI.spec == state1.windows[1].tabs[0].entries[0].url)
+      loadedWindow2 = true;
+
+    if (!interruptedAfter && loadedWindow1 && loadedWindow2) {
+      interruptedAfter = loadCount;
+      ss.setBrowserState(JSON.stringify(state2));
+      return;
+    }
+
+    if (loadCount < numTabs + interruptedAfter)
+      return;
+
+    // We don't actually care about load order in this test, just that they all
+    // do load.
+    is(loadCount, numTabs + interruptedAfter,
+       "test_setBrowserStateInterrupted: all tabs were restored");
+    let count = countTabs();
+    is(count[0], 0,
+       "test_setBrowserStateInterrupted: there are no tabs left needing restore");
+
+    // Remove the progress listener from this window, it will be removed from
+    // theWin when that window is closed (in setBrowserState).
+    window.gBrowser.removeTabsProgressListener(progressListener);
+    Services.ww.unregisterNotification(windowObserver);
+    runNextTest();
+  }
+
+  // We also want to catch the extra windows (there should be 2), so we need to observe domwindowopened
+  function windowObserver(aSubject, aTopic, aData) {
+    let theWin = aSubject.QueryInterface(Ci.nsIDOMWindow);
+    if (aTopic == "domwindowopened") {
+      theWin.addEventListener("load", function() {
+        theWin.removeEventListener("load", arguments.callee, false);
+
+        Services.ww.unregisterNotification(windowObserver);
+        theWin.gBrowser.addTabsProgressListener(progressListener);
+      }, false);
+    }
+  }
+  Services.ww.registerNotification(windowObserver);
+
+  window.gBrowser.addTabsProgressListener(progressListener);
+  ss.setBrowserState(JSON.stringify(state1));
+}
+
+
 function countTabs() {
   let needsRestore = 0,
       isRestoring = 0,
       wasRestored = 0;
 
   let windowsEnum = Services.wm.getEnumerator("navigator:browser");
 
   while (windowsEnum.hasMoreElements()) {
--- a/browser/locales/en-US/chrome/browser/aboutDialog.dtd
+++ b/browser/locales/en-US/chrome/browser/aboutDialog.dtd
@@ -16,8 +16,41 @@
 <!-- LOCALIZATION NOTE (bottomLinks.license): This is a link title that links to about:license. -->
 <!ENTITY bottomLinks.license        "Licensing Information">
 
 <!-- LOCALIZATION NOTE (bottomLinks.rights): This is a link title that links to about:rights. -->
 <!ENTITY bottomLinks.rights         "End User Rights">
 
 <!-- LOCALIZATION NOTE (bottomLinks.privacy): This is a link title that links to http://www.mozilla.com/legal/privacy/. -->
 <!ENTITY bottomLinks.privacy        "Privacy Policy">
+
+<!-- LOCALIZATION NOTE (update.checkingForUpdates): try to make the localized text short (see bug 596813 for screenshots). -->
+<!ENTITY update.checkingForUpdates  "Checking for updates…">
+<!-- LOCALIZATION NOTE (update.checkingAddonCompat): try to make the localized text short (see bug 596813 for screenshots). -->
+<!ENTITY update.checkingAddonCompat "Checking Add-on compatibility…">
+<!-- LOCALIZATION NOTE (update.noUpdatesFound): try to make the localized text short (see bug 596813 for screenshots). -->
+<!ENTITY update.noUpdatesFound      "&brandShortName; is up to date">
+<!-- LOCALIZATION NOTE (update.adminDisabled): try to make the localized text short (see bug 596813 for screenshots). -->
+<!ENTITY update.adminDisabled       "Updates disabled by your system administrator">
+
+<!-- LOCALIZATION NOTE (update.failed.start,update.failed.linkText,update.failed.end):
+     update.failed.start, update.failed.linkText, and update.failed.end all go into
+     one line with linkText being wrapped in an anchor that links to a site to download
+     the latest version of Firefox (e.g. http://www.firefox.com). As this is all in
+     one line, try to make the localized text short (see bug 596813 for screenshots). -->
+<!ENTITY update.failed.start        "Update failed. ">
+<!ENTITY update.failed.linkText     "Download the latest version">
+<!ENTITY update.failed.end          "">
+
+<!-- LOCALIZATION NOTE (update.manual.start,update.manual.end): update.manual.start and update.manual.end
+     all go into one line and have an anchor in between with text that is the same as the link to a site
+     to download the latest version of Firefox (e.g. http://www.firefox.com). As this is all in one line,
+     try to make the localized text short (see bug 596813 for screenshots). -->
+<!ENTITY update.manual.start        "Updates available at ">
+<!ENTITY update.manual.end          "">
+
+<!-- LOCALIZATION NOTE (update.downloading.start,update.downloading.end): update.downloading.start and 
+     update.downloading.end all go into one line, with the amount downloaded inserted in between. As this
+     is all in one line, try to make the localized text short (see bug 596813 for screenshots). The — is
+     the "em dash" (long dash).
+     example: Downloading update — 111 KB of 13 MB -->
+<!ENTITY update.downloading.start   "Downloading update — ">
+<!ENTITY update.downloading.end     "">
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -136,41 +136,33 @@ updatesItem_downloading.accesskey=D
 updatesItem_resume=Resume Downloading %S…
 updatesItem_resumeFallback=Resume Downloading Update…
 updatesItem_resume.accesskey=D
 updatesItem_pending=Apply Downloaded Update Now…
 updatesItem_pendingFallback=Apply Downloaded Update Now…
 updatesItem_pending.accesskey=D
 
 # Check for Updates in the About Dialog - button labels and accesskeys
-# LOCALIZATION NOTE - all of the following update buttons and labels will only
-# be displayed one at a time. So, if a button is displayed no other buttons or
-# labels will be displayed and if a label is displayed no other buttons or
-# labels will be displayed. They will be placed directly under the Firefox
-# version in the about dialog.
-update.checkButton.label=Check for Updates…
-update.checkButton.accesskey=C
+# LOCALIZATION NOTE - all of the following update buttons labels will only be
+# displayed one at a time. So, if a button is displayed nothing else will
+# be displayed alongside of the button. The button when displayed is located
+# directly under the Firefox version in the about dialog (see bug 596813 for
+# screenshots).
+update.checkInsideButton.label=Check for Updates
+update.checkInsideButton.accesskey=C
 update.resumeButton.label=Resume Downloading %S…
 update.resumeButton.accesskey=D
 update.openUpdateUI.applyButton.label=Apply Update…
 update.openUpdateUI.applyButton.accesskey=A
 update.restart.applyButton.label=Apply Update
 update.restart.applyButton.accesskey=A
 update.openUpdateUI.upgradeButton.label=Upgrade Now…
 update.openUpdateUI.upgradeButton.accesskey=U
 update.restart.upgradeButton.label=Upgrade Now
 update.restart.upgradeButton.accesskey=U
-# Check for Updates in the About Dialog - status labels
-update.checkingForUpdate.label=Checking for updates…
-update.checkingAddonCompat.label=Checking add-on compatibility…
-update.noUpdateFound.label=This is the latest available version
-# LOCALIZATION NOTE (update.downloading) — is the "em dash" (long dash)
-# %S is the amount download
-# examples: Downloading update — 111 KB of 13 MB
-update.downloading=Downloading update — %S
 
 # RSS Pretty Print
 feedShowFeedNew=Subscribe to '%S'…
 
 menuOpenAllInTabs.label=Open All in Tabs
 
 # History menu
 menuRestoreAllTabs.label=Restore All Tabs
--- a/browser/themes/gnomestripe/browser/browser.css
+++ b/browser/themes/gnomestripe/browser/browser.css
@@ -1757,21 +1757,16 @@ listitem.style-section {
   color: black;
   font-weight: bold;
 }
 
 panel[dimmed="true"] {
   opacity: 0.5;
 }
 
-/* Remove the resizer from the statusbar compatibility shim */
-#status-bar .statusbar-resizerpanel {
-  display: none;
-}
-
 /* Vertically-center the statusbar compatibility shim, because
    toolbars, even in small-icon mode, are a bit taller than 
    statusbars. */
 #status-bar {
   margin-top: .3em;
 }
 
 /* Remove all borders from statusbarpanel children of
--- a/browser/themes/pinstripe/browser/browser.css
+++ b/browser/themes/pinstripe/browser/browser.css
@@ -2257,21 +2257,16 @@ listitem.style-section {
   color: black;
   font-weight: bold;
 }
 
 panel[dimmed="true"] {
   opacity: 0.5;
 }
 
-/* Remove the resizer from the statusbar compatibility shim */
-#status-bar .statusbar-resizerpanel {
-  display: none;
-}
-
 /* Vertically-center the statusbar compatibility shim, because
    toolbars, even in small-icon mode, are a bit taller than
    statusbars. Also turn off the statusbar border. On Windows
    we have to disable borders on statusbar *and* child statusbar
    elements. */
 #status-bar {
   margin-top: 0.3em;
   -moz-appearance: none;
--- a/browser/themes/winstripe/browser/browser.css
+++ b/browser/themes/winstripe/browser/browser.css
@@ -2159,21 +2159,16 @@ listitem.style-section {
   color: black;
   font-weight: bold;
 }
 
 panel[dimmed="true"] {
   opacity: 0.5;
 }
 
-/* Remove the resizer from the statusbar compatibility shim */
-#status-bar .statusbar-resizerpanel {
-  display: none;
-}
-
 /* Vertically-center the statusbar compatibility shim, because
    toolbars, even in small-icon mode, are a bit taller than
    statusbars. Also turn off the statusbar border. On Windows
    we have to disable borders on statusbar *and* child statusbar
    elements. */
 #status-bar {
   margin-top: .3em;
   border-width: 0;
--- a/build/automation.py.in
+++ b/build/automation.py.in
@@ -207,16 +207,18 @@ class Automation(object):
                  preexec_fn=None,
                  close_fds=False,
                  shell=False,
                  cwd=None,
                  env=None,
                  universal_newlines=False,
                  startupinfo=None,
                  creationflags=0):
+      args = automationutils.wrapCommand(args)
+      print "args: %s" % args
       subprocess.Popen.__init__(self, args, bufsize, executable,
                                 stdin, stdout, stderr,
                                 preexec_fn, close_fds,
                                 shell, cwd, env,
                                 universal_newlines, startupinfo, creationflags)
       self.log = _log
 
     def kill(self):
--- a/build/automationutils.py
+++ b/build/automationutils.py
@@ -31,29 +31,30 @@
 # use your version of this file under the terms of the MPL, indicate your
 # decision by deleting the provisions above and replace them with the notice
 # and other provisions required by the GPL or the LGPL. If you do not delete
 # the provisions above, a recipient may use your version of this file under
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK ***** */
 
-import glob, logging, os, shutil, subprocess, sys
+import glob, logging, os, platform, shutil, subprocess, sys
 import re
 from urlparse import urlparse
 
 __all__ = [
   "addCommonOptions",
   "checkForCrashes",
   "dumpLeakLog",
   "isURL",
   "processLeakLog",
   "getDebuggerInfo",
   "DEBUGGER_INFO",
   "replaceBackSlashes",
+  "wrapCommand",
   ]
 
 # Map of debugging programs to information about them, like default arguments
 # and whether or not they are interactive.
 DEBUGGER_INFO = {
   # gdb requires that you supply the '--args' flag in order to pass arguments
   # after the executable name to the executable.
   "gdb": {
@@ -354,8 +355,21 @@ def processLeakLog(leakLogFile, leakThre
       m = pidRegExp.search(fileName)
       if m:
         processType = m.group(1)
         processPID = m.group(2)
       processSingleLeakFile(thisFile, processPID, processType, leakThreshold)
 
 def replaceBackSlashes(input):
   return input.replace('\\', '/')
+
+def wrapCommand(cmd):
+  """
+  If running on OS X 10.5 or older, wrap |cmd| so that it will
+  be executed as an i386 binary, in case it's a 32-bit/64-bit universal
+  binary.
+  """
+  if platform.system() == "Darwin" and \
+     hasattr(platform, 'mac_ver') and \
+     platform.mac_ver()[0][:4] < '10.6':
+    return ["arch", "-arch", "i386"] + cmd
+  # otherwise just execute the command normally
+  return cmd
--- a/configure.in
+++ b/configure.in
@@ -5996,27 +5996,29 @@ if test -n "$MOZ_WEBM"; then
       if test -z "$GNU_CC"; then
         VPX_ASFLAGS="-f x64 -rnasm -pnasm"
         VPX_X86_ASM=1
       fi
     ;;
     WINNT:x86)
       if test -z "$GNU_CC"; then
         dnl Check for yasm 1.1 or greater.
-        if test "$_YASM_MAJOR_VERSION" -gt "1" -o \( "$_YASM_MAJOR_VERSION" -eq "1" -a "$_YASM_MINOR_VERSION" -ge "1" \) ; then
+        if test -n "$COMPILE_ENVIRONMENT" -a -z "$YASM"; then
+          AC_MSG_ERROR([yasm 1.1 or greater is required to build libvpx on Win32, but it appears not to be installed.  Install it (included in MozillaBuild 1.5.1 and newer) or configure with --disable-webm (which disables the WebM video format). See https://developer.mozilla.org/en/YASM for more details.])
+        elif test -n "$COMPILE_ENVIRONMENT" -a "$_YASM_MAJOR_VERSION" -lt "1" -o \( "$_YASM_MAJOR_VERSION" -eq "1" -a "$_YASM_MINOR_VERSION" -lt "1" \) ; then
+          AC_MSG_ERROR([yasm 1.1 or greater is required to build libvpx on Win32, but you appear to have version $_YASM_MAJOR_VERSION.$_YASM_MINOR_VERSION.  Upgrade to the newest version (included in MozillaBuild 1.5.1 and newer) or configure with --disable-webm (which disables the WebM video format). See https://developer.mozilla.org/en/YASM for more details.])
+        else
           VPX_ASFLAGS="-f win32 -rnasm -pnasm -DPIC"
           VPX_X86_ASM=1
-        else
-          AC_MSG_ERROR([yasm 1.1 or greater is required to build libvpx on Win32, but you appear to have version $_YASM_MAJOR_VERSION.$_YASM_MINOR_VERSION.  Upgrade to the newest version (included in MozillaBuild 1.5.1 and newer) or configure with --disable-webm (which disables the WebM video format). See https://developer.mozilla.org/en/YASM for more details.])
         fi
       fi
     ;;
     esac
 
-    if test "$COMPILE_ENVIROMENT" -a -n "$VPX_X86_ASM" -a -z "$VPX_AS"; then
+    if test -n "$COMPILE_ENVIRONMENT" -a -n "$VPX_X86_ASM" -a -z "$VPX_AS"; then
       AC_MSG_ERROR([yasm is a required build tool for this architecture when webm is enabled. You may either install yasm or --disable-webm (which disables the WebM video format). See https://developer.mozilla.org/en/YASM for more details.])
     fi
 
     if test -n "$VPX_X86_ASM"; then
       AC_DEFINE(VPX_X86_ASM)
     else
       AC_MSG_WARN([No assembler or assembly support for libvpx. Using unoptimized C routines.])
     fi
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -1689,33 +1689,16 @@ public:
   /**
    * Determine whether a content node is focused or not,
    *
    * @param aContent the content node to check
    * @return true if the content node is focused, false otherwise.
    */
   static PRBool IsFocusedContent(nsIContent *aContent);
 
-#ifdef MOZ_IPC
-#ifdef ANDROID
-  static void SetActiveFrameLoader(nsFrameLoader *aFrameLoader)
-  {
-    sActiveFrameLoader = aFrameLoader;
-  }
-
-  static void ClearActiveFrameLoader(const nsFrameLoader *aFrameLoader)
-  {
-    if (sActiveFrameLoader == aFrameLoader)
-      sActiveFrameLoader = nsnull;
-  }
-
-  static already_AddRefed<nsFrameLoader> GetActiveFrameLoader();
-#endif
-#endif
-
 private:
 
   static PRBool InitializeEventTable();
 
   static nsresult EnsureStringBundle(PropertiesFile aFile);
 
   static nsIDOMScriptObjectFactory *GetDOMScriptObjectFactory();
 
@@ -1795,22 +1778,16 @@ private:
   static PRUint32 sRemovableScriptBlockerCount;
   static nsCOMArray<nsIRunnable>* sBlockedScriptRunners;
   static PRUint32 sRunnersCountAtFirstBlocker;
   static PRUint32 sScriptBlockerCountWhereRunnersPrevented;
 
   static nsIInterfaceRequestor* sSameOriginChecker;
 
   static PRBool sIsHandlingKeyBoardEvent;
-
-#ifdef MOZ_IPC
-#ifdef ANDROID
-  static nsFrameLoader *sActiveFrameLoader;
-#endif
-#endif
 };
 
 #define NS_HOLD_JS_OBJECTS(obj, clazz)                                         \
   nsContentUtils::HoldJSObjects(NS_CYCLE_COLLECTION_UPCAST(obj, clazz),        \
                                 &NS_CYCLE_COLLECTION_NAME(clazz))
 
 #define NS_DROP_JS_OBJECTS(obj, clazz)                                         \
   nsContentUtils::DropJSObjects(NS_CYCLE_COLLECTION_UPCAST(obj, clazz))
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -264,22 +264,16 @@ JSRuntime *nsAutoGCRoot::sJSScriptRuntim
 
 PRBool nsContentUtils::sIsHandlingKeyBoardEvent = PR_FALSE;
 
 PRBool nsContentUtils::sInitialized = PR_FALSE;
 
 nsRefPtrHashtable<nsPrefObserverHashKey, nsPrefOldCallback>
   *nsContentUtils::sPrefCallbackTable = nsnull;
 
-#ifdef MOZ_IPC
-#ifdef ANDROID
-nsFrameLoader *nsContentUtils::sActiveFrameLoader = nsnull;
-#endif
-#endif
-
 static PLDHashTable sEventListenerManagersHash;
 
 class EventListenerManagerMapEntry : public PLDHashEntryHdr
 {
 public:
   EventListenerManagerMapEntry(const void *aKey)
     : mKey(aKey)
   {
@@ -6251,27 +6245,16 @@ mozAutoRemovableBlockerRemover::~mozAuto
 PRBool
 nsContentUtils::IsFocusedContent(nsIContent* aContent)
 {
   nsFocusManager* fm = nsFocusManager::GetFocusManager();
 
   return fm && fm->GetFocusedContent() == aContent;
 }
 
-#ifdef MOZ_IPC
-#ifdef ANDROID
-// static
-already_AddRefed<nsFrameLoader>
-nsContentUtils::GetActiveFrameLoader()
-{
-  return nsCOMPtr<nsFrameLoader>(sActiveFrameLoader).forget();
-}
-#endif
-#endif
-
 void nsContentUtils::RemoveNewlines(nsString &aString)
 {
   // strip CR/LF and null
   static const char badChars[] = {'\r', '\n', 0};
   aString.StripChars(badChars);
 }
 
 void
--- a/content/base/src/nsFrameLoader.cpp
+++ b/content/base/src/nsFrameLoader.cpp
@@ -1042,19 +1042,16 @@ nsFrameLoader::SwapWithOtherLoader(nsFra
   return NS_OK;
 }
 
 void
 nsFrameLoader::DestroyChild()
 {
 #ifdef MOZ_IPC
   if (mRemoteBrowser) {
-#ifdef ANDROID
-    nsContentUtils::ClearActiveFrameLoader(this);
-#endif
     mRemoteBrowser->SetOwnerElement(nsnull);
     // If this fails, it's most likely due to a content-process crash,
     // and auto-cleanup will kick in.  Otherwise, the child side will
     // destroy itself and send back __delete__().
     unused << mRemoteBrowser->SendDestroy();
     mRemoteBrowser = nsnull;
   }
 #endif
@@ -1665,19 +1662,16 @@ nsFrameLoader::GetRemoteBrowser()
 }
 #endif
 
 NS_IMETHODIMP
 nsFrameLoader::ActivateRemoteFrame() {
 #ifdef MOZ_IPC
   if (mRemoteBrowser) {
     mRemoteBrowser->Activate();
-#ifdef ANDROID
-    nsContentUtils::SetActiveFrameLoader(this);
-#endif
     return NS_OK;
   }
 #endif
   return NS_ERROR_UNEXPECTED;
 }
 
 NS_IMETHODIMP
 nsFrameLoader::SendCrossProcessMouseEvent(const nsAString& aType,
new file mode 100644
--- /dev/null
+++ b/content/canvas/crashtests/553938-1.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html>
+  <head>
+    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
+    <title>ImageData Crash Test</title>
+  </head>
+  <body onload="crash();">
+    <canvas id="c" width="10" height="10"></canvas>
+    <script>
+      function crash() {
+        var canvas = document.getElementById('c');
+        var ctx = canvas.getContext('2d');
+        var imgData = {data: new Array(10*10*4), width: 10, height: 10};
+
+        imgData[0] = 0;
+        ctx.putImageData(imgData, 0, 0);
+      }
+    </script>
+  </body>
+</html>
--- a/content/canvas/crashtests/crashtests.list
+++ b/content/canvas/crashtests/crashtests.list
@@ -1,2 +1,3 @@
 load 360293-1.html
 load 421715-1.html
+load 553938-1.html
--- a/content/events/src/nsEventStateManager.cpp
+++ b/content/events/src/nsEventStateManager.cpp
@@ -40,19 +40,17 @@
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifdef MOZ_IPC
-#ifdef ANDROID
-#include "mozilla/dom/PBrowserParent.h"
-#endif
+#include "mozilla/dom/TabParent.h"
 #endif
 
 #include "nsCOMPtr.h"
 #include "nsEventStateManager.h"
 #include "nsEventListenerManager.h"
 #include "nsIMEStateManager.h"
 #include "nsContentEventHandler.h"
 #include "nsIContent.h"
@@ -161,19 +159,17 @@
 #include "nsICommandParams.h"
 #include "mozilla/Services.h"
 
 #ifdef XP_MACOSX
 #import <ApplicationServices/ApplicationServices.h>
 #endif
 
 #ifdef MOZ_IPC
-#ifdef ANDROID
-#include "nsFrameLoader.h"
-#endif
+using namespace mozilla::dom;
 #endif
 
 //#define DEBUG_DOCSHELL_FOCUS
 
 #define NS_USER_INTERACTION_INTERVAL 5000 // ms
 
 static NS_DEFINE_CID(kFrameTraversalCID, NS_FRAMETRAVERSAL_CID);
 
@@ -1316,64 +1312,88 @@ nsEventStateManager::PreHandleEvent(nsPr
       if ((msEvent->scrollFlags & nsMouseScrollEvent::kIsHorizontal) ?
            mLastLineScrollConsumedX : mLastLineScrollConsumedY) {
         *aStatus = nsEventStatus_eConsumeNoDefault;
       }
     }
     break;
   case NS_QUERY_SELECTED_TEXT:
     {
+#ifdef MOZ_IPC
+      if (RemoteQueryContentEvent(aEvent))
+        break;
+#endif
       nsContentEventHandler handler(mPresContext);
       handler.OnQuerySelectedText((nsQueryContentEvent*)aEvent);
     }
     break;
   case NS_QUERY_TEXT_CONTENT:
     {
+#ifdef MOZ_IPC
+      if (RemoteQueryContentEvent(aEvent))
+        break;
+#endif
       nsContentEventHandler handler(mPresContext);
       handler.OnQueryTextContent((nsQueryContentEvent*)aEvent);
     }
     break;
   case NS_QUERY_CARET_RECT:
     {
+      // XXX remote event
       nsContentEventHandler handler(mPresContext);
       handler.OnQueryCaretRect((nsQueryContentEvent*)aEvent);
     }
     break;
   case NS_QUERY_TEXT_RECT:
     {
+      // XXX remote event
       nsContentEventHandler handler(mPresContext);
       handler.OnQueryTextRect((nsQueryContentEvent*)aEvent);
     }
     break;
   case NS_QUERY_EDITOR_RECT:
     {
+      // XXX remote event
       nsContentEventHandler handler(mPresContext);
       handler.OnQueryEditorRect((nsQueryContentEvent*)aEvent);
     }
     break;
   case NS_QUERY_CONTENT_STATE:
     {
+      // XXX remote event
       nsContentEventHandler handler(mPresContext);
       handler.OnQueryContentState(static_cast<nsQueryContentEvent*>(aEvent));
     }
     break;
   case NS_QUERY_SELECTION_AS_TRANSFERABLE:
     {
+      // XXX remote event
       nsContentEventHandler handler(mPresContext);
       handler.OnQuerySelectionAsTransferable(static_cast<nsQueryContentEvent*>(aEvent));
     }
     break;
   case NS_QUERY_CHARACTER_AT_POINT:
     {
+      // XXX remote event
       nsContentEventHandler handler(mPresContext);
       handler.OnQueryCharacterAtPoint(static_cast<nsQueryContentEvent*>(aEvent));
     }
     break;
   case NS_SELECTION_SET:
     {
+#ifdef MOZ_IPC
+      nsSelectionEvent *selectionEvent =
+          static_cast<nsSelectionEvent*>(aEvent);
+      if (IsTargetCrossProcess(selectionEvent)) {
+        // Will not be handled locally, remote the event
+        if (GetCrossProcessTarget()->SendSelectionEvent(*selectionEvent))
+          selectionEvent->mSucceeded = PR_TRUE;
+        break;
+      }
+#endif
       nsContentEventHandler handler(mPresContext);
       handler.OnSelectionEvent((nsSelectionEvent*)aEvent);
     }
     break;
   case NS_CONTENT_COMMAND_CUT:
   case NS_CONTENT_COMMAND_COPY:
   case NS_CONTENT_COMMAND_PASTE:
   case NS_CONTENT_COMMAND_DELETE:
@@ -1385,49 +1405,43 @@ nsEventStateManager::PreHandleEvent(nsPr
     }
     break;
   case NS_CONTENT_COMMAND_SCROLL:
     {
       DoContentCommandScrollEvent(static_cast<nsContentCommandEvent*>(aEvent));
     }
     break;
 #ifdef MOZ_IPC
-#ifdef ANDROID
   case NS_TEXT_TEXT:
     {
       nsTextEvent *textEvent = static_cast<nsTextEvent*>(aEvent);
       if (IsTargetCrossProcess(textEvent)) {
         // Will not be handled locally, remote the event
-        mozilla::dom::PBrowserParent *remoteBrowser = GetCrossProcessTarget();
-        if (remoteBrowser &&
-            remoteBrowser->SendTextEvent(*textEvent)) {
+        if (GetCrossProcessTarget()->SendTextEvent(*textEvent)) {
           // Cancel local dispatching
-          *aStatus = nsEventStatus_eConsumeNoDefault;
+          aEvent->flags |= NS_EVENT_FLAG_STOP_DISPATCH;
         }
       }
     }
     break;
   case NS_COMPOSITION_START:
   case NS_COMPOSITION_END:
     {
       nsCompositionEvent *compositionEvent =
           static_cast<nsCompositionEvent*>(aEvent);
       if (IsTargetCrossProcess(compositionEvent)) {
         // Will not be handled locally, remote the event
-        mozilla::dom::PBrowserParent *remoteBrowser = GetCrossProcessTarget();
-        if (remoteBrowser &&
-            remoteBrowser->SendCompositionEvent(*compositionEvent)) {
+        if (GetCrossProcessTarget()->SendCompositionEvent(*compositionEvent)) {
           // Cancel local dispatching
-          *aStatus = nsEventStatus_eConsumeNoDefault;
+          aEvent->flags |= NS_EVENT_FLAG_STOP_DISPATCH;
         }
       }
     }
     break;
-#endif
-#endif
+#endif // MOZ_IPC
   }
   return NS_OK;
 }
 
 static PRInt32
 GetAccessModifierMask(nsISupports* aDocShell)
 {
   nsCOMPtr<nsIDocShellTreeItem> treeItem(do_QueryInterface(aDocShell));
@@ -3250,88 +3264,51 @@ nsEventStateManager::PostHandleEvent(nsP
       mCurrentTarget->GetContentForEvent(presContext, aEvent,
                                          getter_AddRefs(targetContent));
       if (!NodeAllowsClickThrough(targetContent)) {
         *aStatus = nsEventStatus_eConsumeNoDefault;
       }
     }
     break;
 #endif
-
-#ifdef MOZ_IPC
-#ifdef ANDROID
-  case NS_QUERY_SELECTED_TEXT:
-  case NS_QUERY_TEXT_CONTENT:
-  case NS_QUERY_CARET_RECT:
-  case NS_QUERY_TEXT_RECT:
-  case NS_QUERY_EDITOR_RECT:
-  case NS_QUERY_CONTENT_STATE:
-  // We don't remote nsITransferable yet
-  //case NS_QUERY_SELECTION_AS_TRANSFERABLE:
-  case NS_QUERY_CHARACTER_AT_POINT:
-    {
-      nsQueryContentEvent *queryEvent =
-          static_cast<nsQueryContentEvent*>(aEvent);
-      // If local query failed, try remote query
-      if (queryEvent->mSucceeded)
-        break;
-
-      mozilla::dom::PBrowserParent *remoteBrowser = GetCrossProcessTarget();
-      if (remoteBrowser &&
-          remoteBrowser->SendQueryContentEvent(*queryEvent)) {
-        queryEvent->mWasAsync = PR_TRUE;
-        queryEvent->mSucceeded = PR_TRUE;
-      }
-    }
-    break;
-  case NS_SELECTION_SET:
-    {
-      nsSelectionEvent *selectionEvent =
-          static_cast<nsSelectionEvent*>(aEvent);
-      // If local handler failed, try remoting the event
-      if (selectionEvent->mSucceeded)
-        break;
-
-      mozilla::dom::PBrowserParent *remoteBrowser = GetCrossProcessTarget();
-      if (remoteBrowser &&
-          remoteBrowser->SendSelectionEvent(*selectionEvent))
-        selectionEvent->mSucceeded = PR_TRUE;
-    }
-    break;
-#endif // ANDROID
-#endif // MOZ_IPC
   }
 
   //Reset target frame to null to avoid mistargeting after reentrant event
   mCurrentTarget = nsnull;
   mCurrentTargetContent = nsnull;
 
   return ret;
 }
 
 #ifdef MOZ_IPC
-#ifdef ANDROID
-mozilla::dom::PBrowserParent*
+PRBool
+nsEventStateManager::RemoteQueryContentEvent(nsEvent *aEvent)
+{
+  nsQueryContentEvent *queryEvent =
+      static_cast<nsQueryContentEvent*>(aEvent);
+  if (!IsTargetCrossProcess(queryEvent)) {
+    return PR_FALSE;
+  }
+  // Will not be handled locally, remote the event
+  GetCrossProcessTarget()->HandleQueryContentEvent(*queryEvent);
+  return PR_TRUE;
+}
+
+TabParent*
 nsEventStateManager::GetCrossProcessTarget()
 {
-  nsCOMPtr<nsFrameLoader> fl = nsContentUtils::GetActiveFrameLoader();
-  NS_ENSURE_TRUE(fl, nsnull);
-  return fl->GetRemoteBrowser();
+  return TabParent::GetIMETabParent();
 }
 
 PRBool
 nsEventStateManager::IsTargetCrossProcess(nsGUIEvent *aEvent)
 {
-  nsQueryContentEvent stateEvent(PR_TRUE, NS_QUERY_CONTENT_STATE, aEvent->widget);
-  nsContentEventHandler handler(mPresContext);
-  handler.OnQueryContentState(&stateEvent);
-  return !stateEvent.mSucceeded;
+  return TabParent::GetIMETabParent() != nsnull;
 }
 #endif
-#endif
 
 NS_IMETHODIMP
 nsEventStateManager::NotifyDestroyPresContext(nsPresContext* aPresContext)
 {
   nsIMEStateManager::OnDestroyPresContext(aPresContext);
   return NS_OK;
 }
 
--- a/content/events/src/nsEventStateManager.h
+++ b/content/events/src/nsEventStateManager.h
@@ -57,16 +57,22 @@
 
 class nsIPresShell;
 class nsIDocShell;
 class nsIDocShellTreeNode;
 class nsIDocShellTreeItem;
 class imgIContainer;
 class nsDOMDataTransfer;
 
+namespace mozilla {
+namespace dom {
+class TabParent;
+}
+}
+
 /*
  * Event listener manager
  */
 
 class nsEventStateManager : public nsSupportsWeakReference,
                             public nsIEventStateManager,
                             public nsIObserver
 {
@@ -329,21 +335,20 @@ protected:
    * mCurrentTarget->GetNearestWidget().
    */
   void FillInEventFromGestureDown(nsMouseEvent* aEvent);
 
   nsresult DoContentCommandEvent(nsContentCommandEvent* aEvent);
   nsresult DoContentCommandScrollEvent(nsContentCommandEvent* aEvent);
 
 #ifdef MOZ_IPC
-#ifdef ANDROID
-  mozilla::dom::PBrowserParent *GetCrossProcessTarget();
+  PRBool RemoteQueryContentEvent(nsEvent *aEvent);
+  mozilla::dom::TabParent *GetCrossProcessTarget();
   PRBool IsTargetCrossProcess(nsGUIEvent *aEvent);
 #endif
-#endif
 
   PRInt32     mLockCursor;
 
   nsWeakFrame mCurrentTarget;
   nsCOMPtr<nsIContent> mCurrentTargetContent;
   nsWeakFrame mLastMouseOverFrame;
   nsCOMPtr<nsIContent> mLastMouseOverElement;
   nsWeakFrame mLastDragOverFrame;
--- a/content/events/src/nsIMEStateManager.cpp
+++ b/content/events/src/nsIMEStateManager.cpp
@@ -281,17 +281,18 @@ public:
   NS_DECL_NSISELECTIONLISTENER
   NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
 
   nsresult Init(nsIWidget* aWidget,
                 nsPresContext* aPresContext,
-                nsINode* aNode);
+                nsINode* aNode,
+                PRBool aWantUpdates);
   void     Destroy(void);
 
   nsCOMPtr<nsIWidget>            mWidget;
   nsCOMPtr<nsISelection>         mSel;
   nsCOMPtr<nsIContent>           mRootContent;
   nsCOMPtr<nsINode>              mEditableNode;
   PRBool                         mDestroying;
 
@@ -302,20 +303,26 @@ private:
 nsTextStateManager::nsTextStateManager()
 {
   mDestroying = PR_FALSE;
 }
 
 nsresult
 nsTextStateManager::Init(nsIWidget* aWidget,
                          nsPresContext* aPresContext,
-                         nsINode* aNode)
+                         nsINode* aNode,
+                         PRBool aWantUpdates)
 {
   mWidget = aWidget;
 
+  if (!aWantUpdates) {
+    mEditableNode = aNode;
+    return NS_OK;
+  }
+
   nsIPresShell* presShell = aPresContext->PresShell();
 
   // get selection and root content
   nsCOMPtr<nsISelectionController> selCon;
   if (aNode->IsNodeOfType(nsINode::eCONTENT)) {
     nsIFrame* frame = static_cast<nsIContent*>(aNode)->GetPrimaryFrame();
     NS_ENSURE_TRUE(frame, NS_ERROR_UNEXPECTED);
 
@@ -329,22 +336,27 @@ nsTextStateManager::Init(nsIWidget* aWid
 
   nsCOMPtr<nsISelection> sel;
   nsresult rv = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
                                      getter_AddRefs(sel));
   NS_ENSURE_TRUE(sel, NS_ERROR_UNEXPECTED);
 
   nsCOMPtr<nsIDOMRange> selDomRange;
   rv = sel->GetRangeAt(0, getter_AddRefs(selDomRange));
-  NS_ENSURE_SUCCESS(rv, rv);
-  nsCOMPtr<nsIRange> selRange(do_QueryInterface(selDomRange));
-  NS_ENSURE_TRUE(selRange && selRange->GetStartParent(), NS_ERROR_UNEXPECTED);
+
+  if (NS_SUCCEEDED(rv)) {
+    nsCOMPtr<nsIRange> selRange(do_QueryInterface(selDomRange));
+    NS_ENSURE_TRUE(selRange && selRange->GetStartParent(),
+                   NS_ERROR_UNEXPECTED);
 
-  mRootContent = selRange->GetStartParent()->
+    mRootContent = selRange->GetStartParent()->
                      GetSelectionRootContent(presShell);
+  } else {
+    mRootContent = aNode->GetSelectionRootContent(presShell);
+  }
   if (!mRootContent && aNode->IsNodeOfType(nsINode::eDOCUMENT)) {
     // The document node is editable, but there are no contents, this document
     // is not editable.
     return NS_ERROR_NOT_AVAILABLE;
   }
   NS_ENSURE_TRUE(mRootContent, NS_ERROR_UNEXPECTED);
 
   // add text change observer
@@ -585,24 +597,27 @@ nsIMEStateManager::OnTextStateFocus(nsPr
     return NS_OK; // Sometimes, there are no widgets.
   }
 
   rv = widget->OnIMEFocusChange(PR_TRUE);
   if (rv == NS_ERROR_NOT_IMPLEMENTED)
     return NS_OK;
   NS_ENSURE_SUCCESS(rv, rv);
 
+  PRBool wantUpdates = rv != NS_SUCCESS_IME_NO_UPDATES;
+
   // OnIMEFocusChange may cause focus and sTextStateObserver to change
   // In that case return and keep the current sTextStateObserver
   NS_ENSURE_TRUE(!sTextStateObserver, NS_OK);
 
   sTextStateObserver = new nsTextStateManager();
   NS_ENSURE_TRUE(sTextStateObserver, NS_ERROR_OUT_OF_MEMORY);
   NS_ADDREF(sTextStateObserver);
-  rv = sTextStateObserver->Init(widget, aPresContext, editableNode);
+  rv = sTextStateObserver->Init(widget, aPresContext,
+                                editableNode, wantUpdates);
   if (NS_FAILED(rv)) {
     sTextStateObserver->mDestroying = PR_TRUE;
     sTextStateObserver->Destroy();
     NS_RELEASE(sTextStateObserver);
     widget->OnIMEFocusChange(PR_FALSE);
     return rv;
   }
   return NS_OK;
--- a/content/html/content/test/test_bug561636.html
+++ b/content/html/content/test/test_bug561636.html
@@ -5,17 +5,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 -->
 <head>
   <title>Test for Bug 561636</title>
   <script type="application/javascript" src="/MochiKit/packed.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
-<body>
+<body onload="runTest();">
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=561636">Mozilla Bug 561636</a>
 <p id="display"></p>
 <iframe style='width:50px; height: 50px;' name='t'></iframe>
 <iframe style='width:50px; height: 50px;' name='t2' id='i'></iframe>
 <div id="content">
   <form target='t' action='data:text/html,'>
     <input required>
     <input id='a' type='submit'>
@@ -43,69 +43,74 @@ https://bugzilla.mozilla.org/show_bug.cg
 var formSubmitted = [ false, false ];
 var invalidHandled = false;
 
 netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
 var os = Components.classes['@mozilla.org/observer-service;1']
                    .getService(Components.interfaces.nsIObserverService);
 var observers = os.enumerateObservers("invalidformsubmit");
 
-// The following test should not be done if there is no observer for
-// "invalidformsubmit" because the form submission will not be canceled in that
-// case.
-if (observers.hasMoreElements()) {
-  SimpleTest.waitForExplicitFinish();
-
-  // Initialize
-  document.forms[0].addEventListener('submit', function(aEvent) {
-    aEvent.target.removeEventListener('submit', arguments.callee, false);
-    formSubmitted[0] = true;
-  }, false);
+function runTest()
+{
+  // The following test should not be done if there is no observer for
+  // "invalidformsubmit" because the form submission will not be canceled in that
+  // case.
+  netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+  if (observers.hasMoreElements()) {
+    SimpleTest.waitForExplicitFinish();
 
-  document.forms[1].addEventListener('submit', function(aEvent) {
-    aEvent.target.removeEventListener('submit', arguments.callee, false);
-    formSubmitted[1] = true;
-  }, false);
+    // Initialize
+    document.forms[0].addEventListener('submit', function(aEvent) {
+      aEvent.target.removeEventListener('submit', arguments.callee, false);
+      formSubmitted[0] = true;
+    }, false);
 
-  document.forms[2].addEventListener('submit', function(aEvent) {
-    aEvent.target.removeEventListener('submit', arguments.callee, false);
-    formSubmitted[2] = true;
-  }, false);
+    document.forms[1].addEventListener('submit', function(aEvent) {
+      aEvent.target.removeEventListener('submit', arguments.callee, false);
+      formSubmitted[1] = true;
+    }, false);
 
-  document.forms[3].addEventListener('submit', function(aEvent) {
-    aEvent.target.removeEventListener('submit', arguments.callee, false);
-    formSubmitted[3] = true;
+    document.forms[2].addEventListener('submit', function(aEvent) {
+      aEvent.target.removeEventListener('submit', arguments.callee, false);
+      formSubmitted[2] = true;
+    }, false);
+
+    document.forms[3].addEventListener('submit', function(aEvent) {
+      aEvent.target.removeEventListener('submit', arguments.callee, false);
+      formSubmitted[3] = true;
 
-    ok(!formSubmitted[0], "Form 1 should not have been submitted because invalid");
-    ok(!formSubmitted[1], "Form 2 should not have been submitted because invalid");
-    ok(!formSubmitted[2], "Form 3 should not have been submitted because invalid");
-    ok(formSubmitted[3], "Form 4 should have been submitted because valid");
-  }, false);
+      ok(!formSubmitted[0], "Form 1 should not have been submitted because invalid");
+      ok(!formSubmitted[1], "Form 2 should not have been submitted because invalid");
+      ok(!formSubmitted[2], "Form 3 should not have been submitted because invalid");
+      ok(formSubmitted[3], "Form 4 should have been submitted because valid");
 
-  document.forms[4].elements[0].addEventListener('invalid', function(aEvent) {
-    aEvent.target.removeEventListener('invalid', arguments.callee, false);
-    invalidHandled = true;
-  }, false);
+      // Next test.
+      document.forms[4].submit();
+    }, false);
 
-  document.getElementById('i').addEventListener('load', function(aEvent) {
-    aEvent.target.removeEventListener('load', arguments.callee, false);
+    document.forms[4].elements[0].addEventListener('invalid', function(aEvent) {
+      aEvent.target.removeEventListener('invalid', arguments.callee, false);
+      invalidHandled = true;
+    }, false);
 
-    SimpleTest.executeSoon(function () {
-      ok(true, "Form 5 should have been submitted because submit() has been used even if invalid");
-      ok(!invalidHandled, "Invalid event should not have been sent");
+    document.getElementById('i').addEventListener('load', function(aEvent) {
+      aEvent.target.removeEventListener('load', arguments.callee, false);
 
-      SimpleTest.finish();
-    });
-  }, false);
+      SimpleTest.executeSoon(function () {
+        ok(true, "Form 5 should have been submitted because submit() has been used even if invalid");
+        ok(!invalidHandled, "Invalid event should not have been sent");
 
-  document.getElementById('a').click();
-  document.getElementById('b').click();
-  var c = document.getElementById('c');
-  c.focus(); synthesizeKey("VK_RETURN", {type: "keypress"});
-  document.getElementById('s2').click();
+        SimpleTest.finish();
+      });
+    }, false);
 
-  document.forms[3].submit();
+    document.getElementById('a').click();
+    document.getElementById('b').click();
+    var c = document.getElementById('c');
+    c.focus(); synthesizeKey("VK_RETURN", {type: "keypress"});
+    document.getElementById('s2').click();
+  }
 }
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/html/document/test/test_bug446483.html
+++ b/content/html/document/test/test_bug446483.html
@@ -6,38 +6,44 @@ https://bugzilla.mozilla.org/show_bug.cg
 <head>
   <title>Test for Bug 446483</title>
   <script type="application/javascript" src="/MochiKit/packed.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>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=446483">Mozilla Bug 446483</a>
-<p id="display">
-<iframe src="bug446483-iframe.html"></iframe>
-<iframe src="bug446483-iframe.html"></iframe>
-</p>
+<p id="display"></p>
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 446483 **/
 
 function gc() {
   netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
   window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
         .getInterface(Components.interfaces.nsIDOMWindowUtils)
         .garbageCollect();
 }
 
 function runTest() {
+  document.getElementById('display').innerHTML =
+    '<iframe src="bug446483-iframe.html"><\/iframe>\n' +
+    '<iframe src="bug446483-iframe.html"><\/iframe>\n';
+
   setInterval(gc, 1000);
-  setTimeout(function(){document.getElementById('display').innerHTML='';ok(true,''); SimpleTest.finish();}, 4000);
+
+  setTimeout(function() {
+    document.getElementById('display').innerHTML = '';
+    ok(true, '');
+    SimpleTest.finish();
+  }, 4000);
 }
 
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(runTest);
 
 </script>
 </pre>
 </body>
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -245,17 +245,17 @@ nsDOMWindowUtils::SetCSSViewport(float a
   nsIPresShell* presShell = GetPresShell();
   if (!presShell) {
     return NS_ERROR_FAILURE;
   }
 
   nscoord width = nsPresContext::CSSPixelsToAppUnits(aWidthPx);
   nscoord height = nsPresContext::CSSPixelsToAppUnits(aHeightPx);
 
-  presShell->ResizeReflow(width, height);
+  presShell->ResizeReflowOverride(width, height);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::SetDisplayPort(float aXPx, float aYPx,
                                  float aWidthPx, float aHeightPx)
 {
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -57,21 +57,16 @@
 #include "nsCExternalHandlerService.h"
 #include "nsFrameMessageManager.h"
 #include "nsIAlertsService.h"
 #include "nsToolkitCompsCID.h"
 #include "nsIDOMGeoGeolocation.h"
 
 #include "mozilla/dom/ExternalHelperAppParent.h"
 
-#ifdef ANDROID
-#include "AndroidBridge.h"
-using namespace mozilla;
-#endif
-
 using namespace mozilla::ipc;
 using namespace mozilla::net;
 using namespace mozilla::places;
 using mozilla::MonitorAutoEnter;
 
 namespace mozilla {
 namespace dom {
 
@@ -563,43 +558,16 @@ ContentParent::AfterProcessNextEvent(nsI
     }
 
     if (mOldObserver)
         return mOldObserver->AfterProcessNextEvent(thread, recursionDepth);
 
     return NS_OK;
 }
 
-
-bool 
-ContentParent::RecvNotifyIMEChange(const nsString& aText, 
-                                   const PRUint32& aTextLen, 
-                                   const int& aStart, const int& aEnd, 
-                                   const int& aNewEnd)
-{
-#ifdef ANDROID
-    AndroidBridge::Bridge()->NotifyIMEChange(aText.get(), aTextLen,
-                                             aStart, aEnd, aNewEnd);
-    return true;
-#else
-    return false;
-#endif
-}
-
-bool 
-ContentParent::RecvNotifyIME(const int& aType, const int& aStatus)
-{
-#ifdef ANDROID
-    AndroidBridge::Bridge()->NotifyIME(aType, aStatus);
-    return true;
-#else
-    return false;
-#endif
-}
-
 bool
 ContentParent::RecvShowAlertNotification(const nsString& aImageUrl, const nsString& aTitle,
                                          const nsString& aText, const PRBool& aTextClickable,
                                          const nsString& aCookie, const nsString& aName)
 {
     nsCOMPtr<nsIAlertsService> sysAlerts(do_GetService(NS_ALERTSERVICE_CONTRACTID));
     if (sysAlerts) {
         sysAlerts->ShowAlertNotification(aImageUrl, aTitle, aText, aTextClickable,
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -160,21 +160,16 @@ private:
     virtual bool RecvStartVisitedQuery(const IPC::URI& uri);
 
     virtual bool RecvVisitURI(const IPC::URI& uri,
                               const IPC::URI& referrer,
                               const PRUint32& flags);
 
     virtual bool RecvSetURITitle(const IPC::URI& uri,
                                  const nsString& title);
-    
-    virtual bool RecvNotifyIME(const int&, const int&);
-
-    virtual bool RecvNotifyIMEChange(const nsString&, const PRUint32&, const int&, 
-                                     const int&, const int&);
 
     virtual bool RecvShowAlertNotification(const nsString& aImageUrl, const nsString& aTitle,
                                            const nsString& aText, const PRBool& aTextClickable,
                                            const nsString& aCookie, const nsString& aName);
 
     virtual bool RecvLoadURIExternal(const IPC::URI& uri);
 
     virtual bool RecvSyncMessage(const nsString& aMsg, const nsString& aJSON,
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -53,16 +53,17 @@ include "IPC/nsGUIEventIPC.h";
 using gfxMatrix;
 using IPC::URI;
 using nsIntSize;
 using nsCompositionEvent;
 using nsTextEvent;
 using nsQueryContentEvent;
 using nsSelectionEvent;
 using RemoteDOMEvent;
+using nsIMEUpdatePreference;
 
 namespace mozilla {
 namespace dom {
 
 rpc protocol PBrowser
 {
     manager PContent;
 
@@ -85,17 +86,78 @@ parent:
 
     Event(RemoteDOMEvent aEvent);
 
     rpc CreateWindow() returns (PBrowser window);
 
     sync SyncMessage(nsString aMessage, nsString aJSON)
       returns (nsString[] retval);
 
-    QueryContentResult(nsQueryContentEvent event);
+    /**
+     * Notifies chrome that there is a focus change involving an editable
+     * object (input, textarea, document, contentEditable. etc.)
+     *
+     *  focus        PR_TRUE if editable object is receiving focus
+     *               PR_FALSE if losing focus
+     *  preference   Native widget preference for IME updates
+     */
+    sync NotifyIMEFocus(PRBool focus)
+      returns (nsIMEUpdatePreference preference);
+
+    /**
+     * Notifies chrome that there has been a change in text content
+     * One call can encompass both a delete and an insert operation
+     * Only called when NotifyIMEFocus returns PR_TRUE for mWantUpdates
+     *
+     *  offset       Starting offset of the change
+     *  end          Ending offset of the range deleted
+     *  newEnd       New ending offset after insertion
+     *
+     *  for insertion, offset == end
+     *  for deletion, offset == newEnd
+     */
+    NotifyIMETextChange(PRUint32 offset, PRUint32 end, PRUint32 newEnd);
+
+    /**
+     * Notifies chrome that there has been a change in selection
+     * Only called when NotifyIMEFocus returns PR_TRUE for mWantUpdates
+     *
+     *  anchor       Offset where the selection started
+     *  focus        Offset where the caret is
+     */
+    NotifyIMESelection(PRUint32 anchor, PRUint32 focus);
+
+    /**
+     * Notifies chrome to refresh its text cache 
+     * Only called when NotifyIMEFocus returns PR_TRUE for mWantHints
+     *
+     *  text         The entire content of the text field
+     */
+    NotifyIMETextHint(nsString text);
+
+    /**
+     * Instructs chrome to end any pending composition
+     *
+     *  cancel       PR_TRUE if composition should be cancelled
+     *  composition  Text to commit before ending the composition
+     *
+     *  if cancel is PR_TRUE,
+     *    widget should return empty string for composition
+     *  if cancel is PR_FALSE,
+     *    widget should return the current composition text
+     */
+    sync EndIMEComposition(PRBool cancel) returns (nsString composition);
+
+    sync GetIMEEnabled() returns (PRUint32 value);
+
+    SetIMEEnabled(PRUint32 value);
+
+    sync GetIMEOpenState() returns (PRBool value);
+
+    SetIMEOpenState(PRBool value);
 
     PContentPermissionRequest(nsCString aType, URI uri);
 
     PContentDialog(PRUint32 aType, nsCString aName, nsCString aFeatures,
                    PRInt32[] aIntParams, nsString[] aStringParams);
 
     /**
      * Create a layout frame (encapsulating a remote layer tree) for
@@ -145,18 +207,16 @@ child:
              PRInt32 aCharCode,
              PRInt32 aModifiers,
              bool aPreventDefault);
 
     CompositionEvent(nsCompositionEvent event);
 
     TextEvent(nsTextEvent event);
 
-    QueryContentEvent(nsQueryContentEvent event);
-
     SelectionEvent(nsSelectionEvent event);
 
     /**
      * Activate event forwarding from client to parent.
      */
     ActivateFrameEvent(nsString aType, bool capture);
 
     LoadRemoteScript(nsString aURL);
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -99,19 +99,16 @@ parent:
     sync GetCharPref(nsCString prefName) returns (nsCString retValue, nsresult rv);
     sync GetPrefLocalizedString(nsCString prefName) returns (nsString retValue, nsresult rv);
     sync PrefHasUserValue(nsCString prefName) returns (PRBool retValue, nsresult rv);
     sync PrefIsLocked(nsCString prefName) returns (PRBool retValue, nsresult rv);
     sync GetChildList(nsCString domain) returns (nsCString[] list, nsresult rv);
 
     // PermissionsManager messages
     sync TestPermission(URI uri, nsCString type, PRBool exact) returns (PRUint32 retValue);
-    NotifyIME(int aType, int aState);
-    NotifyIMEChange(nsString aText, PRUint32 aTextLen,
-                    int aStart, int aEnd, int aNewEnd);
 
     sync SyncMessage(nsString aMessage, nsString aJSON)
       returns (nsString[] retval);
 
     ShowAlertNotification(nsString imageUrl, 
                           nsString title, 
                           nsString text, 
                           PRBool textClickable,
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -567,58 +567,32 @@ TabChild::RecvTextEvent(const nsTextEven
 {
   nsTextEvent localEvent(event);
   DispatchWidgetEvent(localEvent);
   IPC::ParamTraits<nsTextEvent>::Free(event);
   return true;
 }
 
 bool
-TabChild::RecvQueryContentEvent(const nsQueryContentEvent& event)
-{
-  nsQueryContentEvent localEvent(event);
-  DispatchWidgetEvent(localEvent);
-  // Send result back even if query failed
-  SendQueryContentResult(localEvent);
-  return true;
-}
-
-bool
 TabChild::RecvSelectionEvent(const nsSelectionEvent& event)
 {
   nsSelectionEvent localEvent(event);
   DispatchWidgetEvent(localEvent);
   return true;
 }
 
 bool
 TabChild::DispatchWidgetEvent(nsGUIEvent& event)
 {
-  nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(mWebNav);
-  NS_ENSURE_TRUE(window, false);
-
-  nsIDocShell *docShell = window->GetDocShell();
-  NS_ENSURE_TRUE(docShell, false);
-
-  nsCOMPtr<nsIPresShell> presShell;
-  docShell->GetPresShell(getter_AddRefs(presShell));
-  NS_ENSURE_TRUE(presShell, false);
-
-  nsIFrame *frame = presShell->GetRootFrame();
-  NS_ENSURE_TRUE(frame, false);
-
-  nsIView *view = frame->GetView();
-  NS_ENSURE_TRUE(view, false);
-
-  nsCOMPtr<nsIWidget> widget = view->GetNearestWidget(nsnull);
-  NS_ENSURE_TRUE(widget, false);
+  if (!mWidget)
+    return false;
 
   nsEventStatus status;
-  event.widget = widget;
-  NS_ENSURE_SUCCESS(widget->DispatchEvent(&event, status), false);
+  event.widget = mWidget;
+  NS_ENSURE_SUCCESS(mWidget->DispatchEvent(&event, status), false);
   return true;
 }
 
 mozilla::ipc::PDocumentRendererChild*
 TabChild::AllocPDocumentRenderer(const PRInt32& x,
                                  const PRInt32& y,
                                  const PRInt32& w,
                                  const PRInt32& h,
@@ -996,17 +970,17 @@ TabChild::InitTabChildGlobal()
   return true;
 }
 
 bool
 TabChild::InitWidget(const nsIntSize& size)
 {
     NS_ABORT_IF_FALSE(!mWidget && !mRemoteFrame, "CreateWidget twice?");
 
-    mWidget = nsIWidget::CreatePuppetWidget();
+    mWidget = nsIWidget::CreatePuppetWidget(this);
     if (!mWidget) {
         NS_ERROR("couldn't create fake widget");
         return false;
     }
     mWidget->Create(
         nsnull, 0,              // no parents
         nsIntRect(nsIntPoint(0, 0), size),
         nsnull,                 // HandleWidgetEvent
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -187,17 +187,16 @@ public:
                                 const bool&     aIgnoreRootScrollFrame);
     virtual bool RecvKeyEvent(const nsString& aType,
                               const PRInt32&  aKeyCode,
                               const PRInt32&  aCharCode,
                               const PRInt32&  aModifiers,
                               const bool&     aPreventDefault);
     virtual bool RecvCompositionEvent(const nsCompositionEvent& event);
     virtual bool RecvTextEvent(const nsTextEvent& event);
-    virtual bool RecvQueryContentEvent(const nsQueryContentEvent& event);
     virtual bool RecvSelectionEvent(const nsSelectionEvent& event);
     virtual bool RecvActivateFrameEvent(const nsString& aType, const bool& capture);
     virtual bool RecvLoadRemoteScript(const nsString& aURL);
     virtual bool RecvAsyncMessage(const nsString& aMessage,
                                   const nsString& aJSON);
     virtual mozilla::ipc::PDocumentRendererChild* AllocPDocumentRenderer(
             const PRInt32& x,
             const PRInt32& y,
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -66,36 +66,35 @@
 #include "nsIDOMNSHTMLFrameElement.h"
 #include "nsIDialogCreator.h"
 #include "nsThreadUtils.h"
 #include "nsSerializationHelper.h"
 #include "nsIPromptFactory.h"
 #include "nsIContent.h"
 #include "mozilla/unused.h"
 
-#ifdef ANDROID
-#include "AndroidBridge.h"
-using namespace mozilla;
-#endif
-
 using namespace mozilla::dom;
 using namespace mozilla::ipc;
 using namespace mozilla::layout;
 
 // The flags passed by the webProgress notifications are 16 bits shifted
 // from the ones registered by webProgressListeners.
 #define NOTIFY_FLAG_SHIFT 16
 
 namespace mozilla {
 namespace dom {
 
+TabParent *TabParent::mIMETabParent = nsnull;
+
 NS_IMPL_ISUPPORTS4(TabParent, nsITabParent, nsIAuthPromptProvider, nsISSLStatusProvider, nsISecureBrowserUI)
 
 TabParent::TabParent()
   : mSecurityState(0)
+  , mIMECompositionEnding(PR_FALSE)
+  , mIMEComposing(PR_FALSE)
 {
 }
 
 TabParent::~TabParent()
 {
 }
 
 void
@@ -312,38 +311,242 @@ TabParent::RecvSyncMessage(const nsStrin
 bool
 TabParent::RecvAsyncMessage(const nsString& aMessage,
                             const nsString& aJSON)
 {
   return ReceiveMessage(aMessage, PR_FALSE, aJSON, nsnull);
 }
 
 bool
-TabParent::RecvQueryContentResult(const nsQueryContentEvent& event)
+TabParent::RecvNotifyIMEFocus(const PRBool& aFocus,
+                              nsIMEUpdatePreference* aPreference)
+{
+  nsCOMPtr<nsIWidget> widget = GetWidget();
+  if (!widget)
+    return true;
+
+  mIMETabParent = aFocus ? this : nsnull;
+  mIMESelectionAnchor = 0;
+  mIMESelectionFocus = 0;
+  nsresult rv = widget->OnIMEFocusChange(aFocus);
+
+  if (aFocus) {
+    if (NS_SUCCEEDED(rv) && rv != NS_SUCCESS_IME_NO_UPDATES) {
+      *aPreference = widget->GetIMEUpdatePreference();
+    } else {
+      aPreference->mWantUpdates = PR_FALSE;
+      aPreference->mWantHints = PR_FALSE;
+    }
+  } else {
+    mIMECacheText.Truncate(0);
+  }
+  return true;
+}
+
+bool
+TabParent::RecvNotifyIMETextChange(const PRUint32& aStart,
+                                   const PRUint32& aEnd,
+                                   const PRUint32& aNewEnd)
+{
+  nsCOMPtr<nsIWidget> widget = GetWidget();
+  if (!widget)
+    return true;
+
+  widget->OnIMETextChange(aStart, aEnd, aNewEnd);
+  return true;
+}
+
+bool
+TabParent::RecvNotifyIMESelection(const PRUint32& aAnchor,
+                                  const PRUint32& aFocus)
+{
+  nsCOMPtr<nsIWidget> widget = GetWidget();
+  if (!widget)
+    return true;
+
+  mIMESelectionAnchor = aAnchor;
+  mIMESelectionFocus = aFocus;
+  widget->OnIMESelectionChange();
+  return true;
+}
+
+bool
+TabParent::RecvNotifyIMETextHint(const nsString& aText)
 {
-#ifdef ANDROID
-  if (!event.mSucceeded) {
-    AndroidBridge::Bridge()->ReturnIMEQueryResult(nsnull, 0, 0, 0);
+  // Replace our cache with new text
+  mIMECacheText = aText;
+  return true;
+}
+
+/**
+ * Try to answer query event using cached text.
+ *
+ * For NS_QUERY_SELECTED_TEXT, fail if the cache doesn't contain the whole
+ *  selected range. (This shouldn't happen because PuppetWidget should have
+ *  already sent the whole selection.)
+ *
+ * For NS_QUERY_TEXT_CONTENT, fail only if the cache doesn't overlap with
+ *  the queried range. Note the difference from above. We use
+ *  this behavior because a normal NS_QUERY_TEXT_CONTENT event is allowed to
+ *  have out-of-bounds offsets, so that widget can request content without
+ *  knowing the exact length of text. It's up to widget to handle cases when
+ *  the returned offset/length are different from the queried offset/length.
+ */
+bool
+TabParent::HandleQueryContentEvent(nsQueryContentEvent& aEvent)
+{
+  aEvent.mSucceeded = PR_FALSE;
+  aEvent.mWasAsync = PR_FALSE;
+  aEvent.mReply.mFocusedWidget = nsCOMPtr<nsIWidget>(GetWidget()).get();
+
+  switch (aEvent.message)
+  {
+  case NS_QUERY_SELECTED_TEXT:
+    {
+      aEvent.mReply.mOffset = PR_MIN(mIMESelectionAnchor, mIMESelectionFocus);
+      if (mIMESelectionAnchor == mIMESelectionFocus) {
+        aEvent.mReply.mString.Truncate(0);
+      } else {
+        if (mIMESelectionAnchor > mIMECacheText.Length() ||
+            mIMESelectionFocus > mIMECacheText.Length()) {
+          break;
+        }
+        PRUint32 selLen = mIMESelectionAnchor > mIMESelectionFocus ?
+                          mIMESelectionAnchor - mIMESelectionFocus :
+                          mIMESelectionFocus - mIMESelectionAnchor;
+        aEvent.mReply.mString = Substring(mIMECacheText,
+                                          aEvent.mReply.mOffset,
+                                          selLen);
+      }
+      aEvent.mReply.mReversed = mIMESelectionFocus < mIMESelectionAnchor;
+      aEvent.mReply.mHasSelection = PR_TRUE;
+      aEvent.mSucceeded = PR_TRUE;
+    }
+    break;
+  case NS_QUERY_TEXT_CONTENT:
+    {
+      PRUint32 inputOffset = aEvent.mInput.mOffset,
+               inputEnd = inputOffset + aEvent.mInput.mLength;
+
+      if (inputEnd > mIMECacheText.Length()) {
+        inputEnd = mIMECacheText.Length();
+      }
+      if (inputEnd < inputOffset) {
+        break;
+      }
+      aEvent.mReply.mOffset = inputOffset;
+      aEvent.mReply.mString = Substring(mIMECacheText,
+                                        inputOffset,
+                                        inputEnd - inputOffset);
+      aEvent.mSucceeded = PR_TRUE;
+    }
+    break;
+  }
+  return true;
+}
+
+bool
+TabParent::SendCompositionEvent(const nsCompositionEvent& event)
+{
+  mIMEComposing = event.message == NS_COMPOSITION_START;
+  mIMECompositionStart = PR_MIN(mIMESelectionAnchor, mIMESelectionFocus);
+  if (mIMECompositionEnding)
+    return true;
+  return PBrowserParent::SendCompositionEvent(event);
+}
+
+/**
+ * During ResetInputState or CancelComposition, widget usually sends a
+ * NS_TEXT_TEXT event to finalize or clear the composition, respectively
+ *
+ * Because the event will not reach content in time, we intercept it
+ * here and pass the text as the EndIMEComposition return value
+ */
+bool
+TabParent::SendTextEvent(const nsTextEvent& event)
+{
+  if (mIMECompositionEnding) {
+    mIMECompositionText = event.theText;
     return true;
   }
 
-  switch (event.message) {
-  case NS_QUERY_TEXT_CONTENT:
-    AndroidBridge::Bridge()->ReturnIMEQueryResult(
-        event.mReply.mString.get(), event.mReply.mString.Length(), 0, 0);
-    break;
-  case NS_QUERY_SELECTED_TEXT:
-    AndroidBridge::Bridge()->ReturnIMEQueryResult(
-        event.mReply.mString.get(),
-        event.mReply.mString.Length(),
-        event.GetSelectionStart(),
-        event.GetSelectionEnd() - event.GetSelectionStart());
-    break;
+  // We must be able to simulate the selection because
+  // we might not receive selection updates in time
+  if (!mIMEComposing) {
+    mIMECompositionStart = PR_MIN(mIMESelectionAnchor, mIMESelectionFocus);
+  }
+  mIMESelectionAnchor = mIMESelectionFocus =
+      mIMECompositionStart + event.theText.Length();
+
+  return PBrowserParent::SendTextEvent(event);
+}
+
+bool
+TabParent::SendSelectionEvent(const nsSelectionEvent& event)
+{
+  mIMESelectionAnchor = event.mOffset + (event.mReversed ? event.mLength : 0);
+  mIMESelectionFocus = event.mOffset + (!event.mReversed ? event.mLength : 0);
+  return PBrowserParent::SendSelectionEvent(event);
+}
+
+bool
+TabParent::RecvEndIMEComposition(const PRBool& aCancel,
+                                 nsString* aComposition)
+{
+  nsCOMPtr<nsIWidget> widget = GetWidget();
+  if (!widget)
+    return true;
+
+  mIMECompositionEnding = PR_TRUE;
+
+  if (aCancel) {
+    widget->CancelIMEComposition();
+  } else {
+    widget->ResetInputState();
   }
-#endif
+
+  mIMECompositionEnding = PR_FALSE;
+  *aComposition = mIMECompositionText;
+  mIMECompositionText.Truncate(0);  
+  return true;
+}
+
+bool
+TabParent::RecvGetIMEEnabled(PRUint32* aValue)
+{
+  nsCOMPtr<nsIWidget> widget = GetWidget();
+  if (widget)
+    widget->GetIMEEnabled(aValue);
+  return true;
+}
+
+bool
+TabParent::RecvSetIMEEnabled(const PRUint32& aValue)
+{
+  nsCOMPtr<nsIWidget> widget = GetWidget();
+  if (widget)
+    widget->SetIMEEnabled(aValue);
+  return true;
+}
+
+bool
+TabParent::RecvGetIMEOpenState(PRBool* aValue)
+{
+  nsCOMPtr<nsIWidget> widget = GetWidget();
+  if (widget)
+    widget->GetIMEOpenState(aValue);
+  return true;
+}
+
+bool
+TabParent::RecvSetIMEOpenState(const PRBool& aValue)
+{
+  nsCOMPtr<nsIWidget> widget = GetWidget();
+  if (widget)
+    widget->SetIMEOpenState(aValue);
   return true;
 }
 
 bool
 TabParent::ReceiveMessage(const nsString& aMessage,
                           PRBool aSync,
                           const nsString& aJSON,
                           nsTArray<nsString>* aJSONRetVal)
@@ -495,10 +698,24 @@ TabParent::ShouldDelayDialogs()
 
 already_AddRefed<nsFrameLoader>
 TabParent::GetFrameLoader() const
 {
   nsCOMPtr<nsIFrameLoaderOwner> frameLoaderOwner = do_QueryInterface(mFrameElement);
   return frameLoaderOwner ? frameLoaderOwner->GetFrameLoader() : nsnull;
 }
 
+already_AddRefed<nsIWidget>
+TabParent::GetWidget() const
+{
+  nsCOMPtr<nsIContent> content = do_QueryInterface(mFrameElement);
+  if (!content)
+    return nsnull;
+
+  nsIFrame *frame = content->GetPrimaryFrame();
+  if (!frame)
+    return nsnull;
+
+  return nsCOMPtr<nsIWidget>(frame->GetNearestWidget()).forget();
+}
+
 } // namespace tabs
 } // namespace mozilla
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -86,17 +86,30 @@ public:
     virtual bool RecvEvent(const RemoteDOMEvent& aEvent);
 
     virtual bool AnswerCreateWindow(PBrowserParent** retval);
     virtual bool RecvSyncMessage(const nsString& aMessage,
                                  const nsString& aJSON,
                                  nsTArray<nsString>* aJSONRetVal);
     virtual bool RecvAsyncMessage(const nsString& aMessage,
                                   const nsString& aJSON);
-    virtual bool RecvQueryContentResult(const nsQueryContentEvent& event);
+    virtual bool RecvNotifyIMEFocus(const PRBool& aFocus,
+                                    nsIMEUpdatePreference* aPreference);
+    virtual bool RecvNotifyIMETextChange(const PRUint32& aStart,
+                                         const PRUint32& aEnd,
+                                         const PRUint32& aNewEnd);
+    virtual bool RecvNotifyIMESelection(const PRUint32& aAnchor,
+                                        const PRUint32& aFocus);
+    virtual bool RecvNotifyIMETextHint(const nsString& aText);
+    virtual bool RecvEndIMEComposition(const PRBool& aCancel,
+                                       nsString* aComposition);
+    virtual bool RecvGetIMEEnabled(PRUint32* aValue);
+    virtual bool RecvSetIMEEnabled(const PRUint32& aValue);
+    virtual bool RecvGetIMEOpenState(PRBool* aValue);
+    virtual bool RecvSetIMEOpenState(const PRBool& aValue);
     virtual PContentDialogParent* AllocPContentDialog(const PRUint32& aType,
                                                       const nsCString& aName,
                                                       const nsCString& aFeatures,
                                                       const nsTArray<int>& aIntParams,
                                                       const nsTArray<nsString>& aStringParams);
     virtual bool DeallocPContentDialog(PContentDialogParent* aDialog)
     {
       delete aDialog;
@@ -158,16 +171,22 @@ public:
     JSBool GetGlobalJSObject(JSContext* cx, JSObject** globalp);
 
     NS_DECL_ISUPPORTS
     NS_DECL_NSIAUTHPROMPTPROVIDER
     NS_DECL_NSISECUREBROWSERUI
     NS_DECL_NSISSLSTATUSPROVIDER
 
     void HandleDelayedDialogs();
+
+    static TabParent *GetIMETabParent() { return mIMETabParent; }
+    bool HandleQueryContentEvent(nsQueryContentEvent& aEvent);
+    bool SendCompositionEvent(const nsCompositionEvent& event);
+    bool SendTextEvent(const nsTextEvent& event);
+    bool SendSelectionEvent(const nsSelectionEvent& event);
 protected:
     bool ReceiveMessage(const nsString& aMessage,
                         PRBool aSync,
                         const nsString& aJSON,
                         nsTArray<nsString>* aJSONRetVal = nsnull);
 
     void ActorDestroy(ActorDestroyReason why);
 
@@ -197,16 +216,29 @@ protected:
     virtual PRenderFrameParent* AllocPRenderFrame();
     NS_OVERRIDE
     virtual bool DeallocPRenderFrame(PRenderFrameParent* aFrame);
 
     PRUint32 mSecurityState;
     nsString mSecurityTooltipText;
     nsCOMPtr<nsISupports> mSecurityStatusObject;
 
+    // IME
+    static TabParent *mIMETabParent;
+    nsString mIMECacheText;
+    PRUint32 mIMESelectionAnchor;
+    PRUint32 mIMESelectionFocus;
+    PRPackedBool mIMEComposing;
+    PRPackedBool mIMECompositionEnding;
+    // Buffer to store composition text during ResetInputState
+    // Compositions in almost all cases are small enough for nsAutoString
+    nsAutoString mIMECompositionText;
+    PRUint32 mIMECompositionStart;
+
 private:
     already_AddRefed<nsFrameLoader> GetFrameLoader() const;
+    already_AddRefed<nsIWidget> GetWidget() const;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
--- a/editor/libeditor/base/nsEditor.cpp
+++ b/editor/libeditor/base/nsEditor.cpp
@@ -306,16 +306,31 @@ nsEditor::PostCreate()
   // nuke the modification count, so the doc appears unmodified
   // do this before we notify listeners
   ResetModificationCount();
   
   // update the UI with our state
   NotifyDocumentListeners(eDocumentCreated);
   NotifyDocumentListeners(eDocumentStateChanged);
   
+  // update nsTextStateManager if we have focus
+  if (HasFocus()) {
+    nsFocusManager* fm = nsFocusManager::GetFocusManager();
+    NS_ASSERTION(fm, "no focus manager?");
+
+    nsCOMPtr<nsIContent> focusedContent = fm->GetFocusedContent();
+    if (focusedContent) {
+      nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
+      NS_ASSERTION(ps, "no pres shell even though we have focus");
+      nsPresContext* pc = ps->GetPresContext(); 
+
+      nsIMEStateManager::OnTextStateBlur(pc, nsnull);
+      nsIMEStateManager::OnTextStateFocus(pc, focusedContent);
+    }
+  }
   return NS_OK;
 }
 
 nsresult
 nsEditor::CreateEventListeners()
 {
   // Don't create the handler twice
   if (mEventListener)
new file mode 100644
--- /dev/null
+++ b/editor/libeditor/html/crashtests/499844-1.html
@@ -0,0 +1,15 @@
+<html>
+<head>
+<script type="text/javascript">
+
+function boom()
+{
+  document.body.contentEditable = "true";
+  document.execCommand("outdent", false, null);
+}
+
+</script>
+</head>
+
+<body style="word-spacing: 3px;" onload="boom();"> &#x0301;</body>
+</html>
--- a/editor/libeditor/html/crashtests/crashtests.list
+++ b/editor/libeditor/html/crashtests/crashtests.list
@@ -7,13 +7,14 @@ load 420439.html
 load 428489-1.html
 load 431086-1.xhtml
 load 448329-1.html
 load 448329-2.html
 asserts(8-20) load 448329-3.html
 load 456727-1.html
 load 456727-2.html
 asserts(1) load 467647-1.html # bug 382210
+load 499844-1.html
 load 503709-1.xhtml
 load 513375-1.xhtml
 load 535632-1.xhtml
 load 574558-1.xhtml
 load 582138-1.xhtml
--- a/embedding/android/GeckoAppShell.java
+++ b/embedding/android/GeckoAppShell.java
@@ -267,16 +267,17 @@ class GeckoAppShell
             break;
 
         case NOTIFY_IME_CANCELCOMPOSITION:
             IMEStateUpdater.resetIME();
             break;
 
         case NOTIFY_IME_FOCUSCHANGE:
             GeckoApp.surfaceView.mIMEFocus = state != 0;
+            IMEStateUpdater.resetIME();
             break;
 
         }
     }
 
     public static void notifyIMEChange(String text, int start, int end, int newEnd) {
         if (GeckoApp.surfaceView == null ||
             GeckoApp.surfaceView.inputConnection == null)
--- a/gfx/cairo/cairo/src/cairo-d2d-surface.cpp
+++ b/gfx/cairo/cairo/src/cairo-d2d-surface.cpp
@@ -2636,55 +2636,141 @@ static cairo_int_status_t
 							 srcResource,
 							 0,
 							 &rect);
     }
 
     return rv;
 }
 
+static cairo_int_status_t
+_cairo_d2d_blend_surface(cairo_d2d_surface_t *dst,
+			 cairo_d2d_surface_t *src,
+		 	 const cairo_matrix_t *transform,
+			 cairo_box_t *box,
+			 cairo_clip_t *clip,
+                         cairo_filter_t filter,
+			 float opacity)
+{
+    if (dst == src) {
+	// We cannot do self-blend.
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+    cairo_int_status_t rv = CAIRO_INT_STATUS_SUCCESS;
+
+    _begin_draw_state(dst);
+    _cairo_d2d_set_clip(dst, clip);
+    _cairo_d2d_flush(src);
+    D2D1_SIZE_U sourceSize = src->surfaceBitmap->GetPixelSize();
+
+
+    double x1, x2, y1, y2;
+    if (box) {
+	_cairo_box_to_doubles(box, &x1, &y1, &x2, &y2);
+    } else {
+	x1 = y1 = 0;
+	x2 = dst->rt->GetSize().width;
+	y2 = dst->rt->GetSize().height;
+    }
+
+    if (clip) {
+	const cairo_rectangle_int_t *clipExtent = _cairo_clip_get_extents(clip);
+	x1 = MAX(x1, clipExtent->x);
+	x2 = MIN(x2, clipExtent->x + clipExtent->width);
+	y1 = MAX(y1, clipExtent->y);
+	y2 = MIN(y2, clipExtent->y + clipExtent->height);
+    }
+
+    // We should be in drawing state for this.
+    _begin_draw_state(dst);
+    _cairo_d2d_set_clip (dst, clip);
+    D2D1_RECT_F rectSrc;
+    rectSrc.left = (float)(x1 * transform->xx + transform->x0);
+    rectSrc.top = (float)(y1 * transform->yy + transform->y0);
+    rectSrc.right = (float)(x2 * transform->xx + transform->x0);
+    rectSrc.bottom = (float)(y2 * transform->yy + transform->y0);
+
+    if (rectSrc.left < 0 || rectSrc.top < 0 ||
+	rectSrc.right > sourceSize.width || rectSrc.bottom > sourceSize.height) {
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    D2D1_RECT_F rectDst;
+    rectDst.left = (float)x1;
+    rectDst.top = (float)y1;
+    rectDst.right = (float)x2;
+    rectDst.bottom = (float)y2;
+
+    D2D1_BITMAP_INTERPOLATION_MODE interpMode =
+      D2D1_BITMAP_INTERPOLATION_MODE_LINEAR;
+
+    if (filter == CAIRO_FILTER_NEAREST) {
+      interpMode = D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
+    }
+
+    dst->rt->DrawBitmap(src->surfaceBitmap,
+			rectDst,
+			opacity,
+			interpMode,
+			rectSrc);
+
+    return rv;
+}
 /**
  * This function will text if we can use GPU mem cpy to execute an operation with
  * a surface pattern. If box is NULL it will operate on the entire dst surface.
  */
 static cairo_int_status_t
-_cairo_d2d_try_copy(cairo_d2d_surface_t *dst,
-		    cairo_surface_t *src,
-		    cairo_box_t *box,
-		    const cairo_matrix_t *matrix,
-		    cairo_clip_t *clip,
-		    cairo_operator_t op)
+_cairo_d2d_try_fastblit(cairo_d2d_surface_t *dst,
+		        cairo_surface_t *src,
+		        cairo_box_t *box,
+		        const cairo_matrix_t *matrix,
+		        cairo_clip_t *clip,
+		        cairo_operator_t op,
+                        cairo_filter_t filter,
+			float opacity = 1.0f)
 {
-    if (op != CAIRO_OPERATOR_SOURCE &&
-	!(op == CAIRO_OPERATOR_OVER && src->content == CAIRO_CONTENT_COLOR)) {
-	return CAIRO_INT_STATUS_UNSUPPORTED;
+    if (op == CAIRO_OPERATOR_OVER && src->content == CAIRO_CONTENT_COLOR) {
+	op = CAIRO_OPERATOR_SOURCE;
     }
-
-    cairo_point_int_t translation;
-    if ((box && !box_is_integer(box)) ||
-	!_cairo_matrix_is_integer_translation(matrix, &translation.x, &translation.y)) {
+    if (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_OVER) {
 	return CAIRO_INT_STATUS_UNSUPPORTED;
     }
 
     /* For now we do only D2D sources */
     if (src->type != CAIRO_SURFACE_TYPE_D2D) {
 	return CAIRO_INT_STATUS_UNSUPPORTED;
     }
+
+    cairo_d2d_surface_t *d2dsrc = reinterpret_cast<cairo_d2d_surface_t*>(src);
+    if (op == CAIRO_OPERATOR_OVER && matrix->xy == 0 && matrix->yx == 0) {
+	return _cairo_d2d_blend_surface(dst, d2dsrc, matrix, box, clip, filter, opacity);
+    }
     
+    if (op == CAIRO_OPERATOR_OVER || opacity != 1.0f) {
+	// Past this point we will never get into a situation where we can
+	// support OVER.
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+    
+    cairo_point_int_t translation;
+    if ((box && !box_is_integer(box)) ||
+	!_cairo_matrix_is_integer_translation(matrix, &translation.x, &translation.y)) {
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
     cairo_rectangle_int_t rect;
     if (box) {
 	_cairo_box_round_to_rectangle(box, &rect);
     } else {
 	rect.x = rect.y = 0;
 	rect.width = dst->rt->GetPixelSize().width;
 	rect.height = dst->rt->GetPixelSize().height;
     }
     
-    cairo_d2d_surface_t *d2dsrc = reinterpret_cast<cairo_d2d_surface_t*>(src);
-
     if (d2dsrc->device != dst->device) {
 	// This doesn't work between different devices.
 	return CAIRO_INT_STATUS_UNSUPPORTED;
     }
 
     /* Region we need to clip this operation to */
     cairo_region_t *clipping_region = NULL;
     cairo_region_t *region;
@@ -2707,17 +2793,17 @@ static cairo_int_status_t
 	// Areas outside of the surface do not matter.
 	cairo_rectangle_int_t surface_rect = { 0, 0,
 					       dst->rt->GetPixelSize().width,
 					       dst->rt->GetPixelSize().height };
 	cairo_region_intersect_rectangle(region, &surface_rect);
     }
 
     cairo_int_status_t rv = _cairo_d2d_copy_surface(dst, d2dsrc, &translation, region);
-    
+
     cairo_region_destroy(region);
     
     return rv;
 }
 
 RefPtr<ID2D1RenderTarget> _cairo_d2d_get_temp_rt(cairo_d2d_surface_t *surf, cairo_clip_t *clip)
 {
     RefPtr<ID3D10Texture2D> texture = _cairo_d2d_get_buffer_texture(surf);
@@ -2921,18 +3007,19 @@ static cairo_int_status_t
     if (op == CAIRO_OPERATOR_CLEAR) {
 	return _cairo_d2d_clear(d2dsurf, clip);
     }
 
     if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
 	const cairo_surface_pattern_t *surf_pattern = 
 	    reinterpret_cast<const cairo_surface_pattern_t*>(source);
 
-	status = _cairo_d2d_try_copy(d2dsurf, surf_pattern->surface,
-				     NULL, &source->matrix, clip, op);
+	status = _cairo_d2d_try_fastblit(d2dsurf, surf_pattern->surface,
+				         NULL, &source->matrix, clip,
+                                         op, source->filter);
 
 	if (status != CAIRO_INT_STATUS_UNSUPPORTED) {
 	    return status;
 	}
     }
     RefPtr<ID2D1RenderTarget> target_rt = d2dsurf->rt;
 #ifndef ALWAYS_MANUAL_COMPOSITE
     if (op != CAIRO_OPERATOR_OVER) {
@@ -2981,16 +3068,71 @@ static cairo_int_status_t
 		const cairo_pattern_t	*mask,
 		cairo_clip_t		*clip)
 {
     cairo_d2d_surface_t *d2dsurf = static_cast<cairo_d2d_surface_t*>(surface);
     cairo_rectangle_int_t extents;
 
     cairo_int_status_t status;
 
+    status = (cairo_int_status_t)_cairo_surface_mask_extents (&d2dsurf->base,
+		    op, source,
+		    mask,
+		    clip, &extents);
+    if (unlikely (status))
+	    return status;
+
+
+    D2D1_RECT_F rect = D2D1::RectF(0,
+				   0,
+				   (FLOAT)d2dsurf->rt->GetPixelSize().width,
+				   (FLOAT)d2dsurf->rt->GetPixelSize().height);
+
+    rect.left = (FLOAT)extents.x;
+    rect.right = (FLOAT)(extents.x + extents.width);
+    rect.top = (FLOAT)extents.y;
+    rect.bottom = (FLOAT)(extents.y + extents.height);
+
+    bool isSolidAlphaMask = false;
+    float solidAlphaValue = 1.0f;
+
+    if (mask->type == CAIRO_PATTERN_TYPE_SOLID) {
+	cairo_solid_pattern_t *solidPattern =
+	    (cairo_solid_pattern_t*)mask;
+	if (solidPattern->content = CAIRO_CONTENT_ALPHA) {
+	    isSolidAlphaMask = true;
+	    solidAlphaValue = solidPattern->color.alpha;
+	}
+    }
+
+    if (isSolidAlphaMask) {
+	if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
+	    const cairo_surface_pattern_t *surf_pattern = 
+		reinterpret_cast<const cairo_surface_pattern_t*>(source);
+	    cairo_box_t box;
+	    _cairo_box_from_rectangle(&box, &extents);
+	    cairo_int_status_t rv = _cairo_d2d_try_fastblit(d2dsurf,
+							    surf_pattern->surface,
+							    &box,
+							    &source->matrix,
+							    clip,
+							    op,
+                                                            source->filter,
+							    solidAlphaValue);
+	    if (rv != CAIRO_INT_STATUS_UNSUPPORTED) {
+		return rv;
+	    }
+	}
+    }
+
+    RefPtr<ID2D1Brush> brush = _cairo_d2d_create_brush_for_pattern(d2dsurf, source);
+    if (!brush) {
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
     RefPtr<ID2D1RenderTarget> target_rt = d2dsurf->rt;
 #ifndef ALWAYS_MANUAL_COMPOSITE
     if (op != CAIRO_OPERATOR_OVER) {
 #endif
 	target_rt = _cairo_d2d_get_temp_rt(d2dsurf, clip);
 	if (!target_rt) {
 	    return CAIRO_INT_STATUS_UNSUPPORTED;
 	}
@@ -2999,54 +3141,26 @@ static cairo_int_status_t
 	_begin_draw_state(d2dsurf);
 	status = (cairo_int_status_t)_cairo_d2d_set_clip (d2dsurf, clip);
 
 	if (unlikely(status))
 	    return status;
     }
 #endif
 
-    status = (cairo_int_status_t)_cairo_surface_mask_extents (&d2dsurf->base,
-		    op, source,
-		    mask,
-		    clip, &extents);
-    if (unlikely (status))
-	    return status;
-
-
-    RefPtr<ID2D1Brush> brush = _cairo_d2d_create_brush_for_pattern(d2dsurf, source);
-    if (!brush) {
-	return CAIRO_INT_STATUS_UNSUPPORTED;
-    }
-
-    D2D1_RECT_F rect = D2D1::RectF(0,
-				   0,
-				   (FLOAT)d2dsurf->rt->GetPixelSize().width,
-				   (FLOAT)d2dsurf->rt->GetPixelSize().height);
-
-    rect.left = (FLOAT)extents.x;
-    rect.right = (FLOAT)(extents.x + extents.width);
-    rect.top = (FLOAT)extents.y;
-    rect.bottom = (FLOAT)(extents.y + extents.height);
-
-
-    if (mask->type == CAIRO_PATTERN_TYPE_SOLID) {
-	cairo_solid_pattern_t *solidPattern =
-	    (cairo_solid_pattern_t*)mask;
-	if (solidPattern->content = CAIRO_CONTENT_ALPHA) {
-	    brush->SetOpacity((FLOAT)solidPattern->color.alpha);
-	    target_rt->FillRectangle(rect,
-				     brush);
-	    brush->SetOpacity(1.0);
-
-	    if (target_rt.get() != d2dsurf->rt.get()) {
-		return _cairo_d2d_blend_temp_surface(d2dsurf, op, target_rt, clip);
-	    }
-	    return CAIRO_INT_STATUS_SUCCESS;
+    if (isSolidAlphaMask) {
+	brush->SetOpacity(solidAlphaValue);
+	target_rt->FillRectangle(rect,
+				 brush);
+	brush->SetOpacity(1.0);
+
+	if (target_rt.get() != d2dsurf->rt.get()) {
+	    return _cairo_d2d_blend_temp_surface(d2dsurf, op, target_rt, clip);
 	}
+	return CAIRO_INT_STATUS_SUCCESS;
     }
 
     RefPtr<ID2D1Brush> opacityBrush = _cairo_d2d_create_brush_for_pattern(d2dsurf, mask, true);
     if (!opacityBrush) {
 	return CAIRO_INT_STATUS_UNSUPPORTED;
     }
 
     if (!d2dsurf->maskLayer) {
@@ -3166,18 +3280,19 @@ static cairo_int_status_t
 
     cairo_d2d_surface_t *d2dsurf = static_cast<cairo_d2d_surface_t*>(surface);
     cairo_box_t box;
     bool is_box = _cairo_path_fixed_is_box(path, &box);
 
     if (is_box && source->type == CAIRO_PATTERN_TYPE_SURFACE) {
 	const cairo_surface_pattern_t *surf_pattern = 
 	    reinterpret_cast<const cairo_surface_pattern_t*>(source);
-	cairo_int_status_t rv = _cairo_d2d_try_copy(d2dsurf, surf_pattern->surface,
-						    &box, &source->matrix, clip, op);
+	cairo_int_status_t rv = _cairo_d2d_try_fastblit(d2dsurf, surf_pattern->surface,
+						        &box, &source->matrix, clip, op,
+                                                        source->filter);
 
 	if (rv != CAIRO_INT_STATUS_UNSUPPORTED) {
 	    return rv;
 	}
     }
 
     op = _cairo_d2d_simplify_operator(op, source);
 
--- a/gfx/layers/basic/BasicLayers.cpp
+++ b/gfx/layers/basic/BasicLayers.cpp
@@ -510,17 +510,20 @@ BasicThebesLayerBuffer::DrawTo(ThebesLay
   aTarget->Save();
   // If the entire buffer is valid, we can just draw the whole thing,
   // no need to clip. But we'll still clip if clipping is cheap ---
   // that might let us copy a smaller region of the buffer.
   if (!aLayer->GetValidRegion().Contains(BufferRect()) ||
       IsClippingCheap(aTarget, aLayer->GetVisibleRegion())) {
     // We don't want to draw invalid stuff, so we need to clip. Might as
     // well clip to the smallest area possible --- the visible region.
-    gfxUtils::ClipToRegion(aTarget, aLayer->GetVisibleRegion());
+    // Bug 599189 if there is a non-integer-translation transform in aTarget,
+    // we might sample pixels outside GetVisibleRegion(), which is wrong
+    // and may cause gray lines.
+    gfxUtils::ClipToRegionSnapped(aTarget, aLayer->GetVisibleRegion());
   }
   if (aIsOpaqueContent) {
     aTarget->SetOperator(gfxContext::OPERATOR_SOURCE);
   }
   DrawBufferWithRotation(aTarget, aOpacity,
                          aLayer->GetXResolution(), aLayer->GetYResolution());
   aTarget->Restore();
 }
@@ -1795,17 +1798,17 @@ public:
   {
     mFrontBuffer.Clear();
     mValidRegion.SetEmpty();
     mOldValidRegion.SetEmpty();
     mOldXResolution = 1.0;
     mOldYResolution = 1.0;
 
     if (IsSurfaceDescriptorValid(mFrontBufferDescriptor)) {
-      BasicManager()->ShadowLayerManager::DestroySharedSurface(&mFrontBufferDescriptor);
+      BasicManager()->ShadowLayerManager::DestroySharedSurface(&mFrontBufferDescriptor, mAllocator);
     }
   }
 
   virtual void Paint(gfxContext* aContext,
                      LayerManager::DrawThebesLayerCallback aCallback,
                      void* aCallbackData,
                      float aOpacity);
 
@@ -1911,17 +1914,17 @@ public:
   virtual PRBool Init(gfxSharedImageSurface* front, const nsIntSize& size);
 
   virtual already_AddRefed<gfxSharedImageSurface>
   Swap(gfxSharedImageSurface* newFront);
 
   virtual void DestroyFrontBuffer()
   {
     if (mFrontSurface) {
-      BasicManager()->ShadowLayerManager::DestroySharedSurface(mFrontSurface);
+      BasicManager()->ShadowLayerManager::DestroySharedSurface(mFrontSurface, mAllocator);
     }
     mFrontSurface = nsnull;
   }
 
   virtual void Paint(gfxContext* aContext,
                      LayerManager::DrawThebesLayerCallback aCallback,
                      void* aCallbackData,
                      float aOpacity);
@@ -1995,17 +1998,17 @@ public:
   {}
 
   virtual already_AddRefed<gfxSharedImageSurface>
   Swap(gfxSharedImageSurface* newFront);
 
   virtual void DestroyFrontBuffer()
   {
     if (mFrontSurface) {
-      BasicManager()->ShadowLayerManager::DestroySharedSurface(mFrontSurface);
+      BasicManager()->ShadowLayerManager::DestroySharedSurface(mFrontSurface, mAllocator);
     }
     mFrontSurface = nsnull;
   }
 
   virtual void Paint(gfxContext* aContext,
                      LayerManager::DrawThebesLayerCallback aCallback,
                      void* aCallbackData,
                      float aOpacity);
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -486,28 +486,30 @@ PLayerChild*
 ShadowLayerForwarder::ConstructShadowFor(ShadowableLayer* aLayer)
 {
   NS_ABORT_IF_FALSE(HasShadowManager(), "no manager to forward to");
   return mShadowManager->SendPLayerConstructor(new ShadowLayerChild(aLayer));
 }
 
 
 void
-ShadowLayerManager::DestroySharedSurface(gfxSharedImageSurface* aSurface)
+ShadowLayerManager::DestroySharedSurface(gfxSharedImageSurface* aSurface,
+                                         PLayersParent* aDeallocator)
 {
-  mForwarder->DeallocShmem(aSurface->GetShmem());
+  aDeallocator->DeallocShmem(aSurface->GetShmem());
 }
 
 void
-ShadowLayerManager::DestroySharedSurface(SurfaceDescriptor* aSurface)
+ShadowLayerManager::DestroySharedSurface(SurfaceDescriptor* aSurface,
+                                         PLayersParent* aDeallocator)
 {
   if (PlatformDestroySharedSurface(aSurface)) {
     return;
   }
-  DestroySharedShmemSurface(aSurface, mForwarder);
+  DestroySharedShmemSurface(aSurface, aDeallocator);
 }
 
 
 #if !defined(MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS)
 
 PRBool
 ShadowLayerForwarder::PlatformAllocDoubleBuffer(const gfxIntSize&,
                                                 gfxASurface::gfxContentType,
--- a/gfx/layers/ipc/ShadowLayers.h
+++ b/gfx/layers/ipc/ShadowLayers.h
@@ -321,45 +321,37 @@ private:
 };
 
 
 class ShadowLayerManager : public LayerManager
 {
 public:
   virtual ~ShadowLayerManager() {}
 
-  PRBool HasForwarder() { return !!mForwarder; }
-
-  void SetForwarder(PLayersParent* aForwarder)
-  {
-    NS_ASSERTION(!aForwarder || !HasForwarder(), "stomping live forwarder?");
-    mForwarder = aForwarder;
-  }
-
   virtual void GetBackendName(nsAString& name) { name.AssignLiteral("Shadow"); }
 
-  void DestroySharedSurface(gfxSharedImageSurface* aSurface);
+  void DestroySharedSurface(gfxSharedImageSurface* aSurface,
+                            PLayersParent* aDeallocator);
 
-  void DestroySharedSurface(SurfaceDescriptor* aSurface);
+  void DestroySharedSurface(SurfaceDescriptor* aSurface,
+                            PLayersParent* aDeallocator);
 
   /** CONSTRUCTION PHASE ONLY */
   virtual already_AddRefed<ShadowThebesLayer> CreateShadowThebesLayer() = 0;
   /** CONSTRUCTION PHASE ONLY */
   virtual already_AddRefed<ShadowImageLayer> CreateShadowImageLayer() = 0;
   /** CONSTRUCTION PHASE ONLY */
   virtual already_AddRefed<ShadowCanvasLayer> CreateShadowCanvasLayer() = 0;
 
   static void PlatformSyncBeforeReplyUpdate();
 
 protected:
-  ShadowLayerManager() : mForwarder(NULL) {}
+  ShadowLayerManager() {}
 
   PRBool PlatformDestroySharedSurface(SurfaceDescriptor* aSurface);
-
-  PLayersParent* mForwarder;
 };
 
 
 /**
  * A ShadowableLayer is a Layer can be shared with a parent context
  * through a ShadowLayerForwarder.  A ShadowableLayer maps to a
  * Shadow*Layer in a parent context.
  *
@@ -388,16 +380,25 @@ protected:
 
   PLayerChild* mShadow;
 };
 
 
 class ShadowThebesLayer : public ThebesLayer
 {
 public:
+  /**
+   * CONSTRUCTION PHASE ONLY
+   */
+  void SetParent(PLayersParent* aParent)
+  {
+    NS_ABORT_IF_FALSE(!mAllocator, "Stomping parent?");
+    mAllocator = aParent;
+  }
+
   virtual void InvalidateRegion(const nsIntRegion& aRegion)
   {
     NS_RUNTIMEABORT("ShadowThebesLayers can't fill invalidated regions");
   }
 
   /**
    * CONSTRUCTION PHASE ONLY
    */
@@ -434,26 +435,39 @@ public:
    *
    * Destroy the current front buffer.
    */
   virtual void DestroyFrontBuffer() = 0;
 
   MOZ_LAYER_DECL_NAME("ShadowThebesLayer", TYPE_SHADOW)
 
 protected:
-  ShadowThebesLayer(LayerManager* aManager, void* aImplData) :
-    ThebesLayer(aManager, aImplData) {}
+  ShadowThebesLayer(LayerManager* aManager, void* aImplData)
+    : ThebesLayer(aManager, aImplData)
+    , mAllocator(nsnull)
+  {}
+
+  PLayersParent* mAllocator;
 };
 
 
 class ShadowCanvasLayer : public CanvasLayer
 {
 public:
   /**
    * CONSTRUCTION PHASE ONLY
+   */
+  void SetParent(PLayersParent* aParent)
+  {
+    NS_ABORT_IF_FALSE(!mAllocator, "Stomping parent?");
+    mAllocator = aParent;
+  }
+
+  /**
+   * CONSTRUCTION PHASE ONLY
    *
    * Publish the remote layer's back surface to this shadow, swapping
    * out the old front surface (the new back surface for the remote
    * layer).
    */
   virtual already_AddRefed<gfxSharedImageSurface>
   Swap(gfxSharedImageSurface* aNewFront) = 0;
 
@@ -462,26 +476,39 @@ public:
    *
    * Destroy the current front buffer.
    */
   virtual void DestroyFrontBuffer() = 0;
 
   MOZ_LAYER_DECL_NAME("ShadowCanvasLayer", TYPE_SHADOW)
 
 protected:
-  ShadowCanvasLayer(LayerManager* aManager, void* aImplData) :
-    CanvasLayer(aManager, aImplData) {}
+  ShadowCanvasLayer(LayerManager* aManager, void* aImplData)
+    : CanvasLayer(aManager, aImplData)
+    , mAllocator(nsnull)
+  {}
+
+  PLayersParent* mAllocator;
 };
 
 
 class ShadowImageLayer : public ImageLayer
 {
 public:
   /**
    * CONSTRUCTION PHASE ONLY
+   */
+  void SetParent(PLayersParent* aParent)
+  {
+    NS_ABORT_IF_FALSE(!mAllocator, "Stomping parent?");
+    mAllocator = aParent;
+  }
+
+  /**
+   * CONSTRUCTION PHASE ONLY
    *
    * Initialize this with a (temporary) front surface with the given
    * size.  This is expected to be followed with a Swap() in the same
    * transaction to bring in real pixels.  Init() may only be called
    * once.
    */
   virtual PRBool Init(gfxSharedImageSurface* aFront, const nsIntSize& aSize) = 0;
 
@@ -497,17 +524,21 @@ public:
    *
    * Destroy the current front buffer.
    */
   virtual void DestroyFrontBuffer() = 0;
 
   MOZ_LAYER_DECL_NAME("ShadowImageLayer", TYPE_SHADOW)
 
 protected:
-  ShadowImageLayer(LayerManager* aManager, void* aImplData) :
-    ImageLayer(aManager, aImplData) {}
+  ShadowImageLayer(LayerManager* aManager, void* aImplData)
+    : ImageLayer(aManager, aImplData)
+    , mAllocator(nsnull)
+  {}
+
+  PLayersParent* mAllocator;
 };
 
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // ifndef mozilla_layers_ShadowLayers_h
--- a/gfx/layers/ipc/ShadowLayersParent.cpp
+++ b/gfx/layers/ipc/ShadowLayersParent.cpp
@@ -145,45 +145,51 @@ ShadowLayersParent::RecvUpdate(const nsT
   for (EditArray::index_type i = 0; i < cset.Length(); ++i) {
     const Edit& edit = cset[i];
 
     switch (edit.type()) {
       // Create* ops
     case Edit::TOpCreateThebesLayer: {
       MOZ_LAYERS_LOG(("[ParentSide] CreateThebesLayer"));
 
-      nsRefPtr<ThebesLayer> layer = layer_manager()->CreateShadowThebesLayer();
+      nsRefPtr<ShadowThebesLayer> layer =
+        layer_manager()->CreateShadowThebesLayer();
+      layer->SetParent(this);
       AsShadowLayer(edit.get_OpCreateThebesLayer())->Bind(layer);
       break;
     }
     case Edit::TOpCreateContainerLayer: {
       MOZ_LAYERS_LOG(("[ParentSide] CreateContainerLayer"));
 
       nsRefPtr<ContainerLayer> layer = layer_manager()->CreateContainerLayer();
       AsShadowLayer(edit.get_OpCreateContainerLayer())->Bind(layer);
       break;
     }
     case Edit::TOpCreateImageLayer: {
       MOZ_LAYERS_LOG(("[ParentSide] CreateImageLayer"));
 
-      AsShadowLayer(edit.get_OpCreateImageLayer())->Bind(
-        layer_manager()->CreateShadowImageLayer().get());
+      nsRefPtr<ShadowImageLayer> layer =
+        layer_manager()->CreateShadowImageLayer();
+      layer->SetParent(this);
+      AsShadowLayer(edit.get_OpCreateImageLayer())->Bind(layer);
       break;
     }
     case Edit::TOpCreateColorLayer: {
       MOZ_LAYERS_LOG(("[ParentSide] CreateColorLayer"));
 
       nsRefPtr<ColorLayer> layer = layer_manager()->CreateColorLayer();
       AsShadowLayer(edit.get_OpCreateColorLayer())->Bind(layer);
       break;
     }
     case Edit::TOpCreateCanvasLayer: {
       MOZ_LAYERS_LOG(("[ParentSide] CreateCanvasLayer"));
 
-      nsRefPtr<CanvasLayer> layer = layer_manager()->CreateShadowCanvasLayer();
+      nsRefPtr<ShadowCanvasLayer> layer = 
+        layer_manager()->CreateShadowCanvasLayer();
+      layer->SetParent(this);
       AsShadowLayer(edit.get_OpCreateCanvasLayer())->Bind(layer);
       break;
     }
     case Edit::TOpCreateThebesBuffer: {
       MOZ_LAYERS_LOG(("[ParentSide] CreateThebesBuffer"));
 
       const OpCreateThebesBuffer& otb = edit.get_OpCreateThebesBuffer();
       ShadowThebesLayer* thebes = static_cast<ShadowThebesLayer*>(
--- a/gfx/tests/crashtests/467703-1.xhtml
+++ b/gfx/tests/crashtests/467703-1.xhtml
@@ -1,1 +1,1 @@
-<html xmlns="http://www.w3.org/1999/xhtml" style="margin: 78504em; background: url(http://www.google.com/images/logo_sm.gif); font-size: 305203ch; position: relative; left: 65em;"><head></head><body></body></html>
+<html xmlns="http://www.w3.org/1999/xhtml" style="margin: 78504em; background: url(../../../testing/crashtest/images/tree.gif); font-size: 305203ch; position: relative; left: 65em;"><head></head><body></body></html>
--- a/gfx/thebes/gfxFT2Fonts.cpp
+++ b/gfx/thebes/gfxFT2Fonts.cpp
@@ -127,19 +127,21 @@ FontEntry::CreateFontEntry(const gfxProx
     FT_Error error =
         FT_New_Memory_Face(gfxToolkitPlatform::GetPlatform()->GetFTLibrary(),
                            aFontData, aLength, 0, &face);
     if (error != FT_Err_Ok) {
         NS_Free((void*)aFontData);
         return nsnull;
     }
     FontEntry* fe = FontEntry::CreateFontEntryFromFace(face, aFontData);
-    fe->mItalic = aProxyEntry.mItalic;
-    fe->mWeight = aProxyEntry.mWeight;
-    fe->mStretch = aProxyEntry.mStretch;
+    if (fe) {
+        fe->mItalic = aProxyEntry.mItalic;
+        fe->mWeight = aProxyEntry.mWeight;
+        fe->mStretch = aProxyEntry.mStretch;
+    }
     return fe;
 }
 
 class FTUserFontData {
 public:
     FTUserFontData(FT_Face aFace, const PRUint8* aData)
         : mFace(aFace), mFontData(aData)
     {
--- a/ipc/glue/Shmem.cpp
+++ b/ipc/glue/Shmem.cpp
@@ -365,16 +365,18 @@ Shmem::RevokeRights(IHadBetterBeIPDLCode
 
 // static
 Shmem::SharedMemory*
 Shmem::Alloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
              size_t aNBytes,
              SharedMemoryType aType,
              bool aProtect)
 {
+  NS_ASSERTION(aNBytes <= PR_UINT32_MAX, "Will truncate shmem segment size!");
+
   size_t pageSize = SharedMemory::SystemPageSize();
   SharedMemory* segment = nsnull;
   // |2*pageSize| is for the front and back sentinel
   size_t segmentSize = PageAlignedSize(aNBytes + 2*pageSize);
 
   if (aType == SharedMemory::TYPE_BASIC)
     segment = CreateSegment(segmentSize, SharedMemoryBasic::NULLHandle());
 #ifdef MOZ_HAVE_SHAREDMEMORYSYSV
@@ -390,17 +392,16 @@ Shmem::Alloc(IHadBetterBeIPDLCodeCalling
   char *frontSentinel;
   char *data;
   char *backSentinel;
   GetSections(segment, &frontSentinel, &data, &backSentinel);
 
   // initialize the segment with Shmem-internal information
   Header* header = reinterpret_cast<Header*>(frontSentinel);
   memcpy(header->mMagic, sMagic, sizeof(sMagic));
-  NS_ASSERTION(aNBytes <= PR_UINT32_MAX, "Will truncate shmem segment size!");
   header->mSize = static_cast<uint32>(aNBytes);
 
   if (aProtect)
     Protect(segment);
 
   return segment;
 }
 
@@ -488,31 +489,31 @@ Shmem::SharedMemory*
 Shmem::Alloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
              size_t aNBytes, 
              SharedMemoryType aType,
              bool /*unused*/)
 {
   SharedMemory *segment = nsnull;
 
   if (aType == SharedMemory::TYPE_BASIC)
-    segment = CreateSegment(PageAlignedSize(aNBytes + sizeof(size_t)),
+    segment = CreateSegment(PageAlignedSize(aNBytes + sizeof(uint32)),
                             SharedMemoryBasic::NULLHandle());
 #ifdef MOZ_HAVE_SHAREDMEMORYSYSV
   else if (aType == SharedMemory::TYPE_SYSV)
-    segment = CreateSegment(PageAlignedSize(aNBytes + sizeof(size_t)),
+    segment = CreateSegment(PageAlignedSize(aNBytes + sizeof(uint32)),
                             SharedMemorySysV::NULLHandle());
 #endif
   else
     // Unhandled!!
     NS_ABORT();
 
   if (!segment)
     return 0;
 
-  *PtrToSize(segment) = aNBytes;
+  *PtrToSize(segment) = static_cast<uint32>(aNBytes);
 
   return segment;
 }
 
 // static
 Shmem::SharedMemory*
 Shmem::OpenExisting(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
                     const IPC::Message& aDescriptor,
@@ -555,17 +556,17 @@ Shmem::OpenExisting(IHadBetterBeIPDLCode
   else {
     NS_RUNTIMEABORT("unknown shmem type");
   }
 
   if (!segment)
     return 0;
 
   // this is the only validity check done OPT builds
-  if (size != *PtrToSize(segment))
+  if (size != static_cast<size_t>(*PtrToSize(segment)))
     NS_RUNTIMEABORT("Alloc() segment size disagrees with OpenExisting()'s");
 
   return segment;
 }
 
 // static
 void
 Shmem::Dealloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
--- a/ipc/glue/Shmem.h
+++ b/ipc/glue/Shmem.h
@@ -117,17 +117,17 @@ public:
 #if !defined(DEBUG)
   Shmem(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
         SharedMemory* aSegment, id_t aId) :
     mSegment(aSegment),
     mData(aSegment->memory()),
     mSize(0),
     mId(aId)
   {
-    mSize = *PtrToSize(mSegment);
+    mSize = static_cast<size_t>(*PtrToSize(mSegment));
   }
 #else
   Shmem(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
         SharedMemory* aSegment, id_t aId);
 #endif
 
   ~Shmem()
   {
@@ -269,22 +269,22 @@ private:
     if (0 != (mSize % sizeof(T)))
       NS_RUNTIMEABORT("shmem is not T-aligned");
   }
 
 #if !defined(DEBUG)
   void AssertInvariants() const
   { }
 
-  static size_t*
+  static uint32*
   PtrToSize(SharedMemory* aSegment)
   {
     char* endOfSegment =
       reinterpret_cast<char*>(aSegment->memory()) + aSegment->Size();
-    return reinterpret_cast<size_t*>(endOfSegment - sizeof(size_t));
+    return reinterpret_cast<uint32*>(endOfSegment - sizeof(uint32));
   }
 
 #else
   void AssertInvariants() const;
 #endif
 
   SharedMemory* mSegment;
   void* mData;
--- a/js/src/jsxml.cpp
+++ b/js/src/jsxml.cpp
@@ -6522,17 +6522,17 @@ xml_setNamespace(JSContext *cx, uintN ar
 
     ns = js_ConstructObject(cx, &js_NamespaceClass, NULL, obj,
                             argc == 0 ? 0 : 1, Valueify(vp + 2));
     if (!ns)
         return JS_FALSE;
     vp[0] = OBJECT_TO_JSVAL(ns);
     ns->setNamespaceDeclared(JSVAL_TRUE);
 
-    qnargv[0] = vp[2] = OBJECT_TO_JSVAL(ns);
+    qnargv[0] = OBJECT_TO_JSVAL(ns);
     qnargv[1] = OBJECT_TO_JSVAL(xml->name);
     qn = js_ConstructObject(cx, &js_QNameClass, NULL, NULL, 2, Valueify(qnargv));
     if (!qn)
         return JS_FALSE;
 
     xml->name = qn;
 
     /*
new file mode 100644
--- /dev/null
+++ b/layout/base/crashtests/537631-1.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<html>
+<head></head>
+<body style="position: fixed; -moz-column-count: 2;"><div style="position: absolute; height: 7em;"><br><br></div></body>
+</html>
--- a/layout/base/crashtests/crashtests.list
+++ b/layout/base/crashtests/crashtests.list
@@ -287,16 +287,17 @@ load 534768-1.html
 load 534768-2.html
 load 535721-1.xhtml
 load 535911-1.xhtml
 load 536623-1.xhtml
 load 536720.xul
 load 537059-1.xhtml
 load 537141-1.xhtml
 load 537562-1.xhtml
+load 537631-1.html
 load 538082-1.xul
 load 538207-1.xhtml
 load 538210-1.html
 load 540760.xul
 load 540771-1.xhtml
 load 541869-1.xhtml
 load 541869-2.html
 load 559705.xhtml
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -134,18 +134,18 @@ typedef struct CapturingContentInfo {
   nsIContent* mContent;
 
   CapturingContentInfo() :
     mAllowed(PR_FALSE), mRetargetToElement(PR_FALSE), mPreventDrag(PR_FALSE),
     mContent(nsnull) { }
 } CapturingContentInfo;
 
 #define NS_IPRESSHELL_IID     \
- { 0x34f80395, 0xff82, 0x49fa, \
-    { 0x9c, 0x83, 0xa6, 0xba, 0x49, 0xa8, 0x55, 0x4a } }
+ { 0xb79574cd, 0x2555, 0x4b57, \
+    { 0xb3, 0xf8, 0x27, 0x57, 0x3e, 0x60, 0x74, 0x01 } }
 
 // Constants for ScrollContentIntoView() function
 #define NS_PRESSHELL_SCROLL_TOP      0
 #define NS_PRESSHELL_SCROLL_BOTTOM   100
 #define NS_PRESSHELL_SCROLL_LEFT     0
 #define NS_PRESSHELL_SCROLL_RIGHT    100
 #define NS_PRESSHELL_SCROLL_CENTER   50
 #define NS_PRESSHELL_SCROLL_ANYWHERE -1
@@ -345,16 +345,22 @@ public:
    */
   virtual NS_HIDDEN_(nsresult) InitialReflow(nscoord aWidth, nscoord aHeight) = 0;
 
   /**
    * Reflow the frame model into a new width and height.  The
    * coordinates for aWidth and aHeight must be in standard nscoord's.
    */
   virtual NS_HIDDEN_(nsresult) ResizeReflow(nscoord aWidth, nscoord aHeight) = 0;
+  /**
+   * Reflow, and also change presshell state so as to only permit
+   * reflowing off calls to ResizeReflowOverride() in the future.
+   * ResizeReflow() calls are ignored after ResizeReflowOverride().
+   */
+  virtual NS_HIDDEN_(nsresult) ResizeReflowOverride(nscoord aWidth, nscoord aHeight) = 0;
 
   /**
    * Reflow the frame model with a reflow reason of eReflowReason_StyleChange
    */
   virtual NS_HIDDEN_(void) StyleChangeReflow() = 0;
 
   /**
    * This calls through to the frame manager to get the root frame.
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -736,16 +736,17 @@ public:
   NS_IMETHOD GetDisplaySelection(PRInt16 *aToggle);
   NS_IMETHOD ScrollSelectionIntoView(SelectionType aType, SelectionRegion aRegion, PRBool aIsSynchronous);
   NS_IMETHOD RepaintSelection(SelectionType aType);
 
   virtual NS_HIDDEN_(void) BeginObservingDocument();
   virtual NS_HIDDEN_(void) EndObservingDocument();
   virtual NS_HIDDEN_(nsresult) InitialReflow(nscoord aWidth, nscoord aHeight);
   virtual NS_HIDDEN_(nsresult) ResizeReflow(nscoord aWidth, nscoord aHeight);
+  virtual NS_HIDDEN_(nsresult) ResizeReflowOverride(nscoord aWidth, nscoord aHeight);
   virtual NS_HIDDEN_(void) StyleChangeReflow();
   virtual NS_HIDDEN_(nsIPageSequenceFrame*) GetPageSequenceFrame() const;
   virtual NS_HIDDEN_(nsIFrame*) GetRealPrimaryFrameFor(nsIContent* aContent) const;
 
   virtual NS_HIDDEN_(nsIFrame*) GetPlaceholderFrameFor(nsIFrame* aFrame) const;
   virtual NS_HIDDEN_(void) FrameNeedsReflow(nsIFrame *aFrame, IntrinsicDirty aIntrinsicDirty,
                                             nsFrameState aBitToAdd);
   virtual NS_HIDDEN_(void) FrameNeedsToContinueReflow(nsIFrame *aFrame);
@@ -1002,16 +1003,19 @@ protected:
   // the last reflow was interrupted. In the interrupted case ScheduleReflow is
   // called off a timer, otherwise it is called directly.
   void     MaybeScheduleReflow();
   // Actually schedules a reflow.  This should only be called by
   // MaybeScheduleReflow and the reflow timer ScheduleReflowOffTimer
   // sets up.
   void     ScheduleReflow();
 
+  // Reflow regardless of whether the override bit has been set.
+  nsresult ResizeReflowIgnoreOverride(nscoord aWidth, nscoord aHeight);
+
   // DoReflow returns whether the reflow finished without interruption
   PRBool DoReflow(nsIFrame* aFrame, PRBool aInterruptible);
 #ifdef DEBUG
   void DoVerifyReflow();
   void VerifyHasDirtyRootAncestor(nsIFrame* aFrame);
 #endif
 
   // Helper for ScrollContentIntoView
@@ -1142,16 +1146,18 @@ protected:
   // reflow roots that need to be reflowed, as both a queue and a hashtable
   nsTArray<nsIFrame*> mDirtyRoots;
 
   PRPackedBool mDocumentLoading;
 
   PRPackedBool mIgnoreFrameDestruction;
   PRPackedBool mHaveShutDown;
 
+  PRPackedBool mViewportOverridden;
+
   // This is used to protect ourselves from triggering reflow while in the
   // middle of frame construction and the like... it really shouldn't be
   // needed, one hopes, but it is for now.
   PRUint32  mChangeNestCount;
   
   nsIFrame*   mCurrentEventFrame;
   nsCOMPtr<nsIContent> mCurrentEventContent;
   nsTArray<nsIFrame*> mCurrentEventFrameStack;
@@ -1657,16 +1663,17 @@ PresShell::PresShell()
   mIsActive = PR_TRUE;
   mFrozen = PR_FALSE;
 #ifdef DEBUG
   mPresArenaAllocCount = 0;
 #endif
   mRenderFlags = 0;
   mXResolution = 1.0;
   mYResolution = 1.0;
+  mViewportOverridden = PR_FALSE;
 
   static bool registeredReporter = false;
   if (!registeredReporter) {
     NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(LayoutPresShell));
     NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(LayoutBidi));
     registeredReporter = true;
   }
 
@@ -2793,18 +2800,36 @@ PresShell::sPaintSuppressionCallback(nsI
 
 void
 PresShell::AsyncResizeEventCallback(nsITimer* aTimer, void* aPresShell)
 {
   static_cast<PresShell*>(aPresShell)->FireResizeEvent();
 }
 
 nsresult
+PresShell::ResizeReflowOverride(nscoord aWidth, nscoord aHeight)
+{
+  mViewportOverridden = PR_TRUE;
+  return ResizeReflowIgnoreOverride(aWidth, aHeight);
+}
+
+nsresult
 PresShell::ResizeReflow(nscoord aWidth, nscoord aHeight)
 {
+  if (mViewportOverridden) {
+    // The viewport has been overridden, and this reflow request
+    // didn't ask to ignore the override.  Pretend it didn't happen.
+    return NS_OK;
+  }
+  return ResizeReflowIgnoreOverride(aWidth, aHeight);
+}
+
+nsresult
+PresShell::ResizeReflowIgnoreOverride(nscoord aWidth, nscoord aHeight)
+{
   NS_PRECONDITION(!mIsReflowing, "Shouldn't be in reflow here!");
   NS_PRECONDITION(aWidth != NS_UNCONSTRAINEDSIZE,
                   "shouldn't use unconstrained widths anymore");
   
   // If we don't have a root frame yet, that means we haven't had our initial
   // reflow... If that's the case, and aWidth or aHeight is unconstrained,
   // ignore them altogether.
   nsIFrame* rootFrame = FrameManager()->GetRootFrame();
new file mode 100644
--- /dev/null
+++ b/layout/generic/crashtests/580504-1.xhtml
@@ -0,0 +1,22 @@
+<html xmlns="http://www.w3.org/1999/xhtml" style="-moz-column-width: 1px">
+<head>
+
+<script>
+<![CDATA[
+
+function boom()
+{
+  document.getElementById("d").focus();
+  document.execCommand("inserthtml", false, "<i><font><html><form>a</form></font>");
+  document.execCommand("justifyright", false, "#ffddff");
+}
+
+window.addEventListener("load", boom, false);
+
+]]>
+</script>
+
+</head>
+
+<div style="position: relative;"><div style="float: left; padding: 10px 20px 0pt;"><div contenteditable="true" style="position: absolute;" id="d"></div></div><div style="clear: both; padding: 20px 20px 15px;"></div></div>
+</html>
--- a/layout/generic/crashtests/crashtests.list
+++ b/layout/generic/crashtests/crashtests.list
@@ -294,17 +294,17 @@ load 495875-2.html
 load 499862-1.html
 load 499857-1.html
 asserts(10-90) asserts-if(winWidget,18-108) load 499885-1.xhtml # nscoord_MAX assertions (bug 575011), plus bug 570436 on windows
 load 501535-1.html
 load 503961-1.xhtml
 load 503961-2.html
 load 508168-1.html
 load 508908-1.html
-load 505912-1.html
+skip-if(cocoaWidget) load 505912-1.html # Mac OS X intermittent orange bug 599391
 load 509749-1.html
 load 511482.html
 load 512724-1.html
 load 512725-1.html
 load 513394-1.html
 load 514800-1.html
 load 515811-1.html
 load 517968.html
@@ -323,15 +323,16 @@ load 542136-1.html
 load 547338.xul
 load 547843-1.xhtml
 load 551635-1.html
 load 564368-1.xhtml
 load 564968.xhtml
 load 570160.html
 load 571618-1.svg
 load 574958.xhtml
+load 580504-1.xhtml
 load 585598-1.xhtml
 load 586806-1.html
 load 586806-2.html
 load 586806-3.html
 load 586973-1.html
 load 590404.html
 load 591141.html
--- a/layout/ipc/RenderFrameParent.cpp
+++ b/layout/ipc/RenderFrameParent.cpp
@@ -142,21 +142,21 @@ RenderFrameParent::~RenderFrameParent()
 
 void
 RenderFrameParent::ShadowLayersUpdated()
 {
   mFrameLoader->SetCurrentRemoteFrame(this);
 
   nsIFrame* docFrame = mFrameLoader->GetPrimaryFrameOfOwningContent();
   if (!docFrame) {
-    // Bad, but nothing we can do about it (XXX/cjones: or is there?).
-    // When the new frame is created, we'll probably still be the
-    // current render frame and will get to draw our content then.
-    // Or, we're shutting down and this update goes to /dev/null.
-    NS_WARNING("RenderFrameParent just received a layer update, but our <browser> doesn't have an nsIFrame so we can't invalidate for the update");
+    // Bad, but nothing we can do about it (XXX/cjones: or is there?
+    // maybe bug 589337?).  When the new frame is created, we'll
+    // probably still be the current render frame and will get to draw
+    // our content then.  Or, we're shutting down and this update goes
+    // to /dev/null.
     return;
   }
 
   // FIXME/cjones: we should collect the rects/regions updated for
   // Painted*Layer() calls and pass that region to here, then only
   // invalidate that rect
   //
   // We pass INVALIDATE_NO_THEBES_LAYERS here because we're
@@ -249,20 +249,17 @@ RenderFrameParent::AllocPLayers()
 {
   LayerManager* lm = GetLayerManager();
   if (LayerManager::LAYERS_BASIC != lm->GetBackendType()) {
     NS_WARNING("shadow layers no sprechen GL backend yet");
     return nsnull;
   }    
 
   BasicShadowLayerManager* bslm = static_cast<BasicShadowLayerManager*>(lm);
-  ShadowLayersParent* slp = new ShadowLayersParent(bslm);
-  bslm->SetForwarder(nsnull);   // clear the previous forwarder
-  bslm->SetForwarder(slp);
-  return slp;
+  return new ShadowLayersParent(bslm);
 }
 
 bool
 RenderFrameParent::DeallocPLayers(PLayersParent* aLayers)
 {
   delete aLayers;
   return true;
 }
--- a/layout/reftests/forms/reftest.list
+++ b/layout/reftests/forms/reftest.list
@@ -14,17 +14,17 @@ HTTP(..) == text-control-baseline-1.html
 == textbox-setsize.xul textbox-setsize-ref.xul
 == textarea-resize.html textarea-resize-ref.html
 # an offset seems to apply to the native resizer on windows so skip this test for now
 skip-if(winWidget) == textarea-resize-background.html textarea-resize-background-ref.html
 != textarea-ltr.html textarea-rtl.html
 != textarea-ltr-scrollbar.html textarea-rtl-scrollbar.html
 != textarea-in-ltr-doc-scrollbar.html textarea-in-rtl-doc-scrollbar.html
 != textarea-ltr.html textarea-no-resize.html
-random-if(gtk2Widget) != textarea-rtl.html textarea-no-resize.html # bug 558201
+!= textarea-rtl.html textarea-no-resize.html
 == textarea-setvalue-framereconstruction-1.html textarea-setvalue-framereconstruction-ref.html
 
 == radio-label-dynamic.html radio-label-dynamic-ref.html
 == out-of-bounds-selectedindex.html out-of-bounds-selectedindex-ref.html # test for bug 471741
 != indeterminate-checked.html indeterminate-checked-notref.html
 != indeterminate-unchecked.html indeterminate-unchecked-notref.html
 != indeterminate-native-checked.html indeterminate-native-checked-notref.html
 != indeterminate-native-unchecked.html indeterminate-native-unchecked-notref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-anonymous-boxes/490174-1-ref.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<head></head>
+<body>
+
+<div style="position:absolute; top: 50px; left: 50px; border: 2px solid orange;" id="v">
+  a
+  <select style="width: 200%">
+    <option style="visibility: collapse; overflow: auto; display: table-footer-group;">X</option>
+  </select>
+</div>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-anonymous-boxes/490174-1.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<head></head>
+<body onload="document.getElementById('v').firstChild.data = ' a ';">
+
+<div style="position:absolute; top: 50px; left: 50px; border: 2px solid orange;" id="v">
+  <select style="width: 200%">
+    <option style="visibility: collapse; overflow: auto; display: table-footer-group;">X</option>
+  </select>
+</div>
+
+</body>
+</html>
--- a/layout/reftests/table-anonymous-boxes/reftest.list
+++ b/layout/reftests/table-anonymous-boxes/reftest.list
@@ -29,16 +29,17 @@ fails == 156888-1.html 156888-1-ref.html
 == 373379-1.html 373379-1-ref.html
 random-if(d2d) == 394402-1a.html 394402-1-ref.html # bug 586833
 == 394402-1b.html 394402-1-ref.html # bug 586833
 == 398095-1.html 398095-1-ref.html
 == 407115-1.html 407115-1-ref.html
 == 443616-1a.xhtml 443616-1-ref.html
 == 443616-1b.html 443616-1-ref.html
 == 448111-1.html 448111-1-ref.html
+== 490174-1.html 490174-1-ref.html
 == infer-first-row.html 3x3-ref.html
 == infer-first-row-and-table.html 3x3-ref.html
 == infer-second-row.html 3x3-ref.html
 == infer-second-row-and-table.html 3x3-ref.html
 == infer-table-around-headers-footers-1.html 3x3-ref.html
 == infer-table-around-headers-footers-2.html 3x3-ref.html
 == infer-table-around-headers-footers-3.html 3x3-ref.html
 == infer-rows-inside-rowgroups.html 3x3-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/xul/base/src/crashtests/557174-1.xml
@@ -0,0 +1,1 @@
+<ther:window xmlns:ther="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" a="" e=""><HTML><ther:statusbar l="" c=""><ther:menulist d=""><ther:menu t="" i="" l=""><mat:h xmlns:mat="http://www.w3.org/1998/Math/MathML" w=""/></ther:menu><ther:menupopup p=""/><ther:menu a="" t="" l=""><ther:menuseparator u="" x=""><xht:html xmlns:xht="http://www.w3.org/1999/xhtml" x=""><xht:body d=""><xht:abbr d=""><xht:abbr p=""><xht:small s=""><xht:a s=""><xht:var e=""><xht:samp e=""><xht:code p=""><xht:b e=""><xht:b d=""><xht:del t=""><xht:h4 r=""><xht:var l=""><xht:i r=""><xht:em r=""><xht:em n=""><xht:map g=""><xht:isindex d=""/></xht:map></xht:em></xht:em></xht:i></xht:var></xht:h4></xht:del></xht:b></xht:b></xht:code></xht:samp></xht:var></xht:a></xht:small></xht:abbr></xht:abbr></xht:body></xht:html></ther:menuseparator></ther:menu></ther:menulist></ther:statusbar></HTML></ther:window>
\ No newline at end of file
--- a/layout/xul/base/src/crashtests/crashtests.list
+++ b/layout/xul/base/src/crashtests/crashtests.list
@@ -67,9 +67,10 @@ load 472189.xul
 load 475133.html
 load 488210-1.xhtml
 load 495728-1.xul
 load 508927-1.xul
 load 508927-2.xul
 load 514300-1.xul
 load 536931-1.xhtml
 asserts(1) load 538308-1.xul
+load 557174-1.xml
 load menulist-focused.xhtml
--- a/modules/libpr0n/src/SVGDocumentWrapper.cpp
+++ b/modules/libpr0n/src/SVGDocumentWrapper.cpp
@@ -261,25 +261,27 @@ SVGDocumentWrapper::OnStartRequest(nsIRe
 
 /* void onStopRequest (in nsIRequest request, in nsISupports ctxt,
                        in nsresult status); */
 NS_IMETHODIMP
 SVGDocumentWrapper::OnStopRequest(nsIRequest* aRequest, nsISupports* ctxt,
                                   nsresult status)
 {
   if (mListener) {
+    mListener->OnStopRequest(aRequest, ctxt, status);
     // A few levels up the stack, imgRequest::OnStopRequest is about to tell
     // all of its observers that we know our size and are ready to paint.  That
     // might not be true at this point, though -- so here, we synchronously
     // finish parsing & layout in our helper-document to make sure we can hold
     // up to this promise.
     nsCOMPtr<nsIParser> parser = do_QueryInterface(mListener);
-    parser->ContinueInterruptedParsing();
+    if (!parser->IsComplete()) {
+      parser->ContinueInterruptedParsing();
+    }
     FlushLayout();
-    mListener->OnStopRequest(aRequest, ctxt, status);
     mListener = nsnull;
 
     // In a normal document, this would be called by nsDocShell - but we don't
     // have a nsDocShell. So we do it ourselves. (If we don't, painting will
     // stay suppressed for a little while longer, for no good reason).
     mViewer->LoadComplete(NS_OK);
   }
 
--- a/modules/plugin/test/mochitest/Makefile.in
+++ b/modules/plugin/test/mochitest/Makefile.in
@@ -80,17 +80,16 @@ include $(topsrcdir)/config/rules.mk
   test_npn_asynccall.html \
   test_bug532208.html \
   large-pic.jpg \
   test_twostreams.html \
   test_streamatclose.html \
   neverending.sjs \
   test_newstreamondestroy.html \
   $(warning test_crashing2.html disabled due to random orange; see bug 566049) \
-  test_hanging.html \
   crashing_subpage.html \
   test_GCrace.html \
   test_propertyAndMethod.html \
   test_bug539565-1.html \
   test_bug539565-2.html \
   test_enumerate.html \
   $(NULL)
 
@@ -98,37 +97,45 @@ include $(topsrcdir)/config/rules.mk
 
 ifeq ($(OS_ARCH),WINNT)
 _MOCHITEST_FILES += \
   test_windowed_invalidate.html \
   $(NULL)
 endif
 
 _MOCHICHROME_FILES = \
-  test_bug479979.xul \
   test_npruntime.xul   \
   test_privatemode.xul \
   test_wmode.xul \
   $(NULL)
 
 # Temporarily disable the tests on Linux, see bug 573290 and bug 583591.
 ifneq ($(OS_ARCH),Linux)
+# Temporarily disable the tests on Mac OS X, see bug 599087.
+ifneq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 ifdef MOZ_CRASHREPORTER
 _MOCHICHROME_FILES += \
   test_crash_notify.xul \
   test_crash_notify_no_report.xul \
   test_crash_submit.xul \
   $(NULL)
 endif
 endif
+endif
 
-# Temporarily disable this test on Mac OS X.
+# Temporarily disable these tests on Mac OS X, see bug 599076 and bug 599267
+# and bug 599378.
 ifneq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 _MOCHITEST_FILES += \
   test_crashing.html \
+  test_hanging.html \
+  $(NULL)
+
+_MOCHICHROME_FILES += \
+  test_bug479979.xul \
   $(NULL)
 endif
 
 ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 _MOCHICHROME_FILES += \
   test_convertpoint.xul \
   $(NULL)
 
--- a/modules/plugin/test/mochitest/test_bug479979.xul
+++ b/modules/plugin/test/mochitest/test_bug479979.xul
@@ -1,16 +1,16 @@
 <?xml version="1.0"?>
 <?xml-stylesheet href="chrome://global/skin" type="text/css"?>
 <?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
                  type="text/css"?>
-<window title="NPAPI Private Mode Tests"
+<window title="NPAPI Set Undefined Value Test"
   xmlns:html="http://www.w3.org/1999/xhtml"
   xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
-  <title>NPAPI Private Mode Tests</title>
+  <title>NPAPI Set Undefined Value Test</title>
   <script type="application/javascript" 
    src="chrome://mochikit/content/MochiKit/packed.js"></script>
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
 <body xmlns="http://www.w3.org/1999/xhtml" onload="runTests()">
 <embed id="plugin1" type="application/x-test" width="300" height="300"></embed>
 </body>
 <script class="testbody" type="application/javascript">
--- a/modules/plugin/test/reftest/reftest.list
+++ b/modules/plugin/test/reftest/reftest.list
@@ -1,12 +1,12 @@
 # basic sanity checking
 random-if(!haveTestPlugin) != plugin-sanity.html about:blank
 fails-if(!haveTestPlugin) == plugin-sanity.html div-sanity.html
 fails-if(!haveTestPlugin) == plugin-alpha-zindex.html div-alpha-zindex.html
 fails-if(!haveTestPlugin) == plugin-alpha-opacity.html div-alpha-opacity.html
 fails-if(!haveTestPlugin) == windowless-clipping-1.html windowless-clipping-1-ref.html
 fails-if(!haveTestPlugin) == border-padding-1.html border-padding-1-ref.html
 fails-if(!haveTestPlugin) == border-padding-2.html border-padding-2-ref.html
-asserts-if(http.oscpu.match(/Linux/),0-1) random-if(d2d) fails-if(!haveTestPlugin) skip-if(!prefs.getBoolPref("dom.ipc.plugins.enabled")) == pluginproblemui-direction-1.html pluginproblemui-direction-1-ref.html # assertion is bug 585394
-asserts-if(http.oscpu.match(/Linux/),0-1) fails-if(!haveTestPlugin) skip-if(!prefs.getBoolPref("dom.ipc.plugins.enabled")) == pluginproblemui-direction-2.html pluginproblemui-direction-2-ref.html # assertion is bug 585394
+random-if(cocoaWidget||d2d) fails-if(!haveTestPlugin) skip-if(!prefs.getBoolPref("dom.ipc.plugins.enabled")) == pluginproblemui-direction-1.html pluginproblemui-direction-1-ref.html
+fails-if(!haveTestPlugin) skip-if(!prefs.getBoolPref("dom.ipc.plugins.enabled")) == pluginproblemui-direction-2.html pluginproblemui-direction-2-ref.html
 # Disabled for now to investigate Windows/Linux test failures
 # fails-if(!haveTestPlugin) == border-padding-3.html border-padding-3-ref.html
--- a/testing/xpcshell/runxpcshelltests.py
+++ b/testing/xpcshell/runxpcshelltests.py
@@ -315,16 +315,17 @@ class XPCShellTests(object):
     self.env["XPCOM_MEM_LEAK_LOG"] = leakLogFile
     return leakLogFile
 
   def launchProcess(self, cmd, stdout, stderr, env, cwd):
     """
       Simple wrapper to launch a process.
       On a remote system, this is more complex and we need to overload this function.
     """
+    cmd = wrapCommand(cmd)
     proc = Popen(cmd, stdout=stdout, stderr=stderr, 
                 env=env, cwd=cwd)
     return proc
 
   def communicate(self, proc):
     """
       Simple wrapper to communicate with a process.
       On a remote system, this is overloaded to handle remote process communication.
--- a/toolkit/crashreporter/google-breakpad/Makefile.am
+++ b/toolkit/crashreporter/google-breakpad/Makefile.am
@@ -181,16 +181,17 @@ src_client_linux_linux_dumper_unittest_h
 src_client_linux_linux_dumper_unittest_helper_CC=$(PTHREAD_CC)
 
 src_client_linux_linux_client_unittest_SOURCES = \
 	src/client/linux/handler/exception_handler_unittest.cc \
 	src/client/linux/minidump_writer/directory_reader_unittest.cc \
 	src/client/linux/minidump_writer/line_reader_unittest.cc \
 	src/client/linux/minidump_writer/linux_dumper_unittest.cc \
 	src/client/linux/minidump_writer/minidump_writer_unittest.cc \
+	src/common/memory_unittest.cc \
 	src/testing/gtest/src/gtest-all.cc \
 	src/testing/gtest/src/gtest_main.cc \
 	src/testing/src/gmock-all.cc
 
 src_client_linux_linux_client_unittest_CPPFLAGS = \
 	-I$(top_srcdir)/src/testing/include \
         -I$(top_srcdir)/src/testing/gtest/include \
 	-I$(top_srcdir)/src/testing/gtest \
--- a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.cc
+++ b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.cc
@@ -81,17 +81,17 @@
 #include <ucontext.h>
 #include <unistd.h>
 
 #include <algorithm>
 #include <vector>
 
 #include "common/linux/linux_libc_support.h"
 #include "common/linux/linux_syscall_support.h"
-#include "common/linux/memory.h"
+#include "common/memory.h"
 #include "client/linux/minidump_writer/minidump_writer.h"
 #include "common/linux/guid_creator.h"
 
 // A wrapper for the tgkill syscall: send a signal to a specific thread.
 static int tgkill(pid_t tgid, pid_t tid, int sig) {
   return syscall(__NR_tgkill, tgid, tid, sig);
   return 0;
 }
--- a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler_unittest.cc
+++ b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler_unittest.cc
@@ -27,25 +27,27 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include <string>
 
 #include <stdint.h>
 #include <unistd.h>
 #include <signal.h>
+#include <sys/mman.h>
 #include <sys/poll.h>
 #include <sys/socket.h>
 #include <sys/uio.h>
 
 #include "client/linux/handler/exception_handler.h"
 #include "client/linux/minidump_writer/minidump_writer.h"
 #include "common/linux/eintr_wrapper.h"
 #include "common/linux/linux_libc_support.h"
 #include "common/linux/linux_syscall_support.h"
+#include "google_breakpad/processor/minidump.h"
 #include "breakpad_googletest_includes.h"
 
 using namespace google_breakpad;
 
 static void sigchld_handler(int signo) { }
 
 class ExceptionHandlerTest : public ::testing::Test {
  protected:
@@ -121,16 +123,453 @@ TEST(ExceptionHandlerTest, ChildCrash) {
 
   const std::string minidump_filename = std::string("/tmp/") + filename +
                                         ".dmp";
 
   struct stat st;
   ASSERT_EQ(stat(minidump_filename.c_str(), &st), 0);
   ASSERT_GT(st.st_size, 0u);
   unlink(minidump_filename.c_str());
+  free(filename);
+}
+
+// Test that memory around the instruction pointer is written
+// to the dump as a MinidumpMemoryRegion.
+TEST(ExceptionHandlerTest, InstructionPointerMemory) {
+  int fds[2];
+  ASSERT_NE(pipe(fds), -1);
+
+  // These are defined here so the parent can use them to check the
+  // data from the minidump afterwards.
+  const u_int32_t kMemorySize = 256;  // bytes
+  const int kOffset = kMemorySize / 2;
+  // This crashes with SIGILL on x86/x86-64/arm.
+  const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
+
+  const pid_t child = fork();
+  if (child == 0) {
+    close(fds[0]);
+    ExceptionHandler handler("/tmp", NULL, DoneCallback, (void*) fds[1],
+                             true);
+    // Get some executable memory.
+    char* memory =
+      reinterpret_cast<char*>(mmap(NULL,
+                                   kMemorySize,
+                                   PROT_READ | PROT_WRITE | PROT_EXEC,
+                                   MAP_PRIVATE | MAP_ANON,
+                                   -1,
+                                   0));
+    if (!memory)
+      exit(0);
+
+    // Write some instructions that will crash. Put them in the middle
+    // of the block of memory, because the minidump should contain 128
+    // bytes on either side of the instruction pointer.
+    memcpy(memory + kOffset, instructions, sizeof(instructions));
+    
+    // Now execute the instructions, which should crash.
+    typedef void (*void_function)(void);
+    void_function memory_function =
+      reinterpret_cast<void_function>(memory + kOffset);
+    memory_function();
+  }
+  close(fds[1]);
+
+  int status;
+  ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1);
+  ASSERT_TRUE(WIFSIGNALED(status));
+  ASSERT_EQ(WTERMSIG(status), SIGILL);
+
+  struct pollfd pfd;
+  memset(&pfd, 0, sizeof(pfd));
+  pfd.fd = fds[0];
+  pfd.events = POLLIN | POLLERR;
+
+  const int r = HANDLE_EINTR(poll(&pfd, 1, 0));
+  ASSERT_EQ(r, 1);
+  ASSERT_TRUE(pfd.revents & POLLIN);
+
+  uint32_t len;
+  ASSERT_EQ(read(fds[0], &len, sizeof(len)), (ssize_t)sizeof(len));
+  ASSERT_LT(len, (uint32_t)2048);
+  char* filename = reinterpret_cast<char*>(malloc(len + 1));
+  ASSERT_EQ(read(fds[0], filename, len), len);
+  filename[len] = 0;
+  close(fds[0]);
+
+  const std::string minidump_filename = std::string("/tmp/") + filename +
+                                        ".dmp";
+
+  struct stat st;
+  ASSERT_EQ(stat(minidump_filename.c_str(), &st), 0);
+  ASSERT_GT(st.st_size, 0u);
+
+  // Read the minidump. Locate the exception record and the
+  // memory list, and then ensure that there is a memory region
+  // in the memory list that covers the instruction pointer from
+  // the exception record.
+  Minidump minidump(minidump_filename);
+  ASSERT_TRUE(minidump.Read());
+
+  MinidumpException* exception = minidump.GetException();
+  MinidumpMemoryList* memory_list = minidump.GetMemoryList();
+  ASSERT_TRUE(exception);
+  ASSERT_TRUE(memory_list);
+  ASSERT_LT(0, memory_list->region_count());
+
+  MinidumpContext* context = exception->GetContext();
+  ASSERT_TRUE(context);
+
+  u_int64_t instruction_pointer;
+  switch (context->GetContextCPU()) {
+  case MD_CONTEXT_X86:
+    instruction_pointer = context->GetContextX86()->eip;
+    break;
+  case MD_CONTEXT_AMD64:
+    instruction_pointer = context->GetContextAMD64()->rip;
+    break;
+  case MD_CONTEXT_ARM:
+    instruction_pointer = context->GetContextARM()->iregs[15];
+    break;
+  default:
+    FAIL() << "Unknown context CPU: " << context->GetContextCPU();
+    break;
+  }
+
+  MinidumpMemoryRegion* region =
+    memory_list->GetMemoryRegionForAddress(instruction_pointer);
+  ASSERT_TRUE(region);
+
+  EXPECT_EQ(kMemorySize, region->GetSize());
+  const u_int8_t* bytes = region->GetMemory();
+  ASSERT_TRUE(bytes);
+
+  u_int8_t prefix_bytes[kOffset];
+  u_int8_t suffix_bytes[kMemorySize - kOffset - sizeof(instructions)];
+  memset(prefix_bytes, 0, sizeof(prefix_bytes));
+  memset(suffix_bytes, 0, sizeof(suffix_bytes));
+  EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0);
+  EXPECT_TRUE(memcmp(bytes + kOffset, instructions, sizeof(instructions)) == 0);
+  EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions),
+                     suffix_bytes, sizeof(suffix_bytes)) == 0);
+
+  unlink(minidump_filename.c_str());
+  free(filename);
+}
+
+// Test that the memory region around the instruction pointer is
+// bounded correctly on the low end.
+TEST(ExceptionHandlerTest, InstructionPointerMemoryMinBound) {
+  int fds[2];
+  ASSERT_NE(pipe(fds), -1);
+
+  // These are defined here so the parent can use them to check the
+  // data from the minidump afterwards.
+  const u_int32_t kMemorySize = 256;  // bytes
+  const int kOffset = 0;
+  // This crashes with SIGILL on x86/x86-64/arm.
+  const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
+
+  const pid_t child = fork();
+  if (child == 0) {
+    close(fds[0]);
+    ExceptionHandler handler("/tmp", NULL, DoneCallback, (void*) fds[1],
+                             true);
+    // Get some executable memory.
+    char* memory =
+      reinterpret_cast<char*>(mmap(NULL,
+                                   kMemorySize,
+                                   PROT_READ | PROT_WRITE | PROT_EXEC,
+                                   MAP_PRIVATE | MAP_ANON,
+                                   -1,
+                                   0));
+    if (!memory)
+      exit(0);
+
+    // Write some instructions that will crash. Put them in the middle
+    // of the block of memory, because the minidump should contain 128
+    // bytes on either side of the instruction pointer.
+    memcpy(memory + kOffset, instructions, sizeof(instructions));
+    
+    // Now execute the instructions, which should crash.
+    typedef void (*void_function)(void);
+    void_function memory_function =
+      reinterpret_cast<void_function>(memory + kOffset);
+    memory_function();
+  }
+  close(fds[1]);
+
+  int status;
+  ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1);
+  ASSERT_TRUE(WIFSIGNALED(status));
+  ASSERT_EQ(WTERMSIG(status), SIGILL);
+
+  struct pollfd pfd;
+  memset(&pfd, 0, sizeof(pfd));
+  pfd.fd = fds[0];
+  pfd.events = POLLIN | POLLERR;
+
+  const int r = HANDLE_EINTR(poll(&pfd, 1, 0));
+  ASSERT_EQ(r, 1);
+  ASSERT_TRUE(pfd.revents & POLLIN);
+
+  uint32_t len;
+  ASSERT_EQ(read(fds[0], &len, sizeof(len)), (ssize_t)sizeof(len));
+  ASSERT_LT(len, (uint32_t)2048);
+  char* filename = reinterpret_cast<char*>(malloc(len + 1));
+  ASSERT_EQ(read(fds[0], filename, len), len);
+  filename[len] = 0;
+  close(fds[0]);
+
+  const std::string minidump_filename = std::string("/tmp/") + filename +
+                                        ".dmp";
+
+  struct stat st;
+  ASSERT_EQ(stat(minidump_filename.c_str(), &st), 0);
+  ASSERT_GT(st.st_size, 0u);
+
+  // Read the minidump. Locate the exception record and the
+  // memory list, and then ensure that there is a memory region
+  // in the memory list that covers the instruction pointer from
+  // the exception record.
+  Minidump minidump(minidump_filename);
+  ASSERT_TRUE(minidump.Read());
+
+  MinidumpException* exception = minidump.GetException();
+  MinidumpMemoryList* memory_list = minidump.GetMemoryList();
+  ASSERT_TRUE(exception);
+  ASSERT_TRUE(memory_list);
+  ASSERT_LT(0, memory_list->region_count());
+
+  MinidumpContext* context = exception->GetContext();
+  ASSERT_TRUE(context);
+
+  u_int64_t instruction_pointer;
+  switch (context->GetContextCPU()) {
+  case MD_CONTEXT_X86:
+    instruction_pointer = context->GetContextX86()->eip;
+    break;
+  case MD_CONTEXT_AMD64:
+    instruction_pointer = context->GetContextAMD64()->rip;
+    break;
+  case MD_CONTEXT_ARM:
+    instruction_pointer = context->GetContextARM()->iregs[15];
+    break;
+  default:
+    FAIL() << "Unknown context CPU: " << context->GetContextCPU();
+    break;
+  }
+
+  MinidumpMemoryRegion* region =
+    memory_list->GetMemoryRegionForAddress(instruction_pointer);
+  ASSERT_TRUE(region);
+
+  EXPECT_EQ(kMemorySize / 2, region->GetSize());
+  const u_int8_t* bytes = region->GetMemory();
+  ASSERT_TRUE(bytes);
+
+  u_int8_t suffix_bytes[kMemorySize / 2 - sizeof(instructions)];
+  memset(suffix_bytes, 0, sizeof(suffix_bytes));
+  EXPECT_TRUE(memcmp(bytes + kOffset, instructions, sizeof(instructions)) == 0);
+  EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions),
+                     suffix_bytes, sizeof(suffix_bytes)) == 0);
+
+  unlink(minidump_filename.c_str());
+  free(filename);
+}
+
+// Test that the memory region around the instruction pointer is
+// bounded correctly on the high end.
+TEST(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) {
+  int fds[2];
+  ASSERT_NE(pipe(fds), -1);
+
+  // These are defined here so the parent can use them to check the
+  // data from the minidump afterwards.
+  // Use 4k here because the OS will hand out a single page even
+  // if a smaller size is requested, and this test wants to
+  // test the upper bound of the memory range.
+  const u_int32_t kMemorySize = 4096;  // bytes
+  // This crashes with SIGILL on x86/x86-64/arm.
+  const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
+  const int kOffset = kMemorySize - sizeof(instructions);
+
+  const pid_t child = fork();
+  if (child == 0) {
+    close(fds[0]);
+    ExceptionHandler handler("/tmp", NULL, DoneCallback, (void*) fds[1],
+                             true);
+    // Get some executable memory.
+    char* memory =
+      reinterpret_cast<char*>(mmap(NULL,
+                                   kMemorySize,
+                                   PROT_READ | PROT_WRITE | PROT_EXEC,
+                                   MAP_PRIVATE | MAP_ANON,
+                                   -1,
+                                   0));
+    if (!memory)
+      exit(0);
+
+    // Write some instructions that will crash. Put them in the middle
+    // of the block of memory, because the minidump should contain 128
+    // bytes on either side of the instruction pointer.
+    memcpy(memory + kOffset, instructions, sizeof(instructions));
+    
+    // Now execute the instructions, which should crash.
+    typedef void (*void_function)(void);
+    void_function memory_function =
+      reinterpret_cast<void_function>(memory + kOffset);
+    memory_function();
+  }
+  close(fds[1]);
+
+  int status;
+  ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1);
+  ASSERT_TRUE(WIFSIGNALED(status));
+  ASSERT_EQ(WTERMSIG(status), SIGILL);
+
+  struct pollfd pfd;
+  memset(&pfd, 0, sizeof(pfd));
+  pfd.fd = fds[0];
+  pfd.events = POLLIN | POLLERR;
+
+  const int r = HANDLE_EINTR(poll(&pfd, 1, 0));
+  ASSERT_EQ(r, 1);
+  ASSERT_TRUE(pfd.revents & POLLIN);
+
+  uint32_t len;
+  ASSERT_EQ(read(fds[0], &len, sizeof(len)), (ssize_t)sizeof(len));
+  ASSERT_LT(len, (uint32_t)2048);
+  char* filename = reinterpret_cast<char*>(malloc(len + 1));
+  ASSERT_EQ(read(fds[0], filename, len), len);
+  filename[len] = 0;
+  close(fds[0]);
+
+  const std::string minidump_filename = std::string("/tmp/") + filename +
+                                        ".dmp";
+
+  struct stat st;
+  ASSERT_EQ(stat(minidump_filename.c_str(), &st), 0);
+  ASSERT_GT(st.st_size, 0u);
+
+  // Read the minidump. Locate the exception record and the
+  // memory list, and then ensure that there is a memory region
+  // in the memory list that covers the instruction pointer from
+  // the exception record.
+  Minidump minidump(minidump_filename);
+  ASSERT_TRUE(minidump.Read());
+
+  MinidumpException* exception = minidump.GetException();
+  MinidumpMemoryList* memory_list = minidump.GetMemoryList();
+  ASSERT_TRUE(exception);
+  ASSERT_TRUE(memory_list);
+  ASSERT_LT(0, memory_list->region_count());
+
+  MinidumpContext* context = exception->GetContext();
+  ASSERT_TRUE(context);
+
+  u_int64_t instruction_pointer;
+  switch (context->GetContextCPU()) {
+  case MD_CONTEXT_X86:
+    instruction_pointer = context->GetContextX86()->eip;
+    break;
+  case MD_CONTEXT_AMD64:
+    instruction_pointer = context->GetContextAMD64()->rip;
+    break;
+  case MD_CONTEXT_ARM:
+    instruction_pointer = context->GetContextARM()->iregs[15];
+    break;
+  default:
+    FAIL() << "Unknown context CPU: " << context->GetContextCPU();
+    break;
+  }
+
+  MinidumpMemoryRegion* region =
+    memory_list->GetMemoryRegionForAddress(instruction_pointer);
+  ASSERT_TRUE(region);
+
+  const size_t kPrefixSize = 128;  // bytes
+  EXPECT_EQ(kPrefixSize + sizeof(instructions), region->GetSize());
+  const u_int8_t* bytes = region->GetMemory();
+  ASSERT_TRUE(bytes);
+
+  u_int8_t prefix_bytes[kPrefixSize];
+  memset(prefix_bytes, 0, sizeof(prefix_bytes));
+  EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0);
+  EXPECT_TRUE(memcmp(bytes + kPrefixSize,
+                     instructions, sizeof(instructions)) == 0);
+
+  unlink(minidump_filename.c_str());
+  free(filename);
+}
+
+// Ensure that an extra memory block doesn't get added when the
+// instruction pointer is not in mapped memory.
+TEST(ExceptionHandlerTest, InstructionPointerMemoryNullPointer) {
+  int fds[2];
+  ASSERT_NE(pipe(fds), -1);
+
+
+  const pid_t child = fork();
+  if (child == 0) {
+    close(fds[0]);
+    ExceptionHandler handler("/tmp", NULL, DoneCallback, (void*) fds[1],
+                             true);
+    // Try calling a NULL pointer.
+    typedef void (*void_function)(void);
+    void_function memory_function =
+      reinterpret_cast<void_function>(NULL);
+    memory_function();
+  }
+  close(fds[1]);
+
+  int status;
+  ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1);
+  ASSERT_TRUE(WIFSIGNALED(status));
+  ASSERT_EQ(WTERMSIG(status), SIGSEGV);
+
+  struct pollfd pfd;
+  memset(&pfd, 0, sizeof(pfd));
+  pfd.fd = fds[0];
+  pfd.events = POLLIN | POLLERR;
+
+  const int r = HANDLE_EINTR(poll(&pfd, 1, 0));
+  ASSERT_EQ(r, 1);
+  ASSERT_TRUE(pfd.revents & POLLIN);
+
+  uint32_t len;
+  ASSERT_EQ(read(fds[0], &len, sizeof(len)), (ssize_t)sizeof(len));
+  ASSERT_LT(len, (uint32_t)2048);
+  char* filename = reinterpret_cast<char*>(malloc(len + 1));
+  ASSERT_EQ(read(fds[0], filename, len), len);
+  filename[len] = 0;
+  close(fds[0]);
+
+  const std::string minidump_filename = std::string("/tmp/") + filename +
+                                        ".dmp";
+
+  struct stat st;
+  ASSERT_EQ(stat(minidump_filename.c_str(), &st), 0);
+  ASSERT_GT(st.st_size, 0u);
+
+  // Read the minidump. Locate the exception record and the
+  // memory list, and then ensure that there is a memory region
+  // in the memory list that covers the instruction pointer from
+  // the exception record.
+  Minidump minidump(minidump_filename);
+  ASSERT_TRUE(minidump.Read());
+
+  MinidumpException* exception = minidump.GetException();
+  MinidumpMemoryList* memory_list = minidump.GetMemoryList();
+  ASSERT_TRUE(exception);
+  ASSERT_TRUE(memory_list);
+  ASSERT_EQ((unsigned int)1, memory_list->region_count());
+
+  unlink(minidump_filename.c_str());
+  free(filename);
 }
 
 static const unsigned kControlMsgSize =
     CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred));
 
 static bool
 CrashHandler(const void* crash_context, size_t crash_context_size,
              void* context) {
--- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper.h
+++ b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper.h
@@ -31,17 +31,17 @@
 #define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_
 
 #include <elf.h>
 #include <linux/limits.h>
 #include <stdint.h>
 #include <sys/types.h>
 #include <sys/user.h>
 
-#include "common/linux/memory.h"
+#include "common/memory.h"
 #include "google_breakpad/common/minidump_format.h"
 
 namespace google_breakpad {
 
 typedef typeof(((struct user*) 0)->u_debugreg[0]) debugreg_t;
 
 // Typedef for our parsing of the auxv variables in /proc/pid/auxv.
 #if defined(__i386) || defined(__ARM_EABI__)
--- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper_unittest.cc
+++ b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper_unittest.cc
@@ -30,17 +30,17 @@
 #include <limits.h>
 #include <unistd.h>
 #include <signal.h>
 #include <sys/types.h>
 
 #include "breakpad_googletest_includes.h"
 #include "client/linux/minidump_writer/linux_dumper.h"
 #include "common/linux/file_id.h"
-#include "common/linux/memory.h"
+#include "common/memory.h"
 
 using namespace google_breakpad;
 
 // This provides a wrapper around system calls which may be
 // interrupted by a signal and return EINTR. See man 7 signal.
 #define HANDLE_EINTR(x) ({ \
   typeof(x) __eintr_result__; \
   do { \
--- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer.cc
+++ b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer.cc
@@ -41,16 +41,18 @@
 //     around the system calls in linux_syscall_support.h.
 //   * You may not malloc. There's an alternative allocator in memory.h and
 //     a canonical instance in the LinuxDumper object. We use the placement
 //     new form to allocate objects and we don't delete them.
 
 #include "client/linux/minidump_writer/minidump_writer.h"
 #include "client/minidump_file_writer-inl.h"
 
+#include <algorithm>
+
 #include <errno.h>
 #include <fcntl.h>
 #include <link.h>
 #include <stdio.h>
 #include <unistd.h>
 #include <sys/ucontext.h>
 #include <sys/user.h>
 #include <sys/utsname.h>
@@ -412,30 +414,32 @@ class MinidumpWriter {
 #if !defined(__ARM_EABI__)
         float_state_(&context->float_state),
 #else
         //TODO: fix this after fixing ExceptionHandler
         float_state_(NULL),
 #endif
         crashing_tid_(context->tid),
         crashing_tid_pc_(0),
-        dumper_(crashing_pid) {
+        dumper_(crashing_pid),
+        memory_blocks_(dumper_.allocator()) {
   }
 
   // case (2) above
   MinidumpWriter(const char* filename,
                  pid_t pid,
                  pid_t blame_thread)
       : filename_(filename),
         siginfo_(NULL),         // we fill this in if we find blame_thread
         ucontext_(NULL),
         float_state_(NULL),
         crashing_tid_(blame_thread),
         crashing_tid_pc_(0),    // set if we find blame_thread
-        dumper_(pid) {
+        dumper_(pid),
+        memory_blocks_(dumper_.allocator()) {
   }
 
   bool Init() {
     return dumper_.Init() && minidump_writer_.Open(filename_) &&
            dumper_.ThreadsAttach();
   }
 
   ~MinidumpWriter() {
@@ -462,17 +466,19 @@ class MinidumpWriter {
         r_debug = (struct r_debug*)dyn.d_un.d_ptr;
         continue;
       } else if (dyn.d_tag == DT_NULL)
         break;
     }
 
     // A minidump file contains a number of tagged streams. This is the number
     // of stream which we write.
-    const unsigned kNumWriters = 11 + !!r_debug;
+    unsigned kNumWriters = 12;
+    if (r_debug)
+      ++kNumWriters;
 
     TypedMDRVA<MDRawHeader> header(&minidump_writer_);
     TypedMDRVA<MDRawDirectory> dir(&minidump_writer_);
     if (!header.Allocate())
       return false;
     if (!dir.AllocateArray(kNumWriters))
       return false;
     memset(header.get(), 0, sizeof(MDRawHeader));
@@ -489,16 +495,20 @@ class MinidumpWriter {
     if (!WriteThreadListStream(&dirent))
       return false;
     dir.CopyIndex(dir_index++, &dirent);
 
     if (!WriteMappings(&dirent))
       return false;
     dir.CopyIndex(dir_index++, &dirent);
 
+    if (!WriteMemoryListStream(&dirent))
+      return false;
+    dir.CopyIndex(dir_index++, &dirent);
+
     if (siginfo_ || crashing_tid_pc_) {
       if (!WriteExceptionStream(&dirent))
         return false;
       dir.CopyIndex(dir_index++, &dirent);
     }
 
     if (!WriteSystemInfoStream(&dirent))
       return false;
@@ -700,16 +710,61 @@ class MinidumpWriter {
         UntypedMDRVA memory(&minidump_writer_);
         if (!memory.Allocate(stack_len))
           return false;
         uint8_t* stack_copy = (uint8_t*) dumper_.allocator()->Alloc(stack_len);
         dumper_.CopyFromProcess(stack_copy, thread.thread_id, stack, stack_len);
         memory.Copy(stack_copy, stack_len);
         thread.stack.start_of_memory_range = (uintptr_t) (stack);
         thread.stack.memory = memory.location();
+        memory_blocks_.push_back(thread.stack);
+
+        // Copy 256 bytes around crashing instruction pointer to minidump.
+        const size_t kIPMemorySize = 256;
+        u_int64_t ip = GetInstructionPointer();
+        // Bound it to the upper and lower bounds of the memory map
+        // it's contained within. If it's not in mapped memory,
+        // don't bother trying to write it.
+        bool ip_is_mapped = false;
+        MDMemoryDescriptor ip_memory_d;
+        for (unsigned i = 0; i < dumper_.mappings().size(); ++i) {
+          const MappingInfo& mapping = *dumper_.mappings()[i];
+          if (ip >= mapping.start_addr &&
+              ip < mapping.start_addr + mapping.size) {
+            ip_is_mapped = true;
+            // Try to get 128 bytes before and after the IP, but
+            // settle for whatever's available.
+            ip_memory_d.start_of_memory_range =
+              std::max(mapping.start_addr,
+                       uintptr_t(ip - (kIPMemorySize / 2)));
+            uintptr_t end_of_range = 
+              std::min(uintptr_t(ip + (kIPMemorySize / 2)),
+                       uintptr_t(mapping.start_addr + mapping.size));
+            ip_memory_d.memory.data_size =
+              end_of_range - ip_memory_d.start_of_memory_range;
+            break;
+          }
+        }
+
+        if (ip_is_mapped) {
+          UntypedMDRVA ip_memory(&minidump_writer_);
+          if (!ip_memory.Allocate(ip_memory_d.memory.data_size))
+            return false;
+          uint8_t* memory_copy =
+            (uint8_t*) dumper_.allocator()->Alloc(ip_memory_d.memory.data_size);
+          dumper_.CopyFromProcess(
+            memory_copy,
+            thread.thread_id,
+            reinterpret_cast<void*>(ip_memory_d.start_of_memory_range),
+            ip_memory_d.memory.data_size);
+          ip_memory.Copy(memory_copy, ip_memory_d.memory.data_size);
+          ip_memory_d.memory = ip_memory.location();
+          memory_blocks_.push_back(ip_memory_d);
+        }
+
         TypedMDRVA<RawContextCPU> cpu(&minidump_writer_);
         if (!cpu.Allocate())
           return false;
         my_memset(cpu.get(), 0, sizeof(RawContextCPU));
         CPUFillFromUContext(cpu.get(), ucontext_, float_state_);
         PopSeccompStackFrame(cpu.get(), thread, stack_copy);
         thread.thread_context = cpu.location();
         crashing_thread_context_ = cpu.location();
@@ -723,16 +778,18 @@ class MinidumpWriter {
           return false;
         uint8_t* stack_copy =
             (uint8_t*) dumper_.allocator()->Alloc(info.stack_len);
         dumper_.CopyFromProcess(stack_copy, thread.thread_id, info.stack,
                                 info.stack_len);
         memory.Copy(stack_copy, info.stack_len);
         thread.stack.start_of_memory_range = (uintptr_t)(info.stack);
         thread.stack.memory = memory.location();
+        memory_blocks_.push_back(thread.stack);
+
         TypedMDRVA<RawContextCPU> cpu(&minidump_writer_);
         if (!cpu.Allocate())
           return false;
         my_memset(cpu.get(), 0, sizeof(RawContextCPU));
         CPUFillFromThreadInfo(cpu.get(), info);
         PopSeccompStackFrame(cpu.get(), thread, stack_copy);
         thread.thread_context = cpu.location();
 
@@ -831,16 +888,34 @@ class MinidumpWriter {
       mod.module_name_rva = ld.rva;
 
       list.CopyIndexAfterObject(j++, &mod, MD_MODULE_SIZE);
     }
 
     return true;
   }
 
+  bool WriteMemoryListStream(MDRawDirectory* dirent) {
+    TypedMDRVA<uint32_t> list(&minidump_writer_);
+    if (!list.AllocateObjectAndArray(memory_blocks_.size(),
+                                     sizeof(MDMemoryDescriptor)))
+      return false;
+
+    dirent->stream_type = MD_MEMORY_LIST_STREAM;
+    dirent->location = list.location();
+
+    *list.get() = memory_blocks_.size();
+
+    for (size_t i = 0; i < memory_blocks_.size(); ++i) {
+      list.CopyIndexAfterObject(i, &memory_blocks_[i],
+                                sizeof(MDMemoryDescriptor));
+    }
+    return true;
+  }
+
   bool WriteExceptionStream(MDRawDirectory* dirent) {
     TypedMDRVA<MDRawExceptionStream> exc(&minidump_writer_);
     if (!exc.Allocate())
       return false;
     my_memset(exc.get(), 0, sizeof(MDRawExceptionStream));
 
     dirent->stream_type = MD_EXCEPTION_STREAM;
     dirent->location = exc.location();
@@ -945,16 +1020,32 @@ class MinidumpWriter {
                             dynamic_length);
     debug.CopyIndexAfterObject(0, dso_debug_data, dynamic_length);
     delete[] dso_debug_data;
 
     return true;
   }
 
  private:
+#if defined(__i386)
+  uintptr_t GetInstructionPointer() {
+    return ucontext_->uc_mcontext.gregs[REG_EIP];
+  }
+#elif defined(__x86_64)
+  uintptr_t GetInstructionPointer() {
+    return ucontext_->uc_mcontext.gregs[REG_RIP];
+  }
+#elif defined(__ARM_EABI__)
+  uintptr_t GetInstructionPointer() {
+    return ucontext_->uc_mcontext.arm_ip;
+  }
+#else
+#error "This code has not been ported to your platform yet."
+#endif
+
   void NullifyDirectoryEntry(MDRawDirectory* dirent) {
     dirent->stream_type = 0;
     dirent->location.data_size = 0;
     dirent->location.rva = 0;
   }
 
   bool WriteCPUInformation(MDRawSystemInfo* sys_info) {
     char vendor_id[sizeof(sys_info->cpu.x86_cpu_info.vendor_id) + 1] = {0};
@@ -1195,16 +1286,20 @@ popline:
   uintptr_t crashing_tid_pc_; // set if we're dumping a live process
                               // and find crashing_tid_.  used to
                               // write exception info.  (if we're
                               // dumping a crash, this stays 0 and we
                               // use siginfo_)
   LinuxDumper dumper_;
   MinidumpFileWriter minidump_writer_;
   MDLocationDescriptor crashing_thread_context_;
+  // Blocks of memory written to the dump. These are all currently
+  // written while writing the thread list stream, but saved here
+  // so a memory list stream can be written afterwards.
+  wasteful_vector<MDMemoryDescriptor> memory_blocks_;
 };
 
 bool WriteMinidump(const char* filename, pid_t crashing_process,
                    const void* blob, size_t blob_size) {
   if (blob_size != sizeof(ExceptionHandler::CrashContext))
     return false;
   const ExceptionHandler::CrashContext* context =
       reinterpret_cast<const ExceptionHandler::CrashContext*>(blob);
--- a/toolkit/crashreporter/google-breakpad/src/client/mac/Breakpad.xcodeproj/project.pbxproj
+++ b/toolkit/crashreporter/google-breakpad/src/client/mac/Breakpad.xcodeproj/project.pbxproj
@@ -46,16 +46,21 @@
 		8B31029411F0D54300FCF3E4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D69BFE84028FC02AAC07 /* Foundation.framework */; };
 		8B3102E611F0D74C00FCF3E4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D69BFE84028FC02AAC07 /* Foundation.framework */; };
 		8B3102EB11F0D78000FCF3E4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D69BFE84028FC02AAC07 /* Foundation.framework */; };
 		8B31FC8211EFD2B800FCF3E4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D69BFE84028FC02AAC07 /* Foundation.framework */; };
 		8B4BDAAF12012BC5009C7060 /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B4BDAA7120124EA009C7060 /* libcrypto.dylib */; };
 		8B4BDABE12012CEF009C7060 /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B4BDAA7120124EA009C7060 /* libcrypto.dylib */; };
 		8B4BDAC512012D05009C7060 /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B4BDAA7120124EA009C7060 /* libcrypto.dylib */; };
 		8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; };
+		D244536A12426F00009BBCE0 /* logging.cc in Sources */ = {isa = PBXBuildFile; fileRef = D244535112426EBB009BBCE0 /* logging.cc */; };
+		D244536B12426F00009BBCE0 /* minidump.cc in Sources */ = {isa = PBXBuildFile; fileRef = D244535212426EBB009BBCE0 /* minidump.cc */; };
+		D244536C12426F00009BBCE0 /* pathname_stripper.cc in Sources */ = {isa = PBXBuildFile; fileRef = D244535312426EBB009BBCE0 /* pathname_stripper.cc */; };
+		D244536D12426F00009BBCE0 /* basic_code_modules.cc in Sources */ = {isa = PBXBuildFile; fileRef = D244534F12426E98009BBCE0 /* basic_code_modules.cc */; };
+		D244540B12439BA0009BBCE0 /* memory_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = D244540A12439BA0009BBCE0 /* memory_unittest.cc */; };
 		D24BBBFD121050F000F3D417 /* breakpadUtilities.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = F92C563C0ECD10B3009BE4BA /* breakpadUtilities.dylib */; };
 		D24BBD291211EDB100F3D417 /* MachIPC.mm in Sources */ = {isa = PBXBuildFile; fileRef = F92C53790ECCE635009BE4BA /* MachIPC.mm */; };
 		D24BBD321212CACF00F3D417 /* MachIPC.mm in Sources */ = {isa = PBXBuildFile; fileRef = F92C53790ECCE635009BE4BA /* MachIPC.mm */; };
 		D2A5DD301188633800081F03 /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53690ECCE3FD009BE4BA /* breakpad_nlist_64.cc */; };
 		D2A5DD401188640400081F03 /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53690ECCE3FD009BE4BA /* breakpad_nlist_64.cc */; };
 		D2A5DD411188642E00081F03 /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53690ECCE3FD009BE4BA /* breakpad_nlist_64.cc */; };
 		D2F9A3D51212F87C002747C1 /* exception_handler_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F9A3D41212F87C002747C1 /* exception_handler_test.cc */; };
 		D2F9A43D12131F55002747C1 /* gmock-all.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F9A43C12131F55002747C1 /* gmock-all.cc */; };
@@ -502,16 +507,21 @@
 		8B31007011F0CD3C00FCF3E4 /* GTMDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GTMDefines.h; path = ../../common/mac/GTMDefines.h; sourceTree = SOURCE_ROOT; };
 		8B3101E911F0CDE300FCF3E4 /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; };
 		8B31022211F0CE1000FCF3E4 /* GTMGarbageCollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GTMGarbageCollection.h; path = ../../common/mac/GTMGarbageCollection.h; sourceTree = SOURCE_ROOT; };
 		8B31027711F0D3AF00FCF3E4 /* BreakpadDebug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = BreakpadDebug.xcconfig; path = ../../common/mac/BreakpadDebug.xcconfig; sourceTree = SOURCE_ROOT; };
 		8B31027811F0D3AF00FCF3E4 /* BreakpadRelease.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = BreakpadRelease.xcconfig; path = ../../common/mac/BreakpadRelease.xcconfig; sourceTree = SOURCE_ROOT; };
 		8B31FFF611F0C90500FCF3E4 /* Breakpad.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Breakpad.xcconfig; path = ../../common/mac/Breakpad.xcconfig; sourceTree = SOURCE_ROOT; };
 		8B4BDAA7120124EA009C7060 /* libcrypto.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcrypto.dylib; path = usr/lib/libcrypto.dylib; sourceTree = SDKROOT; };
 		8DC2EF5B0486A6940098B216 /* Breakpad.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Breakpad.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		D244534F12426E98009BBCE0 /* basic_code_modules.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = basic_code_modules.cc; path = ../../processor/basic_code_modules.cc; sourceTree = SOURCE_ROOT; };
+		D244535112426EBB009BBCE0 /* logging.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = logging.cc; path = ../../processor/logging.cc; sourceTree = SOURCE_ROOT; };
+		D244535212426EBB009BBCE0 /* minidump.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = minidump.cc; path = ../../processor/minidump.cc; sourceTree = SOURCE_ROOT; };
+		D244535312426EBB009BBCE0 /* pathname_stripper.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = pathname_stripper.cc; path = ../../processor/pathname_stripper.cc; sourceTree = SOURCE_ROOT; };
+		D244540A12439BA0009BBCE0 /* memory_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = memory_unittest.cc; path = ../../common/memory_unittest.cc; sourceTree = SOURCE_ROOT; };
 		D2F9A3D41212F87C002747C1 /* exception_handler_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = exception_handler_test.cc; path = tests/exception_handler_test.cc; sourceTree = "<group>"; };
 		D2F9A41512131EF0002747C1 /* libgtest.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libgtest.a; sourceTree = BUILT_PRODUCTS_DIR; };
 		D2F9A43C12131F55002747C1 /* gmock-all.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "gmock-all.cc"; path = "../../testing/src/gmock-all.cc"; sourceTree = SOURCE_ROOT; };
 		D2F9A43E12131F65002747C1 /* gtest_main.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = gtest_main.cc; path = ../../testing/gtest/src/gtest_main.cc; sourceTree = "<group>"; };
 		D2F9A43F12131F65002747C1 /* gtest-all.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "gtest-all.cc"; path = "../../testing/gtest/src/gtest-all.cc"; sourceTree = "<group>"; };
 		D2F9A4C4121336C7002747C1 /* client_info.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = client_info.h; path = crash_generation/client_info.h; sourceTree = "<group>"; };
 		D2F9A4C5121336C7002747C1 /* crash_generation_client.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = crash_generation_client.h; path = crash_generation/crash_generation_client.h; sourceTree = "<group>"; };
 		D2F9A4C6121336C7002747C1 /* crash_generation_client.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = crash_generation_client.cc; path = crash_generation/crash_generation_client.cc; sourceTree = "<group>"; };
@@ -754,16 +764,17 @@
 				D2F9A43812131F3B002747C1 /* gtest */,
 				8B31FFF611F0C90500FCF3E4 /* Breakpad.xcconfig */,
 				8B31027711F0D3AF00FCF3E4 /* BreakpadDebug.xcconfig */,
 				8B31027811F0D3AF00FCF3E4 /* BreakpadRelease.xcconfig */,
 				F95BB8A3101F94C300AA053B /* Tools */,
 				32DBCF5E0370ADEE00C91783 /* Breakpad_Prefix.pch */,
 				F92C538D0ECCE6F2009BE4BA /* client */,
 				F92C53600ECCE3D6009BE4BA /* common */,
+				D244536912426EE7009BBCE0 /* processor */,
 				0867D69AFE84028FC02AAC07 /* Frameworks */,
 				034768DFFF38A50411DB9C8B /* Products */,
 				F9C77DDB0F7DD5CF0045F7DB /* UnitTests-Info.plist */,
 			);
 			name = Breakpad;
 			sourceTree = "<group>";
 		};
 		0867D69AFE84028FC02AAC07 /* Frameworks */ = {
@@ -775,16 +786,27 @@
 				F92C554A0ECCF530009BE4BA /* Carbon.framework */,
 				1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */,
 				0867D6A5FE840307C02AAC07 /* AppKit.framework */,
 				0867D69BFE84028FC02AAC07 /* Foundation.framework */,
 			);
 			name = Frameworks;
 			sourceTree = "<group>";
 		};
+		D244536912426EE7009BBCE0 /* processor */ = {
+			isa = PBXGroup;
+			children = (
+				D244535112426EBB009BBCE0 /* logging.cc */,
+				D244535212426EBB009BBCE0 /* minidump.cc */,
+				D244535312426EBB009BBCE0 /* pathname_stripper.cc */,
+				D244534F12426E98009BBCE0 /* basic_code_modules.cc */,
+			);
+			name = processor;
+			sourceTree = "<group>";
+		};
 		D2F9A43812131F3B002747C1 /* gtest */ = {
 			isa = PBXGroup;
 			children = (
 				D2F9A43E12131F65002747C1 /* gtest_main.cc */,
 				D2F9A43F12131F65002747C1 /* gtest-all.cc */,
 				D2F9A43C12131F55002747C1 /* gmock-all.cc */,
 			);
 			name = gtest;
@@ -808,16 +830,17 @@
 				F92C53730ECCE3FD009BE4BA /* protected_memory_allocator.h */,
 			);
 			name = handler;
 			sourceTree = "<group>";
 		};
 		F92C53600ECCE3D6009BE4BA /* common */ = {
 			isa = PBXGroup;
 			children = (
+				D244540A12439BA0009BBCE0 /* memory_unittest.cc */,
 				F92C53870ECCE6C0009BE4BA /* convert_UTF.c */,
 				F92C53880ECCE6C0009BE4BA /* convert_UTF.h */,
 				F92C53850ECCE6AD009BE4BA /* string_conversion.cc */,
 				F92C53860ECCE6AD009BE4BA /* string_conversion.h */,
 				F92C53840ECCE68D009BE4BA /* mac */,
 			);
 			name = common;
 			sourceTree = "<group>";
@@ -1627,32 +1650,37 @@
 				F93DE2D80F82A70E00608B94 /* minidump_file_writer_unittest.cc in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 		F93DE3290F82C55600608B94 /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				D244536A12426F00009BBCE0 /* logging.cc in Sources */,
+				D244536B12426F00009BBCE0 /* minidump.cc in Sources */,
+				D244536C12426F00009BBCE0 /* pathname_stripper.cc in Sources */,
+				D244536D12426F00009BBCE0 /* basic_code_modules.cc in Sources */,
 				D2F9A4E112133AE2002747C1 /* crash_generation_client.cc in Sources */,
 				D2F9A4E212133AE2002747C1 /* crash_generation_server.cc in Sources */,
 				D24BBD321212CACF00F3D417 /* MachIPC.mm in Sources */,
 				D2A5DD411188642E00081F03 /* breakpad_nlist_64.cc in Sources */,
 				F93DE3350F82C66B00608B94 /* dynamic_images.cc in Sources */,
 				F93DE3360F82C66B00608B94 /* exception_handler.cc in Sources */,
 				F93DE3370F82C66B00608B94 /* minidump_generator.cc in Sources */,
 				F93DE3380F82C66B00608B94 /* minidump_file_writer.cc in Sources */,
 				F93DE3390F82C66B00608B94 /* convert_UTF.c in Sources */,
 				F93DE33A0F82C66B00608B94 /* string_conversion.cc in Sources */,
 				F93DE33B0F82C66B00608B94 /* file_id.cc in Sources */,
 				F93DE33C0F82C66B00608B94 /* macho_id.cc in Sources */,
 				F93DE33D0F82C66B00608B94 /* macho_utilities.cc in Sources */,
 				F93DE33E0F82C66B00608B94 /* macho_walker.cc in Sources */,
 				F93DE33F0F82C66B00608B94 /* string_utilities.cc in Sources */,
 				D2F9A3D51212F87C002747C1 /* exception_handler_test.cc in Sources */,
+				D244540B12439BA0009BBCE0 /* memory_unittest.cc in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 		F9C44DA20EF060A8003AEBAA /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
 				F9C44DB20EF07288003AEBAA /* Controller.m in Sources */,
@@ -2095,18 +2123,20 @@
 			};
 			name = Release;
 		};
 		F93DE32E0F82C55700608B94 /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
 				DEBUG_INFORMATION_FORMAT = dwarf;
 				GCC_INLINES_ARE_PRIVATE_EXTERN = NO;
+				GCC_PREPROCESSOR_DEFINITIONS = "BP_LOGGING_INCLUDE=\\\"client/mac/tests/testlogging.h\\\"";
 				GCC_SYMBOLS_PRIVATE_EXTERN = NO;
 				HEADER_SEARCH_PATHS = (
+					../../..,
 					../..,
 					../../testing,
 					../../testing/include,
 					../../testing/gtest,
 					../../testing/gtest/include,
 				);
 				LIBRARY_SEARCH_PATHS = (
 					"$(inherited)",
--- a/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.cc
+++ b/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.cc
@@ -22,16 +22,17 @@
 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+#include <algorithm>
 #include <cstdio>
 
 #include <mach/host_info.h>
 #include <mach/mach_vm.h>
 #include <mach/vm_statistics.h>
 #include <mach-o/dyld.h>
 #include <mach-o/loader.h>
 #include <sys/sysctl.h>
@@ -59,32 +60,34 @@ namespace google_breakpad {
 MinidumpGenerator::MinidumpGenerator()
     : writer_(),
       exception_type_(0),
       exception_code_(0),
       exception_subcode_(0),
       exception_thread_(0),
       crashing_task_(mach_task_self()),
       handler_thread_(mach_thread_self()),
-      dynamic_images_(NULL) {
+      dynamic_images_(NULL),
+      memory_blocks_(&allocator_) {
   GatherSystemInformation();
 }
 
 // constructor when generating from a different process than the
 // crashed process
 MinidumpGenerator::MinidumpGenerator(mach_port_t crashing_task,
                                      mach_port_t handler_thread)
     : writer_(),
       exception_type_(0),
       exception_code_(0),
       exception_subcode_(0),
       exception_thread_(0),
       crashing_task_(crashing_task),
       handler_thread_(handler_thread),
-      dynamic_images_(NULL) {
+      dynamic_images_(NULL),
+      memory_blocks_(&allocator_) {
   if (crashing_task != mach_task_self()) {
     dynamic_images_ = new DynamicImages(crashing_task_);
   } else {
     dynamic_images_ = NULL;
   }
 
   GatherSystemInformation();
 }
@@ -168,16 +171,17 @@ string MinidumpGenerator::UniqueNameInDi
     *unique_name = file_name;
 
   return path;
 }
 
 bool MinidumpGenerator::Write(const char *path) {
   WriteStreamFN writers[] = {
     &MinidumpGenerator::WriteThreadListStream,
+    &MinidumpGenerator::WriteMemoryListStream,
     &MinidumpGenerator::WriteSystemInfoStream,
     &MinidumpGenerator::WriteModuleListStream,
     &MinidumpGenerator::WriteMiscInfoStream,
     &MinidumpGenerator::WriteBreakpadInfoStream,
     // Exception stream needs to be the last entry in this array as it may
     // be omitted in the case where the minidump is written without an
     // exception.
     &MinidumpGenerator::WriteExceptionStream,
@@ -509,16 +513,18 @@ bool MinidumpGenerator::WriteThreadStrea
       = static_cast<mach_msg_type_number_t>(sizeof(state));
 
   if (thread_get_state(thread_id, BREAKPAD_MACHINE_THREAD_STATE,
                        state, &state_count) ==
       KERN_SUCCESS) {
     if (!WriteStack(state, &thread->stack))
       return false;
 
+    memory_blocks_.push_back(thread->stack);
+
     if (!WriteContext(state, &thread->thread_context))
       return false;
 
     thread->thread_id = thread_id;
   } else {
     return false;
   }
 
@@ -561,16 +567,128 @@ bool MinidumpGenerator::WriteThreadListS
 
       list.CopyIndexAfterObject(thread_idx++, &thread, sizeof(MDRawThread));
     }
   }
 
   return true;
 }
 
+bool MinidumpGenerator::WriteMemoryListStream(
+    MDRawDirectory *memory_list_stream) {
+  TypedMDRVA<MDRawMemoryList> list(&writer_);
+
+  // If the dump has an exception, include some memory around the
+  // instruction pointer.
+  const size_t kIPMemorySize = 256;  // bytes
+  bool have_ip_memory = false;
+  MDMemoryDescriptor ip_memory_d;
+  if (exception_thread_ && exception_type_) {
+    breakpad_thread_state_data_t state;
+    mach_msg_type_number_t stateCount
+      = static_cast<mach_msg_type_number_t>(sizeof(state));
+
+    if (thread_get_state(exception_thread_,
+                         BREAKPAD_MACHINE_THREAD_STATE,
+                         state,
+                         &stateCount) == KERN_SUCCESS) {
+      u_int64_t ip = CurrentPCForStack(state);
+      // Bound it to the upper and lower bounds of the region
+      // it's contained within. If it's not in a known memory region,
+      // don't bother trying to write it.
+      mach_vm_address_t addr = ip;
+      mach_vm_size_t size;
+      natural_t nesting_level = 0;
+      vm_region_submap_info_64 info;
+      mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT_64;
+
+      kern_return_t ret =
+        mach_vm_region_recurse(crashing_task_,
+                               &addr,
+                               &size,
+                               &nesting_level,
+                               (vm_region_recurse_info_t)&info,
+                               &info_count);
+      if (ret == KERN_SUCCESS && ip >= addr && ip < (addr + size)) {
+        // Try to get 128 bytes before and after the IP, but
+        // settle for whatever's available.
+        ip_memory_d.start_of_memory_range =
+          std::max(uintptr_t(addr),
+                   uintptr_t(ip - (kIPMemorySize / 2)));
+        uintptr_t end_of_range = 
+          std::min(uintptr_t(ip + (kIPMemorySize / 2)),
+                   uintptr_t(addr + size));
+        ip_memory_d.memory.data_size =
+          end_of_range - ip_memory_d.start_of_memory_range;
+        have_ip_memory = true;
+        // This needs to get appended to the list even though
+        // the memory bytes aren't filled in yet so the entire
+        // list can be written first. The memory bytes will get filled
+        // in after the memory list is written.
+        memory_blocks_.push_back(ip_memory_d);
+      }
+    }
+  }
+
+  // Now fill in the memory list and write it.
+  unsigned memory_count = memory_blocks_.size();
+  if (!list.AllocateObjectAndArray(memory_count,
+                                   sizeof(MDMemoryDescriptor)))
+    return false;
+
+  memory_list_stream->stream_type = MD_MEMORY_LIST_STREAM;
+  memory_list_stream->location = list.location();
+
+  list.get()->number_of_memory_ranges = memory_count;
+
+  unsigned int i;
+  for (i = 0; i < memory_count; ++i) {
+    list.CopyIndexAfterObject(i++, &memory_blocks_[i],
+                              sizeof(MDMemoryDescriptor));
+  }
+
+  if (have_ip_memory) {
+    // Now read the memory around the instruction pointer.
+    UntypedMDRVA ip_memory(&writer_);
+    if (!ip_memory.Allocate(ip_memory_d.memory.data_size))
+      return false;
+
+    if (dynamic_images_) {
+      // Out-of-process.
+      kern_return_t kr;
+
+      void *memory =
+        ReadTaskMemory(
+          crashing_task_,
+          reinterpret_cast<const void *>(ip_memory_d.start_of_memory_range),
+          ip_memory_d.memory.data_size,
+          &kr);
+
+      if (memory == NULL) {
+        return false;
+      }
+
+      ip_memory.Copy(memory, ip_memory_d.memory.data_size);
+      free(memory);
+    } else {
+      // In-process, just copy from local memory.
+      ip_memory.Copy(
+        reinterpret_cast<const void *>(ip_memory_d.start_of_memory_range),
+        ip_memory_d.memory.data_size);
+    }
+
+    ip_memory_d.memory = ip_memory.location();
+    // Write this again now that the data location is filled in.
+    list.CopyIndexAfterObject(i - 1, &ip_memory_d,
+                              sizeof(MDMemoryDescriptor));
+  }
+
+  return true;
+}
+
 bool
 MinidumpGenerator::WriteExceptionStream(MDRawDirectory *exception_stream) {
   TypedMDRVA<MDRawExceptionStream> exception(&writer_);
 
   if (!exception.Allocate())
     return false;
 
   exception_stream->stream_type = MD_EXCEPTION_STREAM;
--- a/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.h
+++ b/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.h
@@ -32,18 +32,19 @@
 #ifndef CLIENT_MAC_GENERATOR_MINIDUMP_GENERATOR_H__
 #define CLIENT_MAC_GENERATOR_MINIDUMP_GENERATOR_H__
 
 #include <mach/mach.h>
 
 #include <string>
 
 #include "client/minidump_file_writer.h"
+#include "common/memory.h"
+#include "common/mac/macho_utilities.h"
 #include "google_breakpad/common/minidump_format.h"
-#include "common/mac/macho_utilities.h"
 
 #include "dynamic_images.h"
 
 namespace google_breakpad {
 
 using std::string;
 
 #if TARGET_CPU_X86_64 || TARGET_CPU_PPC64
@@ -114,16 +115,17 @@ class MinidumpGenerator {
   // the MinidumpGenerator class.
   static void GatherSystemInformation();
 
  private:
     typedef bool (MinidumpGenerator::*WriteStreamFN)(MDRawDirectory *);
 
   // Stream writers
   bool WriteThreadListStream(MDRawDirectory *thread_list_stream);
+  bool WriteMemoryListStream(MDRawDirectory *memory_list_stream);
   bool WriteExceptionStream(MDRawDirectory *exception_stream);
   bool WriteSystemInfoStream(MDRawDirectory *system_info_stream);
   bool WriteModuleListStream(MDRawDirectory *module_list_stream);
   bool WriteMiscInfoStream(MDRawDirectory *misc_info_stream);
   bool WriteBreakpadInfoStream(MDRawDirectory *breakpad_info_stream);
 
   // Helpers
   u_int64_t CurrentPCForStack(breakpad_thread_state_data_t state);
@@ -160,13 +162,22 @@ class MinidumpGenerator {
   // System information
   static char build_string_[16];
   static int os_major_version_;
   static int os_minor_version_;
   static int os_build_number_;
   
   // Information about dynamically loaded code
   DynamicImages *dynamic_images_;
+
+  // PageAllocator makes it possible to allocate memory
+  // directly from the system, even while handling an exception.
+  mutable PageAllocator allocator_;
+
+  // Blocks of memory written to the dump. These are all currently
+  // written while writing the thread list stream, but saved here
+  // so a memory list stream can be written afterwards.
+  wasteful_vector<MDMemoryDescriptor> memory_blocks_;
 };
 
 }  // namespace google_breakpad
 
 #endif  // CLIENT_MAC_GENERATOR_MINIDUMP_GENERATOR_H__
--- a/toolkit/crashreporter/google-breakpad/src/client/mac/tests/exception_handler_test.cc
+++ b/toolkit/crashreporter/google-breakpad/src/client/mac/tests/exception_handler_test.cc
@@ -24,31 +24,45 @@
 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 // exception_handler_test.cc: Unit tests for google_breakpad::ExceptionHandler
 
+#include <sys/mman.h>
 #include <sys/stat.h>
 #include <unistd.h>
 
 #include "breakpad_googletest_includes.h"
 #include "client/mac/handler/exception_handler.h"
 #include "client/mac/tests/auto_tempdir.h"
 #include "common/mac/MachIPC.h"
+#include "google_breakpad/processor/minidump.h"
+
+namespace google_breakpad {
+// This acts as the log sink for INFO logging from the processor
+// logging code. The logging output confuses XCode and makes it think
+// there are unit test failures. testlogging.h handles the overriding.
+std::ostringstream info_log;
+}
 
 namespace {
 using std::string;
 using google_breakpad::AutoTempDir;
 using google_breakpad::ExceptionHandler;
 using google_breakpad::MachPortSender;
 using google_breakpad::MachReceiveMessage;
 using google_breakpad::MachSendMessage;
+using google_breakpad::Minidump;
+using google_breakpad::MinidumpContext;
+using google_breakpad::MinidumpException;
+using google_breakpad::MinidumpMemoryList;
+using google_breakpad::MinidumpMemoryRegion;
 using google_breakpad::ReceivePort;
 using testing::Test;
 
 class ExceptionHandlerTest : public Test {
  public:
   AutoTempDir tempDir;
   string lastDumpName;
 };
@@ -75,17 +89,16 @@ static bool MDCallback(const char *dump_
   (void)write(fd, path.c_str(), path.length() + 1);
   close(fd);
   exit(0);
   // not reached
   return true;
 }
 
 TEST_F(ExceptionHandlerTest, InProcess) {
-  AutoTempDir tempDir;
   // Give the child process a pipe to report back on.
   int fds[2];
   ASSERT_EQ(0, pipe(fds));
   // Fork off a child process so it can crash.
   pid_t pid = fork();
   if (pid == 0) {
     // In the child process.
     close(fds[0]);
@@ -162,18 +175,18 @@ TEST_F(ExceptionHandlerTest, DumpChildPr
   close(fds[1]);
 
   // Read the child's task and thread ports.
   MachReceiveMessage child_message;
   ASSERT_EQ(KERN_SUCCESS,
 	    parent_recv_port.WaitForMessage(&child_message, kTimeoutMs));
   mach_port_t child_task = child_message.GetTranslatedPort(0);
   mach_port_t child_thread = child_message.GetTranslatedPort(1);
-  ASSERT_NE(MACH_PORT_NULL, child_task);
-  ASSERT_NE(MACH_PORT_NULL, child_thread);
+  ASSERT_NE((mach_port_t)MACH_PORT_NULL, child_task);
+  ASSERT_NE((mach_port_t)MACH_PORT_NULL, child_thread);
 
   // Write a minidump of the child process.
   bool result = ExceptionHandler::WriteMinidumpForChild(child_task,
 							child_thread,
 							tempDir.path,
 							ChildMDCallback,
 							this);
   ASSERT_EQ(true, result);
@@ -190,9 +203,395 @@ TEST_F(ExceptionHandlerTest, DumpChildPr
 
   // Child process should have exited with a zero status.
   int ret;
   ASSERT_EQ(pid, waitpid(pid, &ret, 0));
   EXPECT_NE(0, WIFEXITED(ret));
   EXPECT_EQ(0, WEXITSTATUS(ret));
 }
 
+// Test that memory around the instruction pointer is written
+// to the dump as a MinidumpMemoryRegion.
+TEST_F(ExceptionHandlerTest, InstructionPointerMemory) {
+  // Give the child process a pipe to report back on.
+  int fds[2];
+  ASSERT_EQ(0, pipe(fds));
+
+  // These are defined here so the parent can use them to check the
+  // data from the minidump afterwards.
+  const u_int32_t kMemorySize = 256;  // bytes
+  const int kOffset = kMemorySize / 2;
+  // This crashes with SIGILL on x86/x86-64/arm.
+  const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
+
+  pid_t pid = fork();
+  if (pid == 0) {
+    close(fds[0]);
+    ExceptionHandler eh(tempDir.path, NULL, MDCallback, &fds[1], true, NULL);
+    // Get some executable memory.
+    char* memory =
+      reinterpret_cast<char*>(mmap(NULL,
+                                   kMemorySize,
+                                   PROT_READ | PROT_WRITE | PROT_EXEC,
+                                   MAP_PRIVATE | MAP_ANON,
+                                   -1,
+                                   0));
+    if (!memory)
+      exit(0);
+
+    // Write some instructions that will crash. Put them in the middle
+    // of the block of memory, because the minidump should contain 128
+    // bytes on either side of the instruction pointer.
+    memcpy(memory + kOffset, instructions, sizeof(instructions));
+    
+    // Now execute the instructions, which should crash.
+    typedef void (*void_function)(void);
+    void_function memory_function =
+      reinterpret_cast<void_function>(memory + kOffset);
+    memory_function();
+    // not reached
+    exit(1);
+  }
+  // In the parent process.
+  ASSERT_NE(-1, pid);
+  close(fds[1]);
+
+  // Wait for the background process to return the minidump file.
+  close(fds[1]);
+  char minidump_file[PATH_MAX];
+  ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file));
+  ASSERT_NE(0, nbytes);
+  // Ensure that minidump file exists and is > 0 bytes.
+  struct stat st;
+  ASSERT_EQ(0, stat(minidump_file, &st));
+  ASSERT_LT(0, st.st_size);
+
+  // Child process should have exited with a zero status.
+  int ret;
+  ASSERT_EQ(pid, waitpid(pid, &ret, 0));
+  EXPECT_NE(0, WIFEXITED(ret));
+  EXPECT_EQ(0, WEXITSTATUS(ret));
+
+  // Read the minidump. Locate the exception record and the
+  // memory list, and then ensure that there is a memory region
+  // in the memory list that covers the instruction pointer from
+  // the exception record.
+  Minidump minidump(minidump_file);
+  ASSERT_TRUE(minidump.Read());
+
+  MinidumpException* exception = minidump.GetException();
+  MinidumpMemoryList* memory_list = minidump.GetMemoryList();
+  ASSERT_TRUE(exception);
+  ASSERT_TRUE(memory_list);
+  ASSERT_NE((unsigned int)0, memory_list->region_count());
+
+  MinidumpContext* context = exception->GetContext();
+  ASSERT_TRUE(context);
+
+  u_int64_t instruction_pointer;
+  switch (context->GetContextCPU()) {
+  case MD_CONTEXT_X86:
+    instruction_pointer = context->GetContextX86()->eip;
+    break;
+  case MD_CONTEXT_AMD64:
+    instruction_pointer = context->GetContextAMD64()->rip;
+    break;
+  case MD_CONTEXT_ARM:
+    instruction_pointer = context->GetContextARM()->iregs[15];
+    break;
+  default:
+    FAIL() << "Unknown context CPU: " << context->GetContextCPU();
+    break;
+  }
+
+  MinidumpMemoryRegion* region =
+    memory_list->GetMemoryRegionForAddress(instruction_pointer);
+  EXPECT_TRUE(region);
+
+  EXPECT_EQ(kMemorySize, region->GetSize());
+  const u_int8_t* bytes = region->GetMemory();
+  ASSERT_TRUE(bytes);
+
+  u_int8_t prefix_bytes[kOffset];
+  u_int8_t suffix_bytes[kMemorySize - kOffset - sizeof(instructions)];
+  memset(prefix_bytes, 0, sizeof(prefix_bytes));
+  memset(suffix_bytes, 0, sizeof(suffix_bytes));
+  EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0);
+  EXPECT_TRUE(memcmp(bytes + kOffset, instructions, sizeof(instructions)) == 0);
+  EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions),
+                     suffix_bytes, sizeof(suffix_bytes)) == 0);
 }
+
+// Test that the memory region around the instruction pointer is
+// bounded correctly on the low end.
+TEST_F(ExceptionHandlerTest, InstructionPointerMemoryMinBound) {
+  // Give the child process a pipe to report back on.
+  int fds[2];
+  ASSERT_EQ(0, pipe(fds));
+
+  // These are defined here so the parent can use them to check the
+  // data from the minidump afterwards.
+  const u_int32_t kMemorySize = 256;  // bytes
+  const int kOffset = 0;
+  // This crashes with SIGILL on x86/x86-64/arm.
+  const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
+
+  pid_t pid = fork();
+  if (pid == 0) {
+    close(fds[0]);
+    ExceptionHandler eh(tempDir.path, NULL, MDCallback, &fds[1], true, NULL);
+    // Get some executable memory.
+    char* memory =
+      reinterpret_cast<char*>(mmap(NULL,
+                                   kMemorySize,
+                                   PROT_READ | PROT_WRITE | PROT_EXEC,
+                                   MAP_PRIVATE | MAP_ANON,
+                                   -1,
+                                   0));
+    if (!memory)
+      exit(0);
+
+    // Write some instructions that will crash. Put them at the start
+    // of the block of memory, to ensure that the memory bounding
+    // works properly.
+    memcpy(memory + kOffset, instructions, sizeof(instructions));
+    
+    // Now execute the instructions, which should crash.
+    typedef void (*void_function)(void);
+    void_function memory_function =
+      reinterpret_cast<void_function>(memory + kOffset);
+    memory_function();
+    // not reached
+    exit(1);
+  }
+  // In the parent process.
+  ASSERT_NE(-1, pid);
+  close(fds[1]);
+
+  // Wait for the background process to return the minidump file.
+  close(fds[1]);
+  char minidump_file[PATH_MAX];
+  ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file));
+  ASSERT_NE(0, nbytes);
+  // Ensure that minidump file exists and is > 0 bytes.
+  struct stat st;
+  ASSERT_EQ(0, stat(minidump_file, &st));
+  ASSERT_LT(0, st.st_size);
+
+  // Child process should have exited with a zero status.
+  int ret;
+  ASSERT_EQ(pid, waitpid(pid, &ret, 0));
+  EXPECT_NE(0, WIFEXITED(ret));
+  EXPECT_EQ(0, WEXITSTATUS(ret));
+
+  // Read the minidump. Locate the exception record and the
+  // memory list, and then ensure that there is a memory region
+  // in the memory list that covers the instruction pointer from
+  // the exception record.
+  Minidump minidump(minidump_file);
+  ASSERT_TRUE(minidump.Read());
+
+  MinidumpException* exception = minidump.GetException();
+  MinidumpMemoryList* memory_list = minidump.GetMemoryList();
+  ASSERT_TRUE(exception);
+  ASSERT_TRUE(memory_list);
+  ASSERT_NE((unsigned int)0, memory_list->region_count());
+
+  MinidumpContext* context = exception->GetContext();
+  ASSERT_TRUE(context);
+
+  u_int64_t instruction_pointer;
+  switch (context->GetContextCPU()) {
+  case MD_CONTEXT_X86:
+    instruction_pointer = context->GetContextX86()->eip;
+    break;
+  case MD_CONTEXT_AMD64:
+    instruction_pointer = context->GetContextAMD64()->rip;
+    break;
+  case MD_CONTEXT_ARM:
+    instruction_pointer = context->GetContextARM()->iregs[15];
+    break;
+  default:
+    FAIL() << "Unknown context CPU: " << context->GetContextCPU();
+    break;
+  }
+
+  MinidumpMemoryRegion* region =
+    memory_list->GetMemoryRegionForAddress(instruction_pointer);
+  EXPECT_TRUE(region);
+
+  EXPECT_EQ(kMemorySize / 2, region->GetSize());
+  const u_int8_t* bytes = region->GetMemory();
+  ASSERT_TRUE(bytes);
+
+  u_int8_t suffix_bytes[kMemorySize / 2 - sizeof(instructions)];
+  memset(suffix_bytes, 0, sizeof(suffix_bytes));
+  EXPECT_TRUE(memcmp(bytes + kOffset, instructions, sizeof(instructions)) == 0);
+  EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions),
+                     suffix_bytes, sizeof(suffix_bytes)) == 0);
+}
+
+// Test that the memory region around the instruction pointer is
+// bounded correctly on the high end.
+TEST_F(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) {
+  // Give the child process a pipe to report back on.
+  int fds[2];
+  ASSERT_EQ(0, pipe(fds));
+
+  // These are defined here so the parent can use them to check the
+  // data from the minidump afterwards.
+  // Use 4k here because the OS will hand out a single page even
+  // if a smaller size is requested, and this test wants to
+  // test the upper bound of the memory range.
+  const u_int32_t kMemorySize = 4096;  // bytes
+  // This crashes with SIGILL on x86/x86-64/arm.
+  const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
+  const int kOffset = kMemorySize - sizeof(instructions);
+
+  pid_t pid = fork();
+  if (pid == 0) {
+    close(fds[0]);
+    ExceptionHandler eh(tempDir.path, NULL, MDCallback, &fds[1], true, NULL);
+    // Get some executable memory.
+    char* memory =
+      reinterpret_cast<char*>(mmap(NULL,
+                                   kMemorySize,
+                                   PROT_READ | PROT_WRITE | PROT_EXEC,
+                                   MAP_PRIVATE | MAP_ANON,
+                                   -1,
+                                   0));
+    if (!memory)
+      exit(0);
+
+    // Write some instructions that will crash. Put them at the start
+    // of the block of memory, to ensure that the memory bounding
+    // works properly.
+    memcpy(memory + kOffset, instructions, sizeof(instructions));
+    
+    // Now execute the instructions, which should crash.
+    typedef void (*void_function)(void);
+    void_function memory_function =
+      reinterpret_cast<void_function>(memory + kOffset);
+    memory_function();
+    // not reached
+    exit(1);
+  }
+  // In the parent process.
+  ASSERT_NE(-1, pid);
+  close(fds[1]);
+
+  // Wait for the background process to return the minidump file.
+  close(fds[1]);
+  char minidump_file[PATH_MAX];
+  ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file));
+  ASSERT_NE(0, nbytes);
+  // Ensure that minidump file exists and is > 0 bytes.
+  struct stat st;
+  ASSERT_EQ(0, stat(minidump_file, &st));
+  ASSERT_LT(0, st.st_size);
+
+  // Child process should have exited with a zero status.
+  int ret;
+  ASSERT_EQ(pid, waitpid(pid, &ret, 0));
+  EXPECT_NE(0, WIFEXITED(ret));
+  EXPECT_EQ(0, WEXITSTATUS(ret));
+
+  // Read the minidump. Locate the exception record and the
+  // memory list, and then ensure that there is a memory region
+  // in the memory list that covers the instruction pointer from
+  // the exception record.
+  Minidump minidump(minidump_file);
+  ASSERT_TRUE(minidump.Read());
+
+  MinidumpException* exception = minidump.GetException();
+  MinidumpMemoryList* memory_list = minidump.GetMemoryList();
+  ASSERT_TRUE(exception);
+  ASSERT_TRUE(memory_list);
+  ASSERT_NE((unsigned int)0, memory_list->region_count());
+
+  MinidumpContext* context = exception->GetContext();
+  ASSERT_TRUE(context);
+
+  u_int64_t instruction_pointer;
+  switch (context->GetContextCPU()) {
+  case MD_CONTEXT_X86:
+    instruction_pointer = context->GetContextX86()->eip;
+    break;
+  case MD_CONTEXT_AMD64:
+    instruction_pointer = context->GetContextAMD64()->rip;
+    break;
+  case MD_CONTEXT_ARM:
+    instruction_pointer = context->GetContextARM()->iregs[15];
+    break;
+  default:
+    FAIL() << "Unknown context CPU: " << context->GetContextCPU();
+    break;
+  }
+
+  MinidumpMemoryRegion* region =
+    memory_list->GetMemoryRegionForAddress(instruction_pointer);
+  EXPECT_TRUE(region);
+
+  const size_t kPrefixSize = 128;  // bytes
+  EXPECT_EQ(kPrefixSize + sizeof(instructions), region->GetSize());
+  const u_int8_t* bytes = region->GetMemory();
+  ASSERT_TRUE(bytes);
+
+  u_int8_t prefix_bytes[kPrefixSize];
+  memset(prefix_bytes, 0, sizeof(prefix_bytes));
+  EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0);
+  EXPECT_TRUE(memcmp(bytes + kPrefixSize,
+                     instructions, sizeof(instructions)) == 0);
+}
+
+// Ensure that an extra memory block doesn't get added when the
+// instruction pointer is not in mapped memory.
+TEST_F(ExceptionHandlerTest, InstructionPointerMemoryNullPointer) {
+  // Give the child process a pipe to report back on.
+  int fds[2];
+  ASSERT_EQ(0, pipe(fds));
+
+  pid_t pid = fork();
+  if (pid == 0) {
+    close(fds[0]);
+    ExceptionHandler eh(tempDir.path, NULL, MDCallback, &fds[1], true, NULL);
+    // Try calling a NULL pointer.
+    typedef void (*void_function)(void);
+    void_function memory_function =
+      reinterpret_cast<void_function>(NULL);
+    memory_function();
+    // not reached
+    exit(1);
+  }
+  // In the parent process.
+  ASSERT_NE(-1, pid);
+  close(fds[1]);
+
+  // Wait for the background process to return the minidump file.
+  close(fds[1]);
+  char minidump_file[PATH_MAX];
+  ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file));
+  ASSERT_NE(0, nbytes);
+  // Ensure that minidump file exists and is > 0 bytes.
+  struct stat st;
+  ASSERT_EQ(0, stat(minidump_file, &st));
+  ASSERT_LT(0, st.st_size);
+
+  // Child process should have exited with a zero status.
+  int ret;
+  ASSERT_EQ(pid, waitpid(pid, &ret, 0));
+  EXPECT_NE(0, WIFEXITED(ret));
+  EXPECT_EQ(0, WEXITSTATUS(ret));
+
+  // Read the minidump. Locate the exception record and the
+  // memory list, and then ensure that there is only one memory region
+  // in the memory list (the thread memory from the single thread).
+  Minidump minidump(minidump_file);
+  ASSERT_TRUE(minidump.Read());
+
+  MinidumpException* exception = minidump.GetException();
+  MinidumpMemoryList* memory_list = minidump.GetMemoryList();
+  ASSERT_TRUE(exception);
+  ASSERT_TRUE(memory_list);
+  ASSERT_EQ((unsigned int)1, memory_list->region_count());
+}
+
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/client/mac/tests/testlogging.h
@@ -0,0 +1,9 @@
+// This file exists to override the processor logging for unit tests,
+// since it confuses XCode into thinking unit tests have failed.
+#include <sstream>
+
+namespace google_breakpad {
+extern std::ostringstream info_log;
+}
+
+#define BPLOG_INFO_STREAM google_breakpad::info_log
--- a/toolkit/crashreporter/google-breakpad/src/common/linux/memory_unittest.cc
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/memory_unittest.cc
@@ -22,17 +22,17 @@
 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-#include "common/linux/memory.h"
+#include "common/memory.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using namespace google_breakpad;
 
 namespace {
 typedef testing::Test PageAllocatorTest;
 }
 
@@ -69,16 +69,16 @@ typedef testing::Test WastefulVectorTest
 TEST(WastefulVectorTest, Setup) {
   PageAllocator allocator_;
   wasteful_vector<int> v(&allocator_);
   ASSERT_EQ(v.size(), 0u);
 }
 
 TEST(WastefulVectorTest, Simple) {
   PageAllocator allocator_;
-  wasteful_vector<int> v(&allocator_);
+  wasteful_vector<unsigned> v(&allocator_);
 
   for (unsigned i = 0; i < 256; ++i)
     v.push_back(i);
   ASSERT_EQ(v.size(), 256u);
   for (unsigned i = 0; i < 256; ++i)
     ASSERT_EQ(v[i], i);
 }
rename from toolkit/crashreporter/google-breakpad/src/common/linux/memory.h
rename to toolkit/crashreporter/google-breakpad/src/common/memory.h
--- a/toolkit/crashreporter/google-breakpad/src/common/linux/memory.h
+++ b/toolkit/crashreporter/google-breakpad/src/common/memory.h
@@ -22,25 +22,32 @@
 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-#ifndef CLIENT_LINUX_HANDLER_MEMORY_H_
-#define CLIENT_LINUX_HANDLER_MEMORY_H_
+#ifndef GOOGLE_BREAKPAD_COMMON_MEMORY_H_
+#define GOOGLE_BREAKPAD_COMMON_MEMORY_H_
 
 #include <stdint.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <sys/mman.h>
 
+#ifdef __APPLE__
+#define sys_mmap mmap
+#define sys_mmap2 mmap
+#define sys_munmap munmap
+#define MAP_ANONYMOUS MAP_ANON
+#else
 #include "common/linux/linux_syscall_support.h"
+#endif
 
 namespace google_breakpad {
 
 // This is very simple allocator which fetches pages from the kernel directly.
 // Thus, it can be used even when the heap may be corrupted.
 //
 // There is no free operation. The pages are only freed when the object is
 // destroyed.
@@ -191,9 +198,9 @@ class wasteful_vector {
 
 }  // namespace google_breakpad
 
 inline void* operator new(size_t nbytes,
                           google_breakpad::PageAllocator& allocator) {
    return allocator.Alloc(nbytes);
 }
 
-#endif  // CLIENT_LINUX_HANDLER_MEMORY_H_
+#endif  // GOOGLE_BREAKPAD_COMMON_MEMORY_H_
--- a/toolkit/mozapps/update/content/updates.js
+++ b/toolkit/mozapps/update/content/updates.js
@@ -1723,26 +1723,32 @@ var gFinishedPage = {
     // This prevents the user from switching back
     // to the Software Update dialog and clicking "Restart" or "Later"
     // when dealing with the "confirm close" prompts.
     // See bug #350299 for more details.
     gUpdates.wiz.getButton("finish").disabled = true;
     gUpdates.wiz.getButton("extra1").disabled = true;
 
     // Notify all windows that an application quit has been requested.
-    var os = CoC["@mozilla.org/observer-service;1"].
-             getService(CoI.nsIObserverService);
     var cancelQuit = CoC["@mozilla.org/supports-PRBool;1"].
                      createInstance(CoI.nsISupportsPRBool);
-    os.notifyObservers(cancelQuit, "quit-application-requested", "restart");
+    Services.obs.notifyObservers(cancelQuit, "quit-application-requested",
+                                 "restart");
 
     // Something aborted the quit process.
     if (cancelQuit.data)
       return;
 
+    // If already in safe mode restart in safe mode (bug 327119)
+    if (Services.appinfo.inSafeMode) {
+      let env = CoC["@mozilla.org/process/environment;1"].
+                getService(CoI.nsIEnvironment);
+      env.set("MOZ_SAFE_MODE_RESTART", "1");
+    }
+
     // Restart the application
     CoC["@mozilla.org/toolkit/app-startup;1"].getService(CoI.nsIAppStartup).
     quit(CoI.nsIAppStartup.eAttemptQuit | CoI.nsIAppStartup.eRestart);
   },
 
   /**
    * When the user clicks the "Restart Later" instead of the Restart Now" button
    * in the wizard after an update has been downloaded.
--- a/toolkit/mozapps/update/nsUpdateService.js
+++ b/toolkit/mozapps/update/nsUpdateService.js
@@ -46,16 +46,17 @@ Components.utils.import("resource://gre/
 Components.utils.import("resource://gre/modules/FileUtils.jsm");
 Components.utils.import("resource://gre/modules/AddonManager.jsm");
 Components.utils.import("resource://gre/modules/Services.jsm");
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cr = Components.results;
 
+const PREF_APP_UPDATE_ALTWINDOWTYPE       = "app.update.altwindowtype";
 const PREF_APP_UPDATE_AUTO                = "app.update.auto";
 const PREF_APP_UPDATE_BACKGROUND_INTERVAL = "app.update.download.backgroundInterval";
 const PREF_APP_UPDATE_BACKGROUNDERRORS    = "app.update.backgroundErrors";
 const PREF_APP_UPDATE_BACKGROUNDMAXERRORS = "app.update.backgroundMaxErrors";
 const PREF_APP_UPDATE_CERTS_BRANCH        = "app.update.certs.";
 const PREF_APP_UPDATE_CERT_CHECKATTRS     = "app.update.cert.checkAttributes";
 const PREF_APP_UPDATE_CERT_ERRORS         = "app.update.cert.errors";
 const PREF_APP_UPDATE_CERT_MAXERRORS      = "app.update.cert.maxErrors";
@@ -2774,42 +2775,48 @@ Downloader.prototype = {
  */
 function UpdatePrompt() {
 }
 UpdatePrompt.prototype = {
   /**
    * See nsIUpdateService.idl
    */
   checkForUpdates: function UP_checkForUpdates() {
+    if (this._getAltUpdateWindow())
+      return;
+
     this._showUI(null, URI_UPDATE_PROMPT_DIALOG, null, UPDATE_WINDOW_NAME,
                  null, null);
   },
 
   /**
    * See nsIUpdateService.idl
    */
   showUpdateAvailable: function UP_showUpdateAvailable(update) {
     if (getPref("getBoolPref", PREF_APP_UPDATE_SILENT, false) ||
-        this._getUpdateWindow())
+        this._getUpdateWindow() || this._getAltUpdateWindow())
       return;
 
     var stringsPrefix = "updateAvailable_" + update.type + ".";
     var title = gUpdateBundle.formatStringFromName(stringsPrefix + "title",
                                                    [update.name], 1);
     var text = gUpdateBundle.GetStringFromName(stringsPrefix + "text");
     var imageUrl = "";
     this._showUnobtrusiveUI(null, URI_UPDATE_PROMPT_DIALOG, null,
                            UPDATE_WINDOW_NAME, "updatesavailable", update,
                            title, text, imageUrl);
   },
 
   /**
    * See nsIUpdateService.idl
    */
   showUpdateDownloaded: function UP_showUpdateDownloaded(update, background) {
+    if (this._getAltUpdateWindow())
+      return;
+
     if (background) {
       if (getPref("getBoolPref", PREF_APP_UPDATE_SILENT, false))
         return;
 
       var stringsPrefix = "updateDownloaded_" + update.type + ".";
       var title = gUpdateBundle.formatStringFromName(stringsPrefix + "title",
                                                      [update.name], 1);
       var text = gUpdateBundle.GetStringFromName(stringsPrefix + "text");
@@ -2847,17 +2854,18 @@ UpdatePrompt.prototype = {
       Services.ww.openWindow(null, URI_UPDATE_PROMPT_DIALOG, null, openFeatures, arg);
     }
   },
 
   /**
    * See nsIUpdateService.idl
    */
   showUpdateError: function UP_showUpdateError(update) {
-    if (getPref("getBoolPref", PREF_APP_UPDATE_SILENT, false))
+    if (getPref("getBoolPref", PREF_APP_UPDATE_SILENT, false) ||
+        this._getAltUpdateWindow())
       return;
 
     // In some cases, we want to just show a simple alert dialog:
     if (update.state == STATE_FAILED && update.errorCode == WRITE_ERROR) {
       var title = gUpdateBundle.GetStringFromName("updaterIOErrorTitle");
       var text = gUpdateBundle.formatStringFromName("updaterIOErrorMsg",
                                                     [Services.appinfo.name,
                                                      Services.appinfo.name], 2);
@@ -2888,16 +2896,28 @@ UpdatePrompt.prototype = {
   /**
    * Returns the update window if present.
    */
   _getUpdateWindow: function UP__getUpdateWindow() {
     return Services.wm.getMostRecentWindow(UPDATE_WINDOW_NAME);
   },
 
   /**
+   * Returns an alternative update window if present. When a window with this
+   * windowtype is open the application update service won't open the normal
+   * application update user interface window.
+   */
+  _getAltUpdateWindow: function UP__getAltUpdateWindow() {
+    let windowType = getPref("getCharPref", PREF_APP_UPDATE_ALTWINDOWTYPE, null);
+    if (!windowType)
+      return null;
+    return Services.wm.getMostRecentWindow(windowType);
+  },
+
+  /**
    * Initiate a less obtrusive UI, starting with a non-modal notification alert
    * @param   parent
    *          A parent window, can be null
    * @param   uri
    *          The URI string of the dialog to show
    * @param   name
    *          The Window Name of the dialog to show, in case it is already open
    *          and can merely be focused
--- a/toolkit/xre/nsUpdateDriver.cpp
+++ b/toolkit/xre/nsUpdateDriver.cpp
@@ -44,16 +44,17 @@
 #include "nsXULAppAPI.h"
 #include "nsAppRunner.h"
 #include "nsILocalFile.h"
 #include "nsCOMPtr.h"
 #include "nsString.h"
 #include "nsPrintfCString.h"
 #include "prproces.h"
 #include "prlog.h"
+#include "prenv.h"
 #include "nsVersionComparator.h"
 
 #ifdef XP_MACOSX
 #include "nsILocalFileMac.h"
 #include "nsCommandLineServiceMac.h"
 #endif
 
 #if defined(XP_WIN)
@@ -468,16 +469,20 @@ ApplyUpdate(nsIFile *greDir, nsIFile *up
     for (int i = 1; i < appArgc; ++i)
       argv[4 + i] = appArgv[i];
     argv[4 + appArgc] = nsnull;
   } else {
     argv[3] = nsnull;
     argc = 3;
   }
 
+  if (gSafeMode) {
+    PR_SetEnv("MOZ_SAFE_MODE_RESTART=1");
+  }
+
   LOG(("spawning updater process [%s]\n", updaterPath.get()));
 
 #if defined(USE_EXECV)
   chdir(applyToDir.get());
   execv(updaterPath.get(), argv);
 #elif defined(XP_WIN)
   _wchdir(applyToDir.get());
 
--- a/widget/public/nsGUIEventIPC.h
+++ b/widget/public/nsGUIEventIPC.h
@@ -274,12 +274,30 @@ struct ParamTraits<nsSelectionEvent>
            ReadParam(aMsg, aIter, &aResult->mOffset) &&
            ReadParam(aMsg, aIter, &aResult->mLength) &&
            ReadParam(aMsg, aIter, &aResult->mReversed) &&
            ReadParam(aMsg, aIter, &aResult->mExpandToClusterBoundary) &&
            ReadParam(aMsg, aIter, &aResult->mSucceeded);
   }
 };
 
+template<>
+struct ParamTraits<nsIMEUpdatePreference>
+{
+  typedef nsIMEUpdatePreference paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.mWantUpdates);
+    WriteParam(aMsg, aParam.mWantHints);
+  }
+
+  static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
+  {
+    return ReadParam(aMsg, aIter, &aResult->mWantUpdates) &&
+           ReadParam(aMsg, aIter, &aResult->mWantHints);
+  }
+};
+
 } // namespace IPC
 
 #endif // nsGUIEventIPC_h__
 
--- a/widget/public/nsIWidget.h
+++ b/widget/public/nsIWidget.h
@@ -68,16 +68,21 @@ class   imgIContainer;
 class   gfxASurface;
 class   nsIContent;
 class   ViewWrapper;
 
 namespace mozilla {
 namespace layers {
 class LayerManager;
 }
+#ifdef MOZ_IPC
+namespace dom {
+class PBrowserChild;
+}
+#endif
 }
 
 /**
  * Callback function that processes events.
  *
  * The argument is actually a subtype (subclass) of nsEvent which carries
  * platform specific information about the event. Platform specific code
  * knows how to deal with it.
@@ -107,33 +112,40 @@ typedef nsEventStatus (* EVENT_CALLBACK)
 #define NS_NATIVE_PLUGIN_PORT_CG    101
 #endif
 #ifdef XP_WIN
 #define NS_NATIVE_TSF_THREAD_MGR       100
 #define NS_NATIVE_TSF_CATEGORY_MGR     101
 #define NS_NATIVE_TSF_DISPLAY_ATTR_MGR 102
 #endif
 
-// 36762512-d533-4884-9ac3-4ada8594146c
+// 8bd36c8c-8218-4859-bfbc-ca5d78b52f7d
 #define NS_IWIDGET_IID \
-  { 0x36762512, 0xd533, 0x4884, \
-    { 0x9a, 0xc3, 0x4a, 0xda, 0x85, 0x94, 0x14, 0x6c } }
+  { 0x8bd36c8c, 0x8218, 0x4859, \
+    { 0xbf, 0xbc, 0xca, 0x5d, 0x78, 0xb5, 0x2f, 0x7d } }
 
 /*
  * Window shadow styles
  * Also used for the -moz-window-shadow CSS property
  */
 
 #define NS_STYLE_WINDOW_SHADOW_NONE             0
 #define NS_STYLE_WINDOW_SHADOW_DEFAULT          1
 #define NS_STYLE_WINDOW_SHADOW_MENU             2
 #define NS_STYLE_WINDOW_SHADOW_TOOLTIP          3
 #define NS_STYLE_WINDOW_SHADOW_SHEET            4
 
 /**
+ * nsIWidget::OnIMEFocusChange should be called during blur,
+ * but other OnIME*Change methods should not be called
+ */
+#define NS_SUCCESS_IME_NO_UPDATES \
+    NS_ERROR_GENERATE_SUCCESS(NS_ERROR_MODULE_WIDGET, 1)
+
+/**
  * Cursor types.
  */
 
 enum nsCursor {   ///(normal cursor,       usually rendered as an arrow)
                 eCursor_standard, 
                   ///(system is busy,      usually rendered as a hourglass or watch)
                 eCursor_wait, 
                   ///(Selecting something, usually rendered as an IBeam)
@@ -180,20 +192,56 @@ enum nsCursor {   ///(normal cursor,    
 enum nsTopLevelWidgetZPlacement { // for PlaceBehind()
   eZPlacementBottom = 0,  // bottom of the window stack
   eZPlacementBelow,       // just below another widget
   eZPlacementTop          // top of the window stack
 };
 
 
 /**
+ * Preference for receiving IME updates
+ *
+ * If mWantUpdates is true, PuppetWidget will forward
+ * nsIWidget::OnIMETextChange and nsIWidget::OnIMESelectionChange to the chrome
+ * process. This incurs overhead from observers and IPDL. If the IME
+ * implementation on a particular platform doesn't care about OnIMETextChange
+ * and OnIMESelectionChange from content processes, they should set
+ * mWantUpdates to false to avoid these overheads.
+ *
+ * If mWantHints is true, PuppetWidget will forward the content of text fields
+ * to the chrome process to be cached. This way we return the cached content
+ * during query events. (see comments in bug 583976). This only makes sense
+ * for IME implementations that do use query events, otherwise there's a
+ * significant overhead. Platforms that don't use query events should set
+ * mWantHints to false.
+ */
+struct nsIMEUpdatePreference {
+
+  nsIMEUpdatePreference()
+    : mWantUpdates(PR_FALSE), mWantHints(PR_FALSE)
+  {
+  }
+  nsIMEUpdatePreference(PRBool aWantUpdates, PRBool aWantHints)
+    : mWantUpdates(aWantUpdates), mWantHints(aWantHints)
+  {
+  }
+  PRPackedBool mWantUpdates;
+  PRPackedBool mWantHints;
+};
+
+
+/**
  * The base class for all the widgets. It provides the interface for
  * all basic and necessary functionality.
  */
 class nsIWidget : public nsISupports {
+#ifdef MOZ_IPC
+  protected:
+    typedef mozilla::dom::PBrowserChild PBrowserChild;
+#endif
 
   public:
     typedef mozilla::layers::LayerManager LayerManager;
 
     NS_DECLARE_STATIC_IID_ACCESSOR(NS_IWIDGET_IID)
 
     nsIWidget()
       : mLastChild(nsnull)
@@ -1188,16 +1236,19 @@ class nsIWidget : public nsISupports {
     /*
      * An editable node (i.e. input/textarea/design mode document)
      *  is receiving or giving up focus
      * aFocus is true if node is receiving focus
      * aFocus is false if node is giving up focus (blur)
      *
      * If this returns NS_ERROR_*, OnIMETextChange and OnIMESelectionChange
      * and OnIMEFocusChange(PR_FALSE) will be never called.
+     *
+     * If this returns NS_SUCCESS_IME_NO_UPDATES, OnIMEFocusChange(PR_FALSE)
+     * will be called but OnIMETextChange and OnIMESelectionChange will NOT.
      */
     NS_IMETHOD OnIMEFocusChange(PRBool aFocus) = 0;
 
     /*
      * Text content of the focused node has changed
      * aStart is the starting offset of the change
      * aOldEnd is the ending offset of the change
      * aNewEnd is the caret offset after the change
@@ -1207,16 +1258,21 @@ class nsIWidget : public nsISupports {
                                PRUint32 aNewEnd) = 0;
 
     /*
      * Selection has changed in the focused node
      */
     NS_IMETHOD OnIMESelectionChange(void) = 0;
 
     /*
+     * Retrieves preference for IME updates
+     */
+    virtual nsIMEUpdatePreference GetIMEUpdatePreference() = 0;
+
+    /*
      * Call this method when a dialog is opened which has a default button.
      * The button's rectangle should be supplied in aButtonRect.
      */ 
     NS_IMETHOD OnDefaultButtonLoaded(const nsIntRect &aButtonRect) = 0;
 
     /**
      * Compute the overridden system mouse scroll speed on the root content of
      * web pages.  The widget may set the same value as aOriginalDelta.  E.g.,
@@ -1257,17 +1313,17 @@ class nsIWidget : public nsISupports {
      * be fed to it.  Currently used in content processes.  NULL is
      * returned if puppet widgets aren't supported in this build
      * config, on this platform, or for this process type.
      *
      * This function is called "Create" to match CreateInstance().
      * The returned widget must still be nsIWidget::Create()d.
      */
     static already_AddRefed<nsIWidget>
-    CreatePuppetWidget();
+    CreatePuppetWidget(PBrowserChild *aTabChild);
 #endif
 
     /**
      * Reparent this widget's native widget.
      * @param aNewParent the native widget of aNewParent is the new native
      *                   parent widget
      */
     NS_IMETHOD ReparentNativeWidget(nsIWidget* aNewParent) = 0;
--- a/widget/src/android/AndroidBridge.cpp
+++ b/widget/src/android/AndroidBridge.cpp
@@ -33,17 +33,16 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include <android/log.h>
 
 #ifdef MOZ_IPC
-#include "mozilla/dom/ContentChild.h"
 #include "nsXULAppAPI.h"
 #endif
 #include <pthread.h>
 #include <prthread.h>
 #include "nsXPCOMStrings.h"
 
 #include "AndroidBridge.h"
 #include "nsAppShell.h"
@@ -194,34 +193,23 @@ AndroidBridge::EnsureJNIThread()
 }
 
 void
 AndroidBridge::NotifyIME(int aType, int aState)
 {
     if (sBridge)
         JNI()->CallStaticVoidMethod(sBridge->mGeckoAppShellClass, 
                                     sBridge->jNotifyIME,  aType, aState);
-#ifdef MOZ_IPC
-    // It's possible that we are in chrome process
-    //  but sBridge is not initialized yet
-    else if (XRE_GetProcessType() == GeckoProcessType_Content)
-        mozilla::dom::ContentChild::GetSingleton()->SendNotifyIME(aType, aState);
-#endif
 }
 
 void
 AndroidBridge::NotifyIMEChange(const PRUnichar *aText, PRUint32 aTextLen,
                                int aStart, int aEnd, int aNewEnd)
 {
     if (!sBridge) {
-#ifdef MOZ_IPC
-        mozilla::dom::ContentChild::GetSingleton()->
-            SendNotifyIMEChange(nsAutoString(aText), aTextLen,
-                                aStart, aEnd, aNewEnd);
-#endif
         return;
     }
 
     jvalue args[4];
     AutoLocalJNIFrame jniFrame(1);
     args[0].l = JNI()->NewString(aText, aTextLen);
     args[1].i = aStart;
     args[2].i = aEnd;
--- a/widget/src/android/Makefile.in
+++ b/widget/src/android/Makefile.in
@@ -85,18 +85,16 @@ EXTRA_DSO_LDOPTS = \
 	$(QCMS_LIBS) \
 	$(NULL)
 
 
 EXTRA_DSO_LDOPTS += -L$(DIST)/lib
 
 EXPORTS = AndroidBridge.h AndroidJavaWrappers.h
 
-include $(topsrcdir)/config/config.mk
-include $(topsrcdir)/ipc/chromium/chromium-config.mk
 include $(topsrcdir)/config/rules.mk
 
 DEFINES += -D_IMPL_NS_WIDGET
 #DEFINES += -DDEBUG_WIDGETS
 
 LOCAL_INCLUDES += \
 	-I$(topsrcdir)/widget/src/xpwidgets \
 	-I$(topsrcdir)/dom/system/android \
--- a/widget/src/android/nsWindow.cpp
+++ b/widget/src/android/nsWindow.cpp
@@ -556,29 +556,27 @@ nsWindow::DispatchEvent(nsGUIEvent *aEve
 }
 
 nsEventStatus
 nsWindow::DispatchEvent(nsGUIEvent *aEvent)
 {
     if (mEventCallback) {
         nsEventStatus status = (*mEventCallback)(aEvent);
 
-        // Don't track composition if event was dispatched to remote child
-        if (status != nsEventStatus_eConsumeNoDefault)
-            switch (aEvent->message) {
-            case NS_COMPOSITION_START:
-                mIMEComposing = PR_TRUE;
-                break;
-            case NS_COMPOSITION_END:
-                mIMEComposing = PR_FALSE;
-                break;
-            case NS_TEXT_TEXT:
-                mIMEComposingText = static_cast<nsTextEvent*>(aEvent)->theText;
-                break;
-            }
+        switch (aEvent->message) {
+        case NS_COMPOSITION_START:
+            mIMEComposing = PR_TRUE;
+            break;
+        case NS_COMPOSITION_END:
+            mIMEComposing = PR_FALSE;
+            break;
+        case NS_TEXT_TEXT:
+            mIMEComposingText = static_cast<nsTextEvent*>(aEvent)->theText;
+            break;
+        }
         return status;
     }
     return nsEventStatus_eIgnore;
 }
 
 NS_IMETHODIMP
 nsWindow::SetWindowClass(const nsAString& xulWinType)
 {
@@ -1584,16 +1582,17 @@ nsWindow::ResetInputState()
     //ALOGIME("IME: ResetInputState: s=%d", aState);
 
     // Cancel composition on Gecko side
     if (mIMEComposing) {
         nsTextEvent textEvent(PR_TRUE, NS_TEXT_TEXT, this);
         InitEvent(textEvent, nsnull);
         textEvent.theText = mIMEComposingText;
         DispatchEvent(&textEvent);
+        mIMEComposingText.Truncate(0);
 
         nsCompositionEvent event(PR_TRUE, NS_COMPOSITION_END, this);
         InitEvent(event, nsnull);
         DispatchEvent(&event);
     }
 
     AndroidBridge::NotifyIME(AndroidBridge::NOTIFY_IME_RESETINPUTSTATE, 0);
     return NS_OK;
@@ -1621,16 +1620,17 @@ nsWindow::CancelIMEComposition()
 {
     ALOGIME("IME: CancelIMEComposition");
 
     // Cancel composition on Gecko side
     if (mIMEComposing) {
         nsTextEvent textEvent(PR_TRUE, NS_TEXT_TEXT, this);
         InitEvent(textEvent, nsnull);
         DispatchEvent(&textEvent);
+        mIMEComposingText.Truncate(0);
 
         nsCompositionEvent compEvent(PR_TRUE, NS_COMPOSITION_END, this);
         InitEvent(compEvent, nsnull);
         DispatchEvent(&compEvent);
     }
 
     AndroidBridge::NotifyIME(AndroidBridge::NOTIFY_IME_CANCELCOMPOSITION, 0);
     return NS_OK;
@@ -1687,8 +1687,14 @@ nsWindow::OnIMESelectionChange(void)
         return NS_OK;
 
     AndroidBridge::NotifyIMEChange(nsnull, 0, int(event.mReply.mOffset),
                                    int(event.mReply.mOffset + 
                                        event.mReply.mString.Length()), -1);
     return NS_OK;
 }
 
+nsIMEUpdatePreference
+nsWindow::GetIMEUpdatePreference()
+{
+    return nsIMEUpdatePreference(PR_TRUE, PR_TRUE);
+}
+
--- a/widget/src/android/nsWindow.h
+++ b/widget/src/android/nsWindow.h
@@ -154,16 +154,17 @@ public:
     NS_IMETHOD ResetInputState();
     NS_IMETHOD SetIMEEnabled(PRUint32 aState);
     NS_IMETHOD GetIMEEnabled(PRUint32* aState);
     NS_IMETHOD CancelIMEComposition();
 
     NS_IMETHOD OnIMEFocusChange(PRBool aFocus);
     NS_IMETHOD OnIMETextChange(PRUint32 aStart, PRUint32 aOldEnd, PRUint32 aNewEnd);
     NS_IMETHOD OnIMESelectionChange(void);
+    virtual nsIMEUpdatePreference GetIMEUpdatePreference();
 
     LayerManager* GetLayerManager();
     gfxASurface* GetThebesSurface();
 
     NS_IMETHOD ReparentNativeWidget(nsIWidget* aNewParent);
 protected:
     void BringToFront();
     nsWindow *FindTopLevel();
--- a/widget/src/windows/nsWindow.cpp
+++ b/widget/src/windows/nsWindow.cpp
@@ -6869,16 +6869,30 @@ nsWindow::ConfigureChildren(const nsTArr
     nsIntRect bounds;
     w->GetBounds(bounds);
     if (bounds.Size() != configuration.mBounds.Size()) {
       w->Resize(configuration.mBounds.x, configuration.mBounds.y,
                 configuration.mBounds.width, configuration.mBounds.height,
                 PR_TRUE);
     } else if (bounds.TopLeft() != configuration.mBounds.TopLeft()) {
       w->Move(configuration.mBounds.x, configuration.mBounds.y);
+
+
+      if (gfxWindowsPlatform::GetPlatform()->GetRenderMode() ==
+          gfxWindowsPlatform::RENDER_DIRECT2D ||
+          GetLayerManager()->GetBackendType() != LayerManager::LAYERS_BASIC) {
+        // XXX - Workaround for Bug 587508. This will invalidate the part of the
+        // plugin window that might be touched by moving content somehow. The
+        // underlying problem should be found and fixed!
+        nsIntRegion r;
+        r.Sub(bounds, configuration.mBounds);
+        r.MoveBy(-bounds.x,
+                 -bounds.y);
+        w->Invalidate(r.GetBounds(), PR_FALSE);
+      }
     }
     rv = w->SetWindowClipRegion(configuration.mClipRegion, PR_FALSE);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   return NS_OK;
 }
 
 static HRGN
--- a/widget/src/xpwidgets/PuppetWidget.cpp
+++ b/widget/src/xpwidgets/PuppetWidget.cpp
@@ -33,53 +33,56 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
+#include "mozilla/dom/PBrowserChild.h"
 #include "BasicLayers.h"
 
 #include "gfxPlatform.h"
 #include "PuppetWidget.h"
 
 using namespace mozilla::layers;
 using namespace mozilla::widget;
+using namespace mozilla::dom;
 
 static void
 InvalidateRegion(nsIWidget* aWidget, const nsIntRegion& aRegion)
 {
   nsIntRegionRectIterator it(aRegion);
   while(const nsIntRect* r = it.Next()) {
     aWidget->Invalidate(*r, PR_FALSE/*async*/);
   }
 }
 
 /*static*/ already_AddRefed<nsIWidget>
-nsIWidget::CreatePuppetWidget()
+nsIWidget::CreatePuppetWidget(PBrowserChild *aTabChild)
 {
   NS_ABORT_IF_FALSE(nsIWidget::UsePuppetWidgets(),
                     "PuppetWidgets not allowed in this configuration");
 
-  nsCOMPtr<nsIWidget> widget = new PuppetWidget();
+  nsCOMPtr<nsIWidget> widget = new PuppetWidget(aTabChild);
   return widget.forget();
 }
 
 namespace mozilla {
 namespace widget {
 
 // Arbitrary, fungible.
 const size_t PuppetWidget::kMaxDimension = 4000;
 
 NS_IMPL_ISUPPORTS_INHERITED1(PuppetWidget, nsBaseWidget,
                              nsISupportsWeakReference)
 
-PuppetWidget::PuppetWidget()
+PuppetWidget::PuppetWidget(PBrowserChild *aTabChild)
+  : mTabChild(aTabChild)
 {
   MOZ_COUNT_CTOR(PuppetWidget);
 }
 
 PuppetWidget::~PuppetWidget()
 {
   MOZ_COUNT_DTOR(PuppetWidget);
 }
@@ -102,16 +105,19 @@ PuppetWidget::Create(nsIWidget        *a
   mBounds = aRect;
   mEnabled = PR_TRUE;
   mVisible = PR_TRUE;
 
   mSurface = gfxPlatform::GetPlatform()
              ->CreateOffscreenSurface(gfxIntSize(1, 1),
                                       gfxASurface::ContentFromFormat(gfxASurface::ImageFormatARGB32));
 
+  mIMEComposing = PR_FALSE;
+  mIMESuppressNotifySel = PR_FALSE;
+
   PuppetWidget* parent = static_cast<PuppetWidget*>(aParent);
   if (parent) {
     parent->SetChild(this);
     mLayerManager = parent->GetLayerManager();
   }
   else {
     Resize(mBounds.x, mBounds.y, mBounds.width, mBounds.height, PR_FALSE);
   }
@@ -125,32 +131,33 @@ PuppetWidget::CreateChild(const nsIntRec
                           nsIDeviceContext *aContext,
                           nsIAppShell      *aAppShell,
                           nsIToolkit       *aToolkit,
                           nsWidgetInitData *aInitData,
                           PRBool           aForceUseIWidgetParent)
 {
   bool isPopup = aInitData && aInitData->mWindowType == eWindowType_popup;
 
-  nsCOMPtr<nsIWidget> widget = nsIWidget::CreatePuppetWidget();
+  nsCOMPtr<nsIWidget> widget = nsIWidget::CreatePuppetWidget(mTabChild);
   return ((widget &&
            NS_SUCCEEDED(widget->Create(isPopup ? nsnull: this, nsnull, aRect,
                                        aHandleEventFunction,
                                        aContext, aAppShell, aToolkit,
                                        aInitData))) ?
           widget.forget() : nsnull);
 }
 
 NS_IMETHODIMP
 PuppetWidget::Destroy()
 {
   Base::Destroy();
   mPaintTask.Revoke();
   mChild = nsnull;
   mLayerManager = nsnull;
+  mTabChild = nsnull;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PuppetWidget::Show(PRBool aState)
 {
   NS_ASSERTION(mEnabled,
                "does it make sense to Show()/Hide() a disabled widget?");
@@ -238,30 +245,55 @@ PuppetWidget::Update()
   }
 
   if (mDirtyRegion.IsEmpty()) {
     return NS_OK;
   }
   return DispatchPaintEvent();
 }
 
+void
+PuppetWidget::InitEvent(nsGUIEvent& event, nsIntPoint* aPoint)
+{
+  if (nsnull == aPoint) {
+    event.refPoint.x = 0;
+    event.refPoint.y = 0;
+  }
+  else {
+    // use the point override if provided
+    event.refPoint.x = aPoint->x;
+    event.refPoint.y = aPoint->y;
+  }
+  event.time = PR_Now() / 1000;
+}
+
 NS_IMETHODIMP
 PuppetWidget::DispatchEvent(nsGUIEvent* event, nsEventStatus& aStatus)
 {
 #ifdef DEBUG
   debug_DumpEvent(stdout, event->widget, event,
                   nsCAutoString("PuppetWidget"), nsnull);
 #endif
 
   aStatus = nsEventStatus_eIgnore;
   if (mEventCallback) {
+    if (event->message == NS_COMPOSITION_START) {
+      mIMEComposing = PR_TRUE;
+    } else if (event->message == NS_SELECTION_SET) {
+      mIMESuppressNotifySel = PR_TRUE;
+    }
     aStatus = (*mEventCallback)(event);
-  }
 
-  if (mChild) {
+    if (event->message == NS_COMPOSITION_END) {
+      mIMEComposing = PR_FALSE;
+    } else if (event->message == NS_SELECTION_SET) {
+      mIMESuppressNotifySel = PR_FALSE;
+    }
+  } else if (mChild) {
+    event->widget = mChild;
     mChild->DispatchEvent(event, aStatus);
   }
 
   return NS_OK;
 }
 
 LayerManager*
 PuppetWidget::GetLayerManager()
@@ -274,16 +306,171 @@ PuppetWidget::GetLayerManager()
 
 gfxASurface*
 PuppetWidget::GetThebesSurface()
 {
   return mSurface;
 }
 
 nsresult
+PuppetWidget::IMEEndComposition(PRBool aCancel)
+{
+  if (!mIMEComposing)
+    return NS_OK;
+
+  nsEventStatus status;
+  nsTextEvent textEvent(PR_TRUE, NS_TEXT_TEXT, this);
+  InitEvent(textEvent, nsnull);
+  if (!mTabChild ||
+      !mTabChild->SendEndIMEComposition(aCancel, &textEvent.theText)) {
+    return NS_ERROR_FAILURE;
+  }
+  DispatchEvent(&textEvent, status);
+
+  nsCompositionEvent compEvent(PR_TRUE, NS_COMPOSITION_END, this);
+  InitEvent(compEvent, nsnull);
+  DispatchEvent(&compEvent, status);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PuppetWidget::ResetInputState()
+{
+  return IMEEndComposition(PR_FALSE);
+}
+
+NS_IMETHODIMP
+PuppetWidget::CancelComposition()
+{
+  return IMEEndComposition(PR_TRUE);
+}
+
+NS_IMETHODIMP
+PuppetWidget::SetIMEOpenState(PRBool aState)
+{
+  if (mTabChild &&
+      mTabChild->SendSetIMEOpenState(aState))
+    return NS_OK;
+  return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+PuppetWidget::SetIMEEnabled(PRUint32 aState)
+{
+  if (mTabChild &&
+      mTabChild->SendSetIMEEnabled(aState))
+    return NS_OK;
+  return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+PuppetWidget::GetIMEOpenState(PRBool *aState)
+{
+  if (mTabChild &&
+      mTabChild->SendGetIMEOpenState(aState))
+    return NS_OK;
+  return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+PuppetWidget::GetIMEEnabled(PRUint32 *aState)
+{
+  if (mTabChild &&
+      mTabChild->SendGetIMEEnabled(aState))
+    return NS_OK;
+  return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+PuppetWidget::OnIMEFocusChange(PRBool aFocus)
+{
+  if (!mTabChild)
+    return NS_ERROR_FAILURE;
+
+  if (aFocus) {
+    nsEventStatus status;
+    nsQueryContentEvent queryEvent(PR_TRUE, NS_QUERY_TEXT_CONTENT, this);
+    InitEvent(queryEvent, nsnull);
+    // Query entire content
+    queryEvent.InitForQueryTextContent(0, PR_UINT32_MAX);
+    DispatchEvent(&queryEvent, status);
+
+    if (queryEvent.mSucceeded) {
+      mTabChild->SendNotifyIMETextHint(queryEvent.mReply.mString);
+    }
+  } else {
+    // ResetInputState might not have been called yet
+    ResetInputState();
+  }
+
+  mIMEPreference.mWantUpdates = PR_FALSE;
+  mIMEPreference.mWantHints = PR_FALSE;
+  if (!mTabChild->SendNotifyIMEFocus(aFocus, &mIMEPreference))
+    return NS_ERROR_FAILURE;
+
+  if (aFocus) {
+    if (!mIMEPreference.mWantUpdates && !mIMEPreference.mWantHints)
+      // call OnIMEFocusChange on blur but no other updates
+      return NS_SUCCESS_IME_NO_UPDATES;
+    OnIMESelectionChange(); // Update selection
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PuppetWidget::OnIMETextChange(PRUint32 aStart, PRUint32 aEnd, PRUint32 aNewEnd)
+{
+  if (!mTabChild)
+    return NS_ERROR_FAILURE;
+
+  if (mIMEPreference.mWantHints) {
+    nsEventStatus status;
+    nsQueryContentEvent queryEvent(PR_TRUE, NS_QUERY_TEXT_CONTENT, this);
+    InitEvent(queryEvent, nsnull);
+    queryEvent.InitForQueryTextContent(0, PR_UINT32_MAX);
+    DispatchEvent(&queryEvent, status);
+
+    if (queryEvent.mSucceeded) {
+      mTabChild->SendNotifyIMETextHint(queryEvent.mReply.mString);
+    }
+  }
+  if (mIMEPreference.mWantUpdates) {
+    mTabChild->SendNotifyIMETextChange(aStart, aEnd, aNewEnd);
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PuppetWidget::OnIMESelectionChange(void)
+{
+  if (!mTabChild)
+    return NS_ERROR_FAILURE;
+
+  // When we send selection notifications during a composition or during a
+  // set selection event, there is a race condition where the notification
+  // arrives at chrome too late, which leads to chrome thinking the
+  // selection was elsewhere. Suppress notifications here to avoid that.
+  if (mIMEComposing || mIMESuppressNotifySel)
+    return NS_OK;
+
+  if (mIMEPreference.mWantUpdates) {
+    nsEventStatus status;
+    nsQueryContentEvent queryEvent(PR_TRUE, NS_QUERY_SELECTED_TEXT, this);
+    InitEvent(queryEvent, nsnull);
+    DispatchEvent(&queryEvent, status);
+
+    if (queryEvent.mSucceeded) {
+      mTabChild->SendNotifyIMESelection(queryEvent.GetSelectionStart(),
+                                        queryEvent.GetSelectionEnd());
+    }
+  }
+  return NS_OK;
+}
+
+nsresult
 PuppetWidget::DispatchPaintEvent()
 {
   NS_ABORT_IF_FALSE(!mDirtyRegion.IsEmpty(), "paint event logic messed up");
 
   nsIntRect dirtyRect = mDirtyRegion.GetBounds();
   nsPaintEvent event(PR_TRUE, NS_PAINT, this);
   event.refPoint.x = dirtyRect.x;
   event.refPoint.x = dirtyRect.y;
--- a/widget/src/xpwidgets/PuppetWidget.h
+++ b/widget/src/xpwidgets/PuppetWidget.h
@@ -60,17 +60,17 @@ namespace widget {
 class PuppetWidget : public nsBaseWidget, public nsSupportsWeakReference
 {
   typedef nsBaseWidget Base;
 
   // The width and height of the "widget" are clamped to this.
   static const size_t kMaxDimension;
 
 public:
-  PuppetWidget();
+  PuppetWidget(PBrowserChild *aTabChild);
   virtual ~PuppetWidget();
 
   NS_DECL_ISUPPORTS_INHERITED
 
   NS_IMETHOD Create(nsIWidget*        aParent,
                     nsNativeWidget    aNativeParent,
                     const nsIntRect&  aRect,
                     EVENT_CALLBACK    aHandleEventFunction,
@@ -146,54 +146,80 @@ public:
   // PuppetWidgets don't have any concept of titles. 
   NS_IMETHOD SetTitle(const nsAString& aTitle)
   { return NS_ERROR_UNEXPECTED; }
   
   // PuppetWidgets are always at <0, 0>.
   virtual nsIntPoint WidgetToScreenOffset()
   { return nsIntPoint(0, 0); }
 
+  void InitEvent(nsGUIEvent& event, nsIntPoint* aPoint = nsnull);
+
   NS_IMETHOD DispatchEvent(nsGUIEvent* event, nsEventStatus& aStatus);
 
   NS_IMETHOD CaptureRollupEvents(nsIRollupListener* aListener, nsIMenuRollup* aMenuRollup,
                                  PRBool aDoCapture, PRBool aConsumeRollupEvent)
   { return NS_ERROR_UNEXPECTED; }
 
   //
   // nsBaseWidget methods we override
   //
 
 //NS_IMETHOD              CaptureMouse(PRBool aCapture);
   virtual LayerManager*     GetLayerManager();
 //  virtual nsIDeviceContext* GetDeviceContext();
   virtual gfxASurface*      GetThebesSurface();
 
+  NS_IMETHOD ResetInputState();
+  NS_IMETHOD SetIMEOpenState(PRBool aState);
+  NS_IMETHOD GetIMEOpenState(PRBool *aState);
+  NS_IMETHOD SetIMEEnabled(PRUint32 aState);
+  NS_IMETHOD GetIMEEnabled(PRUint32 *aState);
+  NS_IMETHOD CancelComposition();
+  NS_IMETHOD OnIMEFocusChange(PRBool aFocus);
+  NS_IMETHOD OnIMETextChange(PRUint32 aOffset, PRUint32 aEnd,
+                             PRUint32 aNewEnd);
+  NS_IMETHOD OnIMESelectionChange(void);
+
 private:
   nsresult DispatchPaintEvent();
   nsresult DispatchResizeEvent();
 
   void SetChild(PuppetWidget* aChild);
 
+  nsresult IMEEndComposition(PRBool aCancel);
+
   class PaintTask : public nsRunnable {
   public:
     NS_DECL_NSIRUNNABLE
     PaintTask(PuppetWidget* widget) : mWidget(widget) {}
     void Revoke() { mWidget = nsnull; }
   private:
     PuppetWidget* mWidget;
   };
 
+  // TabChild normally holds a strong reference to this PuppetWidget
+  // or its root ancestor, but each PuppetWidget also needs a reference
+  // back to TabChild (e.g. to delegate nsIWidget IME calls to chrome)
+  // So we hold a weak reference to TabChild (PBrowserChild) here.
+  // Since it's possible for TabChild to outlive the PuppetWidget,
+  // we clear this weak reference in Destroy()
+  PBrowserChild *mTabChild;
   // The "widget" to which we delegate events if we don't have an
   // event handler.
   nsRefPtr<PuppetWidget> mChild;
   nsIntRegion mDirtyRegion;
   nsRevocableEventPtr<PaintTask> mPaintTask;
   PRPackedBool mEnabled;
   PRPackedBool mVisible;
   // XXX/cjones: keeping this around until we teach LayerManager to do
   // retained-content-only transactions
   nsRefPtr<gfxASurface> mSurface;
+  // IME
+  nsIMEUpdatePreference mIMEPreference;
+  PRPackedBool mIMEComposing;
+  PRPackedBool mIMESuppressNotifySel;
 };
 
 }  // namespace widget
 }  // namespace mozilla
 
 #endif  // mozilla_widget_PuppetWidget_h__
--- a/widget/src/xpwidgets/nsBaseWidget.h
+++ b/widget/src/xpwidgets/nsBaseWidget.h
@@ -146,16 +146,17 @@ public:
   NS_IMETHOD              CancelIMEComposition() { return NS_OK; }
   NS_IMETHOD              SetAcceleratedRendering(PRBool aEnabled);
   virtual PRBool          GetAcceleratedRendering();
   virtual PRBool          GetShouldAccelerate();
   NS_IMETHOD              GetToggledKeyState(PRUint32 aKeyCode, PRBool* aLEDState) { return NS_ERROR_NOT_IMPLEMENTED; }
   NS_IMETHOD              OnIMEFocusChange(PRBool aFocus) { return NS_ERROR_NOT_IMPLEMENTED; }
   NS_IMETHOD              OnIMETextChange(PRUint32 aStart, PRUint32 aOldEnd, PRUint32 aNewEnd) { return NS_ERROR_NOT_IMPLEMENTED; }
   NS_IMETHOD              OnIMESelectionChange(void) { return NS_ERROR_NOT_IMPLEMENTED; }
+  virtual nsIMEUpdatePreference GetIMEUpdatePreference() { return nsIMEUpdatePreference(PR_FALSE, PR_FALSE); }
   NS_IMETHOD              OnDefaultButtonLoaded(const nsIntRect &aButtonRect) { return NS_ERROR_NOT_IMPLEMENTED; }
   NS_IMETHOD              OverrideSystemMouseScrollSpeed(PRInt32 aOriginalDelta, PRBool aIsHorizontal, PRInt32 &aOverriddenDelta);
   virtual already_AddRefed<nsIWidget>
   CreateChild(const nsIntRect  &aRect,
               EVENT_CALLBACK   aHandleEventFunction,
               nsIDeviceContext *aContext,
               nsIAppShell      *aAppShell = nsnull,
               nsIToolkit       *aToolkit = nsnull,