Merge mozilla-central into tracemonkey.
authorChris Leary <cdleary@mozilla.com>
Tue, 04 Jan 2011 10:51:14 -0800
changeset 59997 132f6cac76248d0eca2708a5b7d78675e58acecc
parent 59996 61e3418e42236db1e305155c7907e08d4e79ce71 (current diff)
parent 59858 2aef39393f5b64b658f77eea8ddcea914bdb80a9 (diff)
child 59998 969691cfe40ea64f80aa37f0596b45f40b99204a
child 60141 22fc8e6cdc4655d0cc55641f72103ad792a7ad09
push idunknown
push userunknown
push dateunknown
milestone2.0b9pre
Merge mozilla-central into tracemonkey.
caps/src/nsScriptSecurityManager.cpp
dom/base/nsDOMClassInfo.cpp
dom/src/jsurl/nsJSProtocolHandler.cpp
js/jetpack/JetpackActorCommon.cpp
js/src/methodjit/Compiler.cpp
js/src/methodjit/InvokeHelpers.cpp
js/src/methodjit/MonoIC.h
--- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/browser.js
+++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/browser.js
@@ -147,17 +147,17 @@ var TestPilotMenuUtils;
   var TestPilotWindowHandlers = {
     onWindowLoad: function() {
       /* "Hold" window load events for TestPilotSetup, passing them along only
        * after startup is complete.  It's hacky, but the benefit is that
        * TestPilotSetup.onWindowLoad can treat all windows the same no matter
        * whether they opened with Firefox on startup or were opened later. */
       TestPilotWindowHandlers.setUpToolbarFeedbackButton();
 
-      if (TestPilotSetup.startupComplete) {
+      if (TestPilotSetup && TestPilotSetup.startupComplete) {
         TestPilotSetup.onWindowLoad(window);
       } else {
         let observerSvc = Cc["@mozilla.org/observer-service;1"]
                              .getService(Ci.nsIObserverService);
         let observer = {
           observe: function(subject, topic, data) {
             observerSvc.removeObserver(this, "testpilot:startup:complete");
             TestPilotSetup.onWindowLoad(window);
--- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/debug.html
+++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/debug.html
@@ -166,26 +166,54 @@
     var task = TestPilotSetup.getTaskById(getEid());
 
     var selector = document.getElementById("status-selector");
     var i = selector.selectedIndex;
     var newStatus = parseInt( selector.options[i].value );
     task.changeStatus(newStatus, false);
   }
 
+  function showIndexFileDropdown() {
+    var prefService  = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);
+    var prefName = "extensions.testpilot.indexFileName";
+    var selector = document.getElementById("index-file-selector");
+    if (prefService.getCharPref(prefName) == "index.json") {
+      selector.selectedIndex = 0;
+    } else {
+      selector.selectedIndex = 1;
+    }
+  }
+
+  function setSelectedIndexFile() {
+    var prefService  = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);
+    var prefName = "extensions.testpilot.indexFileName";
+    var selector = document.getElementById("index-file-selector");
+    var i = selector.selectedIndex;
+    prefService.setCharPref( prefName, selector.options[i].value );
+
+    // DELETE CACHED INDEX FILE
+    var file = Components.classes["@mozilla.org/file/directory_service;1"].
+                     getService(Components.interfaces.nsIProperties).
+                     get("ProfD", Components.interfaces.nsIFile);
+    file.append("TestPilotExperimentFiles");
+    file.append("index.json");
+    if (file.exists()) {
+      file.remove(false);
+    }
+  }
 
 </script>
 
 <style type="text/css">
       canvas { border: 1px solid black; }
     </style>
 
 </head>
 
-<body onload="populateFileDropdown();showSelectedTaskStatus();">
+<body onload="populateFileDropdown();showSelectedTaskStatus();showIndexFileDropdown();">
 
 <fieldset>
 <p><select id="task-selector" onchange="showSelectedTaskStatus();"></select> Current Status = <span id="show-status-span"></span>.
 <button onclick="resetSelectedTask();showSelectedTaskStatus();">Reset Task</button>
 or set it to
 <select id="status-selector" onchange="setSelectedTaskStatus(); showSelectedTaskStatus();">
   <option value="0">0 (New)</option>
   <option value="1">1 (Pending)</option>
@@ -203,16 +231,21 @@ or set it to
 <button onclick="showMetaData();">Show Metadata</button>
 <button onclick="runUnitTests();">Run Tests</button>
 </fieldset>
 <fieldset>
 <p><button onclick="makeThereBeAPopup();">Show Dummy Popup</button>
 <button onclick="reloadAllExperiments();">Reload All Experiments</button>
 <button onclick="remindMe();">Notify Me</button>
 <button onclick="testJarStore();">Test Jar Store</button>
+Index file: 
+<select id="index-file-selector" onchange="setSelectedIndexFile();">
+  <option value="index.json">index.json</option>
+  <option value="index-dev.json">index-dev.json</option>
+</select>
 </p>
 </fieldset>
 <p><span id="debug"></span></p>
 
 
 <textarea id="experiment-code-area" rows="40" cols="80">
 </textarea>
 <select id="file-selector"></select>
--- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/experiment-page.js
+++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/experiment-page.js
@@ -55,17 +55,17 @@ var stringBundle;
     if( results == null )
       return "";
     else
       return results[1];
   }
 
   function uploadData() {
     Components.utils.import("resource://testpilot/modules/setup.js");
-    let eid = parseInt(getUrlParam("eid"));
+    let eid = getUrlParam("eid");
     let task = TestPilotSetup.getTaskById(eid);
 
     // If always-submit-checkbox is checked, set the pref
     if (task._recursAutomatically) {
       let checkBox = document.getElementById("always-submit-checkbox");
       if (checkBox && checkBox.checked) {
         task.setRecurPref(TaskConstants.ALWAYS_SUBMIT);
       }
@@ -92,17 +92,17 @@ var stringBundle;
         uploadStatus.appendChild(willRetryParagraph);
       }
     });
   }
 
   function deleteData() {
     Components.utils.import("resource://testpilot/modules/setup.js");
     Components.utils.import("resource://testpilot/modules/tasks.js");
-    let eid = parseInt(getUrlParam("eid"));
+    let eid = getUrlParam("eid");
     let task = TestPilotSetup.getTaskById(eid);
     task.dataStore.wipeAllData();
     // reload the URL after wiping all data.
     window.location = "chrome://testpilot/content/status.html?eid=" + eid;
   }
 
   function saveCanvas(canvas) {
     const nsIFilePicker = Components.interfaces.nsIFilePicker;
@@ -145,17 +145,17 @@ var stringBundle;
       persist.saveURI(source, null, null, null, null, file);
     }
   }
 
   function exportData() {
     const nsIFilePicker = Components.interfaces.nsIFilePicker;
     let filePicker = Components.classes["@mozilla.org/filepicker;1"].
       createInstance(nsIFilePicker);
-    let eid = parseInt(getUrlParam("eid"));
+    let eid = getUrlParam("eid");
     let task = TestPilotSetup.getTaskById(eid);
 
     filePicker.init(window, null, nsIFilePicker.modeSave);
     filePicker.appendFilters(
 	nsIFilePicker.filterImages | nsIFilePicker.filterAll);
     filePicker.defaultString = task.title + ".csv";
 
     let response = filePicker.show();
@@ -263,34 +263,34 @@ var stringBundle;
         mdNumExt.innerHTML = PluralForm.get(numExt, str).replace("#1", numExt);
       }
     });
   }
 
   function onQuitPageLoad() {
     Components.utils.import("resource://testpilot/modules/setup.js");
     setStrings(PAGE_TYPE_QUIT);
-    let eid = parseInt(getUrlParam("eid"));
+    let eid = getUrlParam("eid");
     let task = TestPilotSetup.getTaskById(eid);
     let header = document.getElementById("about-quit-title");
     header.innerHTML =
       stringBundle.formatStringFromName(
 	"testpilot.quitPage.aboutToQuit", [task.title], 1);
 
     if (task._recursAutomatically) {
       document.getElementById("recur-options").setAttribute("style", "");
       document.getElementById("recur-checkbox-container").
         setAttribute("style", "");
     }
   }
 
   function quitExperiment() {
     Components.utils.import("resource://testpilot/modules/setup.js");
     Components.utils.import("resource://testpilot/modules/tasks.js");
-    let eid = parseInt(getUrlParam("eid"));
+    let eid = getUrlParam("eid");
     let reason = document.getElementById("reason-for-quit").value;
     let task = TestPilotSetup.getTaskById(eid);
     task.optOut(reason, function(success) {
       // load the you-are-canceleed page.
       window.location = "chrome://testpilot/content/status.html?eid=" + eid;
     });
 
     // If opt-out-forever checkbox is checked, opt out forever!
@@ -301,17 +301,17 @@ var stringBundle;
       }
       // quit test so rescheduling
       task._reschedule();
     }
   }
 
   function updateRecurSettings() {
     Components.utils.import("resource://testpilot/modules/setup.js");
-    let eid = parseInt(getUrlParam("eid"));
+    let eid = getUrlParam("eid");
     let experiment = TestPilotSetup.getTaskById(eid);
     let recurSelector = document.getElementById("recur-selector");
     let newValue = recurSelector.options[recurSelector.selectedIndex].value;
     experiment.setRecurPref(parseInt(newValue));
   }
 
   function showRecurControls(experiment) {
     Components.utils.import("resource://testpilot/modules/tasks.js");
@@ -362,19 +362,17 @@ var stringBundle;
   }
 
   function loadExperimentPage() {
     Components.utils.import("resource://testpilot/modules/setup.js");
     Components.utils.import("resource://testpilot/modules/tasks.js");
     var contentDiv = document.getElementById("experiment-specific-text");
     var dataPrivacyDiv = document.getElementById("data-privacy-text");
     // Get experimentID from the GET args of page
-    // TODO no reason actually to do parseInt here -- all it accomplishes
-    // is preventing us from using non-numeric study IDs.
-    var eid = parseInt(getUrlParam("eid"));
+    var eid = getUrlParam("eid");
     var experiment = TestPilotSetup.getTaskById(eid);
     if (!experiment) {
       // Possible that experiments aren't done loading yet.  Try again in
       // a few seconds.
       contentDiv.innerHTML =
         stringBundle.GetStringFromName("testpilot.statusPage.loading");
       window.setTimeout(function() { loadExperimentPage(); }, 2000);
       return;
--- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/survey-generator.js
+++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/content/survey-generator.js
@@ -1,8 +1,45 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Test Pilot.
+ *
+ * The Initial Developer of the Original Code is Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jono X <jono@mozilla.com>
+ *   Raymond Lee <raymond@appcoast.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
+ * 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 ***** */
+
 const MULTIPLE_CHOICE = 0;
 const CHECK_BOXES_WITH_FREE_ENTRY = 1;
 const SCALE = 2;
 const FREE_ENTRY = 3;
 const CHECK_BOXES = 4;
 const MULTIPLE_CHOICE_WITH_FREE_ENTRY = 5;
 
 var stringBundle;
@@ -53,19 +90,26 @@ function onBuiltinSurveyLoad() {
     } else {
       explanation.innerHTML = "";
     }
     drawSurveyForm(task, contentDiv);
   }
 }
 
 function drawSurveyForm(task, contentDiv) {
-  let oldAnswers = task.oldAnswers;
   let surveyQuestions = task.surveyQuestions;
 
+  /* Fill form fields with old survey answers if available --
+   * but not if the survey version has changed since you stored them!!
+   * (bug 576482) */
+  let oldAnswers = null;
+  if (task.oldAnswers && (task.version == task.oldAnswers["version_number"])) {
+    oldAnswers = task.oldAnswers["answers"];
+  }
+
   let submitButton = document.getElementById("survey-submit");
   submitButton.setAttribute("style", "");
   let changeButton = document.getElementById("change-answers");
   changeButton.setAttribute("style", "display:none");
   // Loop through questions and render html form input elements for each
   // one.
   for (let i = 0; i < surveyQuestions.length; i++) {
     let question = surveyQuestions[i].question;
--- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/modules/remote-experiment-loader.js
+++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/modules/remote-experiment-loader.js
@@ -180,17 +180,17 @@ exports.RemoteExperimentLoader.prototype
     this._legacyStudies = [];
     let prefs = require("preferences-service");
     this._baseUrl = prefs.get(BASE_URL_PREF, "");
     if (fileGetterFunction != undefined) {
       this._fileGetter = fileGetterFunction;
     } else {
       this._fileGetter = downloadFile;
     }
-    this._logger.trace("About to instantiate preferences store.");
+    this._logger.trace("About to instantiate jar store.");
     this._jarStore = new JarStore();
     this._experimentFileNames = [];
     let self = this;
     this._logger.trace("About to instantiate cuddlefish loader.");
     this._refreshLoader();
     // set up the unloading
     require("unload").when( function() {
                               self._loader.unload();
@@ -319,21 +319,27 @@ exports.RemoteExperimentLoader.prototype
         this._experimentFileNames.push(j.studyfile);
       }
     }
     return true;
   },
 
   // TODO a bad thing that can go wrong: If we have a net connection but the index file
   // has not changed, we currently don't try to download anything...
+  // The logic is bad because executeCachedIndexFile is called in two different
+  // cases: the one with no network, and the one with network but unchanged file.
 
   // Another bad thing: If there's a jar download that's corrupt or unreadable or has
-    // the wrong permissions or something, we need to kill it and download a new one.
+  // the wrong permissions or something, we need to kill it and download a new one.
+  // Should also try to download a new jar if any required modules are missing
+  // (Which is currently the case!)
 
-  // WTF every jar file I'm downloading appears as 0 bytes with __x__x___ permissions!
+  // (module "about_firefox.js" is not found; there is no about_firefox.jar on disk,
+  // indicating it didn't download, and we're not trying again because index-dev is
+  // unmodified.  Hmmm.)
 
   _cachedIndexNsiFile: null,
   get cachedIndexNsiFile() {
     if (!this._cachedIndexNsiFile) {
       try {
         let file = Cc["@mozilla.org/file/directory_service;1"].
                          getService(Ci.nsIProperties).
                          get("ProfD", Ci.nsIFile);
--- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/modules/tasks.js
+++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/modules/tasks.js
@@ -115,16 +115,20 @@ var TestPilotTask = {
   get title() {
     return this._title;
   },
 
   get id() {
     return this._id;
   },
 
+  get version() {
+    return this._versionNumber;
+  },
+
   get taskType() {
     return null;
   },
 
   get status() {
     return this._status;
   },
 
@@ -243,38 +247,47 @@ var TestPilotTask = {
     } else if (this._status == TaskConstants.STATUS_RESULTS) {
       this.changeStatus(TaskConstants.STATUS_ARCHIVED);
     }
 
     this.onDetailPageOpened();
   }
 };
 
-function TestPilotExperiment(expInfo, dataStore, handlers, webContent) {
+function TestPilotExperiment(expInfo, dataStore, handlers, webContent, dateOverrideFunc) {
   // All four of these are objects defined in the remote experiment file
-  this._init(expInfo, dataStore, handlers, webContent);
+  this._init(expInfo, dataStore, handlers, webContent, dateOverrideFunc);
 }
 TestPilotExperiment.prototype = {
   _init: function TestPilotExperiment__init(expInfo,
 					    dataStore,
 					    handlers,
-                                            webContent) {
+                                            webContent,
+                                            dateOverrideFunc) {
     /* expInfo is a dictionary defined in the remote experiment code, which
      * should have the following properties:
      * startDate (string representation of date)
      * duration (number of days)
      * testName (human-readable string)
      * testId (int)
      * testInfoUrl (url)
      * summary (string - couple of sentences explaining study)
      * thumbnail (url of an image representing the study)
      * optInRequired  (boolean)
      * recursAutomatically (boolean)
      * recurrenceInterval (number of days)
      * versionNumber (int) */
+
+    // dateOverrideFunc: For unit testing. Optional. If provided, will be called
+    // instead of Date.now() for determining the current time.
+    if (dateOverrideFunc) {
+      this._now = dateOverrideFunc;
+    } else {
+      this._now = Date.now;
+    }
     this._taskInit(expInfo.testId, expInfo.testName, expInfo.testInfoUrl,
                    expInfo.summary, expInfo.thumbnail);
     this._webContent = webContent;
     this._dataStore = dataStore;
     this._versionNumber = expInfo.versionNumber;
     this._optInRequired = expInfo.optInRequired;
     // TODO implement opt-in interface for tests that require opt-in.
     this._recursAutomatically = expInfo.recursAutomatically;
@@ -288,18 +301,18 @@ TestPilotExperiment.prototype = {
       this._startDate = Date.parse(startDateString);
     } else {
       // If a start date is provided in expInfo, use that.
       // Otherwise, start immediately!
       if (expInfo.startDate) {
         this._startDate = Date.parse(expInfo.startDate);
         Application.prefs.setValue(prefName, expInfo.startDate);
       } else {
-        this._startDate = Date.now();
-        Application.prefs.setValue(prefName, (new Date()).toString());
+        this._startDate = this._now();
+        Application.prefs.setValue(prefName, (new Date(this._startDate)).toString());
       }
     }
 
     // Duration is specified in days:
     let duration = expInfo.duration || 7; // default 1 week
     this._endDate = this._startDate + duration * (24 * 60 * 60 * 1000);
     this._logger.info("Start date is " + this._startDate.toString());
     this._logger.info("End date is " + this._endDate.toString());
@@ -575,17 +588,17 @@ TestPilotExperiment.prototype = {
   get _dateForDataDeletion() {
     return Application.prefs.getValue(
       DATE_FOR_DATA_DELETION_PREFIX + this._id, "");
   },
 
   checkDate: function TestPilotExperiment_checkDate() {
     // This method handles all date-related status changes and should be
     // called periodically.
-    let currentDate = Date.now();
+    let currentDate = this._now();
 
     // Reset automatically recurring tests:
     if (this._recursAutomatically &&
         this._status >= TaskConstants.STATUS_FINISHED &&
         currentDate >= this._startDate &&
 	currentDate <= this._endDate) {
       // if we've done a permanent opt-out, then don't start over-
       // just keep rescheduling.
@@ -769,17 +782,17 @@ TestPilotExperiment.prototype = {
         if (req.readyState == 4) {
           if (req.status == 200 || req.status == 201 || req.status == 202) {
             let location = req.getResponseHeader("Location");
   	    self._logger.info("DATA WAS POSTED SUCCESSFULLY " + location);
             if (self._uploadRetryTimer) {
               self._uploadRetryTimer.cancel(); // Stop retrying - it worked!
             }
             self.changeStatus(TaskConstants.STATUS_SUBMITTED);
-            self._dateForDataDeletion = Date.now() + TIME_FOR_DATA_DELETION;
+            self._dateForDataDeletion = self._now() + TIME_FOR_DATA_DELETION;
             self._expirationDateForDataSubmission = null;
             callback(true);
           } else {
           /* If something went wrong with the upload, give up for now,
            * but start a timer to try again later.  Maybe the network will
            * be better by then.  "later" starts at 1 hour, but increases
            * using a random exponential function.  This serves as a backoff
            * in cases where a lot of users are trying to submit data at
@@ -914,18 +927,17 @@ TestPilotBuiltinSurvey.prototype = {
 
   get oldAnswers() {
     let surveyResults =
       Application.prefs.getValue(SURVEY_ANSWER_PREFIX + this._id, null);
     if (!surveyResults) {
       return null;
     } else {
       this._logger.info("Trying to json.parse this: " + surveyResults);
-      let surveyJson = sanitizeJSONStrings( JSON.parse(surveyResults) );
-      return surveyJson["answers"];
+      return sanitizeJSONStrings( JSON.parse(surveyResults) );
     }
   },
 
   store: function TestPilotSurvey_store(surveyResults, callback) {
     /* Store answers in preferences store; then, upload the survey data
      * if this is a survey with an associated study (the matching GUIDs
      * will be used to associate the two datasets server-side).
      * If it's not one with an associated survey, just store and set status
--- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/skin/all/css/screen-standalone.css
+++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/skin/all/css/screen-standalone.css
@@ -50,17 +50,17 @@ body {
 	}
 	
 	.dataBox {
 		font-size: 16px;
 		padding: 6px 20px 20px 20px;
 		border-radius: 0.5em;
 		-webkit-border-radius: 0.5em;
 		background: rgba(220, 240, 247, 0.8) url('chrome://testpilot/skin/images/callout.png') no-repeat top center;
-		//display: inline;
+		/* display: inline; */
 	}
 
 #container {
 	margin: 0px auto;
 	width: 950px;
 
 }
 
@@ -119,17 +119,17 @@ body {
 		inset rgba(0, 0, 0, 0.2) 0 1px 1px,
 		inset rgba(255, 255, 255, 1) 0 3px 1px,
 		inset rgba(255, 255, 255, 0.3) 0 16px 0px,
 		inset rgba(0, 0, 0, 0.2) 0 -1px 1px,
 		inset rgba(0, 0, 0, 0.1) 0 -2px 1px, 
 		rgba(255, 255, 255, 1) 0 1px,
 		rgba(133, 153, 166, 0.3) 0px 1px 8.5px;
 	background-color: #e7eaec;
-	//display: inline;
+	/* display: inline; */
 }
 
 .home_button {
 	font-size: 16px;
 	padding: 8px 12px;
 	width: 240px;
 	color: rgba(0, 0, 0, 0.8);
 	border-radius: 0.5em;
@@ -138,71 +138,71 @@ body {
 		inset rgba(0, 0, 0, 0.2) 0 1px 1px,
 		inset rgba(255, 255, 255, 1) 0 3px 1px,
 		inset rgba(255, 255, 255, 0.3) 0 16px 0px,
 		inset rgba(0, 0, 0, 0.2) 0 -1px 1px,
 		inset rgba(0, 0, 0, 0.1) 0 -2px 1px, 
 		rgba(255, 255, 255, 1) 0 1px,
 		rgba(133, 153, 166, 0.3) 0px 1px 8.5px;
 	background-color: #e7eaec;
-	//display: inline;
+	/* display: inline; */
 }
 
 .callout {
 	font-size: 16px;
 	padding: 8px 24px;
 	margin: 24px auto;
 	color: rgba(0, 0, 0, 0.8);
 	border-radius: 0.5em;
 	-webkit-border-radius: 0.5em;
 	background: rgba(220, 240, 247, 0.8) url('chrome://testpilot/skin/images/callout.png') no-repeat top center;
 	box-shadow: 
 		inset rgba(185, 221, 234, 0.2) 0 -10px 8.5px,
 		inset rgba(185, 221, 234, 1) 0 0px 1px,
 		inset rgba(255, 255, 255, 0.2) 0 10px 8.5px;
-	//display: inline;
+	/* display: inline; */
 }
 
 #data-privacy-text {
   width: 320px;
   margin-bottom: 50px;
 }
 
 .home_callout {
 	font-size: 16px;
 	vertical-align: middle;
-	width: 280px;
+ 	/* width: 280px;*/
 	padding: 8px 24px;
 	margin: 8px auto;
 	color: rgba(0, 0, 0, 0.8);
 	border-radius: 0.5em;
 	-webkit-border-radius: 0.5em;
 	background: rgba(220, 240, 247, 0.8) url('chrome://testpilot/skin/images/callout.png') no-repeat top center;
 	box-shadow: 
 		inset rgba(185, 221, 234, 0.2) 0 -10px 8.5px,
 		inset rgba(185, 221, 234, 1) 0 0px 1px,
 		inset rgba(255, 255, 255, 0.2) 0 10px 8.5px;
-	//display: inline;
+	/* display: inline; */
 }
 
 .home_callout_continue {
 	font-size: 16px;
 	vertical-align: middle;
 	width: 280px;
 	padding: 8px 24px;
 	margin: 8px auto;
 	color: rgba(0, 0, 0, 0.8);
 	border-radius: 0.5em;
 	-webkit-border-radius: 0.5em;
 	background: rgba(220, 240, 247, 0.8) url('chrome://testpilot/skin/images/callout_continue.png') no-repeat top center;
 	box-shadow: 
 		inset rgba(185, 221, 234, 0.2) 0 -10px 8.5px,
 		inset rgba(185, 221, 234, 1) 0 0px 1px,
 		inset rgba(255, 255, 255, 0.2) 0 10px 8.5px;
-	//display: inline;
+	/* display: inline; */
 }
 
 .home_callout_continue a {color: #6c9735; text-decoration: none;}
 .home_callout_continue a:hover {color: #6c9735; text-decoration: none; border-bottom: 1px dotted #6c9735;}
 
 
 .homeIcon {
 	margin-top: -32px;
--- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/skin/linux/feedback.css
+++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/skin/linux/feedback.css
@@ -1,12 +1,8 @@
-#feedback-menu-button {
-  -moz-appearance: button;
-}
-
 #pilot-notification-popup {
   -moz-appearance: none;
   background-color: Menu;
   background-image: -moz-linear-gradient(hsla(0,0%,100%,.2), transparent);
   box-shadow: inset 0 0 7px hsla(0,0%,100%,.2),
                    inset 0 1px 0 hsla(0,0%,100%,.3);
   border-radius: 4px;
   border: 1px solid Menu;
--- a/browser/app/profile/extensions/testpilot@labs.mozilla.com/tests/test_data_store.js
+++ b/browser/app/profile/extensions/testpilot@labs.mozilla.com/tests/test_data_store.js
@@ -1,10 +1,12 @@
 EXPORTED_SYMBOLS = ["runAllTests"];
 var Cu = Components.utils;
+var Cc = Components.classes;
+var Ci = Components.interfaces;
 var testsRun = 0;
 var testsPassed = 0;
 
 function cheapAssertEqual(a, b, errorMsg) {
   testsRun += 1;
   if (a == b) {
     dump("UNIT TEST PASSED.\n");
     testsPassed += 1;
@@ -309,27 +311,244 @@ function testRemoteLoaderIndexCache() {
   };
 
   let remoteLoader = new remoteLoaderModule.RemoteExperimentLoader(stubLogger, getFileFunc);
   let data = "Foo bar baz quux";
   remoteLoader._cacheIndexFile(data);
   cheapAssertEqual(remoteLoader._loadCachedIndexFile(), data);
 }
 
+
+function StubDataStore(fileName, tableName, columns) {
+}
+StubDataStore.prototype = {
+  storeEvent: function(uiEvent, callback) {
+    callback(true);
+  },
+  getJSONRows: function(callback) {
+    callback([]);
+  },
+  getAllDataAsJSON: function(useDisplayValues, callback) {
+    callback([]);
+  },
+  wipeAllData: function(callback) {
+    callback(true);
+  },
+  nukeTable: function() {
+  },
+  haveData: function(callback) {
+    callback(true);
+  },
+  getHumanReadableColumnNames: function() {
+  },
+  getPropertyNames: function() {
+  }
+};
+
+function StubWebContent() {
+}
+StubWebContent.prototype = {
+  inProgressHtml: "",
+  completedHtml: "",
+  upcomingHtml: "",
+  canceledHtml: "",
+  remainDataHtml: "",
+  dataExpiredHtml: "",
+  deletedRemainDataHtml: "",
+  inProgressDataPrivacyHtml: "",
+  completedDataPrivacyHtml: "",
+  canceledDataPrivacyHtml: "",
+  dataExpiredDataPrivacyHtml: "",
+  remainDataPrivacyHtml: "",
+  deletedRemainDataPrivacyHtml: "",
+  onPageLoad: function(experiment, document, graphUtils) {
+  }
+};
+
+function StubHandlers() {
+}
+StubHandlers.prototype = {
+  onNewWindow: function(window) {
+  },
+  onWindowClosed: function(window) {
+  },
+  onAppStartup: function() {
+  },
+  onAppShutdown: function() {
+  },
+  onExperimentStartup: function(store) {
+  },
+  onExperimentShutdown: function() {
+  },
+  onEnterPrivateBrowsing: function() {
+  },
+  onExitPrivateBrowsing: function() {
+  },
+  uninstallAll: function() {
+  }
+};
+
+
+function testRecurringStudyStateChange() {
+
+  Cu.import("resource://testpilot/modules/tasks.js");
+
+  let expInfo = {
+    startDate: null,
+    duration: 7,
+    testName: "Unit Test Recurring Study",
+    testId: "unit_test_recur_study",
+    testInfoUrl: "https://testpilot.mozillalabs.com/",
+    summary: "Be sure to wipe all prefs and the store in the setup/teardown",
+    thumbnail: "",
+    optInRequired: false,
+    recursAutomatically: true,
+    recurrenceInterval: 30,
+    versionNumber: 1
+  };
+
+  dump("Looking for prefs to delete...\n");
+  let prefService = Cc["@mozilla.org/preferences-service;1"]
+                     .getService(Ci.nsIPrefService)
+                     .QueryInterface(Ci.nsIPrefBranch2);
+  let prefStem = "extensions.testpilot";
+  let prefNames = prefService.getChildList(prefStem);
+  for each (let prefName in prefNames) {
+    if (prefName.indexOf("unit_test_recur_study") != -1) {
+      dump("Clearing pref " + prefName + "\n");
+      prefService.clearUserPref(prefName);
+    }
+  }
+
+  const START_DATE = 1292629441000;
+  let stubDate = START_DATE;
+
+  let stubDateFunction = function() {
+    return stubDate;
+  };
+
+  let advanceDate = function(days) {
+    stubDate += days * 24 * 60 * 60 * 1000;
+  };
+
+  let dataStore = new StubDataStore();
+  let handlers = new StubHandlers();
+  let webContent = new StubWebContent();
+  let task = new TestPilotExperiment(expInfo, dataStore, handlers, webContent,
+                                    stubDateFunction);
+
+  // for this test, show popup on new study is set to true...
+  prefService.setBoolPref("extensions.testpilot.popup.showOnNewStudy", true);
+  // TODO another test with this turned off.
+
+  cheapAssertEqual(task.id, "unit_test_recur_study", "id should be id");
+  cheapAssertEqual(task.version, 1, "version should be version");
+  cheapAssertEqual(task.title, "Unit Test Recurring Study", "title should be title");
+  cheapAssertEqual(task.taskType, TaskConstants.TYPE_EXPERIMENT, "Should be experiment");
+  // Ensure that end date is 7 days past the start date
+  cheapAssertEqual(task.startDate, START_DATE, "Start date is wrong");
+  cheapAssertEqual(task.endDate, START_DATE + 7 * 24 * 60 * 60 * 1000, "End date is wrong");
+
+  cheapAssertEqual(task.status, TaskConstants.STATUS_NEW, "Status should be new");
+  // advance time a little and check date... b/c showOnNewStudy is true, it won't start:
+  advanceDate(1);
+  task.checkDate();
+  cheapAssertEqual(task.status, TaskConstants.STATUS_NEW, "Status should still be new");
+  // Now we fake the user action needed to advance it from NEW to STARTING:
+  task.changeStatus(TaskConstants.STATUS_STARTING);
+  cheapAssertEqual(task.status, TaskConstants.STATUS_STARTING, "Status should now be starting");
+  // when we check date again, it should advance from starting to in-progress:
+  task.checkDate();
+  cheapAssertEqual(task.status, TaskConstants.STATUS_IN_PROGRESS, "Status should be in progress");
+
+  // Go 7 days into the future
+  advanceDate(7);
+  task.checkDate();
+  cheapAssertEqual(task.status, TaskConstants.STATUS_FINISHED, "Status should be finished");
+
+  /* So how is the buggy bug happening?  It seems like:
+   *
+   * - Data is posted successfully
+   * - Task status set to 6
+   * - (Reschedule is happening somewhere silently in here, which is screwing things
+   *    up maybe?)
+   * - Reload everything
+   * - It's past its end date, so the task status gets switched to Finished.
+   */
+
+  // TODO ideally figure out how to fake an upload - until then, just set status to 6.
+  task.changeStatus(TaskConstants.STATUS_SUBMITTED);
+  cheapAssertEqual(task.status, TaskConstants.STATUS_SUBMITTED, "Should be submitted.");
+
+  // Delete and recreate task, make sure it recovers its status and start/end
+  // dates correctly
+  task = null;
+  let task2 = new TestPilotExperiment(expInfo, dataStore, handlers, webContent,
+                                      stubDateFunction);
+  cheapAssertEqual(task2.status, TaskConstants.STATUS_SUBMITTED, "Status should be submitted!");
+  // It should remain submitted; it should have rescheduled itself to 30 days ahead.
+  cheapAssertEqual(task2.startDate, START_DATE + 30 * 24 * 60 * 60 * 1000,
+                   "Start date is wrong");
+  cheapAssertEqual(task2.endDate, START_DATE + 37 * 24 * 60 * 60 * 1000,
+                   "End date is wrong");
+
+
+
+  // test task._reschedule()?
+  // basically test everything that can happen in checkDate:
+  /* - reset of automatically recurring test (with NEVER_SUBMIT on and with it off) when
+   *    we pass the (new) start date.
+  * - test that we jumped ahead to STARTING if optInRequired is false... and that
+  *    we don't jump ahead to starting if it's true!  And the popup.showOnNewStudy pref
+  *    should have the same effect!!
+  *
+  * - When a test finishes (status is less than finished and current date passes end date)
+  *     then it should switch to finish.  If it recurs automatically, it should then
+  *     reschedule; if study-specific recur pref is ALWAYS_SUBMIT (OR if global
+  *      extensions.testpilot.alwaysSubmitData is true) it should then submit, but if it's
+  *      NEVER_SUBMIT it should then cancel and wipe data.  If none of these are true
+  *      it should just reschedule but remain on status = finished.
+  *
+  *    If it ends and doesn't recur automatically, then it should autosubmit if
+  *      extensions.testpilot.alwaysSubmitData is true.
+  *
+  *    At this point, a data deletion date and an expiration date for data submission
+  *     should be set (unless dat was already wiped as a result of NEVER_SUBMIT).
+  *
+  *
+  * - Data expiration (this clause looks maybe wrong in the code - why is there another
+  *                        autosubmit command here?)
+  *     If study is finished but not submitted and we pass expiration date for data
+  *      submission, cancel and wipe data.  (Expired should really be a status code!!)
+  *    If study is submitted and we pass the date for data deletion, then wipe the data.
+  *
+  * */
+  // Trying to upload when status is already submitted or greater should be a no-op.
+  // Successful submit should advance us to SUBMITTED and generate/store a time for
+  // data deletion.
+
+  /* getWebContent and getDataPrivacyContent should give us back the right strings
+   * based on all the state. */
+
+}
+
+
 function runAllTests() {
   testTheDataStore();
   testFirefoxVersionCheck();
   testStringSanitizer();
   //testTheCuddlefishPreferencesFilesystem();
   //testRemoteLoader();
   testRemoteLoaderIndexCache();
+  testRecurringStudyStateChange();
   dump("TESTING COMPLETE.  " + testsPassed + " out of " + testsRun +
        " tests passed.");
 }
 
+
 //exports.runAllTests = runAllTests;
 
 
 // Test that observers get installed on any windows that are already open.
 
 // Test that every observer that is installed gets uninstalled.
 
 // Test that the observer is writing to the data store correctly.
\ No newline at end of file
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -469,16 +469,20 @@ window[chromehidden~="toolbar"] toolbar:
 #invalid-form-popup > description {
   max-width: 280px;
 }
 
 #geolocation-notification {
   -moz-binding: url("chrome://browser/content/urlbarBindings.xml#geolocation-notification");
 }
 
+#addon-progress-notification {
+  -moz-binding: url("chrome://browser/content/urlbarBindings.xml#addon-progress-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 {
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -700,16 +700,33 @@ const gXPInstallObserver = {
         callback: function() {
           installInfo.install();
         }
       };
 
       PopupNotifications.show(browser, notificationID, messageString, anchorID,
                               action, null, options);
       break;
+    case "addon-install-started":
+      function needsDownload(aInstall) {
+        return aInstall.state != AddonManager.STATE_DOWNLOADED;
+      }
+      // If all installs have already been downloaded then there is no need to
+      // show the download progress
+      if (!installInfo.installs.some(needsDownload))
+        return;
+      notificationID = "addon-progress";
+      messageString = gNavigatorBundle.getString("addonDownloading");
+      messageString = PluralForm.get(installInfo.installs.length, messageString);
+      options.installs = installInfo.installs;
+      options.contentWindow = browser.contentWindow;
+      options.sourceURI = browser.currentURI;
+      PopupNotifications.show(browser, notificationID, messageString, anchorID,
+                              null, null, options);
+      break;
     case "addon-install-failed":
       // TODO This isn't terribly ideal for the multiple failure case
       installInfo.installs.forEach(function(aInstall) {
         var host = (installInfo.originatingURI instanceof Ci.nsIStandardURL) &&
                    installInfo.originatingURI.host;
         if (!host)
           host = (aInstall.sourceURI instanceof Ci.nsIStandardURL) &&
                  aInstall.sourceURI.host;
@@ -1379,16 +1396,17 @@ function prepareForStartup() {
 
   // setup simple gestures support
   gGestureSupport.init(true);
 }
 
 function delayedStartup(isLoadingBlank, mustLoadSidebar) {
   Services.obs.addObserver(gSessionHistoryObserver, "browser:purge-session-history", false);
   Services.obs.addObserver(gXPInstallObserver, "addon-install-disabled", false);
+  Services.obs.addObserver(gXPInstallObserver, "addon-install-started", false);
   Services.obs.addObserver(gXPInstallObserver, "addon-install-blocked", false);
   Services.obs.addObserver(gXPInstallObserver, "addon-install-failed", false);
   Services.obs.addObserver(gXPInstallObserver, "addon-install-complete", false);
   Services.obs.addObserver(gFormSubmitObserver, "invalidformsubmit", false);
 
   BrowserOffline.init();
   OfflineApps.init();
   IndexedDBPromptHelper.init();
@@ -1621,16 +1639,17 @@ function BrowserShutdown()
     FullZoom.destroy();
   }
   catch(ex) {
     Components.utils.reportError(ex);
   }
 
   Services.obs.removeObserver(gSessionHistoryObserver, "browser:purge-session-history");
   Services.obs.removeObserver(gXPInstallObserver, "addon-install-disabled");
+  Services.obs.removeObserver(gXPInstallObserver, "addon-install-started");
   Services.obs.removeObserver(gXPInstallObserver, "addon-install-blocked");
   Services.obs.removeObserver(gXPInstallObserver, "addon-install-failed");
   Services.obs.removeObserver(gXPInstallObserver, "addon-install-complete");
   Services.obs.removeObserver(gPluginHandler.pluginCrashed, "plugin-crashed");
   Services.obs.removeObserver(gFormSubmitObserver, "invalidformsubmit");
 
   try {
     gBrowser.removeProgressListener(window.XULBrowserWindow);
--- a/browser/base/content/test/browser_bug553455.js
+++ b/browser/base/content/test/browser_bug553455.js
@@ -20,16 +20,17 @@ const CHROMEROOT = croot;
 var gApp = document.getElementById("bundle_brand").getString("brandShortName");
 var gVersion = Services.appinfo.version;
 
 function wait_for_notification(aCallback) {
   info("Waiting for notification");
   PopupNotifications.panel.addEventListener("popupshown", function() {
     PopupNotifications.panel.removeEventListener("popupshown", arguments.callee, false);
     info("Saw notification");
+    is(PopupNotifications.panel.childNodes.length, 1, "Should be only one notification");
     aCallback(PopupNotifications.panel);
   }, false);
 }
 
 function wait_for_install_dialog(aCallback) {
   info("Waiting for install dialog");
   Services.wm.addListener({
     onOpenWindow: function(aXULWindow) {
@@ -107,24 +108,28 @@ function test_blocked_install() {
     is(notification.getAttribute("label"),
        gApp + " prevented this site (example.com) from asking you to install " +
        "software on your computer.",
        "Should have seen the right message");
 
     // Click on Allow
     EventUtils.synthesizeMouse(notification.button, 20, 10, {});
 
+    // Notification should have changed to progress notification
+    notification = aPanel.childNodes[0];
+    is(notification.id, "addon-progress-notification", "Should have seen the progress notification");
+
     // Wait for the install confirmation dialog
     wait_for_install_dialog(function(aWindow) {
       // Wait for the complete notification
       wait_for_notification(function(aPanel) {
         let notification = aPanel.childNodes[0];
         is(notification.id, "addon-install-complete-notification", "Should have seen the install complete");
         is(notification.button.label, "Restart Now", "Should have seen the right button");
-        is(notification.getAttribute("label"), 
+        is(notification.getAttribute("label"),
            "XPI Test will be installed after you restart " + gApp + ".",
            "Should have seen the right message");
 
         AddonManager.getAllInstalls(function(aInstalls) {
         is(aInstalls.length, 1, "Should be one pending install");
           aInstalls[0].cancel();
 
           gBrowser.removeTab(gBrowser.selectedTab);
@@ -139,225 +144,267 @@ function test_blocked_install() {
   var triggers = encodeURIComponent(JSON.stringify({
     "XPI": "unsigned.xpi"
   }));
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
 },
 
 function test_whitelisted_install() {
-  // Wait for the install confirmation dialog
-  wait_for_install_dialog(function(aWindow) {
-    // Wait for the complete notification
-    wait_for_notification(function(aPanel) {
-      let notification = aPanel.childNodes[0];
-      is(notification.id, "addon-install-complete-notification", "Should have seen the install complete");
-      is(notification.button.label, "Restart Now", "Should have seen the right button");
-      is(notification.getAttribute("label"),
-         "XPI Test will be installed after you restart " + gApp + ".",
-         "Should have seen the right message");
+  // Wait for the progress notification
+  wait_for_notification(function(aPanel) {
+    let notification = aPanel.childNodes[0];
+    is(notification.id, "addon-progress-notification", "Should have seen the progress notification");
 
-      AddonManager.getAllInstalls(function(aInstalls) {
-        is(aInstalls.length, 1, "Should be one pending install");
-        aInstalls[0].cancel();
+    // Wait for the install confirmation dialog
+    wait_for_install_dialog(function(aWindow) {
+      // Wait for the complete notification
+      wait_for_notification(function(aPanel) {
+        let notification = aPanel.childNodes[0];
+        is(notification.id, "addon-install-complete-notification", "Should have seen the install complete");
+        is(notification.button.label, "Restart Now", "Should have seen the right button");
+        is(notification.getAttribute("label"),
+           "XPI Test will be installed after you restart " + gApp + ".",
+           "Should have seen the right message");
 
-        gBrowser.removeTab(gBrowser.selectedTab);
-        Services.perms.remove("example.com", "install");
-        runNextTest();
+        AddonManager.getAllInstalls(function(aInstalls) {
+          is(aInstalls.length, 1, "Should be one pending install");
+          aInstalls[0].cancel();
+
+          gBrowser.removeTab(gBrowser.selectedTab);
+          Services.perms.remove("example.com", "install");
+          runNextTest();
+        });
       });
+
+      aWindow.document.documentElement.acceptDialog();
     });
-
-    aWindow.document.documentElement.acceptDialog();
   });
 
   var pm = Services.perms;
   pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
 
   var triggers = encodeURIComponent(JSON.stringify({
     "XPI": "unsigned.xpi"
   }));
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
 },
 
 function test_failed_download() {
-  // Wait for the failed notification
+  // Wait for the progress notification
   wait_for_notification(function(aPanel) {
     let notification = aPanel.childNodes[0];
-    is(notification.id, "addon-install-failed-notification", "Should have seen the install fail");
-    is(notification.getAttribute("label"),
-       "The add-on could not be downloaded because of a connection failure " +
-       "on example.com.",
-       "Should have seen the right message");
+    is(notification.id, "addon-progress-notification", "Should have seen the progress notification");
 
-    gBrowser.removeTab(gBrowser.selectedTab);
-    Services.perms.remove("example.com", "install");
-    runNextTest();
+    // Wait for the failed notification
+    wait_for_notification(function(aPanel) {
+      let notification = aPanel.childNodes[0];
+      is(notification.id, "addon-install-failed-notification", "Should have seen the install fail");
+      is(notification.getAttribute("label"),
+         "The add-on could not be downloaded because of a connection failure " +
+         "on example.com.",
+         "Should have seen the right message");
+
+      gBrowser.removeTab(gBrowser.selectedTab);
+      Services.perms.remove("example.com", "install");
+      runNextTest();
+    });
   });
 
   var pm = Services.perms;
   pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
 
   var triggers = encodeURIComponent(JSON.stringify({
     "XPI": "missing.xpi"
   }));
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
 },
 
 function test_corrupt_file() {
-  // Wait for the failed notification
+  // Wait for the progress notification
   wait_for_notification(function(aPanel) {
     let notification = aPanel.childNodes[0];
-    is(notification.id, "addon-install-failed-notification", "Should have seen the install fail");
-    is(notification.getAttribute("label"),
-       "The add-on downloaded from example.com could not be installed " +
-       "because it appears to be corrupt.",
-       "Should have seen the right message");
+    is(notification.id, "addon-progress-notification", "Should have seen the progress notification");
 
-    gBrowser.removeTab(gBrowser.selectedTab);
-    Services.perms.remove("example.com", "install");
-    runNextTest();
+    // Wait for the failed notification
+    wait_for_notification(function(aPanel) {
+      let notification = aPanel.childNodes[0];
+      is(notification.id, "addon-install-failed-notification", "Should have seen the install fail");
+      is(notification.getAttribute("label"),
+         "The add-on downloaded from example.com could not be installed " +
+         "because it appears to be corrupt.",
+         "Should have seen the right message");
+
+      gBrowser.removeTab(gBrowser.selectedTab);
+      Services.perms.remove("example.com", "install");
+      runNextTest();
+    });
   });
 
   var pm = Services.perms;
   pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
 
   var triggers = encodeURIComponent(JSON.stringify({
     "XPI": "corrupt.xpi"
   }));
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
 },
 
 function test_incompatible() {
-  // Wait for the failed notification
+  // Wait for the progress notification
   wait_for_notification(function(aPanel) {
     let notification = aPanel.childNodes[0];
-    is(notification.id, "addon-install-failed-notification", "Should have seen the install fail");
-    is(notification.getAttribute("label"),
-       "XPI Test could not be installed because it is not compatible with " +
-       gApp + " " + gVersion + ".",
-       "Should have seen the right message");
+    is(notification.id, "addon-progress-notification", "Should have seen the progress notification");
 
-    gBrowser.removeTab(gBrowser.selectedTab);
-    Services.perms.remove("example.com", "install");
-    runNextTest();
+    // Wait for the failed notification
+    wait_for_notification(function(aPanel) {
+      let notification = aPanel.childNodes[0];
+      is(notification.id, "addon-install-failed-notification", "Should have seen the install fail");
+      is(notification.getAttribute("label"),
+         "XPI Test could not be installed because it is not compatible with " +
+         gApp + " " + gVersion + ".",
+         "Should have seen the right message");
+
+      gBrowser.removeTab(gBrowser.selectedTab);
+      Services.perms.remove("example.com", "install");
+      runNextTest();
+    });
   });
 
   var pm = Services.perms;
   pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
 
   var triggers = encodeURIComponent(JSON.stringify({
     "XPI": "incompatible.xpi"
   }));
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
 },
 
 function test_restartless() {
-  // Wait for the install confirmation dialog
-  wait_for_install_dialog(function(aWindow) {
-    // Wait for the complete notification
-    wait_for_notification(function(aPanel) {
-      let notification = aPanel.childNodes[0];
-      is(notification.id, "addon-install-complete-notification", "Should have seen the install complete");
-      is(notification.button.label, "Open Add-ons Manager", "Should have seen the right button");
-      is(notification.getAttribute("label"),
-         "XPI Test has been installed successfully.",
-         "Should have seen the right message");
+  // Wait for the progress notification
+  wait_for_notification(function(aPanel) {
+    let notification = aPanel.childNodes[0];
+    is(notification.id, "addon-progress-notification", "Should have seen the progress notification");
 
-      AddonManager.getAllInstalls(function(aInstalls) {
-        is(aInstalls.length, 0, "Should be no pending installs");
+    // Wait for the install confirmation dialog
+    wait_for_install_dialog(function(aWindow) {
+      // Wait for the complete notification
+      wait_for_notification(function(aPanel) {
+        let notification = aPanel.childNodes[0];
+        is(notification.id, "addon-install-complete-notification", "Should have seen the install complete");
+        is(notification.button.label, "Open Add-ons Manager", "Should have seen the right button");
+        is(notification.getAttribute("label"),
+           "XPI Test has been installed successfully.",
+           "Should have seen the right message");
 
-        AddonManager.getAddonByID("restartless-xpi@tests.mozilla.org", function(aAddon) {
-          aAddon.uninstall();
+        AddonManager.getAllInstalls(function(aInstalls) {
+          is(aInstalls.length, 0, "Should be no pending installs");
 
-          gBrowser.removeTab(gBrowser.selectedTab);
-          Services.perms.remove("example.com", "install");
-          runNextTest();
+          AddonManager.getAddonByID("restartless-xpi@tests.mozilla.org", function(aAddon) {
+            aAddon.uninstall();
+
+            gBrowser.removeTab(gBrowser.selectedTab);
+            Services.perms.remove("example.com", "install");
+            runNextTest();
+          });
         });
       });
+
+      aWindow.document.documentElement.acceptDialog();
     });
-
-    aWindow.document.documentElement.acceptDialog();
   });
 
   var pm = Services.perms;
   pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
 
   var triggers = encodeURIComponent(JSON.stringify({
     "XPI": "restartless.xpi"
   }));
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
 },
 
 function test_multiple() {
-  // Wait for the install confirmation dialog
-  wait_for_install_dialog(function(aWindow) {
-    // Wait for the complete notification
-    wait_for_notification(function(aPanel) {
-      let notification = aPanel.childNodes[0];
-      is(notification.id, "addon-install-complete-notification", "Should have seen the install complete");
-      is(notification.button.label, "Restart Now", "Should have seen the right button");
-      is(notification.getAttribute("label"),
-         "2 add-ons will be installed after you restart " + gApp + ".",
-         "Should have seen the right message");
+  // Wait for the progress notification
+  wait_for_notification(function(aPanel) {
+    let notification = aPanel.childNodes[0];
+    is(notification.id, "addon-progress-notification", "Should have seen the progress notification");
 
-      AddonManager.getAllInstalls(function(aInstalls) {
-        is(aInstalls.length, 1, "Should be one pending install");
-        aInstalls[0].cancel();
+    // Wait for the install confirmation dialog
+    wait_for_install_dialog(function(aWindow) {
+      // Wait for the complete notification
+      wait_for_notification(function(aPanel) {
+        let notification = aPanel.childNodes[0];
+        is(notification.id, "addon-install-complete-notification", "Should have seen the install complete");
+        is(notification.button.label, "Restart Now", "Should have seen the right button");
+        is(notification.getAttribute("label"),
+           "2 add-ons will be installed after you restart " + gApp + ".",
+           "Should have seen the right message");
 
-        AddonManager.getAddonByID("restartless-xpi@tests.mozilla.org", function(aAddon) {
-          aAddon.uninstall();
+        AddonManager.getAllInstalls(function(aInstalls) {
+          is(aInstalls.length, 1, "Should be one pending install");
+          aInstalls[0].cancel();
 
-          gBrowser.removeTab(gBrowser.selectedTab);
-          Services.perms.remove("example.com", "install");
-          runNextTest();
+          AddonManager.getAddonByID("restartless-xpi@tests.mozilla.org", function(aAddon) {
+            aAddon.uninstall();
+
+            gBrowser.removeTab(gBrowser.selectedTab);
+            Services.perms.remove("example.com", "install");
+            runNextTest();
+          });
         });
       });
+
+      aWindow.document.documentElement.acceptDialog();
     });
-
-    aWindow.document.documentElement.acceptDialog();
   });
 
   var pm = Services.perms;
   pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
 
   var triggers = encodeURIComponent(JSON.stringify({
     "Unsigned XPI": "unsigned.xpi",
     "Restartless XPI": "restartless.xpi"
   }));
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
 },
 
 function test_url() {
-  // Wait for the install confirmation dialog
-  wait_for_install_dialog(function(aWindow) {
-    // Wait for the complete notification
-    wait_for_notification(function(aPanel) {
-      let notification = aPanel.childNodes[0];
-      is(notification.id, "addon-install-complete-notification", "Should have seen the install complete");
-      is(notification.button.label, "Restart Now", "Should have seen the right button");
-      is(notification.getAttribute("label"),
-         "XPI Test will be installed after you restart " + gApp + ".",
-         "Should have seen the right message");
+  // Wait for the progress notification
+  wait_for_notification(function(aPanel) {
+    let notification = aPanel.childNodes[0];
+    is(notification.id, "addon-progress-notification", "Should have seen the progress notification");
 
-      AddonManager.getAllInstalls(function(aInstalls) {
-        is(aInstalls.length, 1, "Should be one pending install");
-        aInstalls[0].cancel();
+    // Wait for the install confirmation dialog
+    wait_for_install_dialog(function(aWindow) {
+      // Wait for the complete notification
+      wait_for_notification(function(aPanel) {
+        let notification = aPanel.childNodes[0];
+        is(notification.id, "addon-install-complete-notification", "Should have seen the install complete");
+        is(notification.button.label, "Restart Now", "Should have seen the right button");
+        is(notification.getAttribute("label"),
+           "XPI Test will be installed after you restart " + gApp + ".",
+           "Should have seen the right message");
 
-        gBrowser.removeTab(gBrowser.selectedTab);
-        runNextTest();
+        AddonManager.getAllInstalls(function(aInstalls) {
+          is(aInstalls.length, 1, "Should be one pending install");
+          aInstalls[0].cancel();
+
+          gBrowser.removeTab(gBrowser.selectedTab);
+          runNextTest();
+        });
       });
+
+      aWindow.document.documentElement.acceptDialog();
     });
-
-    aWindow.document.documentElement.acceptDialog();
   });
 
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.loadURI(TESTROOT + "unsigned.xpi");
 },
 
 function test_localfile() {
   // Wait for the complete notification
@@ -386,115 +433,132 @@ function test_localfile() {
 function test_wronghost() {
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.addEventListener("load", function() {
     if (gBrowser.currentURI.spec != TESTROOT2 + "enabled.html")
       return;
 
     gBrowser.removeEventListener("load", arguments.callee, true);
 
-    // Wait for the complete notification
+    // Wait for the progress notification
     wait_for_notification(function(aPanel) {
       let notification = aPanel.childNodes[0];
-      is(notification.id, "addon-install-failed-notification", "Should have seen the install fail");
-      is(notification.getAttribute("label"),
-         "The add-on downloaded from example.com could not be installed " +
-         "because it appears to be corrupt.",
-         "Should have seen the right message");
+      is(notification.id, "addon-progress-notification", "Should have seen the progress notification");
+      // Wait for the complete notification
+      wait_for_notification(function(aPanel) {
+        let notification = aPanel.childNodes[0];
+        is(notification.id, "addon-install-failed-notification", "Should have seen the install fail");
+        is(notification.getAttribute("label"),
+           "The add-on downloaded from example.com could not be installed " +
+           "because it appears to be corrupt.",
+           "Should have seen the right message");
 
-      gBrowser.removeTab(gBrowser.selectedTab);
-      runNextTest();
+        gBrowser.removeTab(gBrowser.selectedTab);
+        runNextTest();
+      });
     });
 
     gBrowser.loadURI(TESTROOT + "corrupt.xpi");
   }, true);
   gBrowser.loadURI(TESTROOT2 + "enabled.html");
 },
 
 function test_reload() {
-  // Wait for the install confirmation dialog
-  wait_for_install_dialog(function(aWindow) {
-    // Wait for the complete notification
-    wait_for_notification(function(aPanel) {
-      let notification = aPanel.childNodes[0];
-      is(notification.id, "addon-install-complete-notification", "Should have seen the install complete");
-      is(notification.button.label, "Restart Now", "Should have seen the right button");
-      is(notification.getAttribute("label"),
-         "XPI Test will be installed after you restart " + gApp + ".",
-         "Should have seen the right message");
+  // Wait for the progress notification
+  wait_for_notification(function(aPanel) {
+    let notification = aPanel.childNodes[0];
+    is(notification.id, "addon-progress-notification", "Should have seen the progress notification");
 
-      function test_fail() {
-        ok(false, "Reloading should not have hidden the notification");
-      }
+    // Wait for the install confirmation dialog
+    wait_for_install_dialog(function(aWindow) {
+      // Wait for the complete notification
+      wait_for_notification(function(aPanel) {
+        let notification = aPanel.childNodes[0];
+        is(notification.id, "addon-install-complete-notification", "Should have seen the install complete");
+        is(notification.button.label, "Restart Now", "Should have seen the right button");
+        is(notification.getAttribute("label"),
+           "XPI Test will be installed after you restart " + gApp + ".",
+           "Should have seen the right message");
 
-      PopupNotifications.panel.addEventListener("popuphiding", test_fail, false);
+        function test_fail() {
+          ok(false, "Reloading should not have hidden the notification");
+        }
+
+        PopupNotifications.panel.addEventListener("popuphiding", test_fail, false);
 
-      gBrowser.addEventListener("load", function() {
-        if (gBrowser.currentURI.spec != TESTROOT2 + "enabled.html")
-          return;
+        gBrowser.addEventListener("load", function() {
+          if (gBrowser.currentURI.spec != TESTROOT2 + "enabled.html")
+            return;
 
-        gBrowser.removeEventListener("load", arguments.callee, true);
+          gBrowser.removeEventListener("load", arguments.callee, true);
 
-        PopupNotifications.panel.removeEventListener("popuphiding", test_fail, false);
+          PopupNotifications.panel.removeEventListener("popuphiding", test_fail, false);
 
-        AddonManager.getAllInstalls(function(aInstalls) {
-          is(aInstalls.length, 1, "Should be one pending install");
-          aInstalls[0].cancel();
+          AddonManager.getAllInstalls(function(aInstalls) {
+            is(aInstalls.length, 1, "Should be one pending install");
+            aInstalls[0].cancel();
 
-          gBrowser.removeTab(gBrowser.selectedTab);
-          Services.perms.remove("example.com", "install");
-          runNextTest();
-        });
-      }, true);
-      gBrowser.loadURI(TESTROOT2 + "enabled.html");
+            gBrowser.removeTab(gBrowser.selectedTab);
+            Services.perms.remove("example.com", "install");
+            runNextTest();
+          });
+        }, true);
+        gBrowser.loadURI(TESTROOT2 + "enabled.html");
+      });
+
+      aWindow.document.documentElement.acceptDialog();
     });
-
-    aWindow.document.documentElement.acceptDialog();
   });
 
   var pm = Services.perms;
   pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
 
   var triggers = encodeURIComponent(JSON.stringify({
     "Unsigned XPI": "unsigned.xpi"
   }));
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
 },
 
 function test_theme() {
-  // Wait for the install confirmation dialog
-  wait_for_install_dialog(function(aWindow) {
-    // Wait for the complete notification
-    wait_for_notification(function(aPanel) {
-      let notification = aPanel.childNodes[0];
-      is(notification.id, "addon-install-complete-notification", "Should have seen the install complete");
-      is(notification.button.label, "Restart Now", "Should have seen the right button");
-      is(notification.getAttribute("label"),
-         "Theme Test will be installed after you restart " + gApp + ".",
-         "Should have seen the right message");
+  // Wait for the progress notification
+  wait_for_notification(function(aPanel) {
+    let notification = aPanel.childNodes[0];
+    is(notification.id, "addon-progress-notification", "Should have seen the progress notification");
 
-      gBrowser.removeTab(gBrowser.selectedTab);
-      Services.perms.remove("example.com", "install");
+    // Wait for the install confirmation dialog
+    wait_for_install_dialog(function(aWindow) {
+      // Wait for the complete notification
+      wait_for_notification(function(aPanel) {
+        let notification = aPanel.childNodes[0];
+        is(notification.id, "addon-install-complete-notification", "Should have seen the install complete");
+        is(notification.button.label, "Restart Now", "Should have seen the right button");
+        is(notification.getAttribute("label"),
+           "Theme Test will be installed after you restart " + gApp + ".",
+           "Should have seen the right message");
 
-      AddonManager.getAddonByID("{972ce4c6-7e08-4474-a285-3208198ce6fd}", function(aAddon) {
-        ok(aAddon.userDisabled, "Should be switching away from the default theme.");
-        // Undo the pending theme switch
-        aAddon.userDisabled = false;
+        gBrowser.removeTab(gBrowser.selectedTab);
+        Services.perms.remove("example.com", "install");
 
-        AddonManager.getAddonByID("theme-xpi@tests.mozilla.org", function(aAddon) {
-          isnot(aAddon, null, "Test theme will have been installed");
-          aAddon.uninstall();
+        AddonManager.getAddonByID("{972ce4c6-7e08-4474-a285-3208198ce6fd}", function(aAddon) {
+          ok(aAddon.userDisabled, "Should be switching away from the default theme.");
+          // Undo the pending theme switch
+          aAddon.userDisabled = false;
 
-          runNextTest();
+          AddonManager.getAddonByID("theme-xpi@tests.mozilla.org", function(aAddon) {
+            isnot(aAddon, null, "Test theme will have been installed");
+            aAddon.uninstall();
+
+            runNextTest();
+          });
         });
       });
+
+      aWindow.document.documentElement.acceptDialog();
     });
-
-    aWindow.document.documentElement.acceptDialog();
   });
 
   var pm = Services.perms;
   pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
 
   var triggers = encodeURIComponent(JSON.stringify({
     "Theme XPI": "theme.xpi"
   }));
@@ -539,59 +603,131 @@ function test_renotify_blocked() {
   var triggers = encodeURIComponent(JSON.stringify({
     "XPI": "unsigned.xpi"
   }));
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
 },
 
 function test_renotify_installed() {
-  // Wait for the install confirmation dialog
-  wait_for_install_dialog(function(aWindow) {
-    // Wait for the complete notification
-    wait_for_notification(function(aPanel) {
-      let notification = aPanel.childNodes[0];
-      is(notification.id, "addon-install-complete-notification", "Should have seen the install complete");
+  // Wait for the progress notification
+  wait_for_notification(function(aPanel) {
+    let notification = aPanel.childNodes[0];
+    is(notification.id, "addon-progress-notification", "Should have seen the progress notification");
 
-      // Dismiss the notification
-      aPanel.addEventListener("popuphidden", function () {
-        aPanel.removeEventListener("popuphidden", arguments.callee, false);
+    // Wait for the install confirmation dialog
+    wait_for_install_dialog(function(aWindow) {
+      // Wait for the complete notification
+      wait_for_notification(function(aPanel) {
+        let notification = aPanel.childNodes[0];
+        is(notification.id, "addon-install-complete-notification", "Should have seen the install complete");
 
-        // Install another
-        executeSoon(function () {
-          // Wait for the install confirmation dialog
-          wait_for_install_dialog(function(aWindow) {
-            info("Timeouts after this probably mean bug 589954 regressed");
+        // Dismiss the notification
+        aPanel.addEventListener("popuphidden", function () {
+          aPanel.removeEventListener("popuphidden", arguments.callee, false);
 
-            // Wait for the complete notification
+          // Install another
+          executeSoon(function () {
+            // Wait for the progress notification
             wait_for_notification(function(aPanel) {
               let notification = aPanel.childNodes[0];
-              is(notification.id, "addon-install-complete-notification", "Should have seen the second install complete");
+              is(notification.id, "addon-progress-notification", "Should have seen the progress notification");
+
+              // Wait for the install confirmation dialog
+              wait_for_install_dialog(function(aWindow) {
+                info("Timeouts after this probably mean bug 589954 regressed");
+
+                // Wait for the complete notification
+                wait_for_notification(function(aPanel) {
+                  let notification = aPanel.childNodes[0];
+                  is(notification.id, "addon-install-complete-notification", "Should have seen the second install complete");
 
-              AddonManager.getAllInstalls(function(aInstalls) {
-              is(aInstalls.length, 1, "Should be one pending installs");
-                aInstalls[0].cancel();
+                  AddonManager.getAllInstalls(function(aInstalls) {
+                  is(aInstalls.length, 1, "Should be one pending installs");
+                    aInstalls[0].cancel();
 
-                gBrowser.removeTab(gBrowser.selectedTab);
-                runNextTest();
+                    gBrowser.removeTab(gBrowser.selectedTab);
+                    Services.perms.remove("example.com", "install");
+                    runNextTest();
+                  });
+                });
+
+                aWindow.document.documentElement.acceptDialog();
               });
             });
 
-            aWindow.document.documentElement.acceptDialog();
+            gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
           });
+        }, false);
 
-          gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+        // hide the panel (this simulates the user dismissing it)
+        aPanel.hidePopup();
+      });
+
+      aWindow.document.documentElement.acceptDialog();
+    });
+  });
+
+  var pm = Services.perms;
+  pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
+
+  var triggers = encodeURIComponent(JSON.stringify({
+    "XPI": "unsigned.xpi"
+  }));
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
+},
+
+function test_cancel_restart() {
+  // Wait for the progress notification
+  wait_for_notification(function(aPanel) {
+    let notification = aPanel.childNodes[0];
+    is(notification.id, "addon-progress-notification", "Should have seen the progress notification");
+    let button = document.getAnonymousElementByAttribute(notification, "anonid", "cancel");
+
+    // Cancel the download
+    EventUtils.synthesizeMouse(button, 2, 2, {});
+
+    // Downloads cannot be restarted synchronously, bug 611755
+    executeSoon(function() {
+      // Notification should have changed to cancelled
+      notification = aPanel.childNodes[0];
+      is(notification.id, "addon-install-cancelled-notification", "Should have seen the cancelled notification");
+
+      // Restart the download
+      EventUtils.synthesizeMouse(notification.button, 20, 10, {});
+
+      // Should be back to a progress notification
+      notification = aPanel.childNodes[0];
+      is(notification.id, "addon-progress-notification", "Should have seen the progress notification");
+
+      // Wait for the install confirmation dialog
+      wait_for_install_dialog(function(aWindow) {
+        // Wait for the complete notification
+        wait_for_notification(function(aPanel) {
+          let notification = aPanel.childNodes[0];
+          is(notification.id, "addon-install-complete-notification", "Should have seen the install complete");
+          is(notification.button.label, "Restart Now", "Should have seen the right button");
+          is(notification.getAttribute("label"),
+             "XPI Test will be installed after you restart " + gApp + ".",
+             "Should have seen the right message");
+
+          AddonManager.getAllInstalls(function(aInstalls) {
+            is(aInstalls.length, 1, "Should be one pending install");
+            aInstalls[0].cancel();
+
+            gBrowser.removeTab(gBrowser.selectedTab);
+            Services.perms.remove("example.com", "install");
+            runNextTest();
+          });
         });
-      }, false);
-  
-      // hide the panel (this simulates the user dismissing it)
-      aPanel.hidePopup();
+
+        aWindow.document.documentElement.acceptDialog();
+      });
     });
-
-    aWindow.document.documentElement.acceptDialog();
   });
 
   var pm = Services.perms;
   pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
 
   var triggers = encodeURIComponent(JSON.stringify({
     "XPI": "unsigned.xpi"
   }));
--- a/browser/base/content/test/browser_popupNotification.js
+++ b/browser/base/content/test/browser_popupNotification.js
@@ -16,16 +16,17 @@
  * The Initial Developer of the Original Code is
  * the Mozilla Foundation.
  * Portions created by the Initial Developer are Copyright (C) 2010
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Gavin Sharp <gavin@gavinsharp.com>
  *   Sylvain Pasche <sylvain.pasche@gmail.com>
+ *   Drew Willcoxon <adw@mozilla.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
@@ -87,36 +88,47 @@ function runNextTest() {
   } else {
     doOnPopupEvent("popupshowing", function () {
       info("[Test #" + gTestIndex + "] popup showing");
     });
     doOnPopupEvent("popupshown", function () {
       info("[Test #" + gTestIndex + "] popup shown");
       nextTest.onShown(this);
     });
-  
+
+    // We allow multiple onHidden functions to be defined in an array.  They're
+    // called in the order they appear.
+    let onHiddenArray = nextTest.onHidden instanceof Array ?
+                        nextTest.onHidden :
+                        [nextTest.onHidden];
     doOnPopupEvent("popuphidden", function () {
-      info("[Test #" + gTestIndex + "] popup hidden");
-      nextTest.onHidden(this);
-  
-      goNext();
-    });
+      let onHidden = onHiddenArray.shift();
+      info("[Test #" + gTestIndex + "] popup hidden (" + onHiddenArray.length + " hides remaining)");
+      onHidden.call(nextTest, this);
+      if (!onHiddenArray.length)
+        goNext();
+    }, onHiddenArray.length);
+
     info("[Test #" + gTestIndex + "] added listeners; panel state: " + PopupNotifications.isPanelOpen);
   }
 
   info("[Test #" + gTestIndex + "] running test");
   nextTest.run();
 }
 
-function doOnPopupEvent(eventName, callback) {
+function doOnPopupEvent(eventName, callback, numExpected) {
   gActiveListeners[eventName] = function (event) {
     if (event.target != PopupNotifications.panel)
       return;
-    PopupNotifications.panel.removeEventListener(eventName, gActiveListeners[eventName], false);
-    delete gActiveListeners[eventName];
+    if (typeof(numExpected) === "number")
+      numExpected--;
+    if (!numExpected) {
+      PopupNotifications.panel.removeEventListener(eventName, gActiveListeners[eventName], false);
+      delete gActiveListeners[eventName];
+    }
 
     callback.call(PopupNotifications.panel);
   }
   PopupNotifications.panel.addEventListener(eventName, gActiveListeners[eventName], false);
 }
 
 var gTestIndex = 0;
 var gNewTab;
@@ -330,21 +342,25 @@ var tests = [
     },
     onShown: function (popup) {
       // This also checks that only one element is shown.
       checkPopup(popup, this.notifyObj2);
       is(document.getElementById("geo-notification-icon").boxObject.width, 0,
          "geo anchor shouldn't be visible");
       dismissNotification(popup);
     },
-    onHidden: function (popup) {
-      // Remove the first notification
-      this.firstNotification.remove();
-      ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered");
-    }
+    onHidden: [
+      function (popup) {
+        // Remove the first notification
+        this.firstNotification.remove();
+        ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered");
+      },
+      // The removal triggers another popuphidden event.
+      function (popup) {}
+    ],
   },
   // Test optional params
   { // Test #10
     run: function () {
       this.notifyObj = new basicNotification();
       this.notifyObj.secondaryActions = undefined;
       this.notification = showNotification(this.notifyObj);
     },
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -1095,16 +1095,228 @@
         }
       ]]></constructor>
       <field name="item" readonly="true">
         document.getAnonymousElementByAttribute(this, "anonid", "item");
       </field>
     </implementation>
   </binding>
 
+  <binding id="addon-progress-notification" extends="chrome://global/content/bindings/notification.xml#popup-notification">
+    <content align="start">
+      <xul:image class="popup-notification-icon"
+                 xbl:inherits="popupid"/>
+      <xul:vbox flex="1">
+        <xul:description class="popup-notification-description addon-progress-description"
+                         xbl:inherits="xbl:text=label"/>
+        <xul:spacer flex="1"/>
+        <xul:hbox align="center">
+          <xul:progressmeter anonid="progressmeter" flex="1" mode="undetermined" class="popup-progress-meter"/>
+          <xul:button anonid="cancel" class="popup-progress-cancel" oncommand="document.getBindingParent(this).cancel()"/>
+        </xul:hbox>
+        <xul:label anonid="progresstext" class="popup-progress-label"/>
+        <xul:hbox pack="end">
+          <xul:button anonid="button"
+                      class="popup-notification-menubutton"
+                      type="menu-button"
+                      xbl:inherits="oncommand=buttoncommand,label=buttonlabel,accesskey=buttonaccesskey">
+            <xul:menupopup anonid="menupopup"
+                           xbl:inherits="oncommand=menucommand">
+              <children/>
+              <xul:menuitem class="menuitem-iconic popup-notification-closeitem"
+                            label="&closeNotificationItem.label;"
+                            xbl:inherits="oncommand=closeitemcommand"/>
+            </xul:menupopup>
+          </xul:button>
+        </xul:hbox>
+      </xul:vbox>
+    </content>
+    <implementation>
+      <constructor><![CDATA[
+        this.cancelbtn.setAttribute("tooltiptext", gNavigatorBundle.getString("addonDownloadCancelTooltip"));
+
+        this.notification.options.installs.forEach(function(aInstall) {
+          aInstall.addListener(this);
+        }, this);
+
+        // Calling updateProgress can sometimes cause this notification to be
+        // removed in the middle of refreshing the notification panel which
+        // makes the panel get refreshed again. Just initialise to the
+        // undetermined state.
+        this.setProgress(0, -1);
+      ]]></constructor>
+
+      <destructor><![CDATA[
+        this.destroy();
+      ]]></destructor>
+
+      <field name="progressmeter" readonly="true">
+        document.getAnonymousElementByAttribute(this, "anonid", "progressmeter");
+      </field>
+      <field name="progresstext" readonly="true">
+        document.getAnonymousElementByAttribute(this, "anonid", "progresstext");
+      </field>
+      <field name="cancelbtn" readonly="true">
+        document.getAnonymousElementByAttribute(this, "anonid", "cancel");
+      </field>
+      <field name="DownloadUtils" readonly="true">
+        let utils = {};
+        Components.utils.import("resource://gre/modules/DownloadUtils.jsm", utils);
+        utils.DownloadUtils;
+      </field>
+
+      <method name="destroy">
+        <body><![CDATA[
+          this.notification.options.browser = null;
+          this.notification.options.contentWindow = null;
+          this.notification.options.sourceURI = null;
+          this.notification.options.installs.forEach(function(aInstall) {
+            aInstall.removeListener(this);
+          }, this);
+        ]]></body>
+      </method>
+
+      <method name="setProgress">
+        <parameter name="aProgress"/>
+        <parameter name="aMaxProgress"/>
+        <body><![CDATA[
+          if (aMaxProgress == -1) {
+            this.progressmeter.mode = "undetermined";
+          }
+          else {
+            this.progressmeter.mode = "determined";
+            this.progressmeter.value = (aProgress * 100) / aMaxProgress;
+          }
+
+          let now = Date.now();
+
+          if (!this.notification.lastUpdate) {
+            this.notification.lastUpdate = now;
+            this.notification.lastProgress = aProgress;
+            return;
+          }
+
+          let delta = now - this.notification.lastUpdate;
+          if ((delta < 400) && (aProgress < aMaxProgress))
+            return;
+
+          delta /= 1000;
+
+          // This code is taken from nsDownloadManager.cpp
+          let speed = (aProgress - this.notification.lastProgress) / delta;
+          if (this.notification.speed)
+            speed = speed * 0.9 + this.notification.speed * 0.1;
+
+          this.notification.lastUpdate = now;
+          this.notification.lastProgress = aProgress;
+          this.notification.speed = speed;
+
+          let status = null;
+          [status, this.notification.last] = this.DownloadUtils.getDownloadStatus(aProgress, aMaxProgress, speed, this.notification.last);
+          this.progresstext.value = status;
+        ]]></body>
+      </method>
+
+      <method name="cancel">
+        <body><![CDATA[
+          // Cache these as cancelling the installs will remove this
+          // notification and call destroy
+          let browser = this.notification.browser;
+          let contentWindow = this.notification.options.contentWindow;
+          let sourceURI = this.notification.options.sourceURI;
+
+          let installs = this.notification.options.installs;
+          installs.forEach(function(aInstall) {
+            try {
+              aInstall.cancel();
+            }
+            catch (e) {
+              // Cancel will throw if the download has already failed
+            }
+          }, this);
+
+          let anchorID = "addons-notification-icon";
+          let notificationID = "addon-install-cancelled";
+          let messageString = gNavigatorBundle.getString("addonDownloadCancelled");
+          messageString = PluralForm.get(installs.length, messageString);
+          let buttonText = gNavigatorBundle.getString("addonDownloadRestart");
+          buttonText = PluralForm.get(installs.length, buttonText);
+
+          let action = {
+            label: buttonText,
+            accessKey: gNavigatorBundle.getString("addonDownloadRestart.accessKey"),
+            callback: function() {
+              let weblistener = Cc["@mozilla.org/addons/web-install-listener;1"].
+                                getService(Ci.amIWebInstallListener);
+              if (weblistener.onWebInstallRequested(contentWindow, sourceURI,
+                                                    installs, installs.length)) {
+                installs.forEach(function(aInstall) {
+                  aInstall.install();
+                });
+              }
+            }
+          };
+
+          PopupNotifications.show(browser, notificationID, messageString,
+                                  anchorID, action);
+        ]]></body>
+      </method>
+
+      <method name="updateProgress">
+        <body><![CDATA[
+          let downloadingCount = 0;
+          let progress = 0;
+          let maxProgress = 0;
+
+          this.notification.options.installs.forEach(function(aInstall) {
+            if (aInstall.maxProgress == -1)
+              maxProgress = -1;
+            progress += aInstall.progress;
+            if (maxProgress >= 0)
+              maxProgress += aInstall.maxProgress;
+            if (aInstall.state < AddonManager.STATE_DOWNLOADED)
+              downloadingCount++;
+          });
+
+          if (downloadingCount == 0) {
+            this.destroy();
+            PopupNotifications.remove(this.notification);
+          }
+          else {
+            this.setProgress(progress, maxProgress);
+          }
+        ]]></body>
+      </method>
+
+      <method name="onDownloadProgress">
+        <body><![CDATA[
+          this.updateProgress();
+        ]]></body>
+      </method>
+
+      <method name="onDownloadFailed">
+        <body><![CDATA[
+          this.updateProgress();
+        ]]></body>
+      </method>
+
+      <method name="onDownloadCancelled">
+        <body><![CDATA[
+          this.updateProgress();
+        ]]></body>
+      </method>
+
+      <method name="onDownloadEnded">
+        <body><![CDATA[
+          this.updateProgress();
+        ]]></body>
+      </method>
+    </implementation>
+  </binding>
+
   <binding id="menuitem-tooltip" extends="chrome://global/content/bindings/menu.xml#menuitem">
     <implementation>
       <constructor><![CDATA[
         this.setAttribute("tooltiptext", this.getAttribute("acceltext"));
         // TODO: Simplify this to this.setAttribute("acceltext", "") once bug
         // 592424 is fixed
         document.getAnonymousElementByAttribute(this, "anonid", "accel").firstChild.setAttribute("value", "");
       ]]></constructor>
--- a/browser/components/sessionstore/test/browser/browser_522545.js
+++ b/browser/components/sessionstore/test/browser/browser_522545.js
@@ -50,27 +50,16 @@ function test() {
   is(browserWindowsCount(), 1, "Only one browser window should be open initially");
 
   waitForExplicitFinish();
   requestLongerTimeout(2);
 
   let ss = Cc["@mozilla.org/browser/sessionstore;1"].
            getService(Ci.nsISessionStore);
 
-  function waitForBrowserState(aState, aSetStateCallback) {
-    let tabsRestored = 0;
-    gBrowser.tabContainer.addEventListener("SSTabRestored", function() {
-      if (++tabsRestored == aState.windows[0].tabs.length) {
-        gBrowser.tabContainer.removeEventListener("SSTabRestored", arguments.callee, true);
-        executeSoon(aSetStateCallback);
-      }
-    }, true);
-    ss.setBrowserState(JSON.stringify(aState));
-  }
-
   // This tests the following use case:
   // User opens a new tab which gets focus. The user types something into the
   // address bar, then crashes or quits.
   function test_newTabFocused() {
     let state = {
       windows: [{
         tabs: [
           { entries: [{ url: "about:mozilla" }] },
@@ -251,28 +240,30 @@ function test() {
       is(browser.userTypedValue, null, "userTypedValue is empty to start");
       is(browser.userTypedClear, 0, "userTypedClear is 0 to start");
 
       gURLBar.value = "example.org";
       let event = document.createEvent("Events");
       event.initEvent("input", true, false);
       gURLBar.dispatchEvent(event);
 
-      is(browser.userTypedValue, "example.org",
-         "userTypedValue was set when changing gURLBar.value");
-      is(browser.userTypedClear, 0,
-         "userTypedClear was not changed when changing gURLBar.value");
+      executeSoon(function() {
+        is(browser.userTypedValue, "example.org",
+           "userTypedValue was set when changing gURLBar.value");
+        is(browser.userTypedClear, 0,
+           "userTypedClear was not changed when changing gURLBar.value");
 
-      // Now make sure ss gets these values too
-      let newState = JSON.parse(ss.getBrowserState());
-      is(newState.windows[0].tabs[0].userTypedValue, "example.org",
-         "sessionstore got correct userTypedValue");
-      is(newState.windows[0].tabs[0].userTypedClear, 0,
-         "sessionstore got correct userTypedClear");
-      runNextTest();
+        // Now make sure ss gets these values too
+        let newState = JSON.parse(ss.getBrowserState());
+        is(newState.windows[0].tabs[0].userTypedValue, "example.org",
+           "sessionstore got correct userTypedValue");
+        is(newState.windows[0].tabs[0].userTypedClear, 0,
+           "sessionstore got correct userTypedClear");
+        runNextTest();
+      });
     });
   }
 
   // test_getBrowserState_lotsOfTabsOpening tested userTypedClear in a few cases,
   // but not necessarily any that had legitimate URIs in the state of loading
   // (eg, "http://example.com"), so this test will cover that case.
   function test_userTypedClearLoadURI() {
     let state = {
@@ -298,19 +289,24 @@ function test() {
   }
 
 
   let tests = [test_newTabFocused, test_newTabNotFocused,
                test_existingSHEnd_noClear, test_existingSHMiddle_noClear,
                test_getBrowserState_lotsOfTabsOpening,
                test_getBrowserState_userTypedValue, test_userTypedClearLoadURI];
   let originalState = ss.getBrowserState();
+  let state = {
+    windows: [{
+      tabs: [{ entries: [{ url: "about:blank" }] }]
+    }]
+  };
   function runNextTest() {
     if (tests.length) {
-      tests.shift()();
+      waitForBrowserState(state, tests.shift());
     } else {
       ss.setBrowserState(originalState);
       executeSoon(function () {
         is(browserWindowsCount(), 1, "Only one browser window should be open eventually");
         finish();
       });
     }
   }
--- a/browser/components/shell/src/nsMacShellService.cpp
+++ b/browser/components/shell/src/nsMacShellService.cpp
@@ -427,17 +427,17 @@ nsMacShellService::OpenApplicationWithUR
   if (!uri) 
     return NS_ERROR_OUT_OF_MEMORY;
   
   CFArrayRef uris = ::CFArrayCreate(NULL, (const void**)&uri, 1, NULL);
   if (!uris) {
     ::CFRelease(uri);
     return NS_ERROR_OUT_OF_MEMORY;
   }
-  
+
   LSLaunchURLSpec launchSpec;
   launchSpec.appURL = appURL;
   launchSpec.itemURLs = uris;
   launchSpec.passThruParams = NULL;
   launchSpec.launchFlags = kLSLaunchDefaults;
   launchSpec.asyncRefCon = NULL;
   
   OSErr err = ::LSOpenFromURLSpec(&launchSpec, NULL);
--- a/browser/themes/browserShared.inc
+++ b/browser/themes/browserShared.inc
@@ -1,3 +1,3 @@
 %filter substitution
 
-%define primaryToolbarButtons #back-button, #forward-button, #reload-button, #stop-button, #home-button, #print-button, #downloads-button, #history-button, #bookmarks-button, #bookmarks-menu-button, #new-tab-button, #new-window-button, #cut-button, #copy-button, #paste-button, #fullscreen-button, #zoom-out-button, #zoom-in-button, #sync-button, #feed-button
+%define primaryToolbarButtons #back-button, #forward-button, #reload-button, #stop-button, #home-button, #print-button, #downloads-button, #history-button, #bookmarks-button, #bookmarks-menu-button, #new-tab-button, #new-window-button, #cut-button, #copy-button, #paste-button, #fullscreen-button, #zoom-out-button, #zoom-in-button, #sync-button, #feed-button, #alltabs-button, #tabview-button
--- a/browser/themes/gnomestripe/browser/browser.css
+++ b/browser/themes/gnomestripe/browser/browser.css
@@ -1133,24 +1133,49 @@ toolbar[iconsize="small"] #feed-button {
   -moz-margin-end: 10px;
 }
 
 .popup-notification-icon[popupid="geolocation"] {
   list-style-image: url(chrome://browser/skin/Geolocation-64.png);
 }
 
 .popup-notification-icon[popupid="xpinstall-disabled"],
+.popup-notification-icon[popupid="addon-progress"],
+.popup-notification-icon[popupid="addon-install-cancelled"],
 .popup-notification-icon[popupid="addon-install-blocked"],
 .popup-notification-icon[popupid="addon-install-failed"],
 .popup-notification-icon[popupid="addon-install-complete"] {
   list-style-image: url(chrome://mozapps/skin/extensions/extensionGeneric.png);
   width: 32px;
   height: 32px;
 }
 
+.addon-progress-description {
+  width: 350px;
+  max-width: 350px;
+}
+
+.popup-progress-label,
+.popup-progress-meter {
+  -moz-margin-start: 0;
+  -moz-margin-end: 0;
+}
+
+.popup-progress-cancel {
+  -moz-appearance: none;
+  background: transparent;
+  border: none;
+  padding: 0;
+  margin: 0;
+  -moz-margin-start: 5px;
+  min-height: 0;
+  min-width: 0;
+  list-style-image: url("moz-icon://stock/gtk-cancel?size=menu");
+}
+
 .popup-notification-icon[popupid="indexedDB-permissions-prompt"],
 .popup-notification-icon[popupid="indexedDB-quota-prompt"] {
   list-style-image: url(chrome://global/skin/icons/question-64.png);
 }
 
 .popup-notification-icon[popupid="password-save"],
 .popup-notification-icon[popupid="password-change"] {
   list-style-image: url(chrome://mozapps/skin/passwordmgr/key-64.png);
--- a/browser/themes/pinstripe/browser/browser.css
+++ b/browser/themes/pinstripe/browser/browser.css
@@ -192,17 +192,16 @@ toolbarbutton.bookmark-item > menupopup 
   margin-top: 2px;
   -moz-margin-start: 3px;
 }
 
 .bookmark-item > .toolbarbutton-icon {
   width: 16px;
   min-height: 16px;
   max-height: 16px;
-  padding: 0;
 }
 
 .bookmark-item > .toolbarbutton-icon[label]:not([label=""]),
 .bookmark-item > .toolbarbutton-icon[type="menu"] {
   -moz-margin-end: 5px;
 }
 
 .bookmark-item[container] {
@@ -318,22 +317,21 @@ toolbar:not([mode="icons"]) #restore-but
 }
 
 toolbar:not([mode="icons"]) .toolbarbutton-1:not([type="menu-button"]),
 toolbar:not([mode="icons"]) .toolbarbutton-1 > .toolbarbutton-menubutton-button,
 toolbar:not([mode="icons"]) #restore-button {
   min-width: 0;
 }
 
-.toolbarbutton-1 > .toolbarbutton-icon,
-.toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-icon,
-#restore-button > .toolbarbutton-icon {
-  padding: 0;
-  height: 20px;
-  width: 20px;
+.toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-icon,
+.toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-icon {
+  margin: 2px;
+  height: 16px;
+  width: 16px;
 }
 
 .toolbarbutton-1[disabled="true"] > .toolbarbutton-icon,
 .toolbarbutton-1[type="menu-button"] > .toolbarbutton-menubutton-button[disabled="true"] > .toolbarbutton-icon,
 #restore-button[disabled="true"] > .toolbarbutton-icon {
   opacity: .4;
 }
 
@@ -1757,17 +1755,16 @@ toolbarbutton.chevron > .toolbarbutton-m
 }
 
 /**
  * In-tab close button
  */
 
 .tab-close-button > .toolbarbutton-icon {
   -moz-margin-end: 0px !important;
-  padding: 0;
 }
 
 .tab-close-button {
   list-style-image: url("chrome://global/skin/icons/closetab.png");
   -moz-appearance: none;
   border: none !important;
   padding: 0;
   margin: 0;
@@ -1844,22 +1841,16 @@ toolbarbutton.chevron > .toolbarbutton-m
 
 :-moz-any(#TabsToolbar, #addon-bar) .toolbarbutton-1:not([type="menu-button"]),
 :-moz-any(#TabsToolbar, #addon-bar) .toolbarbutton-1 > .toolbarbutton-menubutton-button,
 :-moz-any(#TabsToolbar, #addon-bar) .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker {
   margin: 0;
   padding: 0 1px;
 }
 
-.tabs-newtab-button > .toolbarbutton-icon,
-:-moz-any(#TabsToolbar, #addon-bar) .toolbarbutton-1 > .toolbarbutton-icon,
-:-moz-any(#TabsToolbar, #addon-bar) .toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-icon {
-  padding: 0;
-}
-
 .tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled="true"]):hover,
 .tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled="true"]):hover,
 :-moz-any(#TabsToolbar, #addon-bar) .toolbarbutton-1:not([type="menu-button"]):not([disabled]):not([open]):hover,
 :-moz-any(#TabsToolbar, #addon-bar) .toolbarbutton-1 > .toolbarbutton-menubutton-button:not([disabled]):hover,
 :-moz-any(#TabsToolbar, #addon-bar) .toolbarbutton-1:not([disabled]):not([buttonover]):hover > .toolbarbutton-menubutton-dropmarker {
   background-image: -moz-linear-gradient(transparent, rgba(0,0,0,.15)) !important;
 }
 
@@ -2060,24 +2051,57 @@ toolbarbutton.chevron > .toolbarbutton-m
   -moz-margin-end: 10px;
 }
 
 .popup-notification-icon[popupid="geolocation"] {
   list-style-image: url(chrome://browser/skin/Geolocation-64.png);
 }
 
 .popup-notification-icon[popupid="xpinstall-disabled"],
+.popup-notification-icon[popupid="addon-progress"],
+.popup-notification-icon[popupid="addon-install-cancelled"],
 .popup-notification-icon[popupid="addon-install-blocked"],
 .popup-notification-icon[popupid="addon-install-failed"],
 .popup-notification-icon[popupid="addon-install-complete"] {
   list-style-image: url(chrome://mozapps/skin/extensions/extensionGeneric.png);
   width: 32px;
   height: 32px;
 }
 
+.addon-progress-description {
+  width: 350px;
+  max-width: 350px;
+}
+
+.popup-progress-label,
+.popup-progress-meter {
+  -moz-margin-start: 0;
+  -moz-margin-end: 0;
+}
+
+.popup-progress-cancel {
+  -moz-appearance: none;
+  min-height: 16px;
+  min-width: 16px;
+  max-height: 16px;
+  max-width: 16px;
+  padding: 0;
+  margin: 0 1px 0 1px;
+  list-style-image: url(chrome://mozapps/skin/downloads/buttons.png);
+  -moz-image-region: rect(0px, 16px, 16px, 0px);
+}
+
+.popup-progress-cancel:hover {
+  -moz-image-region: rect(0px, 32px, 16px, 16px);
+}
+
+.popup-progress-cancel:active {
+  -moz-image-region: rect(0px, 48px, 16px, 32px);
+}
+
 #indexedDB-notification-icon {
   list-style-image: url(chrome://global/skin/icons/question-16.png);
 }
 
 .popup-notification-icon[popupid="indexedDB-permissions-prompt"],
 .popup-notification-icon[popupid="indexedDB-quota-prompt"] {
   list-style-image: url(chrome://global/skin/icons/question-64.png);
 }
--- a/browser/themes/pinstripe/browser/places/organizer.css
+++ b/browser/themes/pinstripe/browser/places/organizer.css
@@ -106,17 +106,17 @@
   background-image: @toolbarbuttonInactiveBackgroundImage@;
 }
 
 #placesToolbar > toolbarbutton[disabled="true"] > .toolbarbutton-icon {
   opacity: 0.5;
 }
 
 #placesToolbar > toolbarbutton > .toolbarbutton-icon {
-  margin: 0 4px;
+  margin: 1px 4px;
 }
 
 #placesToolbar > toolbarbutton > .toolbarbutton-text {
   display: none;
 }
 
 #placesToolbar > toolbarbutton[type="menu"] > .toolbarbutton-menu-dropmarker {
   list-style-image: url(chrome://browser/skin/toolbarbutton-dropmarker.png);
--- a/browser/themes/winstripe/browser/browser.css
+++ b/browser/themes/winstripe/browser/browser.css
@@ -1640,24 +1640,16 @@ richlistitem[type~="action"][actiontype=
   -moz-transition: 1s background-color ease-out;
 }
 
 .tabbrowser-arrowscrollbox > .scrollbutton-down[notifybgtab] {
   background-color: Highlight;
   -moz-transition: none;
 }
 
-.tabs-newtab-button > .toolbarbutton-icon,
-#TabsToolbar > #new-tab-button > .toolbarbutton-icon,
-#TabsToolbar > toolbarpaletteitem > #new-tab-button > .toolbarbutton-icon,
-#alltabs-button > .toolbarbutton-icon {
-  width: auto;
-  height: auto;
-}
-
 .tabs-newtab-button > .toolbarbutton-icon {
   margin-top: -1px;
   margin-bottom: -1px;
 }
 
 .tabs-newtab-button,
 #TabsToolbar > #new-tab-button,
 #TabsToolbar > toolbarpaletteitem > #new-tab-button {
@@ -1877,24 +1869,57 @@ toolbarbutton.bookmark-item[dragover="tr
   list-style-image: url(chrome://browser/skin/Geolocation-64.png);
 }
 
 .geolocation-text-link {
   margin-top: 17px;
 }
 
 .popup-notification-icon[popupid="xpinstall-disabled"],
+.popup-notification-icon[popupid="addon-progress"],
+.popup-notification-icon[popupid="addon-install-cancelled"],
 .popup-notification-icon[popupid="addon-install-blocked"],
 .popup-notification-icon[popupid="addon-install-failed"],
 .popup-notification-icon[popupid="addon-install-complete"] {
   list-style-image: url(chrome://mozapps/skin/extensions/extensionGeneric.png);
   width: 32px;
   height: 32px;
 }
 
+.addon-progress-description {
+  width: 350px;
+  max-width: 350px;
+}
+
+.popup-progress-label,
+.popup-progress-meter {
+  -moz-margin-start: 0;
+  -moz-margin-end: 0;
+}
+
+.popup-progress-cancel {
+  -moz-appearance: none;
+  background: transparent;
+  border: none;
+  padding: 0;
+  margin: 0;
+  min-height: 0;
+  min-width: 0;
+  list-style-image: url(chrome://mozapps/skin/downloads/downloadButtons.png);
+  -moz-image-region: rect(0px, 32px, 16px, 16px);
+}
+
+.popup-progress-cancel:hover {
+  -moz-image-region: rect(16px, 32px, 32px, 16px);
+}
+
+.popup-progress-cancel:active {
+  -moz-image-region: rect(32px, 32px, 48px, 16px);
+}
+
 .popup-notification-icon[popupid="indexedDB-permissions-prompt"],
 .popup-notification-icon[popupid="indexedDB-quota-prompt"] {
   list-style-image: url(chrome://global/skin/icons/question-64.png);
 }
 
 .popup-notification-icon[popupid="password-save"],
 .popup-notification-icon[popupid="password-change"] {
   list-style-image: url(chrome://mozapps/skin/passwordmgr/key-64.png);
--- a/build/automation.py.in
+++ b/build/automation.py.in
@@ -862,17 +862,17 @@ user_pref("camino.use_system_proxy_setti
     tmpfd, processLog = tempfile.mkstemp(suffix='pidlog')
     os.close(tmpfd)
     env["MOZ_PROCESS_LOG"] = processLog
 
     if self.IS_TEST_BUILD and runSSLTunnel:
       # create certificate database for the profile
       certificateStatus = self.fillCertificateDB(profileDir, certPath, utilityPath, xrePath)
       if certificateStatus != 0:
-        self.log.info("TEST-UNEXPECTED FAIL | automation.py | Certificate integration failed")
+        self.log.info("TEST-UNEXPECTED-FAIL | automation.py | Certificate integration failed")
         return certificateStatus
 
       # start ssltunnel to provide https:// URLs capability
       ssltunnel = os.path.join(utilityPath, "ssltunnel" + self.BIN_SUFFIX)
       ssltunnelProcess = self.Process([ssltunnel, 
                                os.path.join(profileDir, "ssltunnel.cfg")], 
                                env = self.environment(xrePath = xrePath))
       self.log.info("INFO | automation.py | SSL tunnel pid: %d", ssltunnelProcess.pid)
@@ -921,19 +921,21 @@ user_pref("camino.use_system_proxy_setti
                      "extensions" directory there if it doesn't exist
    extensionID     - the id of the extension to be used as the containing directory for the
                      extension, i.e.
                  this is the name of the folder in the <profileDir>/extensions/<extensionID>
   """
   def installExtension(self, extensionSource, profileDir, extensionID):
     if (not os.path.exists(extensionSource)):
       self.log.info("INFO | automation.py | Cannot install extension no source at: %s", extensionSource) 
+      return
       
     if (not os.path.exists(profileDir)):
       self.log.info("INFO | automation.py | Cannot install extension invalid profileDir at: %s", profileDir)
+      return
 
     # See if we have an XPI or a directory
     if (os.path.isfile(extensionSource)):
       tmpd = tempfile.mkdtemp()
       extrootdir = self.extractZip(extensionSource, tmpd)
     else:
       extrootdir = extensionSource 
     extnsdir = os.path.join(profileDir, "extensions")
--- a/caps/src/nsScriptSecurityManager.cpp
+++ b/caps/src/nsScriptSecurityManager.cpp
@@ -2172,30 +2172,31 @@ nsScriptSecurityManager::GetScriptPrinci
 // static
 nsIPrincipal*
 nsScriptSecurityManager::GetFunctionObjectPrincipal(JSContext *cx,
                                                     JSObject *obj,
                                                     JSStackFrame *fp,
                                                     nsresult *rv)
 {
     NS_PRECONDITION(rv, "Null out param");
+
+    *rv = NS_OK;
+
     if (!JS_ObjectIsFunction(cx, obj))
     {
         // Protect against pseudo-functions (like SJOWs).
         nsIPrincipal *result = doGetObjectPrincipal(obj);
         if (!result)
             *rv = NS_ERROR_FAILURE;
         return result;
     }
 
     JSFunction *fun = GET_FUNCTION_PRIVATE(cx, obj);
     JSScript *script = JS_GetFunctionScript(cx, fun);
 
-    *rv = NS_OK;
-
     if (!script)
     {
         // A native function: skip it in order to find its scripted caller.
         return nsnull;
     }
 
     JSScript *frameScript = fp ? JS_GetFrameScript(cx, fp) : nsnull;
 
--- a/content/base/src/nsGenericElement.cpp
+++ b/content/base/src/nsGenericElement.cpp
@@ -5349,20 +5349,17 @@ nsGenericElement::PostHandleEventForLink
           aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
         }
       }
     }
     break;
 
   case NS_UI_ACTIVATE:
     {
-      nsCOMPtr<nsIContent> targetContent;
-      aVisitor.mPresContext->EventStateManager()->
-        GetEventTargetContent(aVisitor.mEvent, getter_AddRefs(targetContent));
-      if (targetContent == this) {
+      if (aVisitor.mEvent->originalTarget == this) {
         nsAutoString target;
         GetLinkTarget(target);
         nsContentUtils::TriggerLink(this, aVisitor.mPresContext, absURI, target,
                                     PR_TRUE, PR_TRUE);
         aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
       }
     }
     break;
--- a/content/base/test/Makefile.in
+++ b/content/base/test/Makefile.in
@@ -448,16 +448,18 @@ include $(topsrcdir)/config/rules.mk
 		file_bug604660-4.js \
 		file_bug604660-5.xml \
 		file_bug604660-6.xsl \
 		test_bug605982.html \
 		test_bug606729.html \
 		test_treewalker_nextsibling.xml \
 		test_bug614058.html \
 		test_bug590771.html \
+		test_bug622117.html \
+		test_bug622246.html \
 		$(NULL)
 
 # This test fails on the Mac for some reason
 ifneq (,$(filter gtk2 windows,$(MOZ_WIDGET_TOOLKIT)))
 _TEST_FILES2 += 	test_copyimage.html \
 		$(NULL)
 endif
 
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_bug622117.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=622117
+-->
+<head>
+  <title>Test for Bug 622117</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>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=622117">Mozilla Bug 622117</a>
+<p id="display">
+  <iframe id="testframe"
+          src="data:text/html,<a href='data:text/plain,PASS' onclick='throw 1'>Click me</a>">
+  </iframe>
+</p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 622117 **/
+SimpleTest.waitForExplicitFinish();
+
+const PASSURL = "data:text/plain,PASS";
+
+SimpleTest.waitForFocus(function() {
+  $("testframe").onload = function() {
+    is(this.contentDocument.documentElement.textContent, "PASS", "Should have loaded link");
+    SimpleTest.finish();
+  };
+
+  var win = $("testframe").contentWindow;
+  var a = win.document.getElementsByTagName("a")[0];
+  synthesizeMouseAtCenter(a, {}, win);
+});
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_bug622246.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=622246
+-->
+<head>
+  <title>Test for Bug 622246</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>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=622246">Mozilla Bug 622246</a>
+<p id="display">
+  <iframe id="testframe"
+          src="data:text/html,<span onclick='this.parentNode.removeChild(this)'><a href='data:text/plain,PASS'>Click me</a></span>">
+  </iframe>
+</p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 622246 **/
+SimpleTest.waitForExplicitFinish();
+
+const PASSURL = "data:text/plain,PASS";
+
+SimpleTest.waitForFocus(function() {
+  $("testframe").onload = function() {
+    is(this.contentDocument.documentElement.textContent, "PASS", "Should have loaded link");
+    SimpleTest.finish();
+  };
+
+  var win = $("testframe").contentWindow;
+  var a = win.document.getElementsByTagName("a")[0];
+  synthesizeMouseAtCenter(a, {}, win);
+});
+</script>
+</pre>
+</body>
+</html>
--- a/content/base/test/test_ws_basic_tests.html
+++ b/content/base/test/test_ws_basic_tests.html
@@ -52,16 +52,17 @@ function testWebSocket () {
   ws.onopen = function(e) {
     for (var i = 0; i < params.length; ++i) {
       document.getElementById('log').textContent += "sending " + params[i] + "\n";
       ws.send(params[i]);
     }
   }
   ws.onclose = function(e) {
     is(results.length, 0, "All the messages should have been processed!");
+    ok(e.wasClean, "Connection closed cleanly");
     testWebSocket2();
   }
   ws.onerror = function(e) {
     ok(false, "onerror called!");
     finishWSTest();
   }
   ws.onmessage = function(e) {
     document.getElementById('log').textContent += "\n" + e.data;
@@ -78,16 +79,17 @@ function testWebSocket2() {
   ws.onopen = function(e) {
     for (var i = 0; i < testCount; ++i) {
       ws.send(testMessage + (i + 1));
     }
     ws.send("end");
   }
   ws.onclose = function(e) {
     is(messageCount, testCount, "Didn't receive all the messages!");
+    ok(e.wasClean, "Connection closed cleanly");
     testWebSocket3();
   }
   ws.onerror = function(e) {
     ok(false, "onerror called!");
     finishWSTest();
   }
   ws.onmessage = function(e) {
     ++messageCount;
@@ -108,16 +110,17 @@ function testWebSocket3() {
     for (var i = 0; i < testCount; ++i) {
       forcegc(); // Do something evil, call cycle collector a lot.
       ws.send(testMessage + (i + 1));
     }
     ws.send("end");
   }
   ws.onclose = function(e) {
     is(messageCount, testCount, "Didn't receive all the messages!");
+    ok(e.wasClean, "Connection closed cleanly");
     testWebSocket4();
   }
   ws.onerror = function(e) {
     ok(false, "onerror called!");
     finishWSTest();
   }
   ws.onmessage = function(e) {
     forcegc(); // Do something evil, call cycle collector a lot.
@@ -127,45 +130,48 @@ function testWebSocket3() {
     if (messageCount == testCount) {
       this.onmessage = null;
     }
   }
 }
 
 function testWebSocket4() {
   ws = new WebSocket("ws://mochi.test:8888/tests/content/base/test/file_ws_basic_tests", "test");
+  // String length = (10,000 - 1) * 23 = 229,977 = almost 225 KiB.
   var longString = new Array(10000).join("-huge websocket message");
   ws.onopen = function(e) {
     is(this, ws, "'this' should point to the WebSocket. (1)");
     ws.send(longString);
   }
   ws.onclose = function(e) {
     is(this, ws, "'this' should point to the WebSocket. (2)");
-    //ok(e.wasClean, "Connection should have closed cleanly.");
+    ok(e.wasClean, "Connection closed cleanly");
     testWebSocket5();
   }
   ws.onerror = function(e) {
     ok(false, "onerror called!");
     finishWSTest();
   }
   ws.onmessage = function(e) {
     is(this, ws, "'this' should point to the WebSocket. (3)");
-    is(e.data, longString, "Didn't get the huge message back!");
+    // Do not use |is(e.data, longString, "...");| that results in a _very_ long line.
+    is(e.data.length, longString.length, "Length of received message");
+    ok(e.data == longString, "Content of received message");
     document.getElementById('log').textContent += "\nReceived the huge message";
     this.close();
   }
 }
 
 function testWebSocket5() {
   ws = new WebSocket("ws://mochi.test:8888/tests/content/base/test/file_ws_basic_tests", "test");
   ws.onopen = function(e) {
     this.close();
   }
   ws.onclose = function(e) {
-    //ok(e.wasClean, "Connection should have closed cleanly.");
+    ok(e.wasClean, "Connection closed cleanly");
     is(this.bufferedAmount, 0, "Shouldn't have anything buffered");
     var msg = "some data";
     this.send(msg);
     is(this.bufferedAmount, msg.length, "Should have some data buffered");
     finishWSTest();
   }
   ws.onerror = function(e) {
     ok(false, "onerror called!");
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -2975,17 +2975,18 @@ WebGLContext::ConvertImage(size_t width,
         // So the case we're handling here is when even though no format conversion is needed,
         // we still might have to flip vertically and/or to adjust to a different stride.
 
         size_t row_size = width * dstTexelSize; // doesn't matter, src and dst formats agree
         const PRUint8* src_row = src;
         const PRUint8* src_end = src + height * srcStride;
 
         PRUint8* dst_row = mPixelStoreFlipY ? dst + (height-1) * dstStride : dst;
-        ptrdiff_t dst_delta = mPixelStoreFlipY ? -dstStride : dstStride;
+        ptrdiff_t dstStrideSigned(dstStride);
+        ptrdiff_t dst_delta = mPixelStoreFlipY ? -dstStrideSigned : dstStrideSigned;
 
         while(src_row != src_end) {
             memcpy(dst_row, src_row, row_size);
             src_row += srcStride;
             dst_row += dst_delta;
         }
         return;
     }
--- a/content/canvas/src/WebGLContextNotSupported.cpp
+++ b/content/canvas/src/WebGLContextNotSupported.cpp
@@ -38,16 +38,17 @@
 
 #include "nsIDOMWebGLRenderingContext.h"
 #include "nsDOMClassInfoID.h"
 
 #define DUMMY(func,rtype)  nsresult func (rtype ** aResult) { return NS_ERROR_FAILURE; }
 
 DUMMY(NS_NewCanvasRenderingContextWebGL, nsIDOMWebGLRenderingContext)
 
-DOMCI_DATA(CanvasRenderingContextWebGL, void)
+DOMCI_DATA(WebGLRenderingContext, void)
 DOMCI_DATA(WebGLBuffer, void)
 DOMCI_DATA(WebGLTexture, void)
 DOMCI_DATA(WebGLProgram, void)
 DOMCI_DATA(WebGLShader, void)
 DOMCI_DATA(WebGLFramebuffer, void)
 DOMCI_DATA(WebGLRenderbuffer, void)
 DOMCI_DATA(WebGLUniformLocation, void)
+DOMCI_DATA(WebGLActiveInfo, void)
--- a/content/media/nsAudioStream.cpp
+++ b/content/media/nsAudioStream.cpp
@@ -296,48 +296,61 @@ void nsAudioStream::InitLibrary()
   gAudioStreamLog = PR_NewLogModule("nsAudioStream");
 #endif
 }
 
 void nsAudioStream::ShutdownLibrary()
 {
 }
 
-
 nsIThread *
 nsAudioStream::GetThread()
 {
+  if (!mAudioPlaybackThread) {
+    NS_NewThread(getter_AddRefs(mAudioPlaybackThread));
+  }
   return mAudioPlaybackThread;
 }
 
 nsAudioStream* nsAudioStream::AllocateStream()
 {
 #ifdef MOZ_IPC
   if (XRE_GetProcessType() == GeckoProcessType_Content) {
     return new nsAudioStreamRemote();
   }
 #endif
   return new nsAudioStreamLocal();
 }
 
+class AsyncShutdownPlaybackThread : public nsRunnable
+{
+public:
+  AsyncShutdownPlaybackThread(nsIThread* aThread) : mThread(aThread) {}
+  NS_IMETHODIMP Run() { return mThread->Shutdown(); }
+private:
+  nsCOMPtr<nsIThread> mThread;
+};
+
+nsAudioStream::~nsAudioStream()
+{
+  if (mAudioPlaybackThread) {
+    nsCOMPtr<nsIRunnable> event = new AsyncShutdownPlaybackThread(mAudioPlaybackThread);
+    NS_DispatchToMainThread(event);
+  }
+}
+
 nsAudioStreamLocal::nsAudioStreamLocal() :
   mVolume(1.0),
   mAudioHandle(0),
   mRate(0),
   mChannels(0),
   mFormat(FORMAT_S16_LE),
   mPaused(PR_FALSE),
   mInError(PR_FALSE)
 {
-#ifdef MOZ_IPC
-  // We only need this thread in the main process.
-  if (XRE_GetProcessType() == GeckoProcessType_Default) {
-    NS_NewThread(getter_AddRefs(mAudioPlaybackThread));
-  }
-#endif
 }
 
 nsAudioStreamLocal::~nsAudioStreamLocal()
 {
   Shutdown();
 }
 
 NS_IMPL_THREADSAFE_ISUPPORTS0(nsAudioStreamLocal)
--- a/content/media/nsAudioStream.h
+++ b/content/media/nsAudioStream.h
@@ -49,27 +49,29 @@ public:
 
   enum SampleFormat
   {
     FORMAT_U8,
     FORMAT_S16_LE,
     FORMAT_FLOAT32
   };
 
+  virtual ~nsAudioStream();
+
   // Initialize Audio Library. Some Audio backends require initializing the
   // library before using it. 
   static void InitLibrary();
 
   // Shutdown Audio Library. Some Audio backends require shutting down the
   // library after using it.
   static void ShutdownLibrary();
 
   // Thread, usually for MOZ_IPC handling, that is shared between audio streams.
   // This may return null in the child process
-  virtual nsIThread *GetThread();
+  nsIThread *GetThread();
 
   // AllocateStream will return either a local stream or a remoted stream
   // depending on where you call it from.  If MOZ_IPC is enabled, and you
   // call this from a child process, you may recieve an implementation which
   // forwards to a compositing process.
   static nsAudioStream* AllocateStream();
 
   // Initialize the audio stream. aNumChannels is the number of audio channels 
--- a/content/smil/test/db_smilCSSFromBy.js
+++ b/content/smil/test/db_smilCSSFromBy.js
@@ -56,27 +56,27 @@ var _fromByTestLists =
     new AnimTestcaseFromBy("0", "50",  { fromComp: "0px", // 0 acts like 0px
                                          midComp:  "25px",
                                          toComp:   "50px"}),
     new AnimTestcaseFromBy("30", "10", { fromComp: "30px",
                                          midComp:  "35px",
                                          toComp:   "40px"}),
   ],
   lengthNoUnitsSVG: [
-    new AnimTestcaseFromBy("0", "50",  { fromComp: "0px", // 0 acts like 0px
+    new AnimTestcaseFromBy("0", "50",  { fromComp: "0",
                                          midComp:  "25",
                                          toComp:   "50"}),
     new AnimTestcaseFromBy("30", "10", { fromComp: "30",
                                          midComp:  "35",
                                          toComp:   "40"}),
   ],
   lengthPx: [
-    new AnimTestcaseFromBy("0", "8px", { fromComp: "0px", // 0 acts like 0px
-                                         midComp: "4px",
-                                         toComp: "8px"}),
+    new AnimTestcaseFromBy("0px", "8px", { fromComp: "0px",
+                                           midComp: "4px",
+                                           toComp: "8px"}),
     new AnimTestcaseFromBy("1px", "10px", { midComp: "6px", toComp: "11px"}),
   ],
   opacity: [
     new AnimTestcaseFromBy("1", "-1", { midComp: "0.5", toComp: "0"}),
     new AnimTestcaseFromBy("0.4", "-0.6", { midComp: "0.1", toComp: "0"}),
     new AnimTestcaseFromBy("0.8", "-1.4", { midComp: "0.1", toComp: "0"},
                            "opacities with abs val >1 get clamped too early"),
     new AnimTestcaseFromBy("1.2", "-0.6", { midComp: "0.9", toComp: "0.6"},
--- a/content/smil/test/db_smilCSSFromTo.js
+++ b/content/smil/test/db_smilCSSFromTo.js
@@ -112,31 +112,31 @@ var _fromToTestLists = {
     new AnimTestcaseFromTo("50",  "0", { fromComp: "50px",
                                          midComp:  "25px",
                                          toComp:    "0px"}),
     new AnimTestcaseFromTo("30", "80", { fromComp: "30px",
                                          midComp:  "55px",
                                          toComp:   "80px"}),
   ],
   lengthNoUnitsSVG: [
-    new AnimTestcaseFromTo("0",  "20", { fromComp:  "0px", // 0 acts like 0px
+    new AnimTestcaseFromTo("0",  "20", { fromComp:  "0",
                                          midComp:  "10",
                                          toComp:   "20"}),
     new AnimTestcaseFromTo("50",  "0", { fromComp: "50",
                                          midComp:  "25",
-                                         toComp:    "0px"}), // 0 acts like 0px
+                                         toComp:    "0"}),
     new AnimTestcaseFromTo("30", "80", { fromComp: "30",
                                          midComp:  "55",
                                          toComp:   "80"}),
   ],
   lengthPx: [
-    new AnimTestcaseFromTo("0", "12px", { fromComp: "0px",  // 0 acts like 0px
-                                          midComp:  "6px"}),
-    new AnimTestcaseFromTo("16px", "0", { midComp: "8px",
-                                          toComp:  "0px"}), // 0 acts like 0px
+    new AnimTestcaseFromTo("0px", "12px", { fromComp: "0px",
+                                            midComp:  "6px"}),
+    new AnimTestcaseFromTo("16px", "0px", { midComp: "8px",
+                                            toComp:  "0px"}),
     new AnimTestcaseFromTo("10px", "20px", { midComp: "15px"}),
     new AnimTestcaseFromTo("41px", "1px", { midComp: "21px"}),
   ],
   lengthPctSVG: [
     new AnimTestcaseFromTo("20.5%", "0.5%", { midComp: "10.5%" }),
   ],
   lengthPxPctSVG: [
     new AnimTestcaseFromTo("10px", "10%", { midComp: "15px"},
--- a/content/smil/test/db_smilCSSPaced.js
+++ b/content/smil/test/db_smilCSSPaced.js
@@ -107,31 +107,31 @@ var _pacedTestLists =
                             comp2_3: "10px",
                             comp1:    "8px"
                           }),
   ],
   lengthNoUnitsSVG : [
     new AnimTestcasePaced("2; 0; 4",
                           { comp0:   "2",
                             comp1_6: "1",
-                            comp1_3: "0px",  // 0 acts like 0px
+                            comp1_3: "0",
                             comp2_3: "2",
                             comp1:   "4"
                           }),
     new AnimTestcasePaced("10; 12; 8",
                           { comp0:   "10",
                             comp1_6: "11",
                             comp1_3: "12",
                             comp2_3: "10",
                             comp1:   "8"
                           }),
   ],
   lengthPx : [
-    new AnimTestcasePaced("0; 2px; 6px",
-                          { comp0:   "0px", // 0 acts like 0px
+    new AnimTestcasePaced("0px; 2px; 6px",
+                          { comp0:   "0px",
                             comp1_6: "1px",
                             comp1_3: "2px",
                             comp2_3: "4px",
                             comp1:   "6px"
                           }),
   ],
   lengthPx : [
     new AnimTestcasePaced("0px; 2px; 6px",
--- a/content/svg/content/src/SVGAnimatedLengthList.cpp
+++ b/content/svg/content/src/SVGAnimatedLengthList.cpp
@@ -80,17 +80,17 @@ SVGAnimatedLengthList::SetBaseValueStrin
 
 void
 SVGAnimatedLengthList::ClearBaseValue(PRUint32 aAttrEnum)
 {
   DOMSVGAnimatedLengthList *domWrapper =
     DOMSVGAnimatedLengthList::GetDOMWrapperIfExists(this);
   if (domWrapper) {
     // We must send this notification *before* changing mBaseVal! (See above.)
-    domWrapper->InternalAnimValListWillChangeTo(SVGLengthList());
+    domWrapper->InternalBaseValListWillChangeTo(SVGLengthList());
   }
   mBaseVal.Clear();
   // Caller notifies
 }
 
 nsresult
 SVGAnimatedLengthList::SetAnimValue(const SVGLengthList& aNewAnimValue,
                                     nsSVGElement *aElement,
--- a/content/svg/content/src/SVGAnimatedNumberList.cpp
+++ b/content/svg/content/src/SVGAnimatedNumberList.cpp
@@ -80,17 +80,17 @@ SVGAnimatedNumberList::SetBaseValueStrin
 
 void
 SVGAnimatedNumberList::ClearBaseValue(PRUint32 aAttrEnum)
 {
   DOMSVGAnimatedNumberList *domWrapper =
     DOMSVGAnimatedNumberList::GetDOMWrapperIfExists(this);
   if (domWrapper) {
     // We must send this notification *before* changing mBaseVal! (See above.)
-    domWrapper->InternalAnimValListWillChangeTo(SVGNumberList());
+    domWrapper->InternalBaseValListWillChangeTo(SVGNumberList());
   }
   mBaseVal.Clear();
   // Caller notifies
 }
 
 nsresult
 SVGAnimatedNumberList::SetAnimValue(const SVGNumberList& aNewAnimValue,
                                     nsSVGElement *aElement,
--- a/content/svg/content/src/SVGPathData.cpp
+++ b/content/svg/content/src/SVGPathData.cpp
@@ -549,19 +549,19 @@ SVGPathData::GetMarkerPositioningData(ns
       segEndAngle = AngleOfVector(segEnd - cp1);
       i += 4;
       break;
     }
 
     case nsIDOMSVGPathSeg::PATHSEG_ARC_ABS:
     case nsIDOMSVGPathSeg::PATHSEG_ARC_REL:
     {
-      float rx = mData[i];
-      float ry = mData[i+1];
-      float angle = mData[i+2];
+      double rx = mData[i];
+      double ry = mData[i+1];
+      double angle = mData[i+2];
       PRBool largeArcFlag = mData[i+3] != 0.0f;
       PRBool sweepFlag = mData[i+4] != 0.0f;
       if (segType == nsIDOMSVGPathSeg::PATHSEG_ARC_ABS) {
         segEnd = gfxPoint(mData[i+5], mData[i+6]);
       } else {
         segEnd = segStart + gfxPoint(mData[i+5], mData[i+6]);
       }
 
@@ -590,56 +590,57 @@ SVGPathData::GetMarkerPositioningData(ns
         i += 7;
         break;
       }
       rx = fabs(rx); // F.6.6.1
       ry = fabs(ry);
 
       // F.6.5.1:
       angle = angle * M_PI/180.0;
-      float x1p = cos(angle) * (segStart.x - segEnd.x) / 2.0
-                + sin(angle) * (segStart.y - segEnd.y) / 2.0;
-      float y1p = -sin(angle) * (segStart.x - segEnd.x) / 2.0
-                 + cos(angle)  *(segStart.y - segEnd.y) / 2.0;
+      double x1p =  cos(angle) * (segStart.x - segEnd.x) / 2.0
+                  + sin(angle) * (segStart.y - segEnd.y) / 2.0;
+      double y1p = -sin(angle) * (segStart.x - segEnd.x) / 2.0
+                  + cos(angle) * (segStart.y - segEnd.y) / 2.0;
 
       // This is the root in F.6.5.2 and the numerator under that root:
-      float root;
-      float numerator = rx*rx*ry*ry - rx*rx*y1p*y1p - ry*ry*x1p*x1p;
+      double root;
+      double numerator = rx*rx*ry*ry - rx*rx*y1p*y1p - ry*ry*x1p*x1p;
 
-      if (numerator < 0.0) {
-        // F.6.6 step 3 - |numerator < 0.0| is equivalent to the result of
-        // F.6.6.2 (lamedh) being greater than one. What we have here is radii
-        // that do not reach between segStart and segEnd, so we need to correct
-        // them.
-        float lamedh = 1.0 - numerator/(rx*rx*ry*ry); // equiv to eqn F.6.6.2
-        float s = sqrt(lamedh);
+      if (numerator >= 0.0) {
+        root = sqrt(numerator/(rx*rx*y1p*y1p + ry*ry*x1p*x1p));
+        if (largeArcFlag == sweepFlag)
+          root = -root;
+      } else {
+        // F.6.6 step 3 - |numerator < 0.0|. This is equivalent to the result
+        // of F.6.6.2 (lamedh) being greater than one. What we have here is
+        // ellipse radii that are too small for the ellipse to reach between
+        // segStart and segEnd. We scale the radii up uniformly so that the
+        // ellipse is just big enough to fit (i.e. to the point where there is
+        // exactly one solution).
+
+        double lamedh = 1.0 - numerator/(rx*rx*ry*ry); // equiv to eqn F.6.6.2
+        double s = sqrt(lamedh);
         rx *= s;  // F.6.6.3
         ry *= s;
-        // rx and ry changed, so we have to recompute numerator
-        numerator = rx*rx*ry*ry - rx*rx*y1p*y1p - ry*ry*x1p*x1p;
-        NS_ABORT_IF_FALSE(numerator >= 0,
-                          "F.6.6.3 should prevent this. Will sqrt(-num)!");
+        root = 0.0;
       }
-      root = sqrt(numerator/(rx*rx*y1p*y1p + ry*ry*x1p*x1p));
-      if (largeArcFlag == sweepFlag)
-        root = -root;
+
+      double cxp =  root * rx * y1p / ry;  // F.6.5.2
+      double cyp = -root * ry * x1p / rx;
 
-      float cxp =  root * rx * y1p / ry;  // F.6.5.2
-      float cyp = -root * ry * x1p / rx;
-
-      float theta, delta;
-      theta = CalcVectorAngle(1.0, 0.0,  (x1p-cxp)/rx, (y1p-cyp)/ry); // F.6.5.5
-      delta  = CalcVectorAngle((x1p-cxp)/rx, (y1p-cyp)/ry,
-                               (-x1p-cxp)/rx, (-y1p-cyp)/ry);         // F.6.5.6
+      double theta, delta;
+      theta = CalcVectorAngle(1.0, 0.0, (x1p-cxp)/rx, (y1p-cyp)/ry); // F.6.5.5
+      delta = CalcVectorAngle((x1p-cxp)/rx, (y1p-cyp)/ry,
+                              (-x1p-cxp)/rx, (-y1p-cyp)/ry);         // F.6.5.6
       if (!sweepFlag && delta > 0)
         delta -= 2.0 * M_PI;
       else if (sweepFlag && delta < 0)
         delta += 2.0 * M_PI;
 
-      float tx1, ty1, tx2, ty2;
+      double tx1, ty1, tx2, ty2;
       tx1 = -cos(angle)*rx*sin(theta) - sin(angle)*ry*cos(theta);
       ty1 = -sin(angle)*rx*sin(theta) + cos(angle)*ry*cos(theta);
       tx2 = -cos(angle)*rx*sin(theta+delta) - sin(angle)*ry*cos(theta+delta);
       ty2 = -sin(angle)*rx*sin(theta+delta) + cos(angle)*ry*cos(theta+delta);
 
       if (delta < 0.0f) {
         tx1 = -tx1;
         ty1 = -ty1;
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -2271,22 +2271,30 @@ nsDOMClassInfo::Init()
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMWindowUtils)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMWindowUtils_MOZILLA_2_0_BRANCH)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(Location, nsIDOMLocation)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMLocation)
   DOM_CLASSINFO_MAP_END
 
-  DOM_CLASSINFO_MAP_BEGIN(Navigator, nsIDOMNavigator)
-    DOM_CLASSINFO_MAP_ENTRY(nsIDOMNavigator)
-    DOM_CLASSINFO_MAP_ENTRY(nsIDOMNavigatorGeolocation)
-    DOM_CLASSINFO_MAP_ENTRY(nsIDOMNavigatorDesktopNotification)
-    DOM_CLASSINFO_MAP_ENTRY(nsIDOMClientInformation)
-  DOM_CLASSINFO_MAP_END
+  if (nsNavigator::HasDesktopNotificationSupport()) {
+    DOM_CLASSINFO_MAP_BEGIN(Navigator, nsIDOMNavigator)
+      DOM_CLASSINFO_MAP_ENTRY(nsIDOMNavigator)
+      DOM_CLASSINFO_MAP_ENTRY(nsIDOMNavigatorGeolocation)
+      DOM_CLASSINFO_MAP_ENTRY(nsIDOMNavigatorDesktopNotification)
+      DOM_CLASSINFO_MAP_ENTRY(nsIDOMClientInformation)
+    DOM_CLASSINFO_MAP_END
+  } else {
+    DOM_CLASSINFO_MAP_BEGIN(Navigator, nsIDOMNavigator)
+      DOM_CLASSINFO_MAP_ENTRY(nsIDOMNavigator)
+      DOM_CLASSINFO_MAP_ENTRY(nsIDOMNavigatorGeolocation)
+      DOM_CLASSINFO_MAP_ENTRY(nsIDOMClientInformation)
+    DOM_CLASSINFO_MAP_END
+  }
 
   DOM_CLASSINFO_MAP_BEGIN(Plugin, nsIDOMPlugin)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMPlugin)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(PluginArray, nsIDOMPluginArray)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMPluginArray)
   DOM_CLASSINFO_MAP_END
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -7961,17 +7961,17 @@ nsGlobalWindow::GetLocalStorage(nsIDOMSt
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   NS_ADDREF(*aLocalStorage = mLocalStorage);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsGlobalWindow::GetMoz_indexedDB(nsIIDBFactory** _retval)
+nsGlobalWindow::GetMozIndexedDB(nsIIDBFactory** _retval)
 {
   if (!mIndexedDB) {
     mIndexedDB = indexedDB::IDBFactory::Create();
     NS_ENSURE_TRUE(mIndexedDB, NS_ERROR_FAILURE);
   }
 
   nsCOMPtr<nsIIDBFactory> request(mIndexedDB);
   request.forget(_retval);
@@ -10717,16 +10717,22 @@ nsresult
 nsNavigator::RefreshMIMEArray()
 {
   nsresult rv = NS_OK;
   if (mMimeTypes)
     rv = mMimeTypes->Refresh();
   return rv;
 }
 
+bool
+nsNavigator::HasDesktopNotificationSupport()
+{
+  return nsContentUtils::GetBoolPref("notification.feature.enabled", PR_FALSE);
+}
+
 //*****************************************************************************
 //    nsNavigator::nsIDOMClientInformation
 //*****************************************************************************
 
 NS_IMETHODIMP
 nsNavigator::RegisterContentHandler(const nsAString& aMIMEType, 
                                     const nsAString& aURI, 
                                     const nsAString& aTitle)
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -1051,16 +1051,18 @@ public:
   nsIDocShell *GetDocShell()
   {
     return mDocShell;
   }
 
   void LoadingNewDocument();
   nsresult RefreshMIMEArray();
 
+  static bool HasDesktopNotificationSupport();
+
 protected:
   nsRefPtr<nsMimeTypeArray> mMimeTypes;
   nsRefPtr<nsPluginArray> mPlugins;
   nsRefPtr<nsGeolocation> mGeolocation;
   nsRefPtr<nsDesktopNotificationCenter> mNotification;
   nsIDocShell* mDocShell; // weak reference
 };
 
--- a/dom/indexedDB/test/Makefile.in
+++ b/dom/indexedDB/test/Makefile.in
@@ -96,16 +96,17 @@ TEST_FILES = \
   $(NULL)
 
 ifeq (browser,$(MOZ_BUILD_APP))
 BROWSER_TEST_FILES = \
   browserHelpers.js \
   browser_permissionsPrompt.html \
   browser_permissionsPromptAllow.js \
   browser_permissionsPromptDeny.js \
+  browser_privateBrowsing.js \
   browser_quotaPrompt.html \
   browser_quotaPromptAllow.js \
   browser_quotaPromptDeny.js \
   head.js \
   $(NULL)
 
 libs:: $(BROWSER_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
--- a/dom/indexedDB/test/bfcache_iframe1.html
+++ b/dom/indexedDB/test/bfcache_iframe1.html
@@ -1,13 +1,13 @@
 <!DOCTYPE html>
 <html>
 <head>
   <script>
-moz_indexedDB.open(parent.location).onsuccess = function(e) {
+mozIndexedDB.open(parent.location).onsuccess = function(e) {
   var db = e.result;
   // This should never be called
   db.onversionchange = function(e) {
     db.transaction(["mystore"]).objectStore("mystore").put({ hello: "fail" }, 42);
   }
   db.setVersion("1.0").onsuccess = function(e) {
     trans = e.transaction;
     if (db.objectStoreNames.contains("mystore")) {
--- a/dom/indexedDB/test/bfcache_iframe2.html
+++ b/dom/indexedDB/test/bfcache_iframe2.html
@@ -1,14 +1,14 @@
 <!DOCTYPE html>
 <html>
 <head>
   <script>
 var res = {};
-moz_indexedDB.open(parent.location).onsuccess = function(e) {
+mozIndexedDB.open(parent.location).onsuccess = function(e) {
   var db = e.result;
   res.version = db.version;
   res.storeCount = db.objectStoreNames.length;
 
   req = db.setVersion("2.0");
   req.onblocked = function() {
     res.blockedFired = true;
   }
--- a/dom/indexedDB/test/browser_permissionsPrompt.html
+++ b/dom/indexedDB/test/browser_permissionsPrompt.html
@@ -7,17 +7,17 @@
     <title>Indexed Database Test</title>
 
     <script type="text/javascript;version=1.7">
       function testSteps()
       {
         const name = window.location.pathname;
         const description = "My Test Database";
 
-        let request = moz_indexedDB.open(name, description);
+        let request = mozIndexedDB.open(name, description);
         request.onerror = grabEventAndContinueHandler;
         request.onsuccess = grabEventAndContinueHandler;
         let event = yield;
 
         if (event instanceof Components.interfaces.nsIIDBSuccessEvent) {
           testResult = event.result;
         }
         else {
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/browser_privateBrowsing.js
@@ -0,0 +1,71 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const testPageURL = "http://mochi.test:8888/browser/" +
+  "dom/indexedDB/test/browser_permissionsPrompt.html";
+const notificationID = "indexedDB-permissions-prompt";
+const exceptionText = "5: A mutation operation was attempted on a database " +
+                      "that did not allow mutations.";
+
+function test()
+{
+  waitForExplicitFinish();
+  // Avoids the actual prompt
+  setPermission(testPageURL, "indexedDB");
+  executeSoon(test2);
+}
+
+function test1()
+{
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function () {
+    gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
+
+    setFinishedCallback(function(result, exception) {
+      ok(result instanceof Components.interfaces.nsIIDBDatabase,
+         "First database creation was successful");
+      ok(!exception, "No exception");
+      gBrowser.removeCurrentTab();
+
+      executeSoon(test2);
+    });
+  }, true);
+  content.location = testPageURL;
+}
+
+function test2()
+{
+  let pb = Cc["@mozilla.org/privatebrowsing;1"].
+           getService(Ci.nsIPrivateBrowsingService);
+  pb.privateBrowsingEnabled = true;
+
+  executeSoon(test3);
+}
+
+function test3()
+{
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function () {
+    gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
+
+    setFinishedCallback(function(result, exception) {
+      ok(!result, "No database");
+      is(exception, exceptionText, "Correct exception");
+      gBrowser.removeCurrentTab();
+
+      executeSoon(test4);
+    });
+  }, true);
+  content.location = testPageURL;
+}
+
+function test4()
+{
+  let pb = Cc["@mozilla.org/privatebrowsing;1"].
+           getService(Ci.nsIPrivateBrowsingService);
+  pb.privateBrowsingEnabled = false;
+
+  executeSoon(finish);
+}
--- a/dom/indexedDB/test/browser_quotaPrompt.html
+++ b/dom/indexedDB/test/browser_quotaPrompt.html
@@ -50,17 +50,17 @@
       function testSteps()
       {
         const name = window.location.pathname;
         const description = "My Test Database";
 
         window.addEventListener("indexedDB-addMore", onAddMore, true);
         window.addEventListener("indexedDB-done", onDone, true);
 
-        let request = moz_indexedDB.open(name, description);
+        let request = mozIndexedDB.open(name, description);
         request.onerror = errorHandler;
         request.onsuccess = grabEventAndContinueHandler;
         let event = yield;
 
         db = event.result;
 
         request = db.setVersion("1");
         request.onerror = errorHandler;
--- a/dom/indexedDB/test/error_events_abort_transactions_iframe.html
+++ b/dom/indexedDB/test/error_events_abort_transactions_iframe.html
@@ -51,17 +51,17 @@
       netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
 
       let uri = window.document.documentURIObject;
       Components.classes["@mozilla.org/permissionmanager;1"]
                 .getService(Components.interfaces.nsIPermissionManager)
                 .add(uri, "indexedDB",
                      Components.interfaces.nsIPermissionManager.ALLOW_ACTION);
 
-      let request = moz_indexedDB.open(window.location.pathname);
+      let request = mozIndexedDB.open(window.location.pathname);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield;
 
       let db = event.result;
 
       is(db.version, "", "Correct version");
       is(db.objectStoreNames.length, 0, "Correct objectStoreNames length");
--- a/dom/indexedDB/test/event_propagation_iframe.html
+++ b/dom/indexedDB/test/event_propagation_iframe.html
@@ -90,17 +90,17 @@
       netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
 
       let uri = window.document.documentURIObject;
       Components.classes["@mozilla.org/permissionmanager;1"]
                 .getService(Components.interfaces.nsIPermissionManager)
                 .add(uri, "indexedDB",
                      Components.interfaces.nsIPermissionManager.ALLOW_ACTION);
 
-      let request = moz_indexedDB.open(window.location.pathname);
+      let request = mozIndexedDB.open(window.location.pathname);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield;
 
       let db = event.result;
       db.onerror = errorEventCounter;
       db.addEventListener("error", errorEventCounter, true);
 
--- a/dom/indexedDB/test/exceptions_in_success_events_iframe.html
+++ b/dom/indexedDB/test/exceptions_in_success_events_iframe.html
@@ -55,17 +55,17 @@
       netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
 
       let uri = window.document.documentURIObject;
       Components.classes["@mozilla.org/permissionmanager;1"]
                 .getService(Components.interfaces.nsIPermissionManager)
                 .add(uri, "indexedDB",
                      Components.interfaces.nsIPermissionManager.ALLOW_ACTION);
 
-      let request = moz_indexedDB.open(window.location.pathname);
+      let request = mozIndexedDB.open(window.location.pathname);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield;
 
       let db = event.result;
       db.onerror = function(event) {
         event.preventDefault();
       };
--- a/dom/indexedDB/test/test_add_twice_failure.html
+++ b/dom/indexedDB/test/test_add_twice_failure.html
@@ -11,17 +11,17 @@
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
     function testSteps()
     {
       const name = window.location.pathname;
       const description = "My Test Database";
 
-      let request = moz_indexedDB.open(name, description);
+      let request = mozIndexedDB.open(name, description);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield;
 
       let db = event.result;
 
       request = db.setVersion("1");
       request.onerror = errorHandler;
--- a/dom/indexedDB/test/test_bad_keypath.html
+++ b/dom/indexedDB/test/test_bad_keypath.html
@@ -11,17 +11,17 @@
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
     function testSteps()
     {
       const name = window.location.pathname;
       const description = "My Test Database";
 
-      let request = moz_indexedDB.open(name, description);
+      let request = mozIndexedDB.open(name, description);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield;
 
       let db = event.result;
 
       request = db.setVersion("1");
       request.onerror = errorHandler;
--- a/dom/indexedDB/test/test_clear.html
+++ b/dom/indexedDB/test/test_clear.html
@@ -14,17 +14,17 @@
     function testSteps()
     {
       const READ_WRITE = Components.interfaces.nsIIDBTransaction.READ_WRITE;
 
       const name = window.location.pathname;
       const description = "My Test Database";
       const entryCount = 1000;
 
-      let request = moz_indexedDB.open(name, description);
+      let request = mozIndexedDB.open(name, description);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield;
 
       let db = event.result;
 
       request = db.setVersion("1");
       request.onerror = errorHandler;
--- a/dom/indexedDB/test/test_create_index.html
+++ b/dom/indexedDB/test/test_create_index.html
@@ -23,17 +23,17 @@
         { name: "b", options: { keyPath: "id", autoIncrement: false } },
       ];
       const indexInfo = [
         { name: "1", keyPath: "unique_value", options: { unique: true } },
         { name: "2", keyPath: "value", options: { unique: false } },
         { name: "3", keyPath: "value", options: { unique: false } },
       ];
 
-      let request = moz_indexedDB.open(name, description);
+      let request = mozIndexedDB.open(name, description);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield;
       let db = event.result;
 
       request = db.setVersion("1");
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
--- a/dom/indexedDB/test/test_create_objectStore.html
+++ b/dom/indexedDB/test/test_create_objectStore.html
@@ -27,22 +27,22 @@
         { name: "6" },
         { name: "7", options: null },
         { name: "8", options: { autoIncrement: true } },
         { name: "9", options: { autoIncrement: false } },
         { name: "10", options: { keyPath: "foo", autoIncrement: false } },
         { name: "11", options: { keyPath: "foo", autoIncrement: true } }
       ];
 
-      let request = moz_indexedDB.open(name, description);
+      let request = mozIndexedDB.open(name, description);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield;
 
-      ok(event.source === moz_indexedDB, "event.source is correct");
+      ok(event.source === mozIndexedDB, "event.source is correct");
       let db = event.result;
 
       let count = db.objectStoreNames.length;
       is(count, 0, "correct objectStoreNames length");
 
       request = db.setVersion("1");
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
--- a/dom/indexedDB/test/test_cursor_mutation.html
+++ b/dom/indexedDB/test/test_cursor_mutation.html
@@ -25,17 +25,17 @@
 
         // This one will be added.
         { ss: "237-23-7737", name: "Pat" }
       ];
 
       // Post-add and post-remove data ordered by name.
       const objectStoreDataNameSort = [ 1, 4, 5, 2, 3 ];
 
-      let request = moz_indexedDB.open(window.location.pathname);
+      let request = mozIndexedDB.open(window.location.pathname);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield;
 
       let db = event.result;
 
       db.setVersion("1").onsuccess = grabEventAndContinueHandler;
       event = yield;
--- a/dom/indexedDB/test/test_cursor_update_updates_indexes.html
+++ b/dom/indexedDB/test/test_cursor_update_updates_indexes.html
@@ -26,17 +26,17 @@
         { name: "2", options: { keyPath: "foo" },
           entry: { foo: 1, data: START_DATA } },
         { name: "3", options: { keyPath: "", autoIncrement: true },
           entry: { data: START_DATA } },
         { name: "4", options: { keyPath: "foo", autoIncrement: true },
           entry: { data: START_DATA } },
       ];
 
-      let request = moz_indexedDB.open(name, description);
+      let request = mozIndexedDB.open(name, description);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield;
 
       let db = event.result;
 
       for (let i = 0; i < objectStoreInfo.length; i++) {
         // Create our object stores.
--- a/dom/indexedDB/test/test_cursors.html
+++ b/dom/indexedDB/test/test_cursors.html
@@ -15,17 +15,17 @@
     {
       const name = window.location.pathname;
       const description = "My Test Database";
       const keys = [1, -1, 0, 10, 2000, "q", "z", "two", "b", "a"];
       const sortedKeys = [-1, 0, 1, 10, 2000, "a", "b", "q", "two", "z"];
 
       is(keys.length, sortedKeys.length, "Good key setup");
 
-      let request = moz_indexedDB.open(name, description);
+      let request = mozIndexedDB.open(name, description);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield;
 
       let db = event.result;
 
       request = db.setVersion("1");
       request.onerror = errorHandler;
--- a/dom/indexedDB/test/test_event_source.html
+++ b/dom/indexedDB/test/test_event_source.html
@@ -12,22 +12,22 @@
 
   <script type="text/javascript;version=1.7">
     function testSteps()
     {
       const name = window.location.pathname;
       const description = "My Test Database";
       const objectStoreName = "Objects";
 
-      var request = moz_indexedDB.open(name, description);
+      var request = mozIndexedDB.open(name, description);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       var event = yield;
 
-      ok(event.source === moz_indexedDB, "correct event.source");
+      ok(event.source === mozIndexedDB, "correct event.source");
 
       var db = event.result;
 
       request = db.setVersion("1");
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       event = yield;
 
--- a/dom/indexedDB/test/test_getAll.html
+++ b/dom/indexedDB/test/test_getAll.html
@@ -13,17 +13,17 @@
   <script type="text/javascript;version=1.7">
     function testSteps()
     {
       const name = window.location.pathname;
       const description = "My Test Database";
 
       const values = [ "a", "1", 1, "foo", 300, true, false, 4.5, null ];
 
-      let request = moz_indexedDB.open(name, description);
+      let request = mozIndexedDB.open(name, description);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield;
 
       let db = event.result;
 
       request = db.setVersion("1");
       request.onerror = errorHandler;
--- a/dom/indexedDB/test/test_global_data.html
+++ b/dom/indexedDB/test/test_global_data.html
@@ -13,17 +13,17 @@
   <script type="text/javascript;version=1.7">
     function testSteps()
     {
       const name = window.location.pathname;
       const description = "My Test Database";
       const objectStore =  { name: "Objects",
                              options: { keyPath: "id", autoIncrement: true } };
 
-      let request = moz_indexedDB.open(name, description);
+      let request = mozIndexedDB.open(name, description);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield;
 
       let db1 = event.result;
 
       request = db1.setVersion("1");
       request.onerror = errorHandler;
@@ -32,17 +32,17 @@
 
       is(db1.objectStoreNames.length, 0, "No objectStores in db1");
 
       db1.createObjectStore(objectStore.name, objectStore.options);
 
       continueToNextStep();
       yield;
 
-      request = moz_indexedDB.open(name, description);
+      request = mozIndexedDB.open(name, description);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       event = yield;
 
       let db2 = event.result;
 
       ok(db1 !== db2, "Databases are not the same object");
 
--- a/dom/indexedDB/test/test_index_getAll.html
+++ b/dom/indexedDB/test/test_index_getAll.html
@@ -53,17 +53,17 @@
         { key: "237-23-7733", value: { name: "Ann", height: 52, weight: 110 } },
         { key: "237-23-7735", value: { name: "Sue", height: 58, weight: 130 } },
         { key: "237-23-7732", value: { name: "Bob", height: 60, weight: 120 } },
         { key: "237-23-7736", value: { name: "Joe", height: 65, weight: 150 } },
         { key: "237-23-7737", value: { name: "Pat", height: 65 } },
         { key: "237-23-7734", value: { name: "Ron", height: 73, weight: 180 } }
       ];
 
-      let request = moz_indexedDB.open(name, description);
+      let request = mozIndexedDB.open(name, description);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield;
 
       let db = event.result;
 
       request = db.setVersion("1");
       request.onerror = errorHandler;
--- a/dom/indexedDB/test/test_index_getAllObjects.html
+++ b/dom/indexedDB/test/test_index_getAllObjects.html
@@ -53,17 +53,17 @@
         { key: "237-23-7733", value: { name: "Ann", height: 52, weight: 110 } },
         { key: "237-23-7735", value: { name: "Sue", height: 58, weight: 130 } },
         { key: "237-23-7732", value: { name: "Bob", height: 60, weight: 120 } },
         { key: "237-23-7736", value: { name: "Joe", height: 65, weight: 150 } },
         { key: "237-23-7737", value: { name: "Pat", height: 65 } },
         { key: "237-23-7734", value: { name: "Ron", height: 73, weight: 180 } }
       ];
 
-      let request = moz_indexedDB.open(name, description);
+      let request = mozIndexedDB.open(name, description);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield;
 
       let db = event.result;
 
       request = db.setVersion("1");
       request.onerror = errorHandler;
--- a/dom/indexedDB/test/test_index_object_cursors.html
+++ b/dom/indexedDB/test/test_index_object_cursors.html
@@ -25,17 +25,17 @@
         { name: "height", keyPath: "height", options: { } }
       ];
 
       const data = [
         { ss: "237-23-7732", name: "Ann", height: 60 },
         { ss: "237-23-7733", name: "Bob", height: 65 }
       ];
 
-      let request = moz_indexedDB.open(window.location.pathname);
+      let request = mozIndexedDB.open(window.location.pathname);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield;
 
       let db = event.result;
       db.onerror = errorHandler;
 
       db.setVersion("1").onsuccess = grabEventAndContinueHandler;
--- a/dom/indexedDB/test/test_indexes.html
+++ b/dom/indexedDB/test/test_indexes.html
@@ -64,17 +64,17 @@
         { key: "237-23-7733", value: { name: "Ann", height: 52, weight: 110 } },
         { key: "237-23-7735", value: { name: "Sue", height: 58, weight: 130 } },
         { key: "237-23-7732", value: { name: "Bob", height: 60, weight: 120 } },
         { key: "237-23-7736", value: { name: "Joe", height: 65, weight: 150 } },
         { key: "237-23-7737", value: { name: "Pat", height: 65 } },
         { key: "237-23-7734", value: { name: "Ron", height: 73, weight: 180 } }
       ];
 
-      let request = moz_indexedDB.open(name, description);
+      let request = mozIndexedDB.open(name, description);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield;
 
       let db = event.result;
 
       request = db.setVersion("1");
       request.onerror = errorHandler;
--- a/dom/indexedDB/test/test_indexes_bad_values.html
+++ b/dom/indexedDB/test/test_indexes_bad_values.html
@@ -44,17 +44,17 @@
       const objectStoreDataWeightSort = [
         { key: "237-23-7733", value: { name: "Ann", height: 52, weight: 110 } },
         { key: "237-23-7732", value: { name: "Bob", height: 60, weight: 120 } },
         { key: "237-23-7735", value: { name: "Sue", height: 58, weight: 130 } },
         { key: "237-23-7736", value: { name: "Joe", height: 65, weight: 150 } },
         { key: "237-23-7734", value: { name: "Ron", height: 73, weight: 180 } }
       ];
 
-      let request = moz_indexedDB.open(name, description);
+      let request = mozIndexedDB.open(name, description);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield;
 
       let db = event.result;
 
       request = db.setVersion("1");
       request.onerror = errorHandler;
--- a/dom/indexedDB/test/test_key_requirements.html
+++ b/dom/indexedDB/test/test_key_requirements.html
@@ -11,17 +11,17 @@
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
     function testSteps()
     {
       const name = window.location.pathname;
       const description = "My Test Database";
 
-      let request = moz_indexedDB.open(name, description);
+      let request = mozIndexedDB.open(name, description);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield;
 
       let db = event.result;
       db.addEventListener("error", function(event) {
         event.preventDefault();
       }, false);
--- a/dom/indexedDB/test/test_objectCursors.html
+++ b/dom/indexedDB/test/test_objectCursors.html
@@ -21,17 +21,17 @@
         { name: "b", autoIncrement: true }
       ];
 
       const indexes = [
         { name: "a", options: { } },
         { name: "b", options: { unique: true } }
       ];
 
-      let request = moz_indexedDB.open(name, description);
+      let request = mozIndexedDB.open(name, description);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield;
 
       let db = event.result;
 
       for (let i in objectStores) {
         request = db.setVersion("1");
--- a/dom/indexedDB/test/test_objectStore_inline_autoincrement_key_added_on_put.html
+++ b/dom/indexedDB/test/test_objectStore_inline_autoincrement_key_added_on_put.html
@@ -14,17 +14,17 @@
 
 function testSteps()
 {
   const IDBObjectStore = Components.interfaces.nsIIDBObjectStore;
   const IDBDatabaseException = Components.interfaces.nsIIDBDatabaseException;
   const name = window.location.pathname;
   const description = "My Test Database";
 
-  var request = moz_indexedDB.open(name, description);
+  var request = mozIndexedDB.open(name, description);
   request.onerror = errorHandler;
   request.onsuccess = grabEventAndContinueHandler;
   var event = yield;
 
   var db = event.result;
 
   var test = {
     name: "inline key; key generator",
--- a/dom/indexedDB/test/test_objectStore_remove_values.html
+++ b/dom/indexedDB/test/test_objectStore_remove_values.html
@@ -14,17 +14,17 @@
 
 function testSteps()
 {
   const IDBObjectStore = Components.interfaces.nsIIDBObjectStore;
   const IDBDatabaseException = Components.interfaces.nsIIDBDatabaseException;
   const name = window.location.pathname;
   const description = "My Test Database";
 
-  var request = moz_indexedDB.open(name, description);
+  var request = mozIndexedDB.open(name, description);
   request.onerror = errorHandler;
   request.onsuccess = grabEventAndContinueHandler;
   var event = yield;
 
   var db = event.result;
 
   var data = [
     { name: "inline key; key generator",
--- a/dom/indexedDB/test/test_object_identity.html
+++ b/dom/indexedDB/test/test_object_identity.html
@@ -8,17 +8,17 @@
 
   <script type="text/javascript" src="/MochiKit/packed.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
     function testSteps()
     {
-      let request = moz_indexedDB.open(window.location.pathname, "");
+      let request = mozIndexedDB.open(window.location.pathname, "");
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield;
 
       let db = event.result;
 
       request = db.setVersion("1");
       request.onerror = errorHandler;
--- a/dom/indexedDB/test/test_open_empty_db.html
+++ b/dom/indexedDB/test/test_open_empty_db.html
@@ -14,36 +14,36 @@
     function testSteps()
     {
       const name = window.location.pathname;
       const description = "My Test Database";
 
       let request;
 
       try {
-        request = moz_indexedDB.open("", "");
+        request = mozIndexedDB.open("", "");
         ok(false, "Open with empty name should have thrown!");
       }
       catch(e) {
         is(e instanceof IDBDatabaseException, true, "Got IDBDatabaseException");
         is(e.code, IDBDatabaseException.NON_TRANSIENT_ERR, "Good error code");
         is(request, undefined, "Shouldn't be set to anything");
       }
 
       try {
-        request = moz_indexedDB.open(null, "");
+        request = mozIndexedDB.open(null, "");
         ok(false, "Open with null name should have thrown!");
       }
       catch(e) {
         is(e instanceof IDBDatabaseException, true, "Got IDBDatabaseException");
         is(e.code, IDBDatabaseException.NON_TRANSIENT_ERR, "Good error code");
         is(request, undefined, "Shouldn't be set to anything");
       }
 
-      request = moz_indexedDB.open(name, description);
+      request = mozIndexedDB.open(name, description);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield;
 
       let db = event.result;
       is(db.name, name, "Bad name");
       is(db.version, "", "Bad version");
       is(db.objectStoreNames.length, 0, "Bad objectStores list");
--- a/dom/indexedDB/test/test_open_objectStore.html
+++ b/dom/indexedDB/test/test_open_objectStore.html
@@ -13,17 +13,17 @@
   <script type="text/javascript;version=1.7">
     function testSteps()
     {
       const nsIIDBObjectStore = Components.interfaces.nsIIDBObjectStore;
       const name = window.location.pathname;
       const description = "My Test Database";
       const objectStoreName = "Objects";
 
-      let request = moz_indexedDB.open(name, description);
+      let request = mozIndexedDB.open(name, description);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield;
 
       let db = event.result;
       is(db.objectStoreNames.length, 0, "Bad objectStores list");
 
       request = db.setVersion("1");
--- a/dom/indexedDB/test/test_overlapping_transactions.html
+++ b/dom/indexedDB/test/test_overlapping_transactions.html
@@ -14,17 +14,17 @@
     function testSteps()
     {
       const READ_WRITE = Components.interfaces.nsIIDBTransaction.READ_WRITE;
 
       const name = window.location.pathname;
       const description = "My Test Database";
       const objectStores = [ "foo", "bar" ];
 
-      let request = moz_indexedDB.open(name, description);
+      let request = mozIndexedDB.open(name, description);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield;
 
       let db = event.result;
       is(db.objectStoreNames.length, 0, "Correct objectStoreNames list");
 
       request = db.setVersion("1");
--- a/dom/indexedDB/test/test_put_get_values.html
+++ b/dom/indexedDB/test/test_put_get_values.html
@@ -15,17 +15,17 @@
     {
       const name = window.location.pathname;
       const description = "My Test Database";
       const objectStoreName = "Objects";
 
       let testString = { key: 0, value: "testString" };
       let testInt = { key: 1, value: 1002 };
 
-      let request = moz_indexedDB.open(name, description);
+      let request = mozIndexedDB.open(name, description);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield;
 
       let db = event.result;
 
       request = db.setVersion("1");
       request.onerror = errorHandler;
--- a/dom/indexedDB/test/test_put_get_values_autoIncrement.html
+++ b/dom/indexedDB/test/test_put_get_values_autoIncrement.html
@@ -15,17 +15,17 @@
     {
       const name = window.location.pathname;
       const description = "My Test Database";
       const objectStoreName = "Objects";
 
       let testString = { value: "testString" };
       let testInt = { value: 1002 };
 
-      let request = moz_indexedDB.open(name, description);
+      let request = mozIndexedDB.open(name, description);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield;
 
       let db = event.result;
 
       request = db.setVersion("1");
       request.onerror = errorHandler;
--- a/dom/indexedDB/test/test_readonly_transactions.html
+++ b/dom/indexedDB/test/test_readonly_transactions.html
@@ -15,17 +15,17 @@
     {
       const READ_ONLY = Components.interfaces.nsIIDBTransaction.READ_ONLY;
       const READ_WRITE = Components.interfaces.nsIIDBTransaction.READ_WRITE;
 
       const name = window.location.pathname;
       const description = "My Test Database";
       const osName = "foo";
 
-      let request = moz_indexedDB.open(name, description);
+      let request = mozIndexedDB.open(name, description);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield;
 
       let db = event.result;
       is(db.objectStoreNames.length, 0, "Correct objectStoreNames list");
 
       request = db.setVersion("1");
--- a/dom/indexedDB/test/test_remove_index.html
+++ b/dom/indexedDB/test/test_remove_index.html
@@ -14,17 +14,17 @@
     function testSteps()
     {
       const nsIIDBObjectStore = Components.interfaces.nsIIDBObjectStore;
 
       const name = window.location.pathname;
       const description = "My Test Database";
       const indexName = "My Test Index";
 
-      let request = moz_indexedDB.open(name, description);
+      let request = mozIndexedDB.open(name, description);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield;
 
       let db = event.result;
       is(db.objectStoreNames.length, 0, "Correct objectStoreNames list");
 
       request = db.setVersion("1");
--- a/dom/indexedDB/test/test_remove_objectStore.html
+++ b/dom/indexedDB/test/test_remove_objectStore.html
@@ -16,17 +16,17 @@
       const nsIIDBObjectStore = Components.interfaces.nsIIDBObjectStore;
       const UNKNOWN_ERR =
         Components.interfaces.nsIIDBDatabaseException.UNKNOWN_ERR;
 
       const name = window.location.pathname;
       const description = "My Test Database";
       const objectStoreName = "Objects";
 
-      let request = moz_indexedDB.open(name, description);
+      let request = mozIndexedDB.open(name, description);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield;
 
       let db = event.result;
       is(db.objectStoreNames.length, 0, "Correct objectStoreNames list");
 
       request = db.setVersion("1");
--- a/dom/indexedDB/test/test_request_readyState.html
+++ b/dom/indexedDB/test/test_request_readyState.html
@@ -14,17 +14,17 @@
     function testSteps()
     {
       const name = window.location.pathname;
       const description = "My Test Database";
 
       const LOADING = Components.interfaces.nsIIDBRequest.LOADING;
       const DONE = Components.interfaces.nsIIDBRequest.DONE;
 
-      let request = moz_indexedDB.open(name, description);
+      let request = mozIndexedDB.open(name, description);
       is(request.readyState, LOADING, "Correct readyState");
 
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield;
 
       is(request.readyState, DONE, "Correct readyState");
 
--- a/dom/indexedDB/test/test_setVersion.html
+++ b/dom/indexedDB/test/test_setVersion.html
@@ -15,17 +15,17 @@
 function testSteps()
 {
   const READ_WRITE = Components.interfaces.nsIIDBTransaction.READ_WRITE;
   const VERSION_CHANGE = Components.interfaces.nsIIDBTransaction.VERSION_CHANGE;
 
   const name = window.location.pathname;
   const description = "My Test Database";
 
-  let request = moz_indexedDB.open(name, description);
+  let request = mozIndexedDB.open(name, description);
   request.onerror = errorHandler;
   request.onsuccess = grabEventAndContinueHandler;
   let event = yield;
 
   let db = event.result;
 
   // Check default state.
   is(db.version, "", "Correct default version for a new database.");
--- a/dom/indexedDB/test/test_setVersion_abort.html
+++ b/dom/indexedDB/test/test_setVersion_abort.html
@@ -15,17 +15,17 @@
 function testSteps()
 {
   const READ_WRITE = Components.interfaces.nsIIDBTransaction.READ_WRITE;
   const VERSION_CHANGE = Components.interfaces.nsIIDBTransaction.VERSION_CHANGE;
 
   const name = window.location.pathname;
   const description = "My Test Database";
 
-  let request = moz_indexedDB.open(name, description);
+  let request = mozIndexedDB.open(name, description);
   request.onerror = errorHandler;
   request.onsuccess = grabEventAndContinueHandler;
   let event = yield;
 
   let db = event.result;
 
   request = db.setVersion("1");
   request.onerror = errorHandler;
--- a/dom/indexedDB/test/test_setVersion_events.html
+++ b/dom/indexedDB/test/test_setVersion_events.html
@@ -11,17 +11,17 @@
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
     function testSteps()
     {
       const name = window.location.pathname;
       const description = "My Test Database";
 
-      let request = moz_indexedDB.open(name, description);
+      let request = mozIndexedDB.open(name, description);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield;
 
       let versionChangeEventCount = 0;
       let db1, db2, db3;
 
       db1 = event.result;
@@ -29,17 +29,17 @@
         ok(true, "Got version change event");
         is(event.source, null, "Correct source");
         is(event.target, db1, "Correct target");
         is(event.version, "1", "Correct version");
         is(versionChangeEventCount++, 0, "Correct count");
         db1.close();
       }, false);
 
-      request = moz_indexedDB.open(name, description);
+      request = mozIndexedDB.open(name, description);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       event = yield;
 
       db2 = event.result;
       db2.addEventListener("versionchange", function(event) {
         ok(true, "Got version change event");
         is(event.source, null, "Correct source");
@@ -58,17 +58,17 @@
         is(event.version, "1", "Correct version");
         is(versionChangeEventCount++, 2, "Correct count");
       };
 
       event = yield;
       ok(event.result === event.transaction, "Good result");
       is(event.transaction.mode, IDBTransaction.VERSION_CHANGE, "Correct mode");
 
-      request = moz_indexedDB.open(name, description);
+      request = mozIndexedDB.open(name, description);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       event = yield;
 
       db3 = event.result;
 
       request = db3.setVersion("2");
       request.onerror = errorHandler;
--- a/dom/indexedDB/test/test_success_events_after_abort.html
+++ b/dom/indexedDB/test/test_success_events_after_abort.html
@@ -8,17 +8,17 @@
 
   <script type="text/javascript" src="/MochiKit/packed.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
     function testSteps()
     {
-      let request = moz_indexedDB.open(window.location.pathname);
+      let request = mozIndexedDB.open(window.location.pathname);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield;
 
       let db = event.result;
 
       let request = db.setVersion("1");
       request.onsuccess = grabEventAndContinueHandler;
--- a/dom/indexedDB/test/test_transaction_abort.html
+++ b/dom/indexedDB/test/test_transaction_abort.html
@@ -20,17 +20,17 @@
       const READ_ONLY = Ci.nsIIDBTransaction.READ_ONLY;
       const READ_WRITE = Ci.nsIIDBTransaction.READ_WRITE;
       const VERSION_CHANGE = Ci.nsIIDBTransaction.VERSION_CHANGE;
 
       const name = window.location.pathname;
       const description = "My Test Database";
 
 
-      let request = moz_indexedDB.open(name, description);
+      let request = mozIndexedDB.open(name, description);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield;
 
       let db = event.result;
 
       let transaction;
       let objectStore;
--- a/dom/indexedDB/test/test_transaction_lifetimes.html
+++ b/dom/indexedDB/test/test_transaction_lifetimes.html
@@ -8,17 +8,17 @@
 
   <script type="text/javascript" src="/MochiKit/packed.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
     function testSteps()
     {
-      let request = moz_indexedDB.open(window.location.pathname);
+      let request = mozIndexedDB.open(window.location.pathname);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield;
 
       let db = event.result;
       db.onerror = errorHandler;
 
       db.setVersion("1").onsuccess = grabEventAndContinueHandler;
--- a/dom/indexedDB/test/test_transaction_lifetimes_nested.html
+++ b/dom/indexedDB/test/test_transaction_lifetimes_nested.html
@@ -8,17 +8,17 @@
 
   <script type="text/javascript" src="/MochiKit/packed.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
     function testSteps()
     {
-      let request = moz_indexedDB.open(window.location.pathname);
+      let request = mozIndexedDB.open(window.location.pathname);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield;
 
       let db = event.result;
       db.onerror = errorHandler;
 
       db.setVersion("1").onsuccess = grabEventAndContinueHandler;
--- a/dom/indexedDB/test/test_writer_starvation.html
+++ b/dom/indexedDB/test/test_writer_starvation.html
@@ -16,17 +16,17 @@
       const READ_ONLY = Components.interfaces.nsIIDBTransaction.READ_ONLY;
       const READ_WRITE = Components.interfaces.nsIIDBTransaction.READ_WRITE;
       const VERSION_CHANGE =
         Components.interfaces.nsIIDBTransaction.VERSION_CHANGE;
 
       const name = window.location.pathname;
       const description = "My Test Database";
 
-      let request = moz_indexedDB.open(name, description);
+      let request = mozIndexedDB.open(name, description);
       request.onerror = errorHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield;
 
       let db = event.result;
 
       request = db.setVersion("1");
       request.onerror = errorHandler;
--- a/dom/interfaces/storage/nsIDOMStorageWindow.idl
+++ b/dom/interfaces/storage/nsIDOMStorageWindow.idl
@@ -66,10 +66,10 @@ interface nsIDOMStorageWindow : nsISuppo
   /**
    * Local storage for the current browsing context.
    */
   readonly attribute nsIDOMStorage localStorage;
 
   /**
    * Indexed Databases for the current browsing context.
    */
-  readonly attribute nsIIDBFactory moz_indexedDB;
+  readonly attribute nsIIDBFactory mozIndexedDB;
 };
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -157,19 +157,18 @@ NS_INTERFACE_MAP_BEGIN(TabChild)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF(TabChild)
 NS_IMPL_RELEASE(TabChild)
 
 NS_IMETHODIMP
 TabChild::SetStatus(PRUint32 aStatusType, const PRUnichar* aStatus)
 {
-  NS_NOTREACHED("TabChild::SetStatus not supported in TabChild");
-
-  return NS_ERROR_NOT_IMPLEMENTED;
+  // FIXME/bug 617804: should the platform support this?
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 TabChild::GetWebBrowser(nsIWebBrowser** aWebBrowser)
 {
   NS_NOTREACHED("TabChild::GetWebBrowser not supported in TabChild");
 
   return NS_ERROR_NOT_IMPLEMENTED;
@@ -294,19 +293,18 @@ TabChild::GetTitle(PRUnichar** aTitle)
   NS_NOTREACHED("TabChild::GetTitle not supported in TabChild");
 
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 TabChild::SetTitle(const PRUnichar* aTitle)
 {
-  NS_NOTREACHED("TabChild::SetTitle not supported in TabChild");
-
-  return NS_ERROR_NOT_IMPLEMENTED;
+  // FIXME/bug 617804: should the platform support this?
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 TabChild::GetSiteWindow(void** aSiteWindow)
 {
   NS_NOTREACHED("TabChild::GetSiteWindow not supported in TabChild");
 
   return NS_ERROR_NOT_IMPLEMENTED;
--- a/dom/plugins/PluginInstanceChild.cpp
+++ b/dom/plugins/PluginInstanceChild.cpp
@@ -158,26 +158,29 @@ PluginInstanceChild::PluginInstanceChild
 #endif // MOZ_X11 && XP_UNIX && !XP_MACOSX
 #if defined(OS_WIN)
     memset(&mAlphaExtract, 0, sizeof(mAlphaExtract));
 #endif // OS_WIN
 #if defined(OS_WIN)
     InitPopupMenuHook();
 #endif // OS_WIN
 #ifdef MOZ_X11
+    // Maemo flash can render plugin with any provided rectangle and not require this quirk.
+#ifndef MOZ_PLATFORM_MAEMO
     const char *description = NULL;
     mPluginIface->getvalue(GetNPP(), NPPVpluginDescriptionString,
                            &description);
     if (description) {
         NS_NAMED_LITERAL_CSTRING(flash10Head, "Shockwave Flash 10.");
         if (StringBeginsWith(nsDependentCString(description), flash10Head)) {
           PluginModuleChild::current()->AddQuirk(PluginModuleChild::QUIRK_FLASH_EXPOSE_COORD_TRANSLATION);
         }
     }
 #endif
+#endif
 }
 
 PluginInstanceChild::~PluginInstanceChild()
 {
 #if defined(OS_WIN)
     NS_ASSERTION(!mPluginWindowHWND, "Destroying PluginInstanceChild without NPP_Destroy?");
 #endif
 #if defined(OS_MACOSX)
@@ -2284,19 +2287,19 @@ PluginInstanceChild::CreateOptSurface(vo
         mCurrentSurface = s;
         return true;
     }
 
     NS_RUNTIMEABORT("Shared-memory drawing not expected on Windows.");
 #endif
 
     // Make common shmem implementation working for any platform
-    mCurrentSurface = new gfxSharedImageSurface();
-    return static_cast<gfxSharedImageSurface*>(mCurrentSurface.get())->
-        Init(this, gfxIntSize(mWindow.width, mWindow.height), format);
+    mCurrentSurface =
+        gfxSharedImageSurface::CreateUnsafe(this, gfxIntSize(mWindow.width, mWindow.height), format);
+    return !!mCurrentSurface;
 }
 
 bool
 PluginInstanceChild::MaybeCreatePlatformHelperSurface(void)
 {
     if (!mCurrentSurface) {
         NS_ERROR("Cannot create helper surface without mCurrentSurface");
         return false;
@@ -2769,20 +2772,21 @@ PluginInstanceChild::ShowPluginFrame()
 }
 
 bool
 PluginInstanceChild::ReadbackDifferenceRect(const nsIntRect& rect)
 {
     if (!mBackSurface)
         return false;
 
-    // We can read safely from XSurface and SharedDIBSurface, because
-    // PluginHost is not able to modify that surface
+    // We can read safely from XSurface,SharedDIBSurface and Unsafe SharedMemory,
+    // because PluginHost is not able to modify that surface
 #if defined(MOZ_X11)
-    if (mBackSurface->GetType() != gfxASurface::SurfaceTypeXlib)
+    if (mBackSurface->GetType() != gfxASurface::SurfaceTypeXlib &&
+        !gfxSharedImageSurface::IsSharedImage(mBackSurface))
         return false;
 #elif defined(XP_WIN)
     if (!SharedDIBSurface::IsSharedDIBSurface(mBackSurface))
         return false;
 #else
     return false;
 #endif
 
--- a/dom/plugins/PluginInstanceParent.cpp
+++ b/dom/plugins/PluginInstanceParent.cpp
@@ -492,17 +492,17 @@ PluginInstanceParent::RecvShow(const NPR
                                SurfaceDescriptor* prevSurface)
 {
     nsRefPtr<gfxASurface> surface;
     if (newSurface.type() == SurfaceDescriptor::TShmem) {
         if (!newSurface.get_Shmem().IsReadable()) {
             NS_WARNING("back surface not readable");
             return false;
         }
-        surface = new gfxSharedImageSurface(newSurface.get_Shmem());
+        surface = gfxSharedImageSurface::Open(newSurface.get_Shmem());
     }
 #ifdef MOZ_X11
     else if (newSurface.type() == SurfaceDescriptor::TSurfaceDescriptorX11) {
         SurfaceDescriptorX11 xdesc = newSurface.get_SurfaceDescriptorX11();
         XRenderPictFormat pf;
         pf.id = xdesc.xrenderPictID();
         XRenderPictFormat *incFormat =
             XRenderFindFormat(DefaultXDisplay(), PictFormatID, &pf, 0);
--- a/dom/src/jsurl/nsJSProtocolHandler.cpp
+++ b/dom/src/jsurl/nsJSProtocolHandler.cpp
@@ -896,17 +896,22 @@ nsJSChannel::SetLoadFlags(nsLoadFlags aL
         mStreamChannel->GetLoadGroup(getter_AddRefs(loadGroup));
         if (loadGroup) {
             nsLoadFlags loadGroupFlags;
             loadGroup->GetLoadFlags(&loadGroupFlags);
             loadGroupIsBackground = ((loadGroupFlags & LOAD_BACKGROUND) != 0);
         }
         bogusLoadBackground = !loadGroupIsBackground;
     }
-    
+
+    // Classifying a javascript: URI doesn't help us, and requires
+    // NSS to boot, which we don't have in content processes.  See
+    // https://bugzilla.mozilla.org/show_bug.cgi?id=617838.
+    aLoadFlags &= ~LOAD_CLASSIFY_URI;
+
     // Since the javascript channel is never the actual channel that
     // any data is loaded through, don't ever set the
     // LOAD_DOCUMENT_URI flag on it, since that could lead to two
     // 'document channels' in the loadgroup if a javascript: URL is
     // loaded while a document is being loaded in the same window.
 
     // XXXbz this, and a whole lot of other hackery, could go away if we'd just
     // cancel the current document load on javascript: load start like IE does.
--- a/dom/tests/mochitest/Makefile.in
+++ b/dom/tests/mochitest/Makefile.in
@@ -54,15 +54,15 @@ DIRS	+= \
 	whatwg \
 	geolocation \
 	globalstorage \
 	localstorage \
 	sessionstorage \
 	storageevent \
 	$(NULL)
 
-#needs IPC support
-ifneq (mobile,$(MOZ_BUILD_APP))
-DIRS	+= notification
-endif
+#needs IPC support, also tests do not run successfully in Firefox for now
+#ifneq (mobile,$(MOZ_BUILD_APP))
+#DIRS	+= notification
+#endif
 
 include $(topsrcdir)/config/rules.mk
 
--- a/dom/tests/mochitest/notification/notification_common.js
+++ b/dom/tests/mochitest/notification/notification_common.js
@@ -67,8 +67,12 @@ function reset_prompt() {
 }
 
 function force_click_on_notification(val) {
   netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
   var prefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);
   prefs.setBoolPref("notification.prompt.testing.click_on_notification", val);
 }
 
+function is_feature_enabled() {
+  var prefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);
+  return prefs.getBoolPref("notification.feature.enabled");
+}
--- a/dom/tests/mochitest/notification/test_basic_notification.html
+++ b/dom/tests/mochitest/notification/test_basic_notification.html
@@ -16,34 +16,40 @@ https://bugzilla.mozilla.org/show_bug.cg
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=573588">Basic property tests</a>
 <p id="display"></p>
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
-force_prompt(true);
-force_click_on_notification(false);
-
-SimpleTest.waitForExplicitFinish();
+if (is_feature_enabled() == false) {
+  SimpleTest.finish();
+}
+else
+{
+  force_prompt(true);
+  force_click_on_notification(false);
 
-ok(navigator.mozNotification, "test for notification.");
+  SimpleTest.waitForExplicitFinish();
 
-var notification = navigator.mozNotification.createNotification("test", "test");
-ok(notification, "test to ensure we can create a notification");
+  ok(navigator.mozNotification, "test for notification.");
 
-notification.onclose =  function() {
-  ok(true, "notification was display and is now closing");
-  reset_prompt();
-  SimpleTest.finish();
-};
+  var notification = navigator.mozNotification.createNotification("test", "test");
+  ok(notification, "test to ensure we can create a notification");
 
-notification.onclick =  function() {
-  ok(false, "Click should not have been called.");
+  notification.onclose =  function() {
+      ok(true, "notification was display and is now closing");
+      reset_prompt();
+      SimpleTest.finish();
+  };
+
+  notification.onclick =  function() {
+      ok(false, "Click should not have been called.");
+  }
+
+  notification.show();
 }
 
-notification.show();
-
 </script>
 </pre>
 </body>
 </html>
--- a/dom/tests/mochitest/notification/test_basic_notification_click.html
+++ b/dom/tests/mochitest/notification/test_basic_notification_click.html
@@ -16,39 +16,45 @@ https://bugzilla.mozilla.org/show_bug.cg
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=573588">Basic property tests</a>
 <p id="display"></p>
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
-click_was_called = false;
-
-force_prompt(true);
-force_click_on_notification(true);
+if (is_feature_enabled() == false) {
+  SimpleTest.finish();
+}
+else
+{
+    click_was_called = false;
 
-SimpleTest.waitForExplicitFinish();
+    force_prompt(true);
+    force_click_on_notification(true);
 
-ok(navigator.mozNotification, "test for notification.");
+    SimpleTest.waitForExplicitFinish();
 
-var notification = navigator.mozNotification.createNotification("test", "test");
-ok(notification, "test to ensure we can create a notification");
+    ok(navigator.mozNotification, "test for notification.");
 
-notification.onclose =  function() {
-  ok(true, "notification was display and is now closing");
-  ok(click_was_called, "was notification clicked?");
+    var notification = navigator.mozNotification.createNotification("test", "test");
+    ok(notification, "test to ensure we can create a notification");
+
+    notification.onclose =  function() {
+        ok(true, "notification was display and is now closing");
+        ok(click_was_called, "was notification clicked?");
 
-  reset_prompt();
-  SimpleTest.finish();
-};
+        reset_prompt();
+        SimpleTest.finish();
+    };
 
-notification.onclick =  function() {
-  ok(true, "Click was called.  Good.");
-  click_was_called = true;
+    notification.onclick =  function() {
+        ok(true, "Click was called.  Good.");
+        click_was_called = true;
+    }
+
+    notification.show();
 }
 
-notification.show();
-
 </script>
 </pre>
 </body>
 </html>
--- a/dom/tests/mochitest/notification/test_leak_windowClose.html
+++ b/dom/tests/mochitest/notification/test_leak_windowClose.html
@@ -5,24 +5,30 @@ https://bugzilla.mozilla.org/show_bug.cg
 -->
 <head>
   <title>Test for leak when window closes</title>
   <script type="text/javascript" src="/MochiKit/packed.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 
 <script>
-function boom()
-{
-  document.documentElement.focus();
-  var x = navigator.mozNotification;
-  document.documentElement.addEventListener('', function(){x}, false);
+if (is_feature_enabled() == false) {
   SimpleTest.finish();
 }
+else
+{
+    function boom()
+    {
+        document.documentElement.focus();
+        var x = navigator.mozNotification;
+        document.documentElement.addEventListener('', function(){x}, false);
+        SimpleTest.finish();
+    }
 
-SimpleTest.waitForExplicitFinish();
-window.addEventListener("load", boom, false);
+    SimpleTest.waitForExplicitFinish();
+    window.addEventListener("load", boom, false);
+}
 </script>
 </head>
 <body>
 <p> I like to write tests </p>
 </body>
 </html>
--- a/embedding/android/GeckoApp.java
+++ b/embedding/android/GeckoApp.java
@@ -118,19 +118,21 @@ abstract public class GeckoApp
     {
         if (!checkAndSetLaunchState(LaunchState.Launching, LaunchState.Launched))
             return false;
 
         // unpack files in the components directory
         try {
             unpackComponents();
         } catch (FileNotFoundException fnfe) {
+            Log.e("GeckoApp", "error unpacking components", fnfe);
             showErrorDialog(getString(R.string.error_loading_file));
             return false;
         } catch (IOException ie) {
+            Log.e("GeckoApp", "error unpacking components", ie);
             String msg = ie.getMessage();
             if (msg.equalsIgnoreCase("No space left on device"))
                 showErrorDialog(getString(R.string.no_space_to_start_error));
             else
                 showErrorDialog(getString(R.string.error_loading_file));
             return false;
         }
 
@@ -407,33 +409,39 @@ abstract public class GeckoApp
             intent.setClassName(getPackageName(),
                                 getPackageName() + ".Restarter");
             addEnvToIntent(intent);
             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
                             Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
             Log.i("GeckoAppJava", intent.toString());
             startActivity(intent);
         } catch (Exception e) {
-            Log.i("GeckoAppJava", e.toString());
+            Log.i("GeckoAppJava", "error doing restart", e);
         }
         finish();
     }
 
     public void handleNotification(String action, String alertName, String alertCookie) {
         GeckoAppShell.handleNotification(action, alertName, alertCookie);
     }
 
     private void checkAndLaunchUpdate() {
         Log.i("GeckoAppJava", "Checking for an update");
 
         int statusCode = 8; // UNEXPECTED_ERROR
+        File downloadDir = null;
+        if (Build.VERSION.SDK_INT >= 8)
+            downloadDir = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS);
+        else
+            downloadDir = new File(Environment.getExternalStorageDirectory().getPath(), "download");
 
-        String updateDir = Environment.getExternalStorageDirectory().getPath() + "/downloads/updates/0/";
-        File updateFile = new File(updateDir + "update.apk");
-        File statusFile = new File(updateDir + "update.status");
+        File updateDir = new File(new File(downloadDir, "updates"),"0");
+
+        File updateFile = new File(updateDir, "update.apk");
+        File statusFile = new File(updateDir, "update.status");
 
         if (!statusFile.exists() || !readUpdateStatus(statusFile).equals("pending"))
             return;
 
         if (!updateFile.exists())
             return;
 
         Log.i("GeckoAppJava", "Update is available!");
@@ -448,44 +456,44 @@ abstract public class GeckoApp
                 Log.i("GeckoAppJava", amCmd);
                 Runtime.getRuntime().exec(amCmd);
                 statusCode = 0; // OK
             } else {
                 Log.i("GeckoAppJava", "Cannot rename the update file!");
                 statusCode = 7; // WRITE_ERROR
             }
         } catch (Exception e) {
-            Log.i("GeckoAppJava", e.toString());
+            Log.i("GeckoAppJava", "error launching installer to update", e);
         }
 
         // Update the status file
         String status = statusCode == 0 ? "succeeded\n" : "failed: "+ statusCode + "\n";
 
         OutputStream outStream;
         try {
             byte[] buf = status.getBytes("UTF-8");
             outStream = new FileOutputStream(statusFile);
             outStream.write(buf, 0, buf.length);
             outStream.close();
         } catch (Exception e) {
-            Log.i("GeckoAppJava", e.toString());
+            Log.i("GeckoAppJava", "error writing status file", e);
         }
 
         if (statusCode == 0)
             System.exit(0);
     }
 
     private String readUpdateStatus(File statusFile) {
         String status = "";
         try {
             BufferedReader reader = new BufferedReader(new FileReader(statusFile));
             status = reader.readLine();
             reader.close();
         } catch (Exception e) {
-            Log.i("GeckoAppJava", e.toString());
+            Log.i("GeckoAppJava", "error reading update status", e);
         }
         return status;
     }
 
     static final int FILE_PICKER_REQUEST = 1;
 
     private SynchronousQueue<String> mFilePickerResult = new SynchronousQueue();
     public String showFilePicker(String aMimeType) {
@@ -495,17 +503,17 @@ abstract public class GeckoApp
         GeckoApp.this.
             startActivityForResult(
                 Intent.createChooser(intent,"choose a file"),
                 FILE_PICKER_REQUEST);
         String filePickerResult = "";
         try {
             filePickerResult = mFilePickerResult.take();
         } catch (InterruptedException e) {
-            Log.i("GeckoApp", "error: " + e);
+            Log.i("GeckoApp", "showing file picker ",  e);
         }
         
         return filePickerResult;
     }
     
     @Override
     protected void onActivityResult(int requestCode, int resultCode, 
                                     Intent data) {
@@ -530,18 +538,18 @@ abstract public class GeckoApp
                 int len = is.read(buf);
                 while (len != -1) {
                     fos.write(buf, 0, len);
                     len = is.read(buf);
                 }
                 fos.close();
                 filePickerResult =  file.getAbsolutePath();
             }catch (Exception e) {
-                Log.e("GeckoApp", "error : "+ e);
+                Log.e("GeckoApp", "showing file picker", e);
             }
         }
         try {
             mFilePickerResult.put(filePickerResult);
         } catch (InterruptedException e) {
-            Log.i("GeckoApp", "error: " + e);
+            Log.i("GeckoApp", "error returning file picker result", e);
         }
     }
 }
--- a/embedding/android/GeckoAppShell.java
+++ b/embedding/android/GeckoAppShell.java
@@ -113,18 +113,23 @@ class GeckoAppShell
         File f = new File("/data/data/" + 
                           GeckoApp.mAppContext.getPackageName() + "/tmp");
         if (!f.exists())
             f.mkdirs();
 
         GeckoAppShell.putenv("TMPDIR=" + f.getPath());
 
         f = Environment.getDownloadCacheDirectory();
-        GeckoAppShell.putenv("EXTERNAL_STORAGE" + f.getPath());
-
+        GeckoAppShell.putenv("EXTERNAL_STORAGE=" + f.getPath());
+        File downloadDir = null;
+        if (Build.VERSION.SDK_INT >= 8)
+            downloadDir = GeckoApp.mAppContext.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS);
+        else
+            downloadDir = new File(Environment.getExternalStorageDirectory().getPath(), "download");
+        GeckoAppShell.putenv("DOWNLOADS_DIRECTORY=" + downloadDir.getPath());
         GeckoAppShell.putenv("LANG=" + Locale.getDefault().toString());
 
         loadLibs(apkName);
     }
 
     public static void runGecko(String apkPath, String args, String url) {
         // run gecko -- it will spawn its own thread
         GeckoAppShell.nativeInit();
--- a/embedding/android/GeckoInputConnection.java
+++ b/embedding/android/GeckoInputConnection.java
@@ -1,9 +1,9 @@
-/* -*- Mode: Java; tab-width: 20; indent-tabs-mode: nil; -*-
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * ***** BEGIN LICENSE BLOCK *****
  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  *
  * The contents of this file are subject to the Mozilla Public License Version
  * 1.1 (the "License"); you may not use this file except in compliance with
  * the License. You may obtain a copy of the License at
  * http://www.mozilla.org/MPL/
  *
@@ -104,17 +104,17 @@ public class GeckoInputConnection
 
         // Select text to be deleted
         int delStart, delLen;
         GeckoAppShell.sendEventToGecko(
             new GeckoEvent(GeckoEvent.IME_GET_SELECTION, 0, 0));
         try {
             mQueryResult.take();
         } catch (InterruptedException e) {
-            Log.e("GeckoAppJava", "IME: deleteSurroundingText interrupted");
+            Log.e("GeckoAppJava", "IME: deleteSurroundingText interrupted", e);
             return false;
         }
         delStart = mSelectionStart > leftLength ?
                     mSelectionStart - leftLength : 0;
         delLen = mSelectionStart + rightLength - delStart;
         GeckoAppShell.sendEventToGecko(
             new GeckoEvent(GeckoEvent.IME_SET_SELECTION, delStart, delLen));
 
@@ -189,17 +189,17 @@ public class GeckoInputConnection
         // First we need to ask Gecko to tell us the full contents of the
         // text field we're about to operate on.
         String text;
         GeckoAppShell.sendEventToGecko(
             new GeckoEvent(GeckoEvent.IME_GET_TEXT, 0, Integer.MAX_VALUE));
         try {
             text = mQueryResult.take();
         } catch (InterruptedException e) {
-            Log.e("GeckoAppJava", "IME: performContextMenuAction interrupted");
+            Log.e("GeckoAppJava", "IME: performContextMenuAction interrupted", e);
             return false;
         }
 
         switch (id) {
             case R.id.selectAll:
                 setSelection(0, text.length());
                 break;
             case R.id.cut:
@@ -219,17 +219,17 @@ public class GeckoInputConnection
                 // If there is no selection set, we must be doing "Copy All",
                 // otherwise, we need to get the selection from Gecko
                 if (mSelectionLength > 0) {
                     GeckoAppShell.sendEventToGecko(
                         new GeckoEvent(GeckoEvent.IME_GET_SELECTION, 0, 0));
                     try {
                         text = mQueryResult.take();
                     } catch (InterruptedException e) {
-                        Log.e("GeckoAppJava", "IME: performContextMenuAction interrupted");
+                        Log.e("GeckoAppJava", "IME: performContextMenuAction interrupted", e);
                         return false;
                     }
                 }
                 GeckoAppShell.setClipboardText(text);
                 break;
         }
         return true;
     }
@@ -246,48 +246,48 @@ public class GeckoInputConnection
         extract.partialStartOffset = -1;
         extract.partialEndOffset = -1;
 
         GeckoAppShell.sendEventToGecko(
             new GeckoEvent(GeckoEvent.IME_GET_SELECTION, 0, 0));
         try {
             mQueryResult.take();
         } catch (InterruptedException e) {
-            Log.e("GeckoAppJava", "IME: getExtractedText interrupted");
+            Log.e("GeckoAppJava", "IME: getExtractedText interrupted", e);
             return null;
         }
         extract.selectionStart = mSelectionStart;
         extract.selectionEnd = mSelectionStart + mSelectionLength;
 
         GeckoAppShell.sendEventToGecko(
             new GeckoEvent(GeckoEvent.IME_GET_TEXT, 0, Integer.MAX_VALUE));
         try {
             extract.startOffset = 0;
             extract.text = mQueryResult.take();
 
             if ((flags & GET_EXTRACTED_TEXT_MONITOR) != 0)
                 mUpdateRequest = req;
             return extract;
 
         } catch (InterruptedException e) {
-            Log.e("GeckoAppJava", "IME: getExtractedText interrupted");
+            Log.e("GeckoAppJava", "IME: getExtractedText interrupted", e);
             return null;
         }
     }
 
     @Override
     public CharSequence getTextAfterCursor(int length, int flags) {
         //Log.d("GeckoAppJava", "IME: getTextAfterCursor");
 
         GeckoAppShell.sendEventToGecko(
             new GeckoEvent(GeckoEvent.IME_GET_SELECTION, 0, 0));
         try {
             mQueryResult.take();
         } catch (InterruptedException e) {
-            Log.e("GeckoAppJava", "IME: getTextBefore/AfterCursor interrupted");
+            Log.e("GeckoAppJava", "IME: getTextBefore/AfterCursor interrupted", e);
             return null;
         }
 
         /* Compatible with both positive and negative length
             (no need for separate code for getTextBeforeCursor) */
         int textStart = mSelectionStart;
         int textLength = length;
 
@@ -300,17 +300,17 @@ public class GeckoInputConnection
           }
         }
 
         GeckoAppShell.sendEventToGecko(
             new GeckoEvent(GeckoEvent.IME_GET_TEXT, textStart, textLength));
         try {
             return mQueryResult.take();
         } catch (InterruptedException e) {
-            Log.e("GeckoAppJava", "IME: getTextBefore/AfterCursor: Interrupted!");
+            Log.e("GeckoAppJava", "IME: getTextBefore/AfterCursor: Interrupted!", e);
             return null;
         }
     }
 
     @Override
     public CharSequence getTextBeforeCursor(int length, int flags) {
         //Log.d("GeckoAppJava", "IME: getTextBeforeCursor");
 
@@ -326,17 +326,17 @@ public class GeckoInputConnection
 
         if (!mComposing) {
             // Get current selection
             GeckoAppShell.sendEventToGecko(
                 new GeckoEvent(GeckoEvent.IME_GET_SELECTION, 0, 0));
             try {
                 mQueryResult.take();
             } catch (InterruptedException e) {
-                Log.e("GeckoAppJava", "IME: setComposingText interrupted");
+                Log.e("GeckoAppJava", "IME: setComposingText interrupted", e);
                 return false;
             }
             // Make sure we are in a composition
             GeckoAppShell.sendEventToGecko(
                 new GeckoEvent(GeckoEvent.IME_COMPOSITION_BEGIN, 0, 0));
             mComposing = true;
             mCompositionStart = mSelectionLength >= 0 ?
                 mSelectionStart : mSelectionStart + mSelectionLength;
--- a/embedding/android/GeckoSurfaceView.java
+++ b/embedding/android/GeckoSurfaceView.java
@@ -170,17 +170,17 @@ class GeckoSurfaceView
         } finally {
             mSurfaceLock.unlock();
         }
 
         ByteBuffer bb = null;
         try {
             bb = mSyncBuf.take();
         } catch (InterruptedException ie) {
-            Log.e("GeckoAppJava", "Threw exception while getting sync buf: " + ie);
+            Log.e("GeckoAppJava", "Threw exception while getting sync buf: ", ie);
         }
         if (bb != null && bb.capacity() == (width * height * 2)) {
             mSoftwareBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.RGB_565);
             mSoftwareBitmap.copyPixelsFromBuffer(bb);
             Canvas c = holder.lockCanvas();
             c.drawBitmap(mSoftwareBitmap, 0, 0, null);
             holder.unlockCanvasAndPost(c);
         }
@@ -284,17 +284,17 @@ class GeckoSurfaceView
         try {
             if (mSyncDraw) {
                 if (buffer != mSoftwareBuffer || stride != (mWidth * 2))
                     return;
                 mSyncDraw = false;
                 try {
                     mSyncBuf.put(buffer);
                 } catch (InterruptedException ie) {
-                    Log.e("GeckoAppJava", "Threw exception while getting sync buf: " + ie);
+                    Log.e("GeckoAppJava", "Threw exception while getting sync buf: ", ie);
                 }
                 return;
             }
         } finally {
             mSurfaceLock.unlock();
         }
 
         if (buffer != mSoftwareBuffer || stride != (mWidth * 2))
--- a/embedding/android/NotificationHandler.java.in
+++ b/embedding/android/NotificationHandler.java.in
@@ -1,9 +1,9 @@
-/* -*- Mode: Java; tab-width: 20; indent-tabs-mode: nil; -*-
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * ***** BEGIN LICENSE BLOCK *****
  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  *
  * The contents of this file are subject to the Mozilla Public License Version
  * 1.1 (the "License"); you may not use this file except in compliance with
  * the License. You may obtain a copy of the License at
  * http://www.mozilla.org/MPL/
  *
@@ -89,13 +89,13 @@ public class NotificationHandler
         if (App.ACTION_ALERT_CLICK.equals(action)) {
             // Start or bring to front the main activity
             Intent appIntent = new Intent(Intent.ACTION_MAIN);
             appIntent.setClassName(context, "@ANDROID_PACKAGE_NAME@.App");
             appIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             try {
                 context.startActivity(appIntent);
             } catch (ActivityNotFoundException e) {
-                Log.e("GeckoAppJava", "NotificationHandler Exception: " + e.getMessage());
+                Log.e("GeckoAppJava", "NotificationHandler Exception: ", e);
             }
         }
     }
 }
--- a/gfx/cairo/cairo/src/cairo-pattern.c
+++ b/gfx/cairo/cairo/src/cairo-pattern.c
@@ -2940,17 +2940,17 @@ cairo_pattern_get_surface (cairo_pattern
 			   cairo_surface_t **surface)
 {
     cairo_surface_pattern_t *spat = (cairo_surface_pattern_t*) pattern;
 
     if (pattern->status)
 	return pattern->status;
 
     if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE)
-	return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+	return CAIRO_STATUS_PATTERN_TYPE_MISMATCH;
 
     if (surface)
 	*surface = spat->surface;
 
     return CAIRO_STATUS_SUCCESS;
 }
 
 /**
--- a/gfx/cairo/cairo/src/cairo-quartz-surface.c
+++ b/gfx/cairo/cairo/src/cairo-quartz-surface.c
@@ -2473,16 +2473,17 @@ static cairo_int_status_t
     cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
     cairo_quartz_drawing_state_t state;
     float xprev, yprev;
     int i;
     CGFontRef cgfref = NULL;
 
     cairo_bool_t isClipping = FALSE;
     cairo_bool_t didForceFontSmoothing = FALSE;
+    cairo_antialias_t effective_antialiasing;
 
     if (IS_EMPTY(surface))
 	return CAIRO_STATUS_SUCCESS;
 
     if (num_glyphs <= 0)
 	return CAIRO_STATUS_SUCCESS;
 
     if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_QUARTZ)
@@ -2514,16 +2515,22 @@ static cairo_int_status_t
 	goto BAIL;
     }
 
     /* this doesn't addref */
     cgfref = _cairo_quartz_scaled_font_get_cg_font_ref (scaled_font);
     CGContextSetFont (state.context, cgfref);
     CGContextSetFontSize (state.context, 1.0);
 
+    effective_antialiasing = scaled_font->options.antialias;
+    if (effective_antialiasing == CAIRO_ANTIALIAS_SUBPIXEL &&
+        !surface->base.permit_subpixel_antialiasing) {
+        effective_antialiasing = CAIRO_ANTIALIAS_GRAY;
+    }
+
     switch (scaled_font->options.antialias) {
 	case CAIRO_ANTIALIAS_SUBPIXEL:
 	    CGContextSetShouldAntialias (state.context, TRUE);
 	    CGContextSetShouldSmoothFonts (state.context, TRUE);
 	    if (CGContextSetAllowsFontSmoothingPtr &&
 		!CGContextGetAllowsFontSmoothingPtr (state.context))
 	    {
 		didForceFontSmoothing = TRUE;
--- a/gfx/cairo/cairo/src/cairo-surface-private.h
+++ b/gfx/cairo/cairo/src/cairo-surface-private.h
@@ -58,16 +58,17 @@ struct _cairo_surface {
 
     cairo_reference_count_t ref_count;
     cairo_status_t status;
     unsigned int unique_id;
 
     unsigned finished : 1;
     unsigned is_clear : 1;
     unsigned has_font_options : 1;
+    unsigned permit_subpixel_antialiasing : 1;
 
     cairo_user_data_array_t user_data;
     cairo_user_data_array_t mime_data;
 
     cairo_matrix_t device_transform;
     cairo_matrix_t device_transform_inverse;
 
     /* The actual resolution of the device, in dots per inch. */
--- a/gfx/cairo/cairo/src/cairo-surface.c
+++ b/gfx/cairo/cairo/src/cairo-surface.c
@@ -49,17 +49,18 @@ const cairo_surface_t name = {					\
     NULL,				/* backend */		\
     CAIRO_SURFACE_TYPE_IMAGE,		/* type */		\
     CAIRO_CONTENT_COLOR,		/* content */		\
     CAIRO_REFERENCE_COUNT_INVALID,	/* ref_count */		\
     status,				/* status */		\
     0,					/* unique id */		\
     FALSE,				/* finished */		\
     TRUE,				/* is_clear */		\
-    FALSE,				/* has_font_options */	\
+    FALSE,                             /* has_font_options */	\
+    FALSE,                             /* permit_subpixel_antialiasing */ \
     { 0, 0, 0, NULL, },			/* user_data */		\
     { 0, 0, 0, NULL, },			/* mime_data */         \
     { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 },   /* device_transform */	\
     { 1.0, 0.0,	0.0, 1.0, 0.0, 0.0 },	/* device_transform_inverse */	\
     0.0,				/* x_resolution */	\
     0.0,				/* y_resolution */	\
     0.0,				/* x_fallback_resolution */	\
     0.0,				/* y_fallback_resolution */	\
@@ -342,46 +343,48 @@ void
     surface->content = content;
     surface->type = backend->type;
 
     CAIRO_REFERENCE_COUNT_INIT (&surface->ref_count, 1);
     surface->status = CAIRO_STATUS_SUCCESS;
     surface->unique_id = _cairo_surface_allocate_unique_id ();
     surface->finished = FALSE;
     surface->is_clear = FALSE;
+    surface->has_font_options = FALSE;
+    surface->permit_subpixel_antialiasing = TRUE;
 
     _cairo_user_data_array_init (&surface->user_data);
     _cairo_user_data_array_init (&surface->mime_data);
 
     cairo_matrix_init_identity (&surface->device_transform);
     cairo_matrix_init_identity (&surface->device_transform_inverse);
 
     surface->x_resolution = CAIRO_SURFACE_RESOLUTION_DEFAULT;
     surface->y_resolution = CAIRO_SURFACE_RESOLUTION_DEFAULT;
 
     surface->x_fallback_resolution = CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT;
     surface->y_fallback_resolution = CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT;
 
     _cairo_array_init (&surface->snapshots, sizeof (cairo_surface_t *));
     surface->snapshot_of = NULL;
-
-    surface->has_font_options = FALSE;
 }
 
 static void
 _cairo_surface_copy_similar_properties (cairo_surface_t *surface,
 					cairo_surface_t *other)
 {
     if (other->has_font_options || other->backend != surface->backend) {
 	cairo_font_options_t options;
 
 	cairo_surface_get_font_options (other, &options);
 	_cairo_surface_set_font_options (surface, &options);
     }
 
+    surface->permit_subpixel_antialiasing = other->permit_subpixel_antialiasing;
+
     cairo_surface_set_fallback_resolution (surface,
 					   other->x_fallback_resolution,
 					   other->y_fallback_resolution);
 }
 
 cairo_surface_t *
 _cairo_surface_create_similar_scratch (cairo_surface_t *other,
 				       cairo_content_t	content,
@@ -2482,16 +2485,67 @@ cairo_surface_has_show_text_glyphs (cair
 
     if (surface->backend->has_show_text_glyphs)
 	return surface->backend->has_show_text_glyphs (surface);
     else
 	return surface->backend->show_text_glyphs != NULL;
 }
 slim_hidden_def (cairo_surface_has_show_text_glyphs);
 
+/**
+ * cairo_surface_set_subpixel_antialiasing:
+ * @surface: a #cairo_surface_t
+ *
+ * Sets whether the surface permits subpixel antialiasing. By default,
+ * surfaces permit subpixel antialiasing.
+ *
+ * Enabling subpixel antialiasing for CONTENT_COLOR_ALPHA surfaces generally
+ * requires that the pixels in the areas under a subpixel antialiasing
+ * operation already be opaque.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_surface_set_subpixel_antialiasing (cairo_surface_t *surface,
+                                         cairo_subpixel_antialiasing_t enabled)
+{
+    if (surface->status)
+        return;
+
+    if (surface->finished) {
+        _cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED);
+        return;
+    }
+
+    surface->permit_subpixel_antialiasing =
+        enabled == CAIRO_SUBPIXEL_ANTIALIASING_ENABLED;
+}
+slim_hidden_def (cairo_surface_set_subpixel_antialiasing);
+
+/**
+ * cairo_surface_get_subpixel_antialiasing:
+ * @surface: a #cairo_surface_t
+ *
+ * Gets whether the surface supports subpixel antialiasing. By default,
+ * CAIRO_CONTENT_COLOR surfaces support subpixel antialiasing but other
+ * surfaces do not.
+ *
+ * Since: 1.12
+ **/
+cairo_subpixel_antialiasing_t
+cairo_surface_get_subpixel_antialiasing (cairo_surface_t *surface)
+{
+    if (surface->status)
+        return CAIRO_SUBPIXEL_ANTIALIASING_DISABLED;
+
+    return surface->permit_subpixel_antialiasing ?
+        CAIRO_SUBPIXEL_ANTIALIASING_ENABLED : CAIRO_SUBPIXEL_ANTIALIASING_DISABLED;
+}
+slim_hidden_def (cairo_surface_get_subpixel_antialiasing);
+
 /* Note: the backends may modify the contents of the glyph array as long as
  * they do not return %CAIRO_INT_STATUS_UNSUPPORTED. This makes it possible to
  * avoid copying the array again and again, and edit it in-place.
  * Backends are in fact free to use the array as a generic buffer as they
  * see fit.
  *
  * For show_glyphs backend method, and NOT for show_text_glyphs method,
  * when they do return UNSUPPORTED, they may adjust remaining_glyphs to notify
--- a/gfx/cairo/cairo/src/cairo-tee-surface.c
+++ b/gfx/cairo/cairo/src/cairo-tee-surface.c
@@ -186,35 +186,72 @@ static void
 _cairo_tee_surface_get_font_options (void                  *abstract_surface,
 				     cairo_font_options_t  *options)
 {
     cairo_tee_surface_t *surface = abstract_surface;
 
     _cairo_surface_wrapper_get_font_options (&surface->master, options);
 }
 
+static const cairo_pattern_t *
+_cairo_tee_surface_match_source (cairo_tee_surface_t *surface,
+                                 const cairo_pattern_t *source,
+                                 int index,
+                                 cairo_surface_wrapper_t *dest,
+                                 cairo_surface_pattern_t *temp)
+{
+    cairo_surface_t *s;
+    cairo_status_t status = cairo_pattern_get_surface ((cairo_pattern_t *)source, &s);
+    if (status == CAIRO_STATUS_SUCCESS &&
+        cairo_surface_get_type (s) == CAIRO_SURFACE_TYPE_TEE) {
+        cairo_surface_t *tee_surf = cairo_tee_surface_index (s, index);
+        if (tee_surf->status == CAIRO_STATUS_SUCCESS &&
+            tee_surf->backend == dest->target->backend) {
+            status = _cairo_pattern_init_copy (&temp->base, source);
+            if (status == CAIRO_STATUS_SUCCESS) {
+                cairo_surface_destroy (temp->surface);
+                temp->surface = tee_surf;
+                cairo_surface_reference (temp->surface);
+                return &temp->base;
+            }
+        }
+    }
+
+    return source;
+}
+
 static cairo_int_status_t
 _cairo_tee_surface_paint (void			*abstract_surface,
 			  cairo_operator_t	 op,
 			  const cairo_pattern_t	*source,
 			  cairo_clip_t		*clip)
 {
     cairo_tee_surface_t *surface = abstract_surface;
     cairo_surface_wrapper_t *slaves;
     int n, num_slaves;
     cairo_status_t status;
+    const cairo_pattern_t *matched_source;
+    cairo_surface_pattern_t temp;
 
-    status = _cairo_surface_wrapper_paint (&surface->master, op, source, clip);
+    matched_source = _cairo_tee_surface_match_source (surface, source, 0, &surface->master, &temp);
+    status = _cairo_surface_wrapper_paint (&surface->master, op, matched_source, clip);
+    if (matched_source == &temp.base) {
+        _cairo_pattern_fini (&temp.base);
+    }
     if (unlikely (status))
 	return status;
 
     num_slaves = _cairo_array_num_elements (&surface->slaves);
     slaves = _cairo_array_index (&surface->slaves, 0);
     for (n = 0; n < num_slaves; n++) {
-	status = _cairo_surface_wrapper_paint (&slaves[n], op, source, clip);
+        matched_source = _cairo_tee_surface_match_source (surface, source, n + 1, &slaves[n], &temp);
+	status = _cairo_surface_wrapper_paint (&slaves[n], op, matched_source, clip);
+        if (matched_source == &temp.base) {
+            _cairo_pattern_fini (&temp.base);
+        }
 	if (unlikely (status))
 	    return status;
     }
 
     return CAIRO_STATUS_SUCCESS;
 }
 
 static cairo_int_status_t
@@ -223,27 +260,37 @@ static cairo_int_status_t
 			 const cairo_pattern_t	*source,
 			 const cairo_pattern_t	*mask,
 			 cairo_clip_t		*clip)
 {
     cairo_tee_surface_t *surface = abstract_surface;
     cairo_surface_wrapper_t *slaves;
     int n, num_slaves;
     cairo_status_t status;
+    const cairo_pattern_t *matched_source;
+    cairo_surface_pattern_t temp;
 
+    matched_source = _cairo_tee_surface_match_source (surface, source, 0, &surface->master, &temp);
     status = _cairo_surface_wrapper_mask (&surface->master,
-					  op, source, mask, clip);
+					  op, matched_source, mask, clip);
+    if (matched_source == &temp.base) {
+        _cairo_pattern_fini (&temp.base);
+    }
     if (unlikely (status))
 	return status;
 
     num_slaves = _cairo_array_num_elements (&surface->slaves);
     slaves = _cairo_array_index (&surface->slaves, 0);
     for (n = 0; n < num_slaves; n++) {
+        matched_source = _cairo_tee_surface_match_source (surface, source, n + 1, &slaves[n], &temp);
 	status = _cairo_surface_wrapper_mask (&slaves[n],
-					      op, source, mask, clip);
+					      op, matched_source, mask, clip);
+        if (matched_source == &temp.base) {
+            _cairo_pattern_fini (&temp.base);
+        }
 	if (unlikely (status))
 	    return status;
     }
 
     return CAIRO_STATUS_SUCCESS;
 }
 
 static cairo_int_status_t
@@ -257,35 +304,45 @@ static cairo_int_status_t
 			   double			 tolerance,
 			   cairo_antialias_t		 antialias,
 			   cairo_clip_t			*clip)
 {
     cairo_tee_surface_t *surface = abstract_surface;
     cairo_surface_wrapper_t *slaves;
     int n, num_slaves;
     cairo_status_t status;
+    const cairo_pattern_t *matched_source;
+    cairo_surface_pattern_t temp;
 
+    matched_source = _cairo_tee_surface_match_source (surface, source, 0, &surface->master, &temp);
     status = _cairo_surface_wrapper_stroke (&surface->master,
-					    op, source,
+					    op, matched_source,
 					    path, style,
 					    ctm, ctm_inverse,
 					    tolerance, antialias,
 					    clip);
+    if (matched_source == &temp.base) {
+        _cairo_pattern_fini (&temp.base);
+    }
     if (unlikely (status))
 	return status;
 
     num_slaves = _cairo_array_num_elements (&surface->slaves);
     slaves = _cairo_array_index (&surface->slaves, 0);
     for (n = 0; n < num_slaves; n++) {
+        matched_source = _cairo_tee_surface_match_source (surface, source, n + 1, &slaves[n], &temp);
 	status = _cairo_surface_wrapper_stroke (&slaves[n],
-						op, source,
+						op, matched_source,
 						path, style,
 						ctm, ctm_inverse,
 						tolerance, antialias,
 						clip);
+        if (matched_source == &temp.base) {
+            _cairo_pattern_fini (&temp.base);
+        }
 	if (unlikely (status))
 	    return status;
     }
 
     return CAIRO_STATUS_SUCCESS;
 }
 
 static cairo_int_status_t
@@ -297,33 +354,43 @@ static cairo_int_status_t
 			 double				 tolerance,
 			 cairo_antialias_t		 antialias,
 			 cairo_clip_t			*clip)
 {
     cairo_tee_surface_t *surface = abstract_surface;
     cairo_surface_wrapper_t *slaves;
     int n, num_slaves;
     cairo_status_t status;
+    const cairo_pattern_t *matched_source;
+    cairo_surface_pattern_t temp;
 
+    matched_source = _cairo_tee_surface_match_source (surface, source, 0, &surface->master, &temp);
     status = _cairo_surface_wrapper_fill (&surface->master,
-					  op, source,
+					  op, matched_source,
 					  path, fill_rule,
 					  tolerance, antialias,
 					  clip);
+    if (matched_source == &temp.base) {
+        _cairo_pattern_fini (&temp.base);
+    }
     if (unlikely (status))
 	return status;
 
     num_slaves = _cairo_array_num_elements (&surface->slaves);
     slaves = _cairo_array_index (&surface->slaves, 0);
     for (n = 0; n < num_slaves; n++) {
+        matched_source = _cairo_tee_surface_match_source (surface, source, n + 1, &slaves[n], &temp);
 	status = _cairo_surface_wrapper_fill (&slaves[n],
-					      op, source,
+					      op, matched_source,
 					      path, fill_rule,
 					      tolerance, antialias,
 					      clip);
+        if (matched_source == &temp.base) {
+            _cairo_pattern_fini (&temp.base);
+        }
 	if (unlikely (status))
 	    return status;
     }
 
     return CAIRO_STATUS_SUCCESS;
 }
 
 static cairo_bool_t
@@ -346,46 +413,56 @@ static cairo_int_status_t
 				     cairo_scaled_font_t    *scaled_font,
 				     cairo_clip_t	    *clip)
 {
     cairo_tee_surface_t *surface = abstract_surface;
     cairo_surface_wrapper_t *slaves;
     int n, num_slaves;
     cairo_status_t status;
     cairo_glyph_t *glyphs_copy;
+    const cairo_pattern_t *matched_source;
+    cairo_surface_pattern_t temp;
 
     /* XXX: This copying is ugly. */
     glyphs_copy = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
     if (unlikely (glyphs_copy == NULL))
 	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
     memcpy (glyphs_copy, glyphs, sizeof (cairo_glyph_t) * num_glyphs);
+    matched_source = _cairo_tee_surface_match_source (surface, source, 0, &surface->master, &temp);
     status = _cairo_surface_wrapper_show_text_glyphs (&surface->master, op,
-						      source,
+                              matched_source,
 						      utf8, utf8_len,
 						      glyphs_copy, num_glyphs,
 						      clusters, num_clusters,
 						      cluster_flags,
 						      scaled_font,
 						      clip);
+    if (matched_source == &temp.base) {
+        _cairo_pattern_fini (&temp.base);
+    }
     if (unlikely (status))
 	goto CLEANUP;
 
     num_slaves = _cairo_array_num_elements (&surface->slaves);
     slaves = _cairo_array_index (&surface->slaves, 0);
     for (n = 0; n < num_slaves; n++) {
 	memcpy (glyphs_copy, glyphs, sizeof (cairo_glyph_t) * num_glyphs);
+      matched_source = _cairo_tee_surface_match_source (surface, source, n + 1, &slaves[n], &temp);
 	status = _cairo_surface_wrapper_show_text_glyphs (&slaves[n], op,
-							  source,
+	                          matched_source,
 							  utf8, utf8_len,
 							  glyphs_copy, num_glyphs,
 							  clusters, num_clusters,
 							  cluster_flags,
 							  scaled_font,
 							  clip);
+        if (matched_source == &temp.base) {
+            _cairo_pattern_fini (&temp.base);
+        }
 	if (unlikely (status))
 	    goto CLEANUP;
     }
 
   CLEANUP:
     free (glyphs_copy);
     return status;
 }
--- a/gfx/cairo/cairo/src/cairo-win32-font.c
+++ b/gfx/cairo/cairo/src/cairo-win32-font.c
@@ -1380,16 +1380,17 @@ static cairo_int_status_t
     cairo_win32_surface_t *surface = (cairo_win32_surface_t *)generic_surface;
     cairo_status_t status;
 
     if (width == 0 || height == 0)
 	return CAIRO_STATUS_SUCCESS;
 
     if (_cairo_surface_is_win32 (generic_surface) &&
 	surface->format == CAIRO_FORMAT_RGB24 &&
+	(generic_surface->permit_subpixel_antialiasing || scaled_font->quality != CLEARTYPE_QUALITY) &&
 	op == CAIRO_OPERATOR_OVER &&
 	_cairo_pattern_is_opaque_solid (pattern)) {
 
 	cairo_solid_pattern_t *solid_pattern = (cairo_solid_pattern_t *)pattern;
 
 	/* When compositing OVER on a GDI-understood surface, with a
 	 * solid opaque color, we can just call ExtTextOut directly.
 	 */
@@ -1411,16 +1412,18 @@ static cairo_int_status_t
 	 * surface by drawing the the glyphs onto a DIB, black-on-white then
 	 * inverting. GDI outputs gamma-corrected images so inverted black-on-white
 	 * is very different from white-on-black. We favor the more common
 	 * case where the final output is dark-on-light.
 	 */
 	cairo_win32_surface_t *tmp_surface;
 	cairo_surface_t *mask_surface;
 	cairo_surface_pattern_t mask;
+	cairo_bool_t use_subpixel_antialiasing =
+	    scaled_font->quality == CLEARTYPE_QUALITY && generic_surface->permit_subpixel_antialiasing;
 	RECT r;
 
 	tmp_surface = (cairo_win32_surface_t *)cairo_win32_surface_create_with_dib (CAIRO_FORMAT_ARGB32, width, height);
 	if (tmp_surface->base.status)
 	    return tmp_surface->base.status;
 
 	r.left = 0;
 	r.top = 0;
@@ -1432,17 +1435,17 @@ static cairo_int_status_t
 		                          scaled_font, RGB (0, 0, 0),
 					  dest_x, dest_y,
 					  glyphs, num_glyphs);
 	if (status) {
 	    cairo_surface_destroy (&tmp_surface->base);
 	    return status;
 	}
 
-	if (scaled_font->quality == CLEARTYPE_QUALITY) {
+	if (use_subpixel_antialiasing) {
 	    /* For ClearType, we need a 4-channel mask. If we are compositing on
 	     * a surface with alpha, we need to compute the alpha channel of
 	     * the mask (we just copy the green channel). But for a destination
 	     * surface without alpha the alpha channel of the mask is ignored
 	     */
 
 	    if (surface->format != CAIRO_FORMAT_RGB24)
 		_compute_argb32_mask_alpha (tmp_surface);
@@ -1460,17 +1463,17 @@ static cairo_int_status_t
 
 	/* For op == OVER, no-cleartype, a possible optimization here is to
 	 * draw onto an intermediate ARGB32 surface and alpha-blend that with the
 	 * destination
 	 */
 	_cairo_pattern_init_for_surface (&mask, mask_surface);
 	cairo_surface_destroy (mask_surface);
 
-	if (scaled_font->quality == CLEARTYPE_QUALITY)
+	if (use_subpixel_antialiasing)
 	    mask.base.has_component_alpha = TRUE;
 
 	status = _cairo_surface_composite (op, pattern,
 					   &mask.base,
 					   &surface->base,
 					   source_x, source_y,
 					   0, 0,
 					   dest_x, dest_y,
--- a/gfx/cairo/cairo/src/cairo-xlib-surface.c
+++ b/gfx/cairo/cairo/src/cairo-xlib-surface.c
@@ -3570,16 +3570,17 @@ typedef struct _cairo_xlib_font_glyphset
     GlyphSet		glyphset;
     cairo_format_t	format;
     XRenderPictFormat	*xrender_format;
     cairo_xlib_font_glyphset_free_glyphs_t *pending_free_glyphs;
 } cairo_xlib_font_glyphset_info_t;
 
 typedef struct _cairo_xlib_surface_font_private {
     cairo_scaled_font_t		    *scaled_font;
+    cairo_scaled_font_t         *grayscale_font;
     cairo_xlib_hook_t                close_display_hook;
     cairo_xlib_display_t	    *display;
     cairo_xlib_font_glyphset_info_t  glyphset_info[NUM_GLYPHSETS];
 } cairo_xlib_surface_font_private_t;
 
 /* callback from CloseDisplay */
 static void
 _cairo_xlib_surface_remove_scaled_font (cairo_xlib_display_t	*display,
@@ -3599,16 +3600,20 @@ static void
 
     _cairo_scaled_font_reset_cache (scaled_font);
     CAIRO_MUTEX_UNLOCK (scaled_font->mutex);
 
     if (font_private != NULL) {
 	Display *dpy;
 	int i;
 
+    if (font_private->grayscale_font) {
+        cairo_scaled_font_destroy (font_private->grayscale_font);
+    }
+
 	dpy = _cairo_xlib_display_get_dpy (display);
 	for (i = 0; i < NUM_GLYPHSETS; i++) {
 	    cairo_xlib_font_glyphset_info_t *glyphset_info;
 
 	    glyphset_info = &font_private->glyphset_info[i];
 	    if (glyphset_info->glyphset)
 		XRenderFreeGlyphSet (dpy, glyphset_info->glyphset);
 
@@ -3629,16 +3634,17 @@ static cairo_status_t
     cairo_status_t status;
     int i;
 
     font_private = malloc (sizeof (cairo_xlib_surface_font_private_t));
     if (unlikely (font_private == NULL))
 	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
     font_private->scaled_font = scaled_font;
+    font_private->grayscale_font = NULL;
     status = _cairo_xlib_display_get (dpy, &font_private->display);
     if (unlikely (status)) {
 	free (font_private);
 	return status;
     }
 
     /* initialize and hook into the CloseDisplay callback */
     font_private->close_display_hook.func =
@@ -3671,16 +3677,20 @@ static void
 {
     cairo_xlib_surface_font_private_t *font_private;
 
     font_private = scaled_font->surface_private;
     if (font_private != NULL) {
 	cairo_xlib_display_t *display;
 	int i;
 
+	if (font_private->grayscale_font) {
+        cairo_scaled_font_destroy (font_private->grayscale_font);
+	}
+
 	display = font_private->display;
 	_cairo_xlib_remove_close_display_hook (display,
 					       &font_private->close_display_hook);
 
 	for (i = 0; i < NUM_GLYPHSETS; i++) {
 	    cairo_xlib_font_glyphset_info_t *glyphset_info;
 
 	    glyphset_info = &font_private->glyphset_info[i];
@@ -4417,16 +4427,62 @@ static cairo_bool_t
 	(font_private != NULL && font_private->display != dst->display))
     {
 	return FALSE;
     }
 
     return TRUE;
 }
 
+/* Gets a grayscale version of scaled_font. The grayscale version is cached
+ * in our surface_private data.
+ */
+static cairo_scaled_font_t *
+_cairo_xlib_get_grayscale_font (cairo_xlib_surface_t *dst,
+                                cairo_scaled_font_t *scaled_font)
+{
+    cairo_xlib_surface_font_private_t *font_private = scaled_font->surface_private;
+    cairo_bool_t needs_font;
+
+    if (font_private == NULL) {
+        cairo_status_t status = _cairo_xlib_surface_font_init (dst->dpy, scaled_font);
+        if (unlikely (status))
+            return _cairo_scaled_font_create_in_error (status);
+        font_private = scaled_font->surface_private;
+    }
+
+    CAIRO_MUTEX_LOCK (scaled_font->mutex);
+    needs_font = !font_private->grayscale_font;
+    CAIRO_MUTEX_UNLOCK (scaled_font->mutex);
+
+    if (needs_font) {
+        cairo_font_options_t options;
+        cairo_scaled_font_t *new_font;
+
+        options = scaled_font->options;
+        options.antialias = CAIRO_ANTIALIAS_GRAY;
+        new_font = cairo_scaled_font_create (scaled_font->font_face,
+                                             &scaled_font->font_matrix,
+                                             &scaled_font->ctm, &options);
+
+        CAIRO_MUTEX_LOCK (scaled_font->mutex);
+        if (!font_private->grayscale_font) {
+            font_private->grayscale_font = new_font;
+            new_font = NULL;
+        }
+        CAIRO_MUTEX_UNLOCK (scaled_font->mutex);
+
+        if (new_font) {
+            cairo_scaled_font_destroy (new_font);
+        }
+    }
+
+    return font_private->grayscale_font;
+}
+
 static cairo_int_status_t
 _cairo_xlib_surface_show_glyphs (void                *abstract_dst,
 				 cairo_operator_t     op,
 				 const cairo_pattern_t *src_pattern,
 				 cairo_glyph_t       *glyphs,
 				 int		      num_glyphs,
 				 cairo_scaled_font_t *scaled_font,
 				 cairo_clip_t	     *clip,
@@ -4475,16 +4531,21 @@ static cairo_int_status_t
 
     operation = _categorize_composite_operation (dst, op, src_pattern, TRUE);
     if (operation == DO_UNSUPPORTED)
 	return UNSUPPORTED ("unsupported op");
 
     if (! _cairo_xlib_surface_owns_font (dst, scaled_font))
 	return UNSUPPORTED ("unowned font");
 
+    if (!dst->base.permit_subpixel_antialiasing &&
+        scaled_font->options.antialias == CAIRO_ANTIALIAS_SUBPIXEL) {
+        scaled_font = _cairo_xlib_get_grayscale_font (dst, scaled_font);
+    }
+
     X_DEBUG ((dst->dpy, "show_glyphs (dst=%x)", (unsigned int) dst->drawable));
 
     if (clip_region != NULL &&
 	cairo_region_num_rectangles (clip_region) == 1)
     {
 	cairo_rectangle_int_t glyph_extents;
 	const cairo_rectangle_int_t *clip_extents;
 
--- a/gfx/cairo/cairo/src/cairo.h
+++ b/gfx/cairo/cairo/src/cairo.h
@@ -2101,16 +2101,35 @@ cairo_public void
 cairo_surface_copy_page (cairo_surface_t *surface);
 
 cairo_public void
 cairo_surface_show_page (cairo_surface_t *surface);
 
 cairo_public cairo_bool_t
 cairo_surface_has_show_text_glyphs (cairo_surface_t *surface);
 
+/**
+ * _cairo_subpixel_antialiasing_t:
+ * @CAIRO_SUBPIXEL_ANTIALIASING_ENABLED: subpixel antialiasing is enabled
+ * for this surface.
+ * @CAIRO_SUBPIXEL_ANTIALIASING_DISABLED: subpixel antialiasing is disabled
+ * for this surface.
+ */
+typedef enum _cairo_subpixel_antialiasing_t {
+    CAIRO_SUBPIXEL_ANTIALIASING_ENABLED,
+    CAIRO_SUBPIXEL_ANTIALIASING_DISABLED
+} cairo_subpixel_antialiasing_t;
+
+cairo_public void
+cairo_surface_set_subpixel_antialiasing (cairo_surface_t *surface,
+                                         cairo_subpixel_antialiasing_t enabled);
+
+cairo_public cairo_subpixel_antialiasing_t
+cairo_surface_get_subpixel_antialiasing (cairo_surface_t *surface);
+
 /* Image-surface functions */
 
 /**
  * cairo_format_t:
  * @CAIRO_FORMAT_ARGB32: each pixel is a 32-bit quantity, with
  *   alpha in the upper 8 bits, then red, then green, then blue.
  *   The 32-bit quantities are stored native-endian. Pre-multiplied
  *   alpha is used. (That is, 50% transparent red is 0x80800000,
--- a/gfx/cairo/cairo/src/cairoint.h
+++ b/gfx/cairo/cairo/src/cairoint.h
@@ -2750,16 +2750,18 @@ slim_hidden_proto (cairo_surface_destroy
 slim_hidden_proto (cairo_surface_finish);
 slim_hidden_proto (cairo_surface_flush);
 slim_hidden_proto (cairo_surface_get_content);
 slim_hidden_proto (cairo_surface_get_device_offset);
 slim_hidden_proto (cairo_surface_get_font_options);
 slim_hidden_proto (cairo_surface_get_mime_data);
 slim_hidden_proto (cairo_surface_get_type);
 slim_hidden_proto (cairo_surface_has_show_text_glyphs);
+slim_hidden_proto (cairo_surface_set_subpixel_antialiasing);
+slim_hidden_proto (cairo_surface_get_subpixel_antialiasing);
 slim_hidden_proto (cairo_surface_mark_dirty_rectangle);
 slim_hidden_proto_no_warn (cairo_surface_reference);
 slim_hidden_proto (cairo_surface_set_device_offset);
 slim_hidden_proto (cairo_surface_set_fallback_resolution);
 slim_hidden_proto (cairo_surface_set_mime_data);
 slim_hidden_proto (cairo_surface_show_page);
 slim_hidden_proto (cairo_surface_status);
 slim_hidden_proto (cairo_text_cluster_allocate);
--- a/gfx/cairo/libpixman/src/Makefile.in
+++ b/gfx/cairo/libpixman/src/Makefile.in
@@ -153,17 +153,17 @@ CSRCS += pixman-arm-simd.c
 SSRCS += pixman-arm-simd-asm.S
 DEFINES += -DUSE_ARM_SIMD
 endif
 
 ifdef USE_ARM_NEON_GCC
 CSRCS += pixman-arm-neon.c
 SSRCS += pixman-arm-neon-asm.S
 DEFINES += -DUSE_ARM_NEON
-ARM_NEON_CFLAGS = -mfloat-abi=softfp -mfpu=neon
+ARM_NEON_CFLAGS = -mfpu=neon
 endif
 
 EXPORTS		= pixman.h pixman-version.h
 
 LOCAL_INCLUDES	+= -I$(srcdir) -I$(srcdir)/../../cairo/src
 
 FORCE_STATIC_LIB = 1
 # This library is used by other shared libs in a static build
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -227,20 +227,22 @@ private:
  * Layers are refcounted. The layer manager holds a reference to the
  * root layer, and each container layer holds a reference to its children.
  */
 class THEBES_API LayerManager {
   NS_INLINE_DECL_REFCOUNTING(LayerManager)
 
 public:
   enum LayersBackend {
-    LAYERS_BASIC = 0,
+    LAYERS_NONE = 0,
+    LAYERS_BASIC,
     LAYERS_OPENGL,
     LAYERS_D3D9,
-    LAYERS_D3D10
+    LAYERS_D3D10,
+    LAYERS_LAST
   };
 
   LayerManager() : mDestroyed(PR_FALSE), mSnapEffectiveTransforms(PR_TRUE)
   {
     InitLog();
   }
   virtual ~LayerManager() {}
 
@@ -624,16 +626,30 @@ public:
 
   // Returns true if it's OK to save the contents of aLayer in an
   // opaque surface (a surface without an alpha channel).
   // If we can use a surface without an alpha channel, we should, because
   // it will often make painting of antialiased text faster and higher
   // quality.
   PRBool CanUseOpaqueSurface();
 
+  enum SurfaceMode {
+    SURFACE_OPAQUE,
+    SURFACE_SINGLE_CHANNEL_ALPHA,
+    SURFACE_COMPONENT_ALPHA
+  };
+  SurfaceMode GetSurfaceMode()
+  {
+    if (CanUseOpaqueSurface())
+      return SURFACE_OPAQUE;
+    if (mContentFlags & CONTENT_COMPONENT_ALPHA)
+      return SURFACE_COMPONENT_ALPHA;
+    return SURFACE_SINGLE_CHANNEL_ALPHA;
+  }
+
   /**
    * This setter can be used anytime. The user data for all keys is
    * initially null. Ownership pases to the layer manager.
    */
   void SetUserData(void* aKey, LayerUserData* aData)
   { mUserData.Set(aKey, aData); }
   /**
    * This can be used anytime. Ownership passes to the caller!
@@ -930,22 +946,29 @@ public:
    */
   PRBool UseIntermediateSurface() { return mUseIntermediateSurface; }
 
   /**
    * Returns true if this container has more than one non-empty child
    */
   PRBool HasMultipleChildren();
 
+  /**
+   * Returns true if this container supports children with component alpha.
+   * Should only be called while painting a child of this layer.
+   */
+  PRBool SupportsComponentAlphaChildren() { return mSupportsComponentAlphaChildren; }
+
 protected:
   ContainerLayer(LayerManager* aManager, void* aImplData)
     : Layer(aManager, aImplData),
       mFirstChild(nsnull),
       mLastChild(nsnull),
-      mUseIntermediateSurface(PR_FALSE)
+      mUseIntermediateSurface(PR_FALSE),
+      mSupportsComponentAlphaChildren(PR_FALSE)
   {
     mContentFlags = 0; // Clear NO_TEXT, NO_TEXT_OVER_TRANSPARENT
   }
 
   /**
    * A default implementation of ComputeEffectiveTransforms for use by OpenGL
    * and D3D.
    */
@@ -957,16 +980,17 @@ protected:
   void ComputeEffectiveTransformsForChildren(const gfx3DMatrix& aTransformToSurface);
 
   virtual nsACString& PrintInfo(nsACString& aTo, const char* aPrefix);
 
   Layer* mFirstChild;
   Layer* mLastChild;
   FrameMetrics mFrameMetrics;
   PRPackedBool mUseIntermediateSurface;
+  PRPackedBool mSupportsComponentAlphaChildren;
 };
 
 /**
  * A Layer which just renders a solid color in its visible region. It actually
  * can fill any area that contains the visible region, so if you need to
  * restrict the area filled, set a clip region on this layer.
  */
 class THEBES_API ColorLayer : public Layer {
--- a/gfx/layers/basic/BasicLayers.cpp
+++ b/gfx/layers/basic/BasicLayers.cpp
@@ -157,16 +157,17 @@ class BasicContainerLayer : public Conta
   template<class Container>
   friend void ContainerRemoveChild(Layer* aChild, Container* aContainer);
 
 public:
   BasicContainerLayer(BasicLayerManager* aManager) :
     ContainerLayer(aManager, static_cast<BasicImplData*>(this))
   {
     MOZ_COUNT_CTOR(BasicContainerLayer);
+    mSupportsComponentAlphaChildren = PR_TRUE;
   }
   virtual ~BasicContainerLayer();
 
   virtual void SetVisibleRegion(const nsIntRegion& aRegion)
   {
     NS_ASSERTION(BasicManager()->InConstruction(),
                  "Can only set properties in construction phase");
     ContainerLayer::SetVisibleRegion(aRegion);
@@ -449,16 +450,29 @@ IntersectWithClip(const nsIntRegion& aRe
   gfxRect clip = aContext->GetClipExtents();
   clip.RoundOut();
   nsIntRect r(clip.X(), clip.Y(), clip.Width(), clip.Height());
   nsIntRegion result;
   result.And(aRegion, r);
   return result;
 }
 
+static void
+SetAntialiasingFlags(Layer* aLayer, gfxContext* aTarget)
+{
+  nsRefPtr<gfxASurface> surface = aTarget->CurrentSurface();
+  if (surface->GetContentType() != gfxASurface::CONTENT_COLOR_ALPHA) {
+    // Destination doesn't have alpha channel; no need to set any special flags
+    return;
+  }
+
+  surface->SetSubpixelAntialiasingEnabled(
+      !(aLayer->GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA));
+}
+
 void
 BasicThebesLayer::Paint(gfxContext* aContext,
                         LayerManager::DrawThebesLayerCallback aCallback,
                         void* aCallbackData)
 {
   NS_ASSERTION(BasicManager()->InDrawing(),
                "Can only draw in drawing phase");
   gfxContext* target = BasicManager()->GetTarget();
@@ -483,18 +497,19 @@ BasicThebesLayer::Paint(gfxContext* aCon
       if (!aCallback) {
         BasicManager()->SetTransactionIncomplete();
         return;
       }
 
       target->Save();
       gfxUtils::ClipToRegionSnapped(target, toDraw);
       if (opacity != 1.0) {
-        target->PushGroup(contentType);
+        target->PushGroupAndCopyBackground(contentType);
       }
+      SetAntialiasingFlags(this, target);
       aCallback(this, target, toDraw, nsIntRegion(), aCallbackData);
       if (opacity != 1.0) {
         target->PopGroupToSource();
         target->Paint(opacity);
       }
       target->Restore();
     }
     return;
@@ -510,16 +525,17 @@ BasicThebesLayer::Paint(gfxContext* aCon
     if (state.mContext) {
       // The area that became invalid and is visible needs to be repainted
       // (this could be the whole visible area if our buffer switched
       // from RGB to RGBA, because we might need to repaint with
       // subpixel AA)
       state.mRegionToInvalidate.And(state.mRegionToInvalidate, mVisibleRegion);
       mXResolution = paintXRes;
       mYResolution = paintYRes;
+      SetAntialiasingFlags(this, state.mContext);
       PaintBuffer(state.mContext,
                   state.mRegionToDraw, state.mRegionToInvalidate,
                   aCallback, aCallbackData);
       Mutated();
     } else {
       // It's possible that state.mRegionToInvalidate is nonempty here,
       // if we are shrinking the valid region to nothing.
       NS_ASSERTION(state.mRegionToDraw.IsEmpty(),
@@ -1294,24 +1310,40 @@ BasicLayerManager::PaintLayer(Layer* aLa
   gfxMatrix transform;
   // XXX we need to add some kind of 3D transform support, possibly
   // using pixman?
   NS_ASSERTION(effectiveTransform.Is2D(),
                "Only 2D transforms supported currently");
   effectiveTransform.Is2D(&transform);
   mTarget->SetMatrix(transform);
 
+  PRBool pushedTargetOpaqueRect = PR_FALSE;
+  const nsIntRegion& visibleRegion = aLayer->GetEffectiveVisibleRegion();
+  nsRefPtr<gfxASurface> currentSurface = mTarget->CurrentSurface();
+  const gfxRect& targetOpaqueRect = currentSurface->GetOpaqueRect();
+
+  // Try to annotate currentSurface with a region of pixels that have been
+  // (or will be) painted opaque, if no such region is currently set.
+  if (targetOpaqueRect.IsEmpty() && visibleRegion.GetNumRects() == 1 &&
+      (aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) &&
+      !transform.HasNonAxisAlignedTransform()) {
+    const nsIntRect& bounds = visibleRegion.GetBounds();
+    currentSurface->SetOpaqueRect(
+        mTarget->UserToDevice(gfxRect(bounds.x, bounds.y, bounds.width, bounds.height)));
+    pushedTargetOpaqueRect = PR_TRUE;
+  }
+
   if (needsGroup) {
     // If we need to call PushGroup, we should clip to the smallest possible
     // area first to minimize the size of the temporary surface.
     ClipToContain(mTarget, aLayer->GetEffectiveVisibleRegion().GetBounds());
 
     gfxASurface::gfxContentType type = aLayer->CanUseOpaqueSurface()
         ? gfxASurface::CONTENT_COLOR : gfxASurface::CONTENT_COLOR_ALPHA;
-    mTarget->PushGroup(type);
+    mTarget->PushGroupAndCopyBackground(type);
   }
 
   /* Only paint ourself, or our children - This optimization relies on this! */
   Layer* child = aLayer->GetFirstChild();
   if (!child) {
     BasicImplData* data = ToData(aLayer);
 #ifdef MOZ_LAYERS_HAVE_LOG
     MOZ_LAYERS_LOG(("%s (0x%p) is covered: %i\n", __FUNCTION__,
@@ -1332,16 +1364,20 @@ BasicLayerManager::PaintLayer(Layer* aLa
     mTarget->PopGroupToSource();
     // If the layer is opaque in its visible region we pushed a CONTENT_COLOR
     // group. We need to make sure that only pixels inside the layer's visible
     // region are copied back to the destination.
     gfxUtils::ClipToRegionSnapped(mTarget, aLayer->GetEffectiveVisibleRegion());
     mTarget->Paint(aLayer->GetEffectiveOpacity());
   }
 
+  if (pushedTargetOpaqueRect) {
+    currentSurface->SetOpaqueRect(gfxRect(0, 0, 0, 0));
+  }
+
   if (needsSaveRestore) {
     mTarget->Restore();
   } else {
     mTarget->SetMatrix(savedMatrix);
   }
 }
 
 void
@@ -1691,21 +1727,30 @@ BasicShadowableThebesLayer::CreateBuffer
   if (IsSurfaceDescriptorValid(mBackBuffer)) {
     BasicManager()->DestroyedThebesBuffer(BasicManager()->Hold(this),
                                           mBackBuffer);
     mBackBuffer = SurfaceDescriptor();
   }
 
   // XXX error handling
   SurfaceDescriptor tmpFront;
-  if (!BasicManager()->AllocDoubleBuffer(gfxIntSize(aSize.width, aSize.height),
-                                         aType,
-                                         &tmpFront,
-                                         &mBackBuffer))
-    NS_RUNTIMEABORT("creating ThebesLayer 'back buffer' failed!");
+  if (BasicManager()->ShouldDoubleBuffer()) {
+    if (!BasicManager()->AllocDoubleBuffer(gfxIntSize(aSize.width, aSize.height),
+                                           aType,
+                                           &tmpFront,
+                                           &mBackBuffer)) {
+      NS_RUNTIMEABORT("creating ThebesLayer 'back buffer' failed!");
+    }
+  } else {
+    if (!BasicManager()->AllocBuffer(gfxIntSize(aSize.width, aSize.height),
+                                     aType,
+                                     &mBackBuffer)) {
+      NS_RUNTIMEABORT("creating ThebesLayer 'back buffer' failed!");
+    }
+  }
 
   NS_ABORT_IF_FALSE(!mIsNewBuffer,
                     "Bad! Did we create a buffer twice without painting?");
   mIsNewBuffer = true;
 
   BasicManager()->CreatedThebesBuffer(BasicManager()->Hold(this),
                                       nsIntRegion(),
                                       1.0, 1.0,
@@ -1770,18 +1815,16 @@ BasicShadowableImageLayer::Paint(gfxCont
                                  void* aCallbackData)
 {
   gfxIntSize oldSize = mSize;
   nsRefPtr<gfxPattern> pat = GetAndPaintCurrentImage(aContext, GetEffectiveOpacity());
   if (!pat || !HasShadow())
     return;
 
   if (oldSize != mSize) {
-    NS_ASSERTION(oldSize == gfxIntSize(-1, -1), "video changed size?");
-
     if (mBackSurface) {
       BasicManager()->ShadowLayerForwarder::DestroySharedSurface(mBackSurface);
       mBackSurface = nsnull;
 
       BasicManager()->DestroyedImageBuffer(BasicManager()->Hold(this));
     }
 
     nsRefPtr<gfxSharedImageSurface> tmpFrontSurface;
@@ -1987,17 +2030,17 @@ public:
   virtual ~BasicShadowThebesLayer()
   {
     // If Disconnect() wasn't called on us, then we assume that the
     // remote side shut down and IPC is disconnected, so we let IPDL
     // clean up our front surface Shmem.
     MOZ_COUNT_DTOR(BasicShadowThebesLayer);
   }
 
-  virtual void SetFrontBuffer(const ThebesBuffer& aNewFront,
+  virtual void SetFrontBuffer(const OptionalThebesBuffer& aNewFront,
                               const nsIntRegion& aValidRegion,
                               float aXResolution, float aYResolution);
 
   virtual void SetValidRegion(const nsIntRegion& aRegion)
   {
     mOldValidRegion = mValidRegion;
     ShadowThebesLayer::SetValidRegion(aRegion);
   }
@@ -2052,32 +2095,37 @@ private:
   // Then when we Swap() back/front buffers, we can return these
   // parameters to our partner (adjusted as needed).
   nsIntRegion mOldValidRegion;
   float mOldXResolution;
   float mOldYResolution;
 };
 
 void
-BasicShadowThebesLayer::SetFrontBuffer(const ThebesBuffer& aNewFront,
+BasicShadowThebesLayer::SetFrontBuffer(const OptionalThebesBuffer& aNewFront,
                                        const nsIntRegion& aValidRegion,
                                        float aXResolution, float aYResolution)
 {
   mValidRegion = mOldValidRegion = aValidRegion;
   mXResolution = mOldXResolution = aXResolution;
   mYResolution = mOldYResolution = aYResolution;
+
+  NS_ABORT_IF_FALSE(OptionalThebesBuffer::Tnull_t != aNewFront.type(),
+                    "aNewFront must be valid here!");
+
+  const ThebesBuffer newFront = aNewFront.get_ThebesBuffer();
   nsRefPtr<gfxASurface> newFrontBuffer =
-    BasicManager()->OpenDescriptor(aNewFront.buffer());
+    BasicManager()->OpenDescriptor(newFront.buffer());
 
   nsRefPtr<gfxASurface> unused;
   nsIntRect unusedRect;
   nsIntPoint unusedRotation;
-  mFrontBuffer.Swap(newFrontBuffer, aNewFront.rect(), aNewFront.rotation(),
+  mFrontBuffer.Swap(newFrontBuffer, newFront.rect(), newFront.rotation(),
                     getter_AddRefs(unused), &unusedRect, &unusedRotation);
-  mFrontBufferDescriptor = aNewFront.buffer();
+  mFrontBufferDescriptor = newFront.buffer();
 }
 
 void
 BasicShadowThebesLayer::Swap(const ThebesBuffer& aNewFront,
                              const nsIntRegion& aUpdatedRegion,
                              ThebesBuffer* aNewBack,
                              nsIntRegion* aNewBackValidRegion,
                              float* aNewXResolution, float* aNewYResolution,
--- a/gfx/layers/d3d10/ImageLayerD3D10.cpp
+++ b/gfx/layers/d3d10/ImageLayerD3D10.cpp
@@ -452,16 +452,31 @@ CairoImageD3D10::SetData(const CairoImag
   }
 
   mDevice->CreateShaderResourceView(mTexture, NULL, getter_AddRefs(mSRView));
 }
 
 already_AddRefed<gfxASurface>
 CairoImageD3D10::GetAsSurface()
 {
+  nsRefPtr<ID3D10Texture2D> surfTexture;
+
+  // Make a copy of the texture since our current texture is not suitable for
+  // drawing with Direct2D because it is immutable and cannot be bound as a
+  // render target.
+  D3D10_TEXTURE2D_DESC texDesc;
+  mTexture->GetDesc(&texDesc);
+  texDesc.Usage = D3D10_USAGE_DEFAULT;
+  texDesc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;
+  texDesc.MiscFlags = D3D10_RESOURCE_MISC_GDI_COMPATIBLE;
+
+  mDevice->CreateTexture2D(&texDesc, NULL, getter_AddRefs(surfTexture));
+
+  mDevice->CopyResource(surfTexture, mTexture);
+
   nsRefPtr<gfxASurface> surf =
-    new gfxD2DSurface(mTexture, mHasAlpha ? gfxASurface::CONTENT_COLOR_ALPHA :
-                                            gfxASurface::CONTENT_COLOR);
+    new gfxD2DSurface(surfTexture, mHasAlpha ? gfxASurface::CONTENT_COLOR_ALPHA :
+                                               gfxASurface::CONTENT_COLOR);
   return surf.forget();
 }
 
 } /* layers */
 } /* mozilla */
--- a/gfx/layers/d3d9/CanvasLayerD3D9.cpp
+++ b/gfx/layers/d3d9/CanvasLayerD3D9.cpp
@@ -70,47 +70,28 @@ CanvasLayerD3D9::Initialize(const Data& 
     mDataIsPremultiplied = aData.mGLBufferIsPremultiplied;
     mNeedsYFlip = PR_TRUE;
   } else {
     NS_ERROR("CanvasLayer created without mSurface or mGLContext?");
   }
 
   mBounds.SetRect(0, 0, aData.mSize.width, aData.mSize.height);
 
-  if (mSurface && mSurface->GetType() == gfxASurface::SurfaceTypeD2D) {
-    void *data = mSurface->GetData(&gKeyD3D9Texture);
-    if (data) {
-      mTexture = static_cast<IDirect3DTexture9*>(data);
-      mIsInteropTexture = true;
-      return;
-    }
-  }
-
-  mIsInteropTexture = false;
-
   CreateTexture();
 }
 
 void
 CanvasLayerD3D9::Updated(const nsIntRect& aRect)
 {
   if (!mTexture) {
     CreateTexture();
     NS_WARNING("CanvasLayerD3D9::Updated called but no texture present!");
     return;
   }
 
-#ifdef CAIRO_HAS_D2D_SURFACE
-  if (mIsInteropTexture) {
-    mSurface->Flush();
-    cairo_d2d_finish_device(gfxWindowsPlatform::GetPlatform()->GetD2DDevice());
-    return;
-  }
-#endif
-
   if (mGLContext) {
     // WebGL reads entire surface.
     D3DLOCKED_RECT r;
     HRESULT hr = mTexture->LockRect(0, &r, NULL, 0);
 
     if (FAILED(hr)) {
       NS_WARNING("Failed to lock CanvasLayer texture.");
       return;
--- a/gfx/layers/d3d9/CanvasLayerD3D9.h
+++ b/gfx/layers/d3d9/CanvasLayerD3D9.h
@@ -79,18 +79,15 @@ protected:
   typedef mozilla::gl::GLContext GLContext;
 
   nsRefPtr<gfxASurface> mSurface;
   nsRefPtr<GLContext> mGLContext;
   nsRefPtr<IDirect3DTexture9> mTexture;
 
   PRUint32 mCanvasFramebuffer;
 
-  // Indicates whether our texture was obtained through D2D interop.
-  bool mIsInteropTexture;
-
   PRPackedBool mDataIsPremultiplied;
   PRPackedBool mNeedsYFlip;
 };
 
 } /* layers */
 } /* mozilla */
 #endif /* GFX_CANVASLAYERD3D9_H */
--- a/gfx/layers/d3d9/ContainerLayerD3D9.cpp
+++ b/gfx/layers/d3d9/ContainerLayerD3D9.cpp
@@ -148,40 +148,80 @@ static inline LayerD3D9*
 GetNextSiblingD3D9(LayerD3D9* aLayer)
 {
    Layer* layer = aLayer->GetLayer()->GetNextSibling();
    return layer ? static_cast<LayerD3D9*>(layer->
                                           ImplData())
                  : nsnull;
 }
 
+static PRBool
+HasOpaqueAncestorLayer(Layer* aLayer)
+{
+  for (Layer* l = aLayer->GetParent(); l; l = l->GetParent()) {
+    if (l->GetContentFlags() & Layer::CONTENT_OPAQUE)
+      return PR_TRUE;
+  }
+  return PR_FALSE;
+}
+
 void
 ContainerLayerD3D9::RenderLayer()
 {
   nsRefPtr<IDirect3DSurface9> previousRenderTarget;
   nsRefPtr<IDirect3DTexture9> renderTexture;
   float previousRenderTargetOffset[4];
   RECT oldClipRect;
   float renderTargetOffset[] = { 0, 0, 0, 0 };
   float oldViewMatrix[4][4];
 
   nsIntRect visibleRect = mVisibleRegion.GetBounds();
   PRBool useIntermediate = UseIntermediateSurface();
 
+  mSupportsComponentAlphaChildren = PR_FALSE;
   gfxMatrix contTransform;
   if (useIntermediate) {
     device()->GetRenderTarget(0, getter_AddRefs(previousRenderTarget));
     device()->CreateTexture(visibleRect.width, visibleRect.height, 1,
                             D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8,
                             D3DPOOL_DEFAULT, getter_AddRefs(renderTexture),
                             NULL);
     nsRefPtr<IDirect3DSurface9> renderSurface;
     renderTexture->GetSurfaceLevel(0, getter_AddRefs(renderSurface));
     device()->SetRenderTarget(0, renderSurface);
-    device()->Clear(0, 0, D3DCLEAR_TARGET, D3DCOLOR_RGBA(0, 0, 0, 0), 0, 0);
+
+    if (mVisibleRegion.GetNumRects() == 1 && (GetContentFlags() & CONTENT_OPAQUE)) {
+      // don't need a background, we're going to paint all opaque stuff
+      mSupportsComponentAlphaChildren = PR_TRUE;
+    } else {
+      const gfx3DMatrix& transform3D = GetEffectiveTransform();
+      gfxMatrix transform;
+      // If we have an opaque ancestor layer, then we can be sure that
+      // all the pixels we draw into are either opaque already or will be
+      // covered by something opaque. Otherwise copying up the background is
+      // not safe.
+      HRESULT hr = E_FAIL;
+      if (HasOpaqueAncestorLayer(this) &&
+          transform3D.Is2D(&transform) && !transform.HasNonIntegerTranslation()) {
+        // Copy background up from below
+        RECT dest = { 0, 0, visibleRect.width, visibleRect.height };
+        RECT src = dest;
+        ::OffsetRect(&src,
+                     visibleRect.x + PRInt32(transform.x0),
+                     visibleRect.y + PRInt32(transform.y0));
+        hr = device()->
+          StretchRect(previousRenderTarget, &src, renderSurface, &dest, D3DTEXF_NONE);
+      }
+      if (hr == S_OK) {
+        mSupportsComponentAlphaChildren = PR_TRUE;
+      } else {
+        device()->Clear(0, 0, D3DCLEAR_TARGET, D3DCOLOR_RGBA(0, 0, 0, 0), 0, 0);
+      }
+    }
+
     device()->GetVertexShaderConstantF(CBvRenderTargetOffset, previousRenderTargetOffset, 1);
     renderTargetOffset[0] = (float)visibleRect.x;
     renderTargetOffset[1] = (float)visibleRect.y;
     device()->SetVertexShaderConstantF(CBvRenderTargetOffset, renderTargetOffset, 1);
 
     gfx3DMatrix viewMatrix;
     /*
      * Matrix to transform to viewport space ( <-1.0, 1.0> topleft,
@@ -195,16 +235,18 @@ ContainerLayerD3D9::RenderLayer()
     device()->GetVertexShaderConstantF(CBmProjection, &oldViewMatrix[0][0], 4);
     device()->SetVertexShaderConstantF(CBmProjection, &viewMatrix._11, 4);
   } else {
 #ifdef DEBUG
     PRBool is2d =
 #endif
     GetEffectiveTransform().Is2D(&contTransform);
     NS_ASSERTION(is2d, "Transform must be 2D");
+    mSupportsComponentAlphaChildren = (GetContentFlags() & CONTENT_OPAQUE) ||
+        (mParent && mParent->SupportsComponentAlphaChildren());
   }
 
   /*
    * Render this container's contents.
    */
   for (LayerD3D9* layerToRender = GetFirstChildD3D9();
        layerToRender != nsnull;
        layerToRender = GetNextSiblingD3D9(layerToRender)) {
--- a/gfx/layers/d3d9/DeviceManagerD3D9.cpp
+++ b/gfx/layers/d3d9/DeviceManagerD3D9.cpp
@@ -358,16 +358,30 @@ DeviceManagerD3D9::Init()
 
   hr = mDevice->CreatePixelShader((DWORD*)RGBAShaderPS,
                                   getter_AddRefs(mRGBAPS));
 
   if (FAILED(hr)) {
     return false;
   }
 
+  hr = mDevice->CreatePixelShader((DWORD*)ComponentPass1ShaderPS,
+                                  getter_AddRefs(mComponentPass1PS));
+
+  if (FAILED(hr)) {
+    return false;
+  }
+
+  hr = mDevice->CreatePixelShader((DWORD*)ComponentPass2ShaderPS,
+                                  getter_AddRefs(mComponentPass2PS));
+
+  if (FAILED(hr)) {
+    return false;
+  }
+
   hr = mDevice->CreatePixelShader((DWORD*)YCbCrShaderPS,
                                   getter_AddRefs(mYCbCrPS));
 
   if (FAILED(hr)) {
     return false;
   }
 
   hr = mDevice->CreatePixelShader((DWORD*)SolidColorShaderPS,
@@ -478,16 +492,24 @@ DeviceManagerD3D9::SetShaderMode(ShaderM
     case RGBLAYER:
       mDevice->SetVertexShader(mLayerVS);
       mDevice->SetPixelShader(mRGBPS);
       break;
     case RGBALAYER:
       mDevice->SetVertexShader(mLayerVS);
       mDevice->SetPixelShader(mRGBAPS);
       break;
+    case COMPONENTLAYERPASS1:
+      mDevice->SetVertexShader(mLayerVS);
+      mDevice->SetPixelShader(mComponentPass1PS);
+      break;
+    case COMPONENTLAYERPASS2:
+      mDevice->SetVertexShader(mLayerVS);
+      mDevice->SetPixelShader(mComponentPass2PS);
+      break;
     case YCBCRLAYER:
       mDevice->SetVertexShader(mLayerVS);
       mDevice->SetPixelShader(mYCbCrPS);
       break;
     case SOLIDCOLORLAYER:
       mDevice->SetVertexShader(mLayerVS);
       mDevice->SetPixelShader(mSolidColorPS);
       break;
--- a/gfx/layers/d3d9/DeviceManagerD3D9.h
+++ b/gfx/layers/d3d9/DeviceManagerD3D9.h
@@ -137,16 +137,18 @@ public:
 
   bool IsD3D9Ex() { return mDeviceEx; }
 
   bool HasDynamicTextures() { return mHasDynamicTextures; }
 
   enum ShaderMode {
     RGBLAYER,
     RGBALAYER,
+    COMPONENTLAYERPASS1,
+    COMPONENTLAYERPASS2,
     YCBCRLAYER,
     SOLIDCOLORLAYER
   };
 
   void SetShaderMode(ShaderMode aMode);
 
   /** 
    * Return pointer to the Nv3DVUtils instance 
@@ -200,16 +202,22 @@ private:
   nsRefPtr<IDirect3DVertexShader9> mLayerVS;
 
   /* Pixel shader used for RGB textures */
   nsRefPtr<IDirect3DPixelShader9> mRGBPS;
 
   /* Pixel shader used for RGBA textures */
   nsRefPtr<IDirect3DPixelShader9> mRGBAPS;
 
+  /* Pixel shader used for component alpha textures (pass 1) */
+  nsRefPtr<IDirect3DPixelShader9> mComponentPass1PS;
+
+  /* Pixel shader used for component alpha textures (pass 2) */
+  nsRefPtr<IDirect3DPixelShader9> mComponentPass2PS;
+
   /* Pixel shader used for RGB textures */
   nsRefPtr<IDirect3DPixelShader9> mYCbCrPS;
 
   /* Pixel shader used for solid colors */
   nsRefPtr<IDirect3DPixelShader9> mSolidColorPS;
 
   /* Vertex buffer containing our basic vertex structure */
   nsRefPtr<IDirect3DVertexBuffer9> mVB;
--- a/gfx/layers/d3d9/LayerManagerD3D9.cpp
+++ b/gfx/layers/d3d9/LayerManagerD3D9.cpp
@@ -41,20 +41,16 @@
 #include "ContainerLayerD3D9.h"
 #include "ImageLayerD3D9.h"
 #include "ColorLayerD3D9.h"
 #include "CanvasLayerD3D9.h"
 #include "gfxWindowsPlatform.h"
 #include "nsIGfxInfo.h"
 #include "nsServiceManagerUtils.h"
 
-#ifdef CAIRO_HAS_D2D_SURFACE
-#include "gfxD2DSurface.h"
-#endif
-
 namespace mozilla {
 namespace layers {
 
 DeviceManagerD3D9 *LayerManagerD3D9::mDefaultDeviceManager = nsnull;
 
 LayerManagerD3D9::LayerManagerD3D9(nsIWidget *aWidget)
 {
   mWidget = aWidget;
@@ -205,61 +201,21 @@ LayerManagerD3D9::CreateCanvasLayer()
 
 already_AddRefed<ImageContainer>
 LayerManagerD3D9::CreateImageContainer()
 {
   nsRefPtr<ImageContainer> container = new ImageContainerD3D9(device());
   return container.forget();
 }
 
-cairo_user_data_key_t gKeyD3D9Texture;
-
 void ReleaseTexture(void *texture)
 {
   static_cast<IDirect3DTexture9*>(texture)->Release();
 }
 
-already_AddRefed<gfxASurface>
-LayerManagerD3D9::CreateOptimalSurface(const gfxIntSize &aSize,
-                                   gfxASurface::gfxImageFormat aFormat)
-{
-#ifdef CAIRO_HAS_D2D_SURFACE
-  if ((aFormat != gfxASurface::ImageFormatRGB24 &&
-       aFormat != gfxASurface::ImageFormatARGB32) ||
-      gfxWindowsPlatform::GetPlatform()->GetRenderMode() !=
-        gfxWindowsPlatform::RENDER_DIRECT2D ||
-      !deviceManager()->IsD3D9Ex()) {
-    return LayerManager::CreateOptimalSurface(aSize, aFormat);
-  }
-
-  nsRefPtr<IDirect3DTexture9> texture;
-  
-  HANDLE sharedHandle = 0;
-  device()->CreateTexture(aSize.width, aSize.height, 1,
-                          D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8,
-                          D3DPOOL_DEFAULT, getter_AddRefs(texture), &sharedHandle);
-
-  nsRefPtr<gfxD2DSurface> surface =
-    new gfxD2DSurface(sharedHandle, aFormat == gfxASurface::ImageFormatRGB24 ?
-      gfxASurface::CONTENT_COLOR : gfxASurface::CONTENT_COLOR_ALPHA);
-
-  if (!surface || surface->CairoStatus()) {
-    return LayerManager::CreateOptimalSurface(aSize, aFormat);
-  }
-
-  surface->SetData(&gKeyD3D9Texture,
-                   texture.forget().get(),
-                   ReleaseTexture);
-
-  return surface.forget();
-#else
-  return LayerManager::CreateOptimalSurface(aSize, aFormat);
-#endif
-}
-
 void
 LayerManagerD3D9::ReportFailure(const nsACString &aMsg, HRESULT aCode)
 {
   // We could choose to abort here when hr == E_OUTOFMEMORY.
   nsCString msg;
   msg.Append(aMsg);
   msg.AppendLiteral(" Error code: ");
   msg.AppendInt(PRUint32(aCode));
--- a/gfx/layers/d3d9/LayerManagerD3D9.h
+++ b/gfx/layers/d3d9/LayerManagerD3D9.h
@@ -46,18 +46,16 @@
 #include "gfxContext.h"
 #include "nsIWidget.h"
 
 #include "DeviceManagerD3D9.h"
 
 namespace mozilla {
 namespace layers {
 
-extern cairo_user_data_key_t gKeyD3D9Texture;
-
 class LayerD3D9;
 class ThebesLayerD3D9;
 
 /**
  * This structure is used to pass rectangles to our shader constant. We can use
  * this for passing rectangular areas to SetVertexShaderConstant. In the format
  * of a 4 component float(x,y,width,height). Our vertex shader can then use
  * this to construct rectangular positions from the 0,0-1,1 quad that we source
@@ -146,20 +144,16 @@ public:
   virtual already_AddRefed<ImageLayer> CreateImageLayer();
 
   virtual already_AddRefed<ColorLayer> CreateColorLayer();
 
   virtual already_AddRefed<CanvasLayer> CreateCanvasLayer();
 
   virtual already_AddRefed<ImageContainer> CreateImageContainer();
 
-  virtual already_AddRefed<gfxASurface>
-    CreateOptimalSurface(const gfxIntSize &aSize,
-                         gfxASurface::gfxImageFormat imageFormat);
-
   virtual LayersBackend GetBackendType() { return LAYERS_D3D9; }
   virtual void GetBackendName(nsAString& name) { name.AssignLiteral("Direct3D 9"); }
   bool DeviceWasRemoved() { return deviceManager()->DeviceWasRemoved(); }
 
   /*
    * Helper methods.
    */
   void SetClippingEnabled(PRBool aEnabled);
--- a/gfx/layers/d3d9/LayerManagerD3D9Shaders.h
+++ b/gfx/layers/d3d9/LayerManagerD3D9Shaders.h
@@ -224,16 +224,238 @@ const BYTE RGBAShaderPS[] =
       0,   2,   0,   8,  15, 128, 
       0,   0, 228, 128, 255, 255, 
       0,   0
 };
 #if 0
 //
 // Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
 //
+//   fxc LayerManagerD3D9Shaders.hlsl -EComponentPass1Shader -nologo -Tps_2_0
+//    -FhtmpShaderHeader -VnComponentPass1ShaderPS
+//
+//
+// Parameters:
+//
+//   float fLayerOpacity;
+//   sampler2D s2D;
+//   sampler2D s2DWhite;
+//
+//
+// Registers:
+//
+//   Name          Reg   Size
+//   ------------- ----- ----
+//   fLayerOpacity c0       1
+//   s2D           s0       1
+//   s2DWhite      s1       1
+//
+
+    ps_2_0
+    def c1, 1, 0, 0, 0
+    dcl t0.xy
+    dcl_2d s0
+    dcl_2d s1
+    texld r0, t0, s0
+    texld r1, t0, s1
+    add r0.xyz, r0, -r1
+    add r0.xyz, r0, c1.x
+    mul r0.xyz, r0, c0.x
+    mov r1.xyz, r0
+    mov r1.w, r0.y
+    mov oC0, r1
+
+// approximately 8 instruction slots used (2 texture, 6 arithmetic)
+#endif
+
+const BYTE ComponentPass1ShaderPS[] =
+{
+      0,   2, 255, 255, 254, 255, 
+     57,   0,  67,  84,  65,  66, 
+     28,   0,   0,   0, 175,   0, 
+      0,   0,   0,   2, 255, 255, 
+      3,   0,   0,   0,  28,   0, 
+      0,   0,   0,   1,   0,   0, 
+    168,   0,   0,   0,  88,   0, 
+      0,   0,   2,   0,   0,   0, 
+      1,   0,   0,   0, 104,   0, 
+      0,   0,   0,   0,   0,   0, 
+    120,   0,   0,   0,   3,   0, 
+      0,   0,   1,   0,   0,   0, 
+    124,   0,   0,   0,   0,   0, 
+      0,   0, 140,   0,   0,   0, 
+      3,   0,   1,   0,   1,   0, 
+      0,   0, 152,   0,   0,   0, 
+      0,   0,   0,   0, 102,  76, 
+     97, 121, 101, 114,  79, 112, 
+     97,  99, 105, 116, 121,   0, 
+    171, 171,   0,   0,   3,   0, 
+      1,   0,   1,   0,   1,   0, 
+      0,   0,   0,   0,   0,   0, 
+    115,  50,  68,   0,   4,   0, 
+     12,   0,   1,   0,   1,   0, 
+      1,   0,   0,   0,   0,   0, 
+      0,   0, 115,  50,  68,  87, 
+    104, 105, 116, 101,   0, 171, 
+    171, 171,   4,   0,  12,   0, 
+      1,   0,   1,   0,   1,   0, 
+      0,   0,   0,   0,   0,   0, 
+    112, 115,  95,  50,  95,  48, 
+      0,  77, 105,  99, 114, 111, 
+    115, 111, 102, 116,  32,  40, 
+     82,  41,  32,  72,  76,  83, 
+     76,  32,  83, 104,  97, 100, 
+    101, 114,  32,  67, 111, 109, 
+    112, 105, 108, 101, 114,  32, 
+     57,  46,  50,  57,  46,  57, 
+     53,  50,  46,  51,  49,  49, 
+     49,   0,  81,   0,   0,   5, 
+      1,   0,  15, 160,   0,   0, 
+    128,  63,   0,   0,   0,   0, 
+      0,   0,   0,   0,   0,   0, 
+      0,   0,  31,   0,   0,   2, 
+      0,   0,   0, 128,   0,   0, 
+      3, 176,  31,   0,   0,   2, 
+      0,   0,   0, 144,   0,   8, 
+     15, 160,  31,   0,   0,   2, 
+      0,   0,   0, 144,   1,   8, 
+     15, 160,  66,   0,   0,   3, 
+      0,   0,  15, 128,   0,   0, 
+    228, 176,   0,   8, 228, 160, 
+     66,   0,   0,   3,   1,   0, 
+     15, 128,   0,   0, 228, 176, 
+      1,   8, 228, 160,   2,   0, 
+      0,   3,   0,   0,   7, 128, 
+      0,   0, 228, 128,   1,   0, 
+    228, 129,   2,   0,   0,   3, 
+      0,   0,   7, 128,   0,   0, 
+    228, 128,   1,   0,   0, 160, 
+      5,   0,   0,   3,   0,   0, 
+      7, 128,   0,   0, 228, 128, 
+      0,   0,   0, 160,   1,   0, 
+      0,   2,   1,   0,   7, 128, 
+      0,   0, 228, 128,   1,   0, 
+      0,   2,   1,   0,   8, 128, 
+      0,   0,  85, 128,   1,   0, 
+      0,   2,   0,   8,  15, 128, 
+      1,   0, 228, 128, 255, 255, 
+      0,   0
+};
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+//   fxc LayerManagerD3D9Shaders.hlsl -EComponentPass2Shader -nologo -Tps_2_0
+//    -FhtmpShaderHeader -VnComponentPass2ShaderPS
+//
+//
+// Parameters:
+//
+//   float fLayerOpacity;
+//   sampler2D s2D;
+//   sampler2D s2DWhite;
+//
+//
+// Registers:
+//
+//   Name          Reg   Size
+//   ------------- ----- ----
+//   fLayerOpacity c0       1
+//   s2D           s0       1
+//   s2DWhite      s1       1
+//
+
+    ps_2_0
+    def c1, 1, 0, 0, 0
+    dcl t0.xy
+    dcl_2d s0
+    dcl_2d s1
+    texld r0, t0, s1
+    texld r1, t0, s0
+    add r0.x, -r0.y, r1.y
+    add r1.w, r0.x, c1.x
+    mul r0, r1, c0.x
+    mov oC0, r0
+
+// approximately 6 instruction slots used (2 texture, 4 arithmetic)
+#endif
+
+const BYTE ComponentPass2ShaderPS[] =
+{
+      0,   2, 255, 255, 254, 255, 
+     57,   0,  67,  84,  65,  66, 
+     28,   0,   0,   0, 175,   0, 
+      0,   0,   0,   2, 255, 255, 
+      3,   0,   0,   0,  28,   0, 
+      0,   0,   0,   1,   0,   0, 
+    168,   0,   0,   0,  88,   0, 
+      0,   0,   2,   0,   0,   0, 
+      1,   0,   0,   0, 104,   0, 
+      0,   0,   0,   0,   0,   0, 
+    120,   0,   0,   0,   3,   0, 
+      0,   0,   1,   0,   0,   0, 
+    124,   0,   0,   0,   0,   0, 
+      0,   0, 140,   0,   0,   0, 
+      3,   0,   1,   0,   1,   0, 
+      0,   0, 152,   0,   0,   0, 
+      0,   0,   0,   0, 102,  76, 
+     97, 121, 101, 114,  79, 112, 
+     97,  99, 105, 116, 121,   0, 
+    171, 171,   0,   0,   3,   0, 
+      1,   0,   1,   0,   1,   0, 
+      0,   0,   0,   0,   0,   0, 
+    115,  50,  68,   0,   4,   0, 
+     12,   0,   1,   0,   1,   0, 
+      1,   0,   0,   0,   0,   0, 
+      0,   0, 115,  50,  68,  87, 
+    104, 105, 116, 101,   0, 171, 
+    171, 171,   4,   0,  12,   0, 
+      1,   0,   1,   0,   1,   0, 
+      0,   0,   0,   0,   0,   0, 
+    112, 115,  95,  50,  95,  48, 
+      0,  77, 105,  99, 114, 111, 
+    115, 111, 102, 116,  32,  40, 
+     82,  41,  32,  72,  76,  83, 
+     76,  32,  83, 104,  97, 100, 
+    101, 114,  32,  67, 111, 109, 
+    112, 105, 108, 101, 114,  32, 
+     57,  46,  50,  57,  46,  57, 
+     53,  50,  46,  51,  49,  49, 
+     49,   0,  81,   0,   0,   5, 
+      1,   0,  15, 160,   0,   0, 
+    128,  63,   0,   0,   0,   0, 
+      0,   0,   0,   0,   0,   0, 
+      0,   0,  31,   0,   0,   2, 
+      0,   0,   0, 128,   0,   0, 
+      3, 176,  31,   0,   0,   2, 
+      0,   0,   0, 144,   0,   8, 
+     15, 160,  31,   0,   0,   2, 
+      0,   0,   0, 144,   1,   8, 
+     15, 160,  66,   0,   0,   3, 
+      0,   0,  15, 128,   0,   0, 
+    228, 176,   1,   8, 228, 160, 
+     66,   0,   0,   3,   1,   0, 
+     15, 128,   0,   0, 228, 176, 
+      0,   8, 228, 160,   2,   0, 
+      0,   3,   0,   0,   1, 128, 
+      0,   0,  85, 129,   1,   0, 
+     85, 128,   2,   0,   0,   3, 
+      1,   0,   8, 128,   0,   0, 
+      0, 128,   1,   0,   0, 160, 
+      5,   0,   0,   3,   0,   0, 
+     15, 128,   1,   0, 228, 128, 
+      0,   0,   0, 160,   1,   0, 
+      0,   2,   0,   8,  15, 128, 
+      0,   0, 228, 128, 255, 255, 
+      0,   0
+};
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
 //   fxc LayerManagerD3D9Shaders.hlsl -ERGBShader -nologo -Tps_2_0
 //    -FhtmpShaderHeader -VnRGBShaderPS
 //
 //
 // Parameters:
 //
 //   float fLayerOpacity;
 //   sampler2D s2D;
--- a/gfx/layers/d3d9/LayerManagerD3D9Shaders.hlsl
+++ b/gfx/layers/d3d9/LayerManagerD3D9Shaders.hlsl
@@ -3,16 +3,17 @@ float4 vRenderTargetOffset;
 float4x4 mProjection;
 
 typedef float4 rect;
 rect vTextureCoords;
 rect vLayerQuad;
 
 texture tex0;
 sampler s2D;
+sampler s2DWhite;
 sampler s2DY;
 sampler s2DCb;
 sampler s2DCr;
 
 float fLayerOpacity;
 float4 fLayerColor;
 
 struct VS_INPUT {
@@ -52,16 +53,32 @@ VS_OUTPUT LayerQuadVS(const VS_INPUT aVe
   position = vTextureCoords.xy;
   size = vTextureCoords.zw;
   outp.vTexCoords.x = position.x + aVertex.vPosition.x * size.x;
   outp.vTexCoords.y = position.y + aVertex.vPosition.y * size.y;
 
   return outp;
 }
 
+float4 ComponentPass1Shader(const VS_OUTPUT aVertex) : COLOR
+{
+  float4 src = tex2D(s2D, aVertex.vTexCoords);
+  float4 alphas = 1.0 - tex2D(s2DWhite, aVertex.vTexCoords) + src;
+  alphas.a = alphas.g;
+  return alphas * fLayerOpacity;
+}
+
+float4 ComponentPass2Shader(const VS_OUTPUT aVertex) : COLOR
+{
+  float4 src = tex2D(s2D, aVertex.vTexCoords);
+  float4 alphas = 1.0 - tex2D(s2DWhite, aVertex.vTexCoords) + src;
+  src.a = alphas.g;
+  return src * fLayerOpacity;
+}
+
 float4 RGBAShader(const VS_OUTPUT aVertex) : COLOR
 {
   return tex2D(s2D, aVertex.vTexCoords) * fLayerOpacity;
 }
 
 float4 RGBShader(const VS_OUTPUT aVertex) : COLOR
 {
   float4 result;
--- a/gfx/layers/d3d9/ThebesLayerD3D9.cpp
+++ b/gfx/layers/d3d9/ThebesLayerD3D9.cpp
@@ -34,27 +34,25 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "ThebesLayerD3D9.h"
 #include "gfxPlatform.h"
 
 #include "gfxWindowsPlatform.h"
-#ifdef CAIRO_HAS_D2D_SURFACE
-#include "gfxD2DSurface.h"
-#endif
+#include "gfxTeeSurface.h"
+#include "gfxUtils.h"
 
 namespace mozilla {
 namespace layers {
 
 ThebesLayerD3D9::ThebesLayerD3D9(LayerManagerD3D9 *aManager)
   : ThebesLayer(aManager, NULL)
   , LayerD3D9(aManager)
-  , mD2DSurfaceInitialized(false)
 {
   mImplData = static_cast<LayerD3D9*>(this);
   aManager->deviceManager()->mLayersWithResources.AppendElement(this);
 }
 
 ThebesLayerD3D9::~ThebesLayerD3D9()
 {
   if (mD3DManager) {
@@ -65,213 +63,207 @@ ThebesLayerD3D9::~ThebesLayerD3D9()
 /**
  * Retention threshold - amount of pixels intersection required to enable
  * layer content retention. This is a guesstimate. Profiling could be done to
  * figure out the optimal threshold.
  */
 #define RETENTION_THRESHOLD 16384
 
 void
-ThebesLayerD3D9::SetVisibleRegion(const nsIntRegion &aRegion)
+ThebesLayerD3D9::InvalidateRegion(const nsIntRegion &aRegion)
 {
-  if (aRegion.IsEqual(mVisibleRegion)) {
-    return;
-  }
-
-  nsIntRegion oldVisibleRegion = mVisibleRegion;
-  ThebesLayer::SetVisibleRegion(aRegion);
-
-  if (!mTexture) {
-    // If we don't need to retain content initialize lazily. This is good also
-    // because we might get mIsOpaqueSurface set later than the first call to
-    // SetVisibleRegion.
-    return;
-  }
-
-  D3DFORMAT fmt = (CanUseOpaqueSurface() && !mD2DSurface) ?
-                    D3DFMT_X8R8G8B8 : D3DFMT_A8R8G8B8;
-
-  D3DSURFACE_DESC desc;
-  mTexture->GetLevelDesc(0, &desc);
-
-  if (fmt != desc.Format) {
-    // The new format isn't compatible with the old texture, toss out the old
-    // texture.
-    mTexture = nsnull;
-  }
-
-  VerifyContentType();
+  mValidRegion.Sub(mValidRegion, aRegion);
+}
 
-  nsRefPtr<IDirect3DTexture9> oldTexture = mTexture;
-
-  nsIntRect oldBounds = oldVisibleRegion.GetBounds();
-  nsIntRect newBounds = mVisibleRegion.GetBounds();
-
-  CreateNewTexture(gfxIntSize(newBounds.width, newBounds.height));
-
-  // Old visible region will become the region that is covered by both the
-  // old and the new visible region.
-  oldVisibleRegion.And(oldVisibleRegion, mVisibleRegion);
-  // No point in retaining parts which were not valid.
-  oldVisibleRegion.And(oldVisibleRegion, mValidRegion);
-
-  nsIntRect largeRect = oldVisibleRegion.GetLargestRectangle();
-
-  // If we had no hardware texture before or have no retained area larger than
-  // the retention threshold, we're not retaining and are done here. If our
-  // texture creation failed this can mean a device reset is pending and we
-  // should silently ignore the failure. In the future when device failures
-  // are properly handled we should test for the type of failure and gracefully
-  // handle different failures. See bug 569081.
-  if (!oldTexture || !mTexture ||
-      largeRect.width * largeRect.height < RETENTION_THRESHOLD) {
-    mValidRegion.SetEmpty();
-    return;
-  }
-
+void
+ThebesLayerD3D9::CopyRegion(IDirect3DTexture9* aSrc, const nsIntPoint &aSrcOffset,
+                            IDirect3DTexture9* aDest, const nsIntPoint &aDestOffset,
+                            const nsIntRegion &aCopyRegion, nsIntRegion* aValidRegion)
+{
   nsRefPtr<IDirect3DSurface9> srcSurface, dstSurface;
-  oldTexture->GetSurfaceLevel(0, getter_AddRefs(srcSurface));
-  mTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface));
+  aSrc->GetSurfaceLevel(0, getter_AddRefs(srcSurface));
+  aDest->GetSurfaceLevel(0, getter_AddRefs(dstSurface));
 
   nsIntRegion retainedRegion;
-  nsIntRegionRectIterator iter(oldVisibleRegion);
+  nsIntRegionRectIterator iter(aCopyRegion);
   const nsIntRect *r;
   while ((r = iter.Next())) {
     if (r->width * r->height > RETENTION_THRESHOLD) {
       RECT oldRect, newRect;
 
       // Calculate the retained rectangle's position on the old and the new
       // surface.
-      oldRect.left = r->x - oldBounds.x;
-      oldRect.top = r->y - oldBounds.y;
+      oldRect.left = r->x - aSrcOffset.x;
+      oldRect.top = r->y - aSrcOffset.y;
       oldRect.right = oldRect.left + r->width;
       oldRect.bottom = oldRect.top + r->height;
 
-      newRect.left = r->x - newBounds.x;
-      newRect.top = r->y - newBounds.y;
+      newRect.left = r->x - aDestOffset.x;
+      newRect.top = r->y - aDestOffset.y;
       newRect.right = newRect.left + r->width;
       newRect.bottom = newRect.top + r->height;
 
       // Copy data from our old texture to the new one
       HRESULT hr = device()->
         StretchRect(srcSurface, &oldRect, dstSurface, &newRect, D3DTEXF_NONE);
 
       if (SUCCEEDED(hr)) {
         retainedRegion.Or(retainedRegion, *r);
       }
     }
   }
 
   // Areas which were valid and were retained are still valid
-  mValidRegion.And(mValidRegion, retainedRegion);  
+  aValidRegion->And(*aValidRegion, retainedRegion);
 }
 
-
-void
-ThebesLayerD3D9::InvalidateRegion(const nsIntRegion &aRegion)
+static PRUint64 RectArea(const nsIntRect& aRect)
 {
-  mValidRegion.Sub(mValidRegion, aRegion);
+  return aRect.width*PRUint64(aRect.height);
 }
 
 void
-ThebesLayerD3D9::RenderLayer()
+ThebesLayerD3D9::UpdateTextures(SurfaceMode aMode)
 {
-  if (mVisibleRegion.IsEmpty()) {
-    return;
-  }
-
   nsIntRect visibleRect = mVisibleRegion.GetBounds();
 
-  // We differentiate between these formats since D3D9 will only allow us to
-  // call GetDC on an opaque surface.
-  D3DFORMAT fmt = (CanUseOpaqueSurface() && !mD2DSurface) ?
-                    D3DFMT_X8R8G8B8 : D3DFMT_A8R8G8B8;
-
-  if (mTexture) {
-    D3DSURFACE_DESC desc;
-    mTexture->GetLevelDesc(0, &desc);
+  if (HaveTextures(aMode)) {
+    if (mTextureRect != visibleRect) {
+      nsRefPtr<IDirect3DTexture9> oldTexture = mTexture;
+      nsRefPtr<IDirect3DTexture9> oldTextureOnWhite = mTextureOnWhite;
 
-    if (fmt != desc.Format) {
-      // The new format isn't compatible with the old texture, toss out the old
-      // texture.
-      mTexture = nsnull;
-      mValidRegion.SetEmpty();
-    }
-  }
+      NS_ASSERTION(mTextureRect.Contains(mValidRegion.GetBounds()),
+                   "How can we have valid data outside the texture?");
+      nsIntRegion retainRegion;
+      // The region we want to retain is the valid data that is inside
+      // the new visible region
+      retainRegion.And(mValidRegion, mVisibleRegion);
 
-  VerifyContentType();
-
-  if (!mTexture) {
-    CreateNewTexture(gfxIntSize(visibleRect.width, visibleRect.height));
-    
-    if (!mTexture) {
-	NS_WARNING("Failed to create texture for thebes layer - not drawing.");
-	return;
-    }
+      CreateNewTextures(gfxIntSize(visibleRect.width, visibleRect.height), aMode);
 
-    mValidRegion.SetEmpty();
-  }
-
-  if (!mValidRegion.IsEqual(mVisibleRegion)) {
-    /* We use the bounds of the visible region because we draw the bounds of
-     * this region when we draw this entire texture. We have to make sure that
-     * the areas that aren't filled with content get their background drawn.
-     * This is an issue for opaque surfaces, which otherwise won't get their
-     * background painted.
-     */
-    nsIntRegion region;
-    region.Sub(mVisibleRegion, mValidRegion);
-
-    DrawRegion(region);
+      // If our texture creation failed this can mean a device reset is pending and we
+      // should silently ignore the failure. In the future when device failures
+      // are properly handled we should test for the type of failure and gracefully
+      // handle different failures. See bug 569081.
+      if (!HaveTextures(aMode)) {
+        mValidRegion.SetEmpty();
+      } else {
+        CopyRegion(oldTexture, mTextureRect.TopLeft(), mTexture, visibleRect.TopLeft(),
+                   retainRegion, &mValidRegion);
+        if (aMode == SURFACE_COMPONENT_ALPHA) {
+          CopyRegion(oldTextureOnWhite, mTextureRect.TopLeft(), mTextureOnWhite, visibleRect.TopLeft(),
+                     retainRegion, &mValidRegion);
+        }
+      }
 
-    mValidRegion = mVisibleRegion;
+      mTextureRect = visibleRect;
+    }
+  } else {
+    CreateNewTextures(gfxIntSize(visibleRect.width, visibleRect.height), aMode);
+    mTextureRect = visibleRect;
+    
+    NS_ASSERTION(mValidRegion.IsEmpty(), "Someone forgot to empty the region");
   }
-
-  SetShaderTransformAndOpacity();
+}
 
-#ifdef CAIRO_HAS_D2D_SURFACE
-  if (mD2DSurface && CanUseOpaqueSurface()) {
-    mD3DManager->SetShaderMode(DeviceManagerD3D9::RGBLAYER);
-  } else
-#endif
-  mD3DManager->SetShaderMode(DeviceManagerD3D9::RGBALAYER);
-
-  device()->SetTexture(0, mTexture);
-
+void
+ThebesLayerD3D9::RenderVisibleRegion()
+{
   nsIntRegionRectIterator iter(mVisibleRegion);
 
   const nsIntRect *iterRect;
   while ((iterRect = iter.Next())) {
     device()->SetVertexShaderConstantF(CBvLayerQuad,
                                        ShaderConstantRect(iterRect->x,
                                                           iterRect->y,
                                                           iterRect->width,
                                                           iterRect->height),
                                        1);
 
     device()->SetVertexShaderConstantF(CBvTextureCoords,
       ShaderConstantRect(
-        (float)(iterRect->x - visibleRect.x) / (float)visibleRect.width,
-        (float)(iterRect->y - visibleRect.y) / (float)visibleRect.height,
-        (float)iterRect->width / (float)visibleRect.width,
-        (float)iterRect->height / (float)visibleRect.height), 1);
+        (float)(iterRect->x - mTextureRect.x) / (float)mTextureRect.width,
+        (float)(iterRect->y - mTextureRect.y) / (float)mTextureRect.height,
+        (float)iterRect->width / (float)mTextureRect.width,
+        (float)iterRect->height / (float)mTextureRect.height), 1);
 
     device()->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
   }
+}
+
+void
+ThebesLayerD3D9::RenderLayer()
+{
+  if (mVisibleRegion.IsEmpty()) {
+    return;
+  }
+
+  SurfaceMode mode = GetSurfaceMode();
+  if (mode == SURFACE_COMPONENT_ALPHA &&
+      (!mParent || !mParent->SupportsComponentAlphaChildren())) {
+    mode = SURFACE_SINGLE_CHANNEL_ALPHA;
+  }
+  VerifyContentType(mode);
+  UpdateTextures(mode);
+  if (!HaveTextures(mode)) {
+    NS_WARNING("Texture creation failed");
+    return;
+  }
+
+  if (!mValidRegion.IsEqual(mVisibleRegion)) {
+    /* We use the bounds of the visible region because we draw the bounds of
+     * this region when we draw this entire texture. We have to make sure that
+     * the areas that aren't filled with content get their background drawn.
+     * This is an issue for opaque surfaces, which otherwise won't get their
+     * background painted.
+     */
+    nsIntRegion region;
+    region.Sub(mVisibleRegion, mValidRegion);
+    DrawRegion(region, mode);
+
+    mValidRegion = mVisibleRegion;
+  }
+
+  SetShaderTransformAndOpacity();
+
+  if (mode == SURFACE_COMPONENT_ALPHA) {
+    mD3DManager->SetShaderMode(DeviceManagerD3D9::COMPONENTLAYERPASS1);
+    device()->SetTexture(0, mTexture);
+    device()->SetTexture(1, mTextureOnWhite);
+    device()->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO);
+    device()->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCCOLOR);
+    RenderVisibleRegion();
+
+    mD3DManager->SetShaderMode(DeviceManagerD3D9::COMPONENTLAYERPASS2);
+    device()->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
+    device()->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);
+    RenderVisibleRegion();
+
+    // Restore defaults
+    device()->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
+    device()->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
+    device()->SetTexture(1, NULL);
+  } else {
+    mD3DManager->SetShaderMode(DeviceManagerD3D9::RGBALAYER);
+    device()->SetTexture(0, mTexture);
+    RenderVisibleRegion();
+  }
 
   // Set back to default.
   device()->SetVertexShaderConstantF(CBvTextureCoords,
                                      ShaderConstantRect(0, 0, 1.0f, 1.0f),
                                      1);
 }
 
 void
 ThebesLayerD3D9::CleanResources()
 {
   mTexture = nsnull;
+  mTextureOnWhite = nsnull;
+  mValidRegion.SetEmpty();
 }
 
 void
 ThebesLayerD3D9::LayerManagerDestroyed()
 {
   mD3DManager->deviceManager()->mLayersWithResources.RemoveElement(this);
   mD3DManager = nsnull;
 }
@@ -284,201 +276,263 @@ ThebesLayerD3D9::GetLayer()
 
 PRBool
 ThebesLayerD3D9::IsEmpty()
 {
   return !mTexture;
 }
 
 void
-ThebesLayerD3D9::VerifyContentType()
+ThebesLayerD3D9::VerifyContentType(SurfaceMode aMode)
 {
-#ifdef CAIRO_HAS_D2D_SURFACE
-  if (mD2DSurface) {
-    gfxASurface::gfxContentType type = CanUseOpaqueSurface() ?
-      gfxASurface::CONTENT_COLOR : gfxASurface::CONTENT_COLOR_ALPHA;
+  if (!mTexture)
+    return;
+
+  D3DSURFACE_DESC desc;
+  mTexture->GetLevelDesc(0, &desc);
+
+  switch (aMode) {
+  case SURFACE_OPAQUE:
+    if (desc.Format == D3DFMT_X8R8G8B8 && !mTextureOnWhite)
+      return;
+    break;
+
+  case SURFACE_SINGLE_CHANNEL_ALPHA:
+    if (desc.Format == D3DFMT_A8R8G8B8 && !mTextureOnWhite)
+      return;
+    break;
+
+  case SURFACE_COMPONENT_ALPHA:
+    if (mTextureOnWhite) {
+      NS_ASSERTION(desc.Format == D3DFMT_X8R8G8B8, "Wrong format for component alpha texture");
+      return;
+    }
+    break;
+  }
+
+  // The new format isn't compatible with the old texture(s), toss out the old
+  // texture(s).
+  mTexture = nsnull;
+  mTextureOnWhite = nsnull;
+  mValidRegion.SetEmpty();
+}
 
-    if (type != mD2DSurface->GetContentType()) {
-      // We could choose to recreate only the D2D surface, but since we can't
-      // use retention the synchronisation overhead probably isn't worth it.
-      mD2DSurface = nsnull;
-      mTexture = nsnull;
-    }
+class OpaqueRenderer {
+public:
+  OpaqueRenderer(const nsIntRegion& aUpdateRegion) :
+    mUpdateRegion(aUpdateRegion), mDC(NULL) {}
+  already_AddRefed<gfxWindowsSurface> Begin(LayerD3D9* aLayer);
+  void End();
+  IDirect3DTexture9* GetTexture() { return mTmpTexture; }
+
+private:
+  const nsIntRegion& mUpdateRegion;
+  nsRefPtr<IDirect3DTexture9> mTmpTexture;
+  nsRefPtr<IDirect3DSurface9> mSurface;
+  HDC mDC;
+};
+
+already_AddRefed<gfxWindowsSurface>
+OpaqueRenderer::Begin(LayerD3D9* aLayer)
+{
+  nsIntRect bounds = mUpdateRegion.GetBounds();
+
+  HRESULT hr = aLayer->device()->
+      CreateTexture(bounds.width, bounds.height, 1, 0, D3DFMT_X8R8G8B8,
+                    D3DPOOL_SYSTEMMEM, getter_AddRefs(mTmpTexture), NULL);
+
+  if (FAILED(hr)) {
+    aLayer->ReportFailure(NS_LITERAL_CSTRING("Failed to create temporary texture in system memory."), hr);
+    return nsnull;
   }
-#endif
+
+  hr = mTmpTexture->GetSurfaceLevel(0, getter_AddRefs(mSurface));
+
+  if (FAILED(hr)) {
+    // Uh-oh, bail.
+    NS_WARNING("Failed to get texture surface level.");
+    return nsnull;
+  }
+
+  hr = mSurface->GetDC(&mDC);
+  if (FAILED(hr)) {
+    NS_WARNING("Failed to get device context for texture surface.");
+    return nsnull;
+  }
+
+  nsRefPtr<gfxWindowsSurface> result = new gfxWindowsSurface(mDC);
+  return result.forget();
 }
 
 void
-ThebesLayerD3D9::DrawRegion(const nsIntRegion &aRegion)
+OpaqueRenderer::End()
+{
+  mSurface->ReleaseDC(mDC);
+}
+
+static void
+FillSurface(gfxASurface* aSurface, const nsIntRegion& aRegion,
+            const nsIntPoint& aOffset, const gfxRGBA& aColor)
+{
+  nsRefPtr<gfxContext> ctx = new gfxContext(aSurface);
+  ctx->Translate(-gfxPoint(aOffset.x, aOffset.y));
+  gfxUtils::ClipToRegion(ctx, aRegion);
+  ctx->SetColor(aColor);
+  ctx->Paint();
+}
+
+void
+ThebesLayerD3D9::DrawRegion(const nsIntRegion &aRegion, SurfaceMode aMode)
 {
   HRESULT hr;
   nsIntRect visibleRect = mVisibleRegion.GetBounds();
   nsRefPtr<gfxContext> context;
 
-#ifdef CAIRO_HAS_D2D_SURFACE
-  if (mD2DSurface) {
-    context = new gfxContext(mD2DSurface);
-    nsIntRegionRectIterator iter(aRegion);
+  nsRefPtr<gfxASurface> destinationSurface;
+  nsIntRect bounds = aRegion.GetBounds();
+  nsRefPtr<IDirect3DTexture9> tmpTexture;
+  OpaqueRenderer opaqueRenderer(aRegion);
+  OpaqueRenderer opaqueRendererOnWhite(aRegion);
+
+  switch (aMode)
+  {
+    case SURFACE_OPAQUE:
+      destinationSurface = opaqueRenderer.Begin(this);
+      break;
+
+    case SURFACE_SINGLE_CHANNEL_ALPHA: {
+      hr = device()->CreateTexture(bounds.width, bounds.height, 1,
+                                   0, D3DFMT_A8R8G8B8,
+                                   D3DPOOL_SYSTEMMEM, getter_AddRefs(tmpTexture), NULL);
 
-    context->Translate(gfxPoint(-visibleRect.x, -visibleRect.y));
-    context->NewPath();
-    const nsIntRect *iterRect;
-    while ((iterRect = iter.Next())) {
-      context->Rectangle(gfxRect(iterRect->x, iterRect->y, iterRect->width, iterRect->height));      
-    }
-    context->Clip();
-    if (!mD2DSurfaceInitialized || 
-        mD2DSurface->GetContentType() != gfxASurface::CONTENT_COLOR) {
-      context->SetOperator(gfxContext::OPERATOR_CLEAR);
-      context->Paint();
-      context->SetOperator(gfxContext::OPERATOR_OVER);
-      mD2DSurfaceInitialized = true;
+      if (FAILED(hr)) {
+        ReportFailure(NS_LITERAL_CSTRING("Failed to create temporary texture in system memory."), hr);
+        return;
+      }
+
+      // XXX - We may consider retaining a SYSTEMMEM texture texture the size
+      // of our DEFAULT texture and then use UpdateTexture and add dirty rects
+      // to update in a single call.
+      nsRefPtr<gfxWindowsSurface> dest = new gfxWindowsSurface(
+          gfxIntSize(bounds.width, bounds.height), gfxASurface::ImageFormatARGB32);
+      // If the contents of this layer don't require component alpha in the
+      // end of rendering, it's safe to enable Cleartype since all the Cleartype
+      // glyphs must be over (or under) opaque pixels.
+      dest->SetSubpixelAntialiasingEnabled(!(mContentFlags & CONTENT_COMPONENT_ALPHA));
+      destinationSurface = dest.forget();
+      break;
     }
 
-    LayerManagerD3D9::CallbackInfo cbInfo = mD3DManager->GetCallbackInfo();
-    cbInfo.Callback(this, context, aRegion, nsIntRegion(), cbInfo.CallbackData);
-    mD2DSurface->Flush();
-
-    // XXX - This call is quite expensive, we may want to consider doing our
-    // drawing in a seperate 'validation' iteration. And then flushing once for
-    // all the D2D surfaces we might have drawn, before doing our D3D9 rendering
-    // loop.
-    cairo_d2d_finish_device(gfxWindowsPlatform::GetPlatform()->GetD2DDevice());
-    return;
-  }
-#endif
-
-  D3DFORMAT fmt = CanUseOpaqueSurface() ? D3DFMT_X8R8G8B8 : D3DFMT_A8R8G8B8;
-  nsIntRect bounds = aRegion.GetBounds();
-
-  gfxASurface::gfxImageFormat imageFormat = gfxASurface::ImageFormatARGB32;
-  nsRefPtr<gfxASurface> destinationSurface;
-
-  nsRefPtr<IDirect3DTexture9> tmpTexture;
-  hr = device()->CreateTexture(bounds.width, bounds.height, 1,
-                               0, fmt,
-                               D3DPOOL_SYSTEMMEM, getter_AddRefs(tmpTexture), NULL);
-
-  if (FAILED(hr)) {
-    ReportFailure(NS_LITERAL_CSTRING("Failed to create temporary texture in system memory."), hr);
-    return;
-  }
-
-  nsRefPtr<IDirect3DSurface9> surf;
-  HDC dc;
-  if (CanUseOpaqueSurface()) {
-    hr = tmpTexture->GetSurfaceLevel(0, getter_AddRefs(surf));
-
-    if (FAILED(hr)) {
-      // Uh-oh, bail.
-      NS_WARNING("Failed to get texture surface level.");
-      return;
+    case SURFACE_COMPONENT_ALPHA: {
+      nsRefPtr<gfxWindowsSurface> onBlack = opaqueRenderer.Begin(this);
+      nsRefPtr<gfxWindowsSurface> onWhite = opaqueRendererOnWhite.Begin(this);
+      FillSurface(onBlack, aRegion, bounds.TopLeft(), gfxRGBA(0.0, 0.0, 0.0, 1.0));
+      FillSurface(onWhite, aRegion, bounds.TopLeft(), gfxRGBA(1.0, 1.0, 1.0, 1.0));
+      gfxASurface* surfaces[2] = { onBlack.get(), onWhite.get() };
+      destinationSurface = new gfxTeeSurface(surfaces, NS_ARRAY_LENGTH(surfaces));
+      // Using this surface as a source will likely go horribly wrong, since
+      // only the onBlack surface will really be used, so alpha information will
+      // be incorrect.
+      destinationSurface->SetAllowUseAsSource(PR_FALSE);
+      break;
     }
-
-    hr = surf->GetDC(&dc);
-
-    if (FAILED(hr)) {
-      NS_WARNING("Failed to get device context for texture surface.");
-      return;
-    }
-
-    destinationSurface = new gfxWindowsSurface(dc);
-  } else {
-    // XXX - We may consider retaining a SYSTEMMEM texture texture the size
-    // of our DEFAULT texture and then use UpdateTexture and add dirty rects
-    // to update in a single call.
-    destinationSurface =
-    gfxPlatform::GetPlatform()->
-      CreateOffscreenSurface(gfxIntSize(bounds.width,
-                                        bounds.height),
-                             gfxASurface::ContentFromFormat(imageFormat));
   }
 
   context = new gfxContext(destinationSurface);
   context->Translate(gfxPoint(-bounds.x, -bounds.y));
   LayerManagerD3D9::CallbackInfo cbInfo = mD3DManager->GetCallbackInfo();
   cbInfo.Callback(this, context, aRegion, nsIntRegion(), cbInfo.CallbackData);
 
-  if (CanUseOpaqueSurface()) {
-    surf->ReleaseDC(dc);
-  } else {
-    D3DLOCKED_RECT r;
-    tmpTexture->LockRect(0, &r, NULL, 0);
+  nsAutoTArray<IDirect3DTexture9*,2> srcTextures;
+  nsAutoTArray<IDirect3DTexture9*,2> destTextures;
+  switch (aMode)
+  {
+    case SURFACE_OPAQUE:
+      opaqueRenderer.End();
+      srcTextures.AppendElement(opaqueRenderer.GetTexture());
+      destTextures.AppendElement(mTexture);
+      break;
+
+    case SURFACE_SINGLE_CHANNEL_ALPHA: {
+      D3DLOCKED_RECT r;
+      tmpTexture->LockRect(0, &r, NULL, 0);
 
-    nsRefPtr<gfxImageSurface> imgSurface =
-    new gfxImageSurface((unsigned char *)r.pBits,
-                        gfxIntSize(bounds.width,
-                                   bounds.height),
-                        r.Pitch,
-                        imageFormat);
+      nsRefPtr<gfxImageSurface> imgSurface =
+        new gfxImageSurface((unsigned char *)r.pBits,
+                            gfxIntSize(bounds.width,
+                                       bounds.height),
+                            r.Pitch,
+                            gfxASurface::ImageFormatARGB32);
 
-    context = new gfxContext(imgSurface);
-    context->SetSource(destinationSurface);
-    context->SetOperator(gfxContext::OPERATOR_SOURCE);
-    context->Paint();
+      context = new gfxContext(imgSurface);
+      context->SetSource(destinationSurface);
+      context->SetOperator(gfxContext::OPERATOR_SOURCE);
+      context->Paint();
 
-    imgSurface = NULL;
+      imgSurface = NULL;
+
+      tmpTexture->UnlockRect(0);
 
-    tmpTexture->UnlockRect(0);
-  }
+      srcTextures.AppendElement(tmpTexture);
+      destTextures.AppendElement(mTexture);
+      break;
+    }
 
-  nsRefPtr<IDirect3DSurface9> srcSurface;
-  nsRefPtr<IDirect3DSurface9> dstSurface;
-
-  mTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface));
-  tmpTexture->GetSurfaceLevel(0, getter_AddRefs(srcSurface));
+    case SURFACE_COMPONENT_ALPHA: {
+      opaqueRenderer.End();
+      opaqueRendererOnWhite.End();
+      srcTextures.AppendElement(opaqueRenderer.GetTexture());
+      destTextures.AppendElement(mTexture);
+      srcTextures.AppendElement(opaqueRendererOnWhite.GetTexture());
+      destTextures.AppendElement(mTextureOnWhite);
+      break;
+    }
+  }
+  NS_ASSERTION(srcTextures.Length() == destTextures.Length(), "Mismatched lengths");
 
-  nsIntRegionRectIterator iter(aRegion);
-  const nsIntRect *iterRect;
-  while ((iterRect = iter.Next())) {
-    RECT rect;
-    rect.left = iterRect->x - bounds.x;
-    rect.top = iterRect->y - bounds.y;
-    rect.right = rect.left + iterRect->width;
-    rect.bottom = rect.top + iterRect->height;
-    POINT point;
-    point.x = iterRect->x - visibleRect.x;
-    point.y = iterRect->y - visibleRect.y;
-    device()->UpdateSurface(srcSurface, &rect, dstSurface, &point);
+  for (PRUint32 i = 0; i < srcTextures.Length(); ++i) {
+    nsRefPtr<IDirect3DSurface9> srcSurface;
+    nsRefPtr<IDirect3DSurface9> dstSurface;
+
+    destTextures[i]->GetSurfaceLevel(0, getter_AddRefs(dstSurface));
+    srcTextures[i]->GetSurfaceLevel(0, getter_AddRefs(srcSurface));
+
+    nsIntRegionRectIterator iter(aRegion);
+    const nsIntRect *iterRect;
+    while ((iterRect = iter.Next())) {
+      RECT rect;
+      rect.left = iterRect->x - bounds.x;
+      rect.top = iterRect->y - bounds.y;
+      rect.right = rect.left + iterRect->width;
+      rect.bottom = rect.top + iterRect->height;
+      POINT point;
+      point.x = iterRect->x - visibleRect.x;
+      point.y = iterRect->y - visibleRect.y;
+      device()->UpdateSurface(srcSurface, &rect, dstSurface, &point);
+    }
   }
 }
 
 void
-ThebesLayerD3D9::CreateNewTexture(const gfxIntSize &aSize)
+ThebesLayerD3D9::CreateNewTextures(const gfxIntSize &aSize,
+                                   SurfaceMode aMode)
 {
-  if (aSize.width == 0 | aSize.height == 0) {
+  if (aSize.width == 0 || aSize.height == 0) {
     // Nothing to do.
     return;
   }
 
   mTexture = nsnull;
-  PRBool canUseOpaqueSurface = CanUseOpaqueSurface();
-#ifdef CAIRO_HAS_D2D_SURFACE
-  if (gfxWindowsPlatform::GetPlatform()->GetRenderMode() ==
-      gfxWindowsPlatform::RENDER_DIRECT2D) {
-        if (mD3DManager->deviceManager()->IsD3D9Ex()) {
-          // We should have D3D9Ex where we have D2D.
-          HANDLE sharedHandle = 0;
-          device()->CreateTexture(aSize.width, aSize.height, 1,
-                                  D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8,
-                                  D3DPOOL_DEFAULT, getter_AddRefs(mTexture), &sharedHandle);
-
-          mD2DSurfaceInitialized = false;
-          mD2DSurface = new gfxD2DSurface(sharedHandle, canUseOpaqueSurface ?
-            gfxASurface::CONTENT_COLOR : gfxASurface::CONTENT_COLOR_ALPHA);
-
-          // If there's an error, go on and do what we always do.
-          if (mD2DSurface->CairoStatus()) {
-            mD2DSurface = nsnull;
-            mTexture = nsnull;
-          }
-        }
-  }
-#endif
-  if (!mTexture) {
+  mTextureOnWhite = nsnull;
+  device()->CreateTexture(aSize.width, aSize.height, 1,
+                          D3DUSAGE_RENDERTARGET,
+                          aMode != SURFACE_SINGLE_CHANNEL_ALPHA ? D3DFMT_X8R8G8B8 : D3DFMT_A8R8G8B8,
+                          D3DPOOL_DEFAULT, getter_AddRefs(mTexture), NULL);
+  if (aMode == SURFACE_COMPONENT_ALPHA) {
     device()->CreateTexture(aSize.width, aSize.height, 1,
-                            D3DUSAGE_RENDERTARGET, canUseOpaqueSurface ? D3DFMT_X8R8G8B8 : D3DFMT_A8R8G8B8,
-                            D3DPOOL_DEFAULT, getter_AddRefs(mTexture), NULL);
+                            D3DUSAGE_RENDERTARGET,
+                            D3DFMT_X8R8G8B8,
+                            D3DPOOL_DEFAULT, getter_AddRefs(mTextureOnWhite), NULL);
   }
 }
 
 } /* namespace layers */
 } /* namespace mozilla */
--- a/gfx/layers/d3d9/ThebesLayerD3D9.h
+++ b/gfx/layers/d3d9/ThebesLayerD3D9.h
@@ -47,45 +47,65 @@ namespace layers {
 
 class ThebesLayerD3D9 : public ThebesLayer,
                         public LayerD3D9
 {
 public:
   ThebesLayerD3D9(LayerManagerD3D9 *aManager);
   virtual ~ThebesLayerD3D9();
 
-  /* Layer implementation */
-  void SetVisibleRegion(const nsIntRegion& aRegion);
-
   /* ThebesLayer implementation */
   void InvalidateRegion(const nsIntRegion& aRegion);
 
   /* LayerD3D9 implementation */
   Layer* GetLayer();
   virtual PRBool IsEmpty();
   virtual void RenderLayer();
   virtual void CleanResources();
   virtual void LayerManagerDestroyed();
 
 private:
   /*
    * D3D9 texture
    */
   nsRefPtr<IDirect3DTexture9> mTexture;
+  /*
+   * D3D9 texture for render-on-white when doing component alpha
+   */
+  nsRefPtr<IDirect3DTexture9> mTextureOnWhite;
+  /**
+   * Visible region bounds used when we drew the contents of the textures
+   */
+  nsIntRect mTextureRect;
 
-  /* Checks if our D2D surface has the right content type */
-  void VerifyContentType();
+  bool HaveTextures(SurfaceMode aMode)
+  {
+    return mTexture && (aMode != SURFACE_COMPONENT_ALPHA || mTextureOnWhite);
+  }
 
-  /* This contains the D2D surface if we have one */
-  nsRefPtr<gfxASurface> mD2DSurface;
+  /* Checks if our surface has the right content type */
+  void VerifyContentType(SurfaceMode aMode);
 
-  bool mD2DSurfaceInitialized;
+  /* Ensures we have the necessary texture object(s) and that they correspond
+   * to mVisibleRegion.GetBounds(). This creates new texture objects as
+   * necessary and also copies existing valid texture data if necessary.
+   */
+  void UpdateTextures(SurfaceMode aMode);
+
+  /* Render the rectangles of mVisibleRegion with D3D9 using the currently
+   * bound textures, target, shaders, etc.
+   */
+  void RenderVisibleRegion();
 
   /* Have a region of our layer drawn */
-  void DrawRegion(const nsIntRegion &aRegion);
+  void DrawRegion(const nsIntRegion &aRegion, SurfaceMode aMode);
 
   /* Create a new texture */
-  void CreateNewTexture(const gfxIntSize &aSize);
+  void CreateNewTextures(const gfxIntSize &aSize, SurfaceMode aMode);
+
+  void CopyRegion(IDirect3DTexture9* aSrc, const nsIntPoint &aSrcOffset,
+                  IDirect3DTexture9* aDest, const nsIntPoint &aDestOffset,
+                  const nsIntRegion &aCopyRegion, nsIntRegion* aValidRegion);
 };
 
 } /* layers */
 } /* mozilla */
 #endif /* GFX_THEBESLAYERD3D9_H */
--- a/gfx/layers/d3d9/genshaders.sh
+++ b/gfx/layers/d3d9/genshaders.sh
@@ -1,13 +1,17 @@
 tempfile=tmpShaderHeader
 rm LayerManagerD3D9Shaders.h
 fxc LayerManagerD3D9Shaders.hlsl -ELayerQuadVS -nologo -Fh$tempfile -VnLayerQuadVS
 cat $tempfile >> LayerManagerD3D9Shaders.h
 fxc LayerManagerD3D9Shaders.hlsl -ERGBAShader -nologo -Tps_2_0 -Fh$tempfile -VnRGBAShaderPS
 cat $tempfile >> LayerManagerD3D9Shaders.h
+fxc LayerManagerD3D9Shaders.hlsl -EComponentPass1Shader -nologo -Tps_2_0 -Fh$tempfile -VnComponentPass1ShaderPS
+cat $tempfile >> LayerManagerD3D9Shaders.h
+fxc LayerManagerD3D9Shaders.hlsl -EComponentPass2Shader -nologo -Tps_2_0 -Fh$tempfile -VnComponentPass2ShaderPS
+cat $tempfile >> LayerManagerD3D9Shaders.h
 fxc LayerManagerD3D9Shaders.hlsl -ERGBShader -nologo -Tps_2_0 -Fh$tempfile -VnRGBShaderPS
 cat $tempfile >> LayerManagerD3D9Shaders.h
 fxc LayerManagerD3D9Shaders.hlsl -EYCbCrShader -nologo -Tps_2_0 -Fh$tempfile -VnYCbCrShaderPS
 cat $tempfile >> LayerManagerD3D9Shaders.h
 fxc LayerManagerD3D9Shaders.hlsl -ESolidColorShader -nologo -Tps_2_0 -Fh$tempfile -VnSolidColorShaderPS
 cat $tempfile >> LayerManagerD3D9Shaders.h
 rm $tempfile
--- a/gfx/layers/ipc/PLayers.ipdl
+++ b/gfx/layers/ipc/PLayers.ipdl
@@ -49,16 +49,17 @@ using gfxRGBA;
 using nsIntPoint;
 using nsIntRect;
 using nsIntRegion;
 using nsIntSize;
 using mozilla::GraphicsFilterType;
 using mozilla::layers::FrameMetrics;
 using mozilla::layers::SurfaceDescriptorX11;
 using mozilla::null_t;
+using mozilla::LayersBackend;
 
 /**
  * The layers protocol is spoken between thread contexts that manage
  * layer (sub)trees.  The protocol comprises atomically publishing
  * layer subtrees to a "shadow" thread context (which grafts the
  * subtree into its own tree), and atomically updating a published
  * subtree.  ("Atomic" in this sense is wrt painting.)
  */
@@ -86,17 +87,17 @@ struct ThebesBuffer {
   SurfaceDescriptor buffer;
   nsIntRect rect;
   nsIntPoint rotation; 
 };
 union OptionalThebesBuffer { ThebesBuffer; null_t; };
 
 struct OpCreateThebesBuffer {
   PLayer layer;
-  ThebesBuffer initialFront;
+  OptionalThebesBuffer initialFront;
   nsIntRegion frontValidRegion;
   float xResolution;
   float yResolution;
 };
 struct OpDestroyThebesFrontBuffer { PLayer layer; };
 
 struct OpCreateCanvasBuffer {
   PLayer layer;
@@ -238,13 +239,16 @@ sync protocol PLayers {
   manages PLayer;
 
 parent:
   async PLayer();
 
   sync Update(Edit[] cset)
     returns (EditReply[] reply);
 
+  sync GetParentType()
+    returns (LayersBackend backend);
+
   async __delete__();
 };
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/ipc/ShadowLayerUtilsX11.cpp
+++ b/gfx/layers/ipc/ShadowLayerUtilsX11.cpp
@@ -98,41 +98,45 @@ SurfaceDescriptorX11::OpenForeign() cons
 }
 
 PRBool
 ShadowLayerForwarder::PlatformAllocDoubleBuffer(const gfxIntSize& aSize,
                                                 gfxASurface::gfxContentType aContent,
                                                 SurfaceDescriptor* aFrontBuffer,
                                                 SurfaceDescriptor* aBackBuffer)
 {
+  return (PlatformAllocBuffer(aSize, aContent, aFrontBuffer) &&
+          PlatformAllocBuffer(aSize, aContent, aBackBuffer));
+}
+
+PRBool
+ShadowLayerForwarder::PlatformAllocBuffer(const gfxIntSize& aSize,
+                                          gfxASurface::gfxContentType aContent,
+                                          SurfaceDescriptor* aBuffer)
+{
   if (!UsingXCompositing()) {
     // If we're not using X compositing, we're probably compositing on
     // the client side, in which case X surfaces would just slow
     // things down.  Use Shmem instead.
     return PR_FALSE;
   }
 
   gfxPlatform* platform = gfxPlatform::GetPlatform();
-  nsRefPtr<gfxASurface> front = platform->CreateOffscreenSurface(aSize, aContent);
-  nsRefPtr<gfxASurface> back = platform->CreateOffscreenSurface(aSize, aContent);
-  if (!front || !back ||
-      front->GetType() != gfxASurface::SurfaceTypeXlib ||
-      back->GetType() != gfxASurface::SurfaceTypeXlib) {
+  nsRefPtr<gfxASurface> buffer = platform->CreateOffscreenSurface(aSize, aContent);
+  if (!buffer ||
+      buffer->GetType() != gfxASurface::SurfaceTypeXlib) {
     NS_ERROR("creating Xlib front/back surfaces failed!");
     return PR_FALSE;
   }
 
-  gfxXlibSurface* frontX = static_cast<gfxXlibSurface*>(front.get());
-  gfxXlibSurface* backX = static_cast<gfxXlibSurface*>(back.get());
+  gfxXlibSurface* bufferX = static_cast<gfxXlibSurface*>(buffer.get());
   // Release Pixmap ownership to the layers model
-  frontX->ReleasePixmap();
-  backX->ReleasePixmap();
+  bufferX->ReleasePixmap();
 
-  *aFrontBuffer = SurfaceDescriptorX11(frontX);
-  *aBackBuffer = SurfaceDescriptorX11(backX);
+  *aBuffer = SurfaceDescriptorX11(bufferX);
   return PR_TRUE;
 }
 
 /*static*/ already_AddRefed<gfxASurface>
 ShadowLayerForwarder::PlatformOpenDescriptor(const SurfaceDescriptor& aSurface)
 {
   if (SurfaceDescriptor::TSurfaceDescriptorX11 != aSurface.type()) {
     return nsnull;
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -118,17 +118,19 @@ private:
   Transaction& operator=(const Transaction&);
 };
 struct AutoTxnEnd {
   AutoTxnEnd(Transaction* aTxn) : mTxn(aTxn) {}
   ~AutoTxnEnd() { mTxn->End(); }
   Transaction* mTxn;
 };
 
-ShadowLayerForwarder::ShadowLayerForwarder() : mShadowManager(NULL)
+ShadowLayerForwarder::ShadowLayerForwarder()
+ : mShadowManager(NULL)
+ , mParentBackend(LayerManager::LAYERS_NONE)
 {
   mTxn = new Transaction();
 }
 
 ShadowLayerForwarder::~ShadowLayerForwarder()
 {
   NS_ABORT_IF_FALSE(mTxn->Finished(), "unfinished transaction?");
   delete mTxn;
@@ -184,20 +186,24 @@ ShadowLayerForwarder::CreatedCanvasLayer
 void
 ShadowLayerForwarder::CreatedThebesBuffer(ShadowableLayer* aThebes,
                                           const nsIntRegion& aFrontValidRegion,
                                           float aXResolution,
                                           float aYResolution,
                                           const nsIntRect& aBufferRect,
                                           const SurfaceDescriptor& aTempFrontBuffer)
 {
+  OptionalThebesBuffer buffer = null_t();
+  if (SurfaceDescriptor::T__None != aTempFrontBuffer.type()) {
+    buffer = ThebesBuffer(aTempFrontBuffer,
+                          aBufferRect,
+                          nsIntPoint(0, 0));
+  }
   mTxn->AddEdit(OpCreateThebesBuffer(NULL, Shadow(aThebes),
-                                     ThebesBuffer(aTempFrontBuffer,
-                                                  aBufferRect,
-                                                  nsIntPoint(0, 0)),
+                                     buffer,
                                      aFrontValidRegion,
                                      aXResolution,
                                      aYResolution));
 }
 
 void
 ShadowLayerForwarder::CreatedImageBuffer(ShadowableLayer* aImage,
                                          nsIntSize aSize,
@@ -367,16 +373,28 @@ ShadowLayerForwarder::EndTransaction(Inf
     MOZ_LAYERS_LOG(("[LayersForwarder] WARNING: sending transaction failed!"));
     return PR_FALSE;
   }
 
   MOZ_LAYERS_LOG(("[LayersForwarder] ... done"));
   return PR_TRUE;
 }
 
+LayersBackend
+ShadowLayerForwarder::GetParentBackendType()
+{
+  if (mParentBackend == LayerManager::LAYERS_NONE) {
+    LayersBackend backend;
+    if (mShadowManager->SendGetParentType(&backend)) {
+      mParentBackend = backend;
+    }
+  }
+  return mParentBackend;
+}
+
 static gfxASurface::gfxImageFormat
 OptimalFormatFor(gfxASurface::gfxContentType aContent)
 {
   switch (aContent) {
   case gfxASurface::CONTENT_COLOR:
 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
     return gfxASurface::ImageFormatRGB16_565;
 #else
@@ -408,39 +426,47 @@ OptimalShmemType()
 }
 
 PRBool
 ShadowLayerForwarder::AllocDoubleBuffer(const gfxIntSize& aSize,
                                         gfxASurface::gfxContentType aContent,
                                         gfxSharedImageSurface** aFrontBuffer,
                                         gfxSharedImageSurface** aBackBuffer)
 {
-  NS_ABORT_IF_FALSE(HasShadowManager(), "no manager to forward to");
-
-  gfxASurface::gfxImageFormat format = OptimalFormatFor(aContent);
-  SharedMemory::SharedMemoryType shmemType = OptimalShmemType();
-
-  nsRefPtr<gfxSharedImageSurface> front = new gfxSharedImageSurface();
-  nsRefPtr<gfxSharedImageSurface> back = new gfxSharedImageSurface();
-  if (!front->InitUnsafe(mShadowManager, aSize, format, shmemType) ||
-      !back->InitUnsafe(mShadowManager, aSize, format, shmemType))
-    return PR_FALSE;
-
-  *aFrontBuffer = NULL;       *aBackBuffer = NULL;
-  front.swap(*aFrontBuffer);  back.swap(*aBackBuffer);
-  return PR_TRUE;
+  return AllocBuffer(aSize, aContent, aFrontBuffer) &&
+         AllocBuffer(aSize, aContent, aBackBuffer);
 }
 
 void
 ShadowLayerForwarder::DestroySharedSurface(gfxSharedImageSurface* aSurface)
 {
   mShadowManager->DeallocShmem(aSurface->GetShmem());
 }
 
 PRBool
+ShadowLayerForwarder::AllocBuffer(const gfxIntSize& aSize,
+                                  gfxASurface::gfxContentType aContent,
+                                  gfxSharedImageSurface** aBuffer)
+{
+  NS_ABORT_IF_FALSE(HasShadowManager(), "no manager to forward to");
+
+  gfxASurface::gfxImageFormat format = OptimalFormatFor(aContent);
+  SharedMemory::SharedMemoryType shmemType = OptimalShmemType();
+
+  nsRefPtr<gfxSharedImageSurface> back =
+    gfxSharedImageSurface::CreateUnsafe(mShadowManager, aSize, format, shmemType);
+  if (!back)
+    return PR_FALSE;
+
+  *aBuffer = nsnull;
+  back.swap(*aBuffer);
+  return PR_TRUE;
+}
+
+PRBool
 ShadowLayerForwarder::AllocDoubleBuffer(const gfxIntSize& aSize,
                                         gfxASurface::gfxContentType aContent,
                                         SurfaceDescriptor* aFrontBuffer,
                                         SurfaceDescriptor* aBackBuffer)
 {
   PRBool tryPlatformSurface = PR_TRUE;
 #ifdef DEBUG
   tryPlatformSurface = !PR_GetEnv("MOZ_LAYERS_FORCE_SHMEM_SURFACES");
@@ -457,27 +483,50 @@ ShadowLayerForwarder::AllocDoubleBuffer(
     return PR_FALSE;
   }
 
   *aFrontBuffer = front->GetShmem();
   *aBackBuffer = back->GetShmem();
   return PR_TRUE;
 }
 
+PRBool
+ShadowLayerForwarder::AllocBuffer(const gfxIntSize& aSize,
+                                  gfxASurface::gfxContentType aContent,
+                                  SurfaceDescriptor* aBuffer)
+{
+  PRBool tryPlatformSurface = PR_TRUE;
+#ifdef DEBUG
+  tryPlatformSurface = !PR_GetEnv("MOZ_LAYERS_FORCE_SHMEM_SURFACES");
+#endif
+  if (tryPlatformSurface &&
+      PlatformAllocBuffer(aSize, aContent, aBuffer)) {
+    return PR_TRUE;
+  }
+
+  nsRefPtr<gfxSharedImageSurface> buffer;
+  if (!AllocBuffer(aSize, aContent,
+                   getter_AddRefs(buffer)))
+    return PR_FALSE;
+
+  *aBuffer = buffer->GetShmem();
+  return PR_TRUE;
+}
+
 /*static*/ already_AddRefed<gfxASurface>
 ShadowLayerForwarder::OpenDescriptor(const SurfaceDescriptor& aSurface)
 {
   nsRefPtr<gfxASurface> surf = PlatformOpenDescriptor(aSurface);
   if (surf) {
     return surf.forget();
   }
 
   switch (aSurface.type()) {
   case SurfaceDescriptor::TShmem: {
-    surf = new gfxSharedImageSurface(aSurface.get_Shmem());
+    surf = gfxSharedImageSurface::Open(aSurface.get_Shmem());
     return surf.forget();
   }
   default:
     NS_RUNTIMEABORT("unexpected SurfaceDescriptor type!");
     return nsnull;
   }
 }
 
@@ -541,16 +590,24 @@ PRBool
 ShadowLayerForwarder::PlatformAllocDoubleBuffer(const gfxIntSize&,
                                                 gfxASurface::gfxContentType,
                                                 SurfaceDescriptor*,
                                                 SurfaceDescriptor*)
 {
   return PR_FALSE;
 }
 
+PRBool
+ShadowLayerForwarder::PlatformAllocBuffer(const gfxIntSize&,
+                                          gfxASurface::gfxContentType,
+                                          SurfaceDescriptor*)
+{
+  return PR_FALSE;
+}
+
 /*static*/ already_AddRefed<gfxASurface>
 ShadowLayerForwarder::PlatformOpenDescriptor(const SurfaceDescriptor&)
 {
   return nsnull;
 }
 
 PRBool
 ShadowLayerForwarder::PlatformDestroySharedSurface(SurfaceDescriptor*)
--- a/gfx/layers/ipc/ShadowLayers.h
+++ b/gfx/layers/ipc/ShadowLayers.h
@@ -106,16 +106,18 @@ class Transaction;
  * There are only shadow types for layers that have different shadow
  * vs. not-shadow behavior.  ColorLayers and ContainerLayers behave
  * the same way in both regimes (so far).
  */
 
 class ShadowLayerForwarder
 {
 public:
+  typedef LayerManager::LayersBackend LayersBackend;
+
   virtual ~ShadowLayerForwarder();
 
   /**
    * Begin recording a transaction to be forwarded atomically to a
    * ShadowLayerManager.
    */
   void BeginTransaction();
 
@@ -280,55 +282,76 @@ public:
    * SurfaceDescriptor variant below.
    */
   PRBool AllocDoubleBuffer(const gfxIntSize& aSize,
                            gfxASurface::gfxContentType aContent,
                            gfxSharedImageSurface** aFrontBuffer,
                            gfxSharedImageSurface** aBackBuffer);
   void DestroySharedSurface(gfxSharedImageSurface* aSurface);
 
+  PRBool AllocBuffer(const gfxIntSize& aSize,
+                     gfxASurface::gfxContentType aContent,
+                     gfxSharedImageSurface** aBuffer);
+
   /**
    * In the absence of platform-specific buffers these fall back to
    * Shmem/gfxSharedImageSurface.
    */
   PRBool AllocDoubleBuffer(const gfxIntSize& aSize,
                            gfxASurface::gfxContentType aContent,
                            SurfaceDescriptor* aFrontBuffer,
                            SurfaceDescriptor* aBackBuffer);
 
+  PRBool AllocBuffer(const gfxIntSize& aSize,
+                     gfxASurface::gfxContentType aContent,
+                     SurfaceDescriptor* aBuffer);
+
   static already_AddRefed<gfxASurface>
   OpenDescriptor(const SurfaceDescriptor& aSurface);
 
   void DestroySharedSurface(SurfaceDescriptor* aSurface);
 
   /**
    * Construct a shadow of |aLayer| on the "other side", at the
    * ShadowLayerManager.
    */
   PLayerChild* ConstructShadowFor(ShadowableLayer* aLayer);
 
+  LayersBackend GetParentBackendType();
+
+  /*
+   * No need to use double buffer in system memory with GPU rendering,
+   * texture used as front buffer.
+   */
+  bool ShouldDoubleBuffer() { return GetParentBackendType() == LayerManager::LAYERS_BASIC; }
+
 protected:
   ShadowLayerForwarder();
 
   PLayersChild* mShadowManager;
 
 private:
   PRBool PlatformAllocDoubleBuffer(const gfxIntSize& aSize,
                                    gfxASurface::gfxContentType aContent,
                                    SurfaceDescriptor* aFrontBuffer,
                                    SurfaceDescriptor* aBackBuffer);
 
+  PRBool PlatformAllocBuffer(const gfxIntSize& aSize,
+                             gfxASurface::gfxContentType aContent,
+                             SurfaceDescriptor* aBuffer);
+
   static already_AddRefed<gfxASurface>
   PlatformOpenDescriptor(const SurfaceDescriptor& aDescriptor);
 
   PRBool PlatformDestroySharedSurface(SurfaceDescriptor* aSurface);
 
   static void PlatformSyncBeforeUpdate();
 
   Transaction* mTxn;
+  LayersBackend mParentBackend;
 };
 
 
 class ShadowLayerManager : public LayerManager
 {
 public:
   virtual ~ShadowLayerManager() {}
 
@@ -463,17 +486,17 @@ class ShadowThebesLayer : public ShadowL
 {
 public:
   /**
    * CONSTRUCTION PHASE ONLY
    *
    * Override the front buffer and its valid region with the specified
    * values.  This is called when a new buffer has been created.
    */
-  virtual void SetFrontBuffer(const ThebesBuffer& aNewFront,
+  virtual void SetFrontBuffer(const OptionalThebesBuffer& aNewFront,
                               const nsIntRegion& aValidRegion,
                               float aXResolution, float aYResolution) = 0;
 
   virtual void InvalidateRegion(const nsIntRegion& aRegion)
   {
     NS_RUNTIMEABORT("ShadowThebesLayers can't fill invalidated regions");
   }
 
--- a/gfx/layers/ipc/ShadowLayersParent.cpp
+++ b/gfx/layers/ipc/ShadowLayersParent.cpp
@@ -202,33 +202,35 @@ ShadowLayersParent::RecvUpdate(const Inf
     }
     case Edit::TOpCreateCanvasBuffer: {
       MOZ_LAYERS_LOG(("[ParentSide] CreateCanvasBuffer"));
 
       const OpCreateCanvasBuffer& ocb = edit.get_OpCreateCanvasBuffer();
       ShadowCanvasLayer* canvas = static_cast<ShadowCanvasLayer*>(
         AsShadowLayer(ocb)->AsLayer());
       nsRefPtr<gfxSharedImageSurface> front =
-        new gfxSharedImageSurface(ocb.initialFront());
+        gfxSharedImageSurface::Open(ocb.initialFront());
       CanvasLayer::Data data;
       data.mSurface = front;
       data.mSize = ocb.size();
 
       canvas->Initialize(data);
 
       break;
     }
     case Edit::TOpCreateImageBuffer: {
       MOZ_LAYERS_LOG(("[ParentSide] CreateImageBuffer"));
 
       const OpCreateImageBuffer ocb = edit.get_OpCreateImageBuffer();
       ShadowImageLayer* image = static_cast<ShadowImageLayer*>(
         AsShadowLayer(ocb)->AsLayer());
 
-      image->Init(new gfxSharedImageSurface(ocb.initialFront()), ocb.size());
+      nsRefPtr<gfxSharedImageSurface> surf =
+        gfxSharedImageSurface::Open(ocb.initialFront());
+      image->Init(surf, ocb.size());
 
       break;
     }
     case Edit::TOpDestroyThebesFrontBuffer: {
       MOZ_LAYERS_LOG(("[ParentSide] DestroyThebesFrontBuffer"));
 
       const OpDestroyThebesFrontBuffer& odfb =
         edit.get_OpDestroyThebesFrontBuffer();
@@ -391,35 +393,37 @@ ShadowLayersParent::RecvUpdate(const Inf
     case Edit::TOpPaintCanvas: {
       MOZ_LAYERS_LOG(("[ParentSide] Paint CanvasLayer"));
 
       const OpPaintCanvas& op = edit.get_OpPaintCanvas();
       ShadowLayerParent* shadow = AsShadowLayer(op);
       ShadowCanvasLayer* canvas =
         static_cast<ShadowCanvasLayer*>(shadow->AsLayer());
 
-      nsRefPtr<gfxSharedImageSurface> newBack =
-        canvas->Swap(new gfxSharedImageSurface(op.newFrontBuffer()));
+      nsRefPtr<gfxSharedImageSurface> newFront =
+        gfxSharedImageSurface::Open(op.newFrontBuffer());
+      nsRefPtr<gfxSharedImageSurface> newBack = canvas->Swap(newFront);
       canvas->Updated(op.updated());
 
       replyv.push_back(OpBufferSwap(shadow, NULL,
                                     newBack->GetShmem()));
 
       break;
     }
     case Edit::TOpPaintImage: {
       MOZ_LAYERS_LOG(("[ParentSide] Paint ImageLayer"));
 
       const OpPaintImage& op = edit.get_OpPaintImage();
       ShadowLayerParent* shadow = AsShadowLayer(op);
       ShadowImageLayer* image =
         static_cast<ShadowImageLayer*>(shadow->AsLayer());
 
-      nsRefPtr<gfxSharedImageSurface> newBack =
-        image->Swap(new gfxSharedImageSurface(op.newFrontBuffer()));
+      nsRefPtr<gfxSharedImageSurface> newFront =
+        gfxSharedImageSurface::Open(op.newFrontBuffer());
+      nsRefPtr<gfxSharedImageSurface> newBack = image->Swap(newFront);
 
       replyv.push_back(OpBufferSwap(shadow, NULL,
                                     newBack->GetShmem()));
 
       break;
     }
 
     default:
@@ -439,16 +443,23 @@ ShadowLayersParent::RecvUpdate(const Inf
   // other's buffer contents.
   ShadowLayerManager::PlatformSyncBeforeReplyUpdate();
 
   Frame()->ShadowLayersUpdated();
 
   return true;
 }
 
+bool
+ShadowLayersParent::RecvGetParentType(LayersBackend* aBackend)
+{
+  *aBackend = layer_manager()->GetBackendType();
+  return true;
+}
+
 PLayerParent*
 ShadowLayersParent::AllocPLayer()
 {
   return new ShadowLayerParent();
 }
 
 bool
 ShadowLayersParent::DeallocPLayer(PLayerParent* actor)
--- a/gfx/layers/ipc/ShadowLayersParent.h
+++ b/gfx/layers/ipc/ShadowLayersParent.h
@@ -67,16 +67,18 @@ public:
   ShadowLayerManager* layer_manager() const { return mLayerManager; }
 
   ContainerLayer* GetRoot() const { return mRoot; }
 
 protected:
   NS_OVERRIDE virtual bool RecvUpdate(const EditArray& cset,
                                       EditReplyArray* reply);
 
+  NS_OVERRIDE virtual bool RecvGetParentType(LayersBackend* aBackend);
+
   NS_OVERRIDE virtual PLayerParent* AllocPLayer();
   NS_OVERRIDE virtual bool DeallocPLayer(PLayerParent* actor);
 
 private:
   RenderFrameParent* Frame();
 
   nsRefPtr<ShadowLayerManager> mLayerManager;
   // Hold the root because it might be grafted under various
--- a/gfx/layers/opengl/ThebesLayerOGL.cpp
+++ b/gfx/layers/opengl/ThebesLayerOGL.cpp
@@ -190,25 +190,26 @@ ThebesLayerBufferOGL::RenderTo(const nsI
   // Note BGR: Cairo's image surfaces are always in what
   // OpenGL and our shaders consider BGR format.
   ColorTextureLayerProgram *program =
     aManager->GetColorTextureLayerProgram(mTexImage->GetShaderProgramType());
 
   float xres = mLayer->GetXResolution();
   float yres = mLayer->GetYResolution();
 
+  program->Activate();
+  program->SetLayerOpacity(mLayer->GetEffectiveOpacity());
+  program->SetLayerTransform(mLayer->GetEffectiveTransform());
+  program->SetRenderOffset(aOffset);
+  program->SetTextureUnit(0);
+
   nsIntRegionRectIterator iter(mLayer->GetEffectiveVisibleRegion());
   while (const nsIntRect *iterRect = iter.Next()) {
     nsIntRect quadRect = *iterRect;
-    program->Activate();
     program->SetLayerQuadRect(quadRect);
-    program->SetLayerOpacity(mLayer->GetEffectiveOpacity());
-    program->SetLayerTransform(mLayer->GetEffectiveTransform());
-    program->SetRenderOffset(aOffset);
-    program->SetTextureUnit(0);
     DEBUG_GL_ERROR_CHECK(gl());
 
     quadRect.MoveBy(-GetOriginOffset());
 
     // The buffer rect and rotation are resolution-neutral; with a
     // non-1.0 resolution, only the texture size is scaled by the
     // resolution.  So map the quadrent rect into the space scaled to
     // the texture size and let GL do the rest.
@@ -611,16 +612,22 @@ private:
   nsIntRect mBufferRect;
   nsIntPoint mBufferRotation;
 };
 
 void
 ShadowBufferOGL::Upload(gfxASurface* aUpdate, const nsIntRegion& aUpdated,
                         const nsIntRect& aRect, const nsIntPoint& aRotation)
 {
+  gfxIntSize size = aUpdate->GetSize();
+  if (GetSize() != nsIntSize(size.width, size.height)) {
+    CreateTexture(aUpdate->GetContentType(),
+                  nsIntSize(size.width, size.height));
+  }
+
   nsIntRegion destRegion(aUpdated);
   // aUpdated is in screen coordinates.  Move it so that the layer's
   // top-left is 0,0
   nsIntPoint visTopLeft = mLayer->GetVisibleRegion().GetBounds().TopLeft();
   destRegion.MoveBy(-visTopLeft);
 
   // |aUpdated|, |aRect|, and |aRotation| are in thebes-layer space,
   // unadjusted for resolution.  The texture is in device space, so
@@ -653,36 +660,30 @@ ShadowThebesLayerOGL::ShadowThebesLayerO
 {
   mImplData = static_cast<LayerOGL*>(this);
 }
 
 ShadowThebesLayerOGL::~ShadowThebesLayerOGL()
 {}
 
 void
-ShadowThebesLayerOGL::SetFrontBuffer(const ThebesBuffer& aNewFront,
+ShadowThebesLayerOGL::SetFrontBuffer(const OptionalThebesBuffer& aNewFront,
                                      const nsIntRegion& aValidRegion,
                                      float aXResolution, float aYResolution)
 {
   if (mDestroyed) {
     return;
   }
 
   if (!mBuffer) {
     mBuffer = new ShadowBufferOGL(this);
   }
 
-  nsRefPtr<gfxASurface> surf = ShadowLayerForwarder::OpenDescriptor(aNewFront.buffer());
-  gfxIntSize size = surf->GetSize();
-  mBuffer->CreateTexture(surf->GetContentType(),
-                         nsIntSize(size.width, size.height));
-
-
-
-  mDeadweight = aNewFront.buffer();
+  NS_ASSERTION(OptionalThebesBuffer::Tnull_t == aNewFront.type(),
+               "Only one system-memory buffer expected");
 }
 
 void
 ShadowThebesLayerOGL::Swap(const ThebesBuffer& aNewFront,
                            const nsIntRegion& aUpdatedRegion,
                            ThebesBuffer* aNewBack,
                            nsIntRegion* aNewBackValidRegion,
                            float* aNewXResolution, float* aNewYResolution,
@@ -701,19 +702,16 @@ ShadowThebesLayerOGL::Swap(const ThebesB
   *aReadOnlyFront = null_t();
   aFrontUpdatedRegion->SetEmpty();
 }
 
 void
 ShadowThebesLayerOGL::DestroyFrontBuffer()
 {
   mBuffer = nsnull;
-  if (SurfaceDescriptor::T__None != mDeadweight.type()) {
-    mOGLManager->DestroySharedSurface(&mDeadweight, mAllocator);
-  }
 }
 
 void
 ShadowThebesLayerOGL::Destroy()
 {
   if (!mDestroyed) {
     mDestroyed = PR_TRUE;
     mBuffer = nsnull;
--- a/gfx/layers/opengl/ThebesLayerOGL.h
+++ b/gfx/layers/opengl/ThebesLayerOGL.h
@@ -90,17 +90,17 @@ private:
 class ShadowThebesLayerOGL : public ShadowThebesLayer,
                              public LayerOGL
 {
 public:
   ShadowThebesLayerOGL(LayerManagerOGL *aManager);
   virtual ~ShadowThebesLayerOGL();
 
   // ShadowThebesLayer impl
-  virtual void SetFrontBuffer(const ThebesBuffer& aNewFront,
+  virtual void SetFrontBuffer(const OptionalThebesBuffer& aNewFront,
                               const nsIntRegion& aValidRegion,
                               float aXResolution, float aYResolution);
   virtual void
   Swap(const ThebesBuffer& aNewFront, const nsIntRegion& aUpdatedRegion,
        ThebesBuffer* aNewBack, nsIntRegion* aNewBackValidRegion,
        float* aNewXResolution, float* aNewYResolution,
        OptionalThebesBuffer* aReadOnlyFront, nsIntRegion* aFrontUpdatedRegion);
   virtual void DestroyFrontBuffer();
@@ -109,20 +109,14 @@ public:
   void Destroy();
   Layer* GetLayer();
   virtual PRBool IsEmpty();
   virtual void RenderLayer(int aPreviousFrameBuffer,
                            const nsIntPoint& aOffset);
 
 private:
   nsRefPtr<ShadowBufferOGL> mBuffer;
-
-
-  // XXX FIXME TEMP: hold on to this so that we can free it in DestroyFrontBuffer()
-  SurfaceDescriptor mDeadweight;
-
-
 };
 #endif  // MOZ_IPC
 
 } /* layers */
 } /* mozilla */
 #endif /* GFX_THEBESLAYEROGL_H */
--- a/gfx/src/nsRect.h
+++ b/gfx/src/nsRect.h
@@ -287,18 +287,19 @@ struct NS_GFX nsIntRect {
   void MoveBy(const nsIntPoint& aPoint) {x += aPoint.x; y += aPoint.y;}
   void SizeTo(PRInt32 aWidth, PRInt32 aHeight) {width = aWidth; height = aHeight;}
   void SizeTo(const nsIntSize& aSize) {SizeTo(aSize.width, aSize.height);}
   void SizeBy(PRInt32 aDeltaWidth, PRInt32 aDeltaHeight) {width += aDeltaWidth;
                                                           height += aDeltaHeight;}
 
   PRBool Contains(const nsIntRect& aRect) const
   {
-    return (PRBool) ((aRect.x >= x) && (aRect.y >= y) &&
-                     (aRect.XMost() <= XMost()) && (aRect.YMost() <= YMost()));
+    return aRect.IsEmpty() ||
+        (PRBool) ((aRect.x >= x) && (aRect.y >= y) &&
+                  (aRect.XMost() <= XMost()) && (aRect.YMost() <= YMost()));
   }
   PRBool Contains(PRInt32 aX, PRInt32 aY) const
   {
     return (PRBool) ((aX >= x) && (aY >= y) &&
                      (aX < XMost()) && (aY < YMost()));
   }
   PRBool Contains(const nsIntPoint& aPoint) const { return Contains(aPoint.x, aPoint.y); }
 
--- a/gfx/src/nsRegion.cpp
+++ b/gfx/src/nsRegion.cpp
@@ -1334,22 +1334,32 @@ nsIntRegion nsRegion::ToOutsidePixels (n
   const nsRect* currentRect;
   while ((currentRect = rgnIter.Next())) {
     nsIntRect deviceRect = currentRect->ToOutsidePixels(aAppUnitsPerPixel);
     result.Or(result, deviceRect);
   }
   return result;
 }
 
-// This algorithm works in three phases:
+// A cell's "value" is a pair consisting of
+// a) the area of the subrectangle it corresponds to, if it's in
+// aContainingRect and in the region, 0 otherwise
+// b) the area of the subrectangle it corresponds to, if it's in the region,
+// 0 otherwise
+// Addition, subtraction and identity are defined on these values in the
+// obvious way. Partial order is lexicographic.
+// A "large negative value" is defined with large negative numbers for both
+// fields of the pair. This negative value has the property that adding any
+// number of non-negative values to it always results in a negative value.
+//
+// The GetLargestRectangle algorithm works in three phases:
 //  1) Convert the region into a grid by adding vertical/horizontal lines for
 //     each edge of each rectangle in the region.
 //  2) For each rectangle in the region, for each cell it contains, set that
-//     cells's value to the area of the subrectangle it corresponds to. Cells
-//     that are not contained by any rectangle have the value 0.
+//     cells's value as described above.
 //  3) Calculate the submatrix with the largest sum such that none of its cells
 //     contain any 0s (empty regions). The rectangle represented by the
 //     submatrix is the largest rectangle in the region.
 //
 // Let k be the number of rectangles in the region.
 // Let m be the height of the grid generated in step 1.
 // Let n be the width of the grid generated in step 1.
 //
@@ -1365,17 +1375,17 @@ nsIntRegion nsRegion::ToOutsidePixels (n
 // Phase3 = function (G, A, m, n) {
 //   let (t,b,l,r,_) = MaxSum2D(A,m,n)
 //   return rect(G[t],G[l],G[r],G[b]);
 // }
 // MaxSum2D = function (A, m, n) {
 //   S = array(m+1,n+1)
 //   S[0][i] = 0 for i in [0,n]
 //   S[j][0] = 0 for j in [0,m]
-//   S[j][i] = (if A[j-1][i-1] = 0 then some large negative number else A[j-1][i-1])
+//   S[j][i] = (if A[j-1][i-1] = 0 then some large negative value else A[j-1][i-1])
 //           + S[j-1][n] + S[j][i-1] - S[j-1][i-1]
 //
 //   // top, bottom, left, right, area
 //   var maxRect = (-1, -1, -1, -1, 0);
 //
 //   for all (m',m'') in [0, m]^2 {
 //     let B = { S[m'][i] - S[m''][i] | 0 <= i <= n }
 //     let ((l,r),area) = MaxSum1D(B,n+1)
@@ -1447,130 +1457,177 @@ namespace {
     PRInt32 GetNumStops() const { return mStops.Length(); }
 
   private:
     nsTArray<nscoord> mStops;
   };
 
   const PRInt64 kVeryLargeNegativeNumber = 0xffff000000000000ll;
 
+  struct SizePair {
+    PRInt64 mSizeContainingRect;
+    PRInt64 mSize;
+
+    SizePair() : mSizeContainingRect(0), mSize(0) {}
+
+    static SizePair VeryLargeNegative() {
+      SizePair result;
+      result.mSize = result.mSizeContainingRect = kVeryLargeNegativeNumber;
+      return result;
+    }
+    SizePair& operator=(const SizePair& aOther) {
+      mSizeContainingRect = aOther.mSizeContainingRect;
+      mSize = aOther.mSize;
+      return *this;
+    }
+    PRBool operator<(const SizePair& aOther) const {
+      if (mSizeContainingRect < aOther.mSizeContainingRect)
+        return PR_TRUE;
+      if (mSizeContainingRect > aOther.mSizeContainingRect)
+        return PR_FALSE;
+      return mSize < aOther.mSize;
+    }
+    PRBool operator>(const SizePair& aOther) const {
+      return aOther.operator<(*this);
+    }
+    SizePair operator+(const SizePair& aOther) const {
+      SizePair result = *this;
+      result.mSizeContainingRect += aOther.mSizeContainingRect;
+      result.mSize += aOther.mSize;
+      return result;
+    }
+    SizePair operator-(const SizePair& aOther) const {
+      SizePair result = *this;
+      result.mSizeContainingRect -= aOther.mSizeContainingRect;
+      result.mSize -= aOther.mSize;
+      return result;
+    }
+  };
+
   // Returns the sum and indices of the subarray with the maximum sum of the
   // given array (A,n), assuming the array is already in prefix sum form.
-  PRInt64 MaxSum1D(const nsTArray<PRInt64> &A, PRInt32 n,
-                   PRInt32 *minIdx, PRInt32 *maxIdx) {
+  SizePair MaxSum1D(const nsTArray<SizePair> &A, PRInt32 n,
+                    PRInt32 *minIdx, PRInt32 *maxIdx) {
     // The min/max indicies of the largest subarray found so far
-    PRInt64 min = 0,
-            max = 0;
+    SizePair min, max;
     PRInt32 currentMinIdx = 0;
 
     *minIdx = 0;
     *maxIdx = 0;
 
     // Because we're given the array in prefix sum form, we know the first
     // element is 0
     for(PRInt32 i = 1; i < n; i++) {
-      PRInt64 cand = A[i] - min;
+      SizePair cand = A[i] - min;
       if (cand > max) {
         max = cand;
         *minIdx = currentMinIdx;
         *maxIdx = i;
       }
       if (min > A[i]) {
         min = A[i];
         currentMinIdx = i;
       }
     }
 
     return max;
   }
 }
 
-nsRect nsRegion::GetLargestRectangle () const {
+nsRect nsRegion::GetLargestRectangle (const nsRect& aContainingRect) const {
   nsRect bestRect;
 
-  if (!mRectCount)
+  if (mRectCount <= 1) {
+    bestRect = mBoundRect;
     return bestRect;
+  }
 
   AxisPartition xaxis, yaxis;
 
   // Step 1: Calculate the grid lines
   nsRegionRectIterator iter(*this);
   const nsRect *currentRect;
   while ((currentRect = iter.Next())) {
     xaxis.InsertCoord(currentRect->x);
     xaxis.InsertCoord(currentRect->XMost());
     yaxis.InsertCoord(currentRect->y);
     yaxis.InsertCoord(currentRect->YMost());
   }
+  if (!aContainingRect.IsEmpty()) {
+    xaxis.InsertCoord(aContainingRect.x);
+    xaxis.InsertCoord(aContainingRect.XMost());
+    yaxis.InsertCoord(aContainingRect.y);
+    yaxis.InsertCoord(aContainingRect.YMost());
+  }
 
   // Step 2: Fill out the grid with the areas
   // Note: due to the ordering of rectangles in the region, it is not always
   // possible to combine steps 2 and 3 so we don't try to be clever.
   PRInt32 matrixHeight = yaxis.GetNumStops() - 1;
   PRInt32 matrixWidth = xaxis.GetNumStops() - 1;
   PRInt32 matrixSize = matrixHeight * matrixWidth;
-  nsTArray<PRInt64> areas(matrixSize);
+  nsTArray<SizePair> areas(matrixSize);
   areas.SetLength(matrixSize);
-  memset(areas.Elements(), 0, matrixSize * sizeof(PRInt64));
 
   iter.Reset();
   while ((currentRect = iter.Next())) {
     PRInt32 xstart = xaxis.IndexOf(currentRect->x);
     PRInt32 xend = xaxis.IndexOf(currentRect->XMost());
     PRInt32 y = yaxis.IndexOf(currentRect->y);
     PRInt32 yend = yaxis.IndexOf(currentRect->YMost());
 
     for (; y < yend; y++) {
       nscoord height = yaxis.StopSize(y);
       for (PRInt32 x = xstart; x < xend; x++) {
         nscoord width = xaxis.StopSize(x);
-        areas[y*matrixWidth+x] = width*PRInt64(height);
+        PRInt64 size = width*PRInt64(height);
+        if (currentRect->Intersects(aContainingRect)) {
+          areas[y*matrixWidth+x].mSizeContainingRect = size;
+        }
+        areas[y*matrixWidth+x].mSize = size;
       }
     }
   }
 
   // Step 3: Find the maximum submatrix sum that does not contain a rectangle
   {
     // First get the prefix sum array
     PRInt32 m = matrixHeight + 1;
     PRInt32 n = matrixWidth + 1;
-    nsTArray<PRInt64> pareas(m*n);
+    nsTArray<SizePair> pareas(m*n);
     pareas.SetLength(m*n);
-    // Zero out the first row
-    for (PRInt32 x = 0; x < n; x++)
-      pareas[x] = 0;
     for (PRInt32 y = 1; y < m; y++) {
-      // Zero out the left column
-      pareas[y*n] = 0;
       for (PRInt32 x = 1; x < n; x++) {
-        PRInt64 area = areas[(y-1)*matrixWidth+x-1];
-        if (!area)
-          area = kVeryLargeNegativeNumber;
-        area += pareas[    y*n+x-1]
-              + pareas[(y-1)*n+x  ]
-              - pareas[(y-1)*n+x-1];
+        SizePair area = areas[(y-1)*matrixWidth+x-1];
+        if (!area.mSize) {
+          area = SizePair::VeryLargeNegative();
+        }
+        area = area + pareas[    y*n+x-1]
+                    + pareas[(y-1)*n+x  ]
+                    - pareas[(y-1)*n+x-1];
         pareas[y*n+x] = area;
       }
     }
 
     // No longer need the grid
     areas.SetLength(0);
 
-    PRInt64 bestArea = 0;
+    SizePair bestArea;
     struct {
       PRInt32 left, top, right, bottom;
     } bestRectIndices = { 0, 0, 0, 0 };
     for (PRInt32 m1 = 0; m1 < m; m1++) {
       for (PRInt32 m2 = m1+1; m2 < m; m2++) {
-        nsTArray<PRInt64> B;
+        nsTArray<SizePair> B;
         B.SetLength(n);
-        for (PRInt32 i = 0; i < n; i++)
+        for (PRInt32 i = 0; i < n; i++) {
           B[i] = pareas[m2*n+i] - pareas[m1*n+i];
+        }
         PRInt32 minIdx, maxIdx;
-        PRInt64 area = MaxSum1D(B, n, &minIdx, &maxIdx);
+        SizePair area = MaxSum1D(B, n, &minIdx, &maxIdx);
         if (area > bestArea) {
           bestRectIndices.left = minIdx;
           bestRectIndices.top = m1;
           bestRectIndices.right = maxIdx;
           bestRectIndices.bottom = m2;
           bestArea = area;
         }
       }
@@ -1587,16 +1644,42 @@ nsRect nsRegion::GetLargestRectangle () 
 
 void nsRegion::SimplifyOutward (PRUint32 aMaxRects)
 {
   NS_ASSERTION(aMaxRects >= 1, "Invalid max rect count");
   
   if (mRectCount <= aMaxRects)
     return;
 
+  // Try combining rects in horizontal bands into a single rect
+  RgnRect* pRect = mRectListHead.next;
+  while (pRect != &mRectListHead)
+  {
+    // Combine with the following rectangle if they have the same YMost
+    // or if they overlap vertically. This ensures that all overlapping
+    // rectangles are merged, preserving the invariant that rectangles
+    // don't overlap.
+    // The goal here is to try to keep groups of rectangles that are vertically
+    // discontiguous as separate rectangles in the final region. This is
+    // simple and fast to implement and page contents tend to vary more
+    // vertically than horizontally (which is why our rectangles are stored
+    // sorted by y-coordinate, too).
+    while (pRect->next != &mRectListHead &&
+           pRect->YMost () >= pRect->next->y)
+    {
+      pRect->UnionRect(*pRect, *pRect->next);
+      delete Remove (pRect->next);
+    }
+
+    pRect = pRect->next;
+  }
+
+  if (mRectCount <= aMaxRects)
+    return;
+
   *this = GetBounds();
 }
 
 void nsRegion::SimplifyInward (PRUint32 aMaxRects)
 {
   NS_ASSERTION(aMaxRects >= 1, "Invalid max rect count");
 
   if (mRectCount <= aMaxRects)
--- a/gfx/src/nsRegion.h
+++ b/gfx/src/nsRegion.h
@@ -179,17 +179,23 @@ public:
   PRUint32 GetNumRects () const { return mRectCount; }
   const nsRect& GetBounds () const { return mBoundRect; }
   // Converts this region from aFromAPP, an appunits per pixel ratio, to
   // aToAPP. This applies nsRect::ConvertAppUnitsRoundOut/In to each rect of
   // the region.
   nsRegion ConvertAppUnitsRoundOut (PRInt32 aFromAPP, PRInt32 aToAPP) const;
   nsRegion ConvertAppUnitsRoundIn (PRInt32 aFromAPP, PRInt32 aToAPP) const;
   nsIntRegion ToOutsidePixels (nscoord aAppUnitsPerPixel) const;
-  nsRect GetLargestRectangle () const;
+  /**
+   * Gets the largest rectangle contained in the region.
+   * @param aContainingRect if non-empty, we choose a rectangle that
+   * maximizes the area intersecting with aContainingRect (and break ties by
+   * then choosing the largest rectangle overall)
+   */
+  nsRect GetLargestRectangle (const nsRect& aContainingRect = nsRect()) const;
 
   /**
    * Make sure the region has at most aMaxRects by adding area to it
    * if necessary. The simplified region will be a superset of the
    * original region. The simplified region's bounding box will be
    * the same as for the current region.
    */
   void SimplifyOutward (PRUint32 aMaxRects);
@@ -416,17 +422,20 @@ public:
   PRBool IsComplex () const { return mImpl.IsComplex (); }
   PRBool IsEqual (const nsIntRegion& aRegion) const
   {
     return mImpl.IsEqual (aRegion.mImpl);
   }
   PRUint32 GetNumRects () const { return mImpl.GetNumRects (); }
   nsIntRect GetBounds () const { return FromRect (mImpl.GetBounds ()); }
   nsRegion ToAppUnits (nscoord aAppUnitsPerPixel) const;
-  nsIntRect GetLargestRectangle () const { return FromRect (mImpl.GetLargestRectangle()); }
+  nsIntRect GetLargestRectangle (const nsIntRect& aContainingRect = nsIntRect()) const
+  {
+    return FromRect (mImpl.GetLargestRectangle( ToRect(aContainingRect) ));
+  }
 
   /**
    * Make sure the region has at most aMaxRects by adding area to it
    * if necessary. The simplified region will be a superset of the
    * original region. The simplified region's bounding box will be
    * the same as for the current region.
    */
   void SimplifyOutward (PRUint32 aMaxRects)
--- a/gfx/tests/TestRegion.cpp
+++ b/gfx/tests/TestRegion.cpp
@@ -126,28 +126,50 @@ class TestLargestRegion {
       nsRect largest = r2.GetLargestRectangle();
       if (largest.width * largest.height != tests[i].expectedArea) {
         fail("Did not succesfully find largest rectangle in two-rect-subtract region on iteration %d", i);
         success = PR_FALSE;
       }
     }
     return success;
   }
+  static PRBool TestContainsSpecifiedRect() {
+    nsRegion r(nsRect(0, 0, 100, 100));
+    r.Or(r, nsRect(0, 300, 50, 50));
+    if (r.GetLargestRectangle(nsRect(0, 300, 10, 10)) != nsRect(0, 300, 50, 50)) {
+      fail("Chose wrong rectangle");
+      return PR_FALSE;
+    }
+    return PR_TRUE;
+  }
+  static PRBool TestContainsSpecifiedOverflowingRect() {
+    nsRegion r(nsRect(0, 0, 100, 100));
+    r.Or(r, nsRect(0, 300, 50, 50));
+    if (r.GetLargestRectangle(nsRect(0, 290, 10, 20)) != nsRect(0, 300, 50, 50)) {
+      fail("Chose wrong rectangle");
+      return PR_FALSE;
+    }
+    return PR_TRUE;
+  }
 public:
   static PRBool Test() {
     if (!TestSingleRect(nsRect(0, 52, 720, 480)) ||
         !TestSingleRect(nsRect(-20, 40, 50, 20)) ||
         !TestSingleRect(nsRect(-20, 40, 10, 8)) ||
         !TestSingleRect(nsRect(-20, -40, 10, 8)) ||
         !TestSingleRect(nsRect(-10, -10, 20, 20)))
       return PR_FALSE;
     if (!TestNonRectangular())
       return PR_FALSE;
     if (!TwoRectTest())
       return PR_FALSE;
+    if (!TestContainsSpecifiedRect())
+      return PR_FALSE;
+    if (!TestContainsSpecifiedOverflowingRect())
+      return PR_FALSE;
     passed("TestLargestRegion");
     return PR_TRUE;
   }
 };
 
 int main(int argc, char** argv) {
   ScopedXPCOM xpcom("TestRegion");
   if (xpcom.failed())
--- a/gfx/thebes/GLContextProviderCGL.mm
+++ b/gfx/thebes/GLContextProviderCGL.mm
@@ -293,24 +293,26 @@ class TextureImageCGL : public BasicText
                                           const nsIntSize&,
                                           GLenum,
                                           TextureImage::ContentType,
                                           GLContext*);
 public:
     ~TextureImageCGL()
     {
         if (mPixelBuffer) {
+            mGLContext->MakeCurrent();
             mGLContext->fDeleteBuffers(1, &mPixelBuffer);
         }
     }
 
 protected:
     already_AddRefed<gfxASurface>
     GetSurfaceForUpdate(const gfxIntSize& aSize, ImageFormat aFmt)
     {
+        mGLContext->MakeCurrent();
         if (!mGLContext->IsExtensionSupported(GLContext::ARB_pixel_buffer_object)) {
             return gfxPlatform::GetPlatform()->
                 CreateOffscreenSurface(aSize, gfxASurface::ContentFromFormat(aFmt));
         }
 
         if (!mPixelBuffer) {
             mGLContext->fGenBuffers(1, &mPixelBuffer);
         }
@@ -334,25 +336,27 @@ protected:
                                  aSize.width * 4, aFmt);
 
         return surf.forget();
     }
   
     bool FinishedSurfaceUpdate()
     {
         if (mPixelBuffer) {
+            mGLContext->MakeCurrent();
             mGLContext->fUnmapBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER);
             return true;
         }
         return false;
     }
 
     void FinishedSurfaceUpload()
     {
         if (mPixelBuffer) {
+            mGLContext->MakeCurrent();
             mGLContext->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, 0);
         }
     }
 
 private:
     TextureImageCGL(GLuint aTexture,
                     const nsIntSize& aSize,
                     GLenum aWrapMode,
--- a/gfx/thebes/Makefile.in
+++ b/gfx/thebes/Makefile.in
@@ -27,19 +27,21 @@ EXPORTS	= \
 	gfxImageSurface.h \
 	gfxMatrix.h \
 	gfxPath.h \
 	gfxPattern.h \
 	gfxPlatform.h \
 	gfxPoint.h \
 	gfxRect.h \
 	gfxSkipChars.h \
+	gfxTeeSurface.h \
 	gfxTypes.h \
 	gfxTextRunCache.h \
 	gfxTextRunWordCache.h \
+	gfxUnicodeProperties.h \
 	gfxUtils.h \
 	gfxUserFontSet.h \
 	GLDefs.h \
 	GLContext.h \
 	GLContextSymbols.h \
 	GLContextProvider.h \
 	GLContextProviderImpl.h \
 	nsCoreAnimationSupport.h \
@@ -176,16 +178,17 @@ CPPSRCS	= \
 	gfxAtoms.cpp \
 	gfxMatrix.cpp \
 	gfxPath.cpp \
 	gfxPattern.cpp \
 	gfxPlatform.cpp \
 	gfxPlatformFontList.cpp \
 	gfxRect.cpp \
 	gfxSkipChars.cpp \
+	gfxTeeSurface.cpp \
 	gfxTextRunCache.cpp \
 	gfxTextRunWordCache.cpp \
 	gfxUserFontSet.cpp \
 	gfxUtils.cpp \
 	gfxUnicodeProperties.cpp \
 	gfxScriptItemizer.cpp \
 	gfxHarfBuzzShaper.cpp \
 	GLContext.cpp \
--- a/gfx/thebes/gfxASurface.cpp
+++ b/gfx/thebes/gfxASurface.cpp
@@ -216,16 +216,19 @@ gfxASurface::Init(cairo_surface_t* surfa
 
     mSurface = surface;
     mSurfaceValid = PR_TRUE;
 
     if (existingSurface) {
         mFloatingRefs = 0;
     } else {
         mFloatingRefs = 1;
+        if (cairo_surface_get_content(surface) != CAIRO_CONTENT_COLOR) {
+            cairo_surface_set_subpixel_antialiasing(surface, CAIRO_SUBPIXEL_ANTIALIASING_DISABLED);
+        }
     }
 }
 
 gfxASurface::gfxSurfaceType
 gfxASurface::GetType() const
 {
     if (!mSurfaceValid)
         return (gfxSurfaceType)-1;
@@ -424,16 +427,33 @@ gfxASurface::FormatFromContent(gfxASurfa
         case CONTENT_ALPHA:
             return ImageFormatA8;
         case CONTENT_COLOR:
         default:
             return ImageFormatRGB24;
     }
 }
 
+void
+gfxASurface::SetSubpixelAntialiasingEnabled(PRBool aEnabled)
+{
+    if (!mSurfaceValid)
+        return;
+    cairo_surface_set_subpixel_antialiasing(mSurface,
+        aEnabled ? CAIRO_SUBPIXEL_ANTIALIASING_ENABLED : CAIRO_SUBPIXEL_ANTIALIASING_DISABLED);
+}
+
+PRBool
+gfxASurface::GetSubpixelAntialiasingEnabled()
+{
+    if (!mSurfaceValid)
+      return PR_FALSE;
+    return cairo_surface_get_subpixel_antialiasing(mSurface) == CAIRO_SUBPIXEL_ANTIALIASING_ENABLED;
+}
+
 PRInt32
 gfxASurface::BytePerPixelFromFormat(gfxImageFormat format)
 {
     switch (format) {
         case ImageFormatARGB32:
         case ImageFormatRGB24:
             return 4;
         case ImageFormatRGB16_565:
--- a/gfx/thebes/gfxASurface.h
+++ b/gfx/thebes/gfxASurface.h
@@ -172,16 +172,19 @@ public:
      * hints to the context about any special rendering considerations.  See
      * gfxContext::SetFlag for documentation.
      */
     virtual PRInt32 GetDefaultContextFlags() const { return 0; }
 
     static gfxContentType ContentFromFormat(gfxImageFormat format);
     static gfxImageFormat FormatFromContent(gfxContentType format);
 
+    void SetSubpixelAntialiasingEnabled(PRBool aEnabled);
+    PRBool GetSubpixelAntialiasingEnabled();
+
     /**
      * Record number of bytes for given surface type.  Use positive bytes
      * for allocations and negative bytes for deallocations.
      */
     static void RecordMemoryUsedForSurfaceType(gfxASurface::gfxSurfaceType aType,
                                                PRInt32 aBytes);
 
     /**
@@ -194,46 +197,76 @@ public:
     void RecordMemoryFreed();
 
     PRInt32 KnownMemoryUsed() { return mBytesRecorded; }
 
     static PRInt32 BytePerPixelFromFormat(gfxImageFormat format);
 
     virtual const gfxIntSize GetSize() const { return gfxIntSize(-1, -1); }
 
+    void SetOpaqueRect(const gfxRect& aRect) {
+        if (aRect.IsEmpty()) {
+            mOpaqueRect = nsnull;
+        } else if (mOpaqueRect) {
+            *mOpaqueRect = aRect;
+        } else {
+            mOpaqueRect = new gfxRect(aRect);
+        }
+    }
+    const gfxRect& GetOpaqueRect() {
+        if (mOpaqueRect)
+            return *mOpaqueRect;
+        static const gfxRect empty(0, 0, 0, 0);
+        return empty;
+    }
+
     virtual PRBool SupportsSelfCopy() { return PR_TRUE; }
 
+    /**
+     * Mark the surface as being allowed/not allowed to be used as a source.
+     * This currently has no effect other than triggering assertions in some
+     * cases.
+     */
+    void SetAllowUseAsSource(PRBool aAllow) { mAllowUseAsSource = aAllow; }
+    PRBool GetAllowUseAsSource() { return mAllowUseAsSource; }
+
 protected:
-    gfxASurface() : mSurface(nsnull), mFloatingRefs(0), mBytesRecorded(0), mSurfaceValid(PR_FALSE)
+    gfxASurface() : mSurface(nsnull), mFloatingRefs(0), mBytesRecorded(0),
+                    mSurfaceValid(PR_FALSE), mAllowUseAsSource(PR_TRUE)
     {
         MOZ_COUNT_CTOR(gfxASurface);
     }
 
     static gfxASurface* GetSurfaceWrapper(cairo_surface_t *csurf);
     static void SetSurfaceWrapper(cairo_surface_t *csurf, gfxASurface *asurf);
 
+    // NB: Init() *must* be called from within subclass's
+    // constructors.  It's unsafe to call it after the ctor finishes;
+    // leaks and use-after-frees are possible.
     void Init(cairo_surface_t *surface, PRBool existingSurface = PR_FALSE);
 
     virtual ~gfxASurface()
     {
         RecordMemoryFreed();
 
         MOZ_COUNT_DTOR(gfxASurface);
     }
 
     cairo_surface_t *mSurface;
+    nsAutoPtr<gfxRect> mOpaqueRect;
 
 private:
     static void SurfaceDestroyFunc(void *data);
 
     PRInt32 mFloatingRefs;
     PRInt32 mBytesRecorded;
 
 protected:
     PRPackedBool mSurfaceValid;
+    PRPackedBool mAllowUseAsSource;
 };
 
 /**
  * An Unknown surface; used to wrap unknown cairo_surface_t returns from cairo
  */
 class THEBES_API gfxUnknownSurface : public gfxASurface {
 public:
     gfxUnknownSurface(cairo_surface_t *surf) {
--- a/gfx/thebes/gfxContext.cpp
+++ b/gfx/thebes/gfxContext.cpp
@@ -49,17 +49,17 @@
 
 #include "gfxContext.h"
 
 #include "gfxColor.h"
 #include "gfxMatrix.h"
 #include "gfxASurface.h"
 #include "gfxPattern.h"
 #include "gfxPlatform.h"
-
+#include "gfxTeeSurface.h"
 
 gfxContext::gfxContext(gfxASurface *surface) :
     mSurface(surface)
 {
     MOZ_COUNT_CTOR(gfxContext);
 
     mCairo = cairo_create(surface->CairoSurface());
     mFlags = surface->GetDefaultContextFlags();
@@ -687,16 +687,17 @@ gfxContext::GetDeviceColor(gfxRGBA& c)
                                   &c.g,
                                   &c.b,
                                   &c.a) == CAIRO_STATUS_SUCCESS;
 }
 
 void
 gfxContext::SetSource(gfxASurface *surface, const gfxPoint& offset)
 {
+    NS_ASSERTION(surface->GetAllowUseAsSource(), "Surface not allowed to be used as source!");
     cairo_set_source_surface(mCairo, surface->CairoSurface(), offset.x, offset.y);
 }
 
 void
 gfxContext::SetPattern(gfxPattern *pattern)
 {
     cairo_set_source(mCairo, pattern->CairoPattern());
 }
@@ -741,16 +742,66 @@ gfxContext::Paint(gfxFloat alpha)
 // groups
 
 void
 gfxContext::PushGroup(gfxASurface::gfxContentType content)
 {
     cairo_push_group_with_content(mCairo, (cairo_content_t) content);
 }
 
+static gfxRect
+GetRoundOutDeviceClipExtents(gfxContext* aCtx)
+{
+    gfxContextMatrixAutoSaveRestore save(aCtx);
+    aCtx->IdentityMatrix();
+    gfxRect r = aCtx->GetClipExtents();
+    r.RoundOut();
+    return r;
+}
+
+static void
+CopySurface(gfxASurface* aSrc, gfxASurface* aDest)
+{
+  cairo_t *cr = cairo_create(aDest->CairoSurface());
+  cairo_set_source_surface(cr, aSrc->CairoSurface(), 0, 0);
+  cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+  cairo_paint(cr);
+  cairo_destroy(cr);
+}
+
+void
+gfxContext::PushGroupAndCopyBackground(gfxASurface::gfxContentType content)
+{
+    if (content == gfxASurface::CONTENT_COLOR_ALPHA) {
+        nsRefPtr<gfxASurface> s = CurrentSurface();
+        if (s->GetContentType() == gfxASurface::CONTENT_COLOR ||
+            s->GetOpaqueRect().Contains(GetRoundOutDeviceClipExtents(this))) {
+            cairo_push_group_with_content(mCairo, CAIRO_CONTENT_COLOR);
+            nsRefPtr<gfxASurface> d = CurrentSurface();
+
+            if (d->GetType() == gfxASurface::SurfaceTypeTee) {
+                NS_ASSERTION(s->GetType() == gfxASurface::SurfaceTypeTee, "Mismatched types");
+                nsAutoTArray<nsRefPtr<gfxASurface>,2> ss;
+                nsAutoTArray<nsRefPtr<gfxASurface>,2> ds;
+                static_cast<gfxTeeSurface*>(s.get())->GetSurfaces(&ss);
+                static_cast<gfxTeeSurface*>(d.get())->GetSurfaces(&ds);
+                NS_ASSERTION(ss.Length() == ds.Length(), "Mismatched lengths");
+                for (PRUint32 i = 0; i < ss.Length(); ++i) {
+                    CopySurface(ss[i], ds[i]);
+                }
+            } else {
+                CopySurface(s, d);
+            }
+            d->SetOpaqueRect(s->GetOpaqueRect());
+            return;
+        }
+    }
+    cairo_push_group_with_content(mCairo, (cairo_content_t) content);
+}
+
 already_AddRefed<gfxPattern>
 gfxContext::PopGroup()
 {
     cairo_pattern_t *pat = cairo_pop_group(mCairo);
     gfxPattern *wrapper = new gfxPattern(pat);
     cairo_pattern_destroy(pat);
     NS_IF_ADDREF(wrapper);
     return wrapper;
--- a/gfx/thebes/gfxContext.h
+++ b/gfx/thebes/gfxContext.h
@@ -590,16 +590,28 @@ public:
      * space.
      */
     gfxRect GetClipExtents();
 
     /**
      * Groups
      */
     void PushGroup(gfxASurface::gfxContentType content = gfxASurface::CONTENT_COLOR);
+    /**
+     * Like PushGroup, but if the current surface is CONTENT_COLOR and
+     * content is CONTENT_COLOR_ALPHA, makes the pushed surface CONTENT_COLOR
+     * instead and copies the contents of the current surface to the pushed
+     * surface. This is good for pushing opacity groups, since blending the
+     * group back to the current surface with some alpha applied will give
+     * the correct results and using an opaque pushed surface gives better
+     * quality and performance.
+     * This API really only makes sense if you do a PopGroupToSource and
+     * immediate Paint with OPERATOR_OVER.
+     */
+    void PushGroupAndCopyBackground(gfxASurface::gfxContentType content = gfxASurface::CONTENT_COLOR);
     already_AddRefed<gfxPattern> PopGroup();
     void PopGroupToSource();
 
     /**
      ** Hit Testing - check if given point is in the current path
      **/
     PRBool PointInFill(const gfxPoint& pt);
     PRBool PointInStroke(const gfxPoint& pt);
@@ -774,9 +786,32 @@ public:
         return mMatrix;
     }
 
 private:
     gfxContext *mContext;
     gfxMatrix   mMatrix;
 };
 
+
+class THEBES_API gfxContextAutoDisableSubpixelAntialiasing {
+public:
+    gfxContextAutoDisableSubpixelAntialiasing(gfxContext *aContext, PRBool aDisable)
+    {
+        if (aDisable) {
+            mSurface = aContext->CurrentSurface();
+            mSubpixelAntialiasingEnabled = mSurface->GetSubpixelAntialiasingEnabled();
+            mSurface->SetSubpixelAntialiasingEnabled(PR_FALSE);
+        }
+    }
+    ~gfxContextAutoDisableSubpixelAntialiasing()
+    {
+        if (mSurface) {
+            mSurface->SetSubpixelAntialiasingEnabled(mSubpixelAntialiasingEnabled);
+        }
+    }
+
+private:
+    nsRefPtr<gfxASurface> mSurface;
+    PRPackedBool mSubpixelAntialiasingEnabled;
+};
+
 #endif /* GFX_CONTEXT_H */
--- a/gfx/thebes/gfxDWriteFonts.cpp
+++ b/gfx/thebes/gfxDWriteFonts.cpp
@@ -63,27 +63,73 @@ GetCairoAntialiasOption(gfxFont::Antiali
         return CAIRO_ANTIALIAS_NONE;
     case gfxFont::kAntialiasGrayscale:
         return CAIRO_ANTIALIAS_GRAY;
     case gfxFont::kAntialiasSubpixel:
         return CAIRO_ANTIALIAS_SUBPIXEL;
     }
 }
 
+// Code to determine whether Windows is set to use ClearType font smoothing;
+// based on private functions in cairo-win32-font.c
+
+#ifndef SPI_GETFONTSMOOTHINGTYPE
+#define SPI_GETFONTSMOOTHINGTYPE 0x200a
+#endif
+#ifndef FE_FONTSMOOTHINGCLEARTYPE
+#define FE_FONTSMOOTHINGCLEARTYPE 2
+#endif
+
+static bool
+HasClearType()
+{
+    OSVERSIONINFO versionInfo;
+    versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+
+    return (GetVersionEx(&versionInfo) &&
+            (versionInfo.dwMajorVersion > 5 ||
+             (versionInfo.dwMajorVersion == 5 &&
+              versionInfo.dwMinorVersion >= 1))); // XP or newer
+}
+
+static bool
+UsingClearType()
+{
+    BOOL fontSmoothing;
+    if (!SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &fontSmoothing, 0) ||
+        !fontSmoothing)
+    {
+        return false;    
+    }
+
+    if (!HasClearType()) {
+        return false;
+    }
+
+    UINT type;
+    if (SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &type, 0) &&
+        type == FE_FONTSMOOTHINGCLEARTYPE)
+    {
+        return true;
+    }
+    return false;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // gfxDWriteFont
 gfxDWriteFont::gfxDWriteFont(gfxFontEntry *aFontEntry,
                              const gfxFontStyle *aFontStyle,
                              PRBool aNeedsBold,
                              AntialiasOption anAAOption)
     : gfxFont(aFontEntry, aFontStyle, anAAOption)
     , mCairoFontFace(nsnull)
     , mCairoScaledFont(nsnull)
     , mNeedsOblique(PR_FALSE)
     , mNeedsBold(aNeedsBold)
+    , mUsingClearType(PR_FALSE)
 {
     gfxDWriteFontEntry *fe =
         static_cast<gfxDWriteFontEntry*>(aFontEntry);
     nsresult rv;
     DWRITE_FONT_SIMULATIONS sims = DWRITE_FONT_SIMULATIONS_NONE;
     if ((GetStyle()->style & (FONT_STYLE_ITALIC | FONT_STYLE_OBLIQUE)) &&
         !fe->IsItalic()) {
             // For this we always use the font_matrix for uniformity. Not the
@@ -96,16 +142,22 @@ gfxDWriteFont::gfxDWriteFont(gfxFontEntr
 
     rv = fe->CreateFontFace(getter_AddRefs(mFontFace), sims);
 
     if (NS_FAILED(rv)) {
         mIsValid = PR_FALSE;
         return;
     }
 
+    if ((anAAOption == gfxFont::kAntialiasDefault && UsingClearType()) ||
+        anAAOption == gfxFont::kAntialiasSubpixel)
+    {
+        mUsingClearType = PR_TRUE;
+    }
+
     ComputeMetrics();
 
     if (FontCanSupportHarfBuzz()) {
         mHarfBuzzShaper = new gfxHarfBuzzShaper(this);
     }
 }
 
 gfxDWriteFont::~gfxDWriteFont()
@@ -422,8 +474,34 @@ gfxDWriteFont::GetFontTable(PRUint32 aTa
         hb_blob_t *blob;
         if (mFontEntry->GetExistingFontTable(aTag, &blob)) {
             return blob;
         }
     }
 
     return nsnull;
 }
+
+PRInt32
+gfxDWriteFont::GetHintedGlyphWidth(gfxContext *aCtx, PRUint16 aGID)
+{
+    if (!mGlyphWidths.IsInitialized()) {
+        mGlyphWidths.Init(200);
+    }
+
+    PRInt32 width;
+    if (mGlyphWidths.Get(aGID, &width)) {
+        return width;
+    }
+
+    DWRITE_GLYPH_METRICS glyphMetrics;
+    HRESULT hr = mFontFace->GetGdiCompatibleGlyphMetrics(
+                  GetAdjustedSize(), 1.0f, nsnull, TRUE,
+                  &aGID, 1, &glyphMetrics, FALSE);
+
+    if (NS_SUCCEEDED(hr)) {
+        width = NS_lround(glyphMetrics.advanceWidth * mFUnitsConvFactor) << 16;
+        mGlyphWidths.Put(aGID, width);
+        return width;
+    }
+
+    return -1;
+}
--- a/gfx/thebes/gfxDWriteFonts.h
+++ b/gfx/thebes/gfxDWriteFonts.h
@@ -74,29 +74,40 @@ public:
     gfxFloat GetAdjustedSize() const { return mAdjustedSize; }
 
     IDWriteFontFace *GetFontFace() { return mFontFace.get(); }
 
     // override gfxFont table access function to bypass gfxFontEntry cache,
     // use DWrite API to get direct access to system font data
     virtual hb_blob_t *GetFontTable(PRUint32 aTag);
 
+    virtual PRBool ProvidesHintedWidths() const {
+        return !mUsingClearType;
+    }
+
+    virtual PRInt32 GetHintedGlyphWidth(gfxContext *aCtx, PRUint16 aGID);
+
 protected:
     virtual void CreatePlatformShaper();
 
     void ComputeMetrics();
 
     cairo_font_face_t *CairoFontFace();
 
     cairo_scaled_font_t *CairoScaledFont();
 
     static void DestroyBlobFunc(void* userArg);
 
     nsRefPtr<IDWriteFontFace> mFontFace;
     cairo_font_face_t *mCairoFontFace;
     cairo_scaled_font_t *mCairoScaledFont;
 
     gfxFont::Metrics mMetrics;
-    PRBool mNeedsOblique;
-    PRBool mNeedsBold;
+
+    // cache of glyph widths in 16.16 fixed-point pixels
+    nsDataHashtable<nsUint32HashKey,PRInt32>    mGlyphWidths;
+
+    PRPackedBool mNeedsOblique;
+    PRPackedBool mNeedsBold;
+    PRPackedBool mUsingClearType;
 };
 
 #endif
--- a/gfx/thebes/gfxDWriteShaper.cpp
+++ b/gfx/thebes/gfxDWriteShaper.cpp
@@ -170,34 +170,59 @@ trymoreglyphs:
         WORD gID = indices[0];
         nsAutoTArray<FLOAT, 400> advances;
         nsAutoTArray<DWRITE_GLYPH_OFFSET, 400> glyphOffsets;
         if (!advances.SetLength(actualGlyphs) || 
             !glyphOffsets.SetLength(actualGlyphs)) {
             continue;
         }
 
-        hr = analyzer->GetGlyphPlacements(aString + range.start, 
-                                          clusters.Elements(),
-                                          textProperties.Elements(),
-                                          range.Length(),
-                                          indices.Elements(),
-                                          glyphProperties.Elements(),
-                                          actualGlyphs,
-                                          font->GetFontFace(),
-                                          font->GetAdjustedSize(),
-                                          FALSE,
-                                          FALSE,
-                                          &runHead->mScript,
-                                          NULL,
-                                          NULL,
-                                          NULL,
-                                          0,
-                                          advances.Elements(),
-                                          glyphOffsets.Elements());
+        if (mFont->ProvidesHintedWidths()) {
+            hr = analyzer->GetGdiCompatibleGlyphPlacements(
+                                              aString + range.start,
+                                              clusters.Elements(),
+                                              textProperties.Elements(),
+                                              range.Length(),
+                                              indices.Elements(),
+                                              glyphProperties.Elements(),
+                                              actualGlyphs,
+                                              font->GetFontFace(),
+                                              font->GetAdjustedSize(),
+                                              1.0,
+                                              nsnull,
+                                              TRUE,
+                                              FALSE,
+                                              FALSE,
+                                              &runHead->mScript,
+                                              NULL,
+                                              NULL,
+                                              NULL,
+                                              0,
+                                              advances.Elements(),
+                                              glyphOffsets.Elements());
+        } else {
+            hr = analyzer->GetGlyphPlacements(aString + range.start,
+                                              clusters.Elements(),
+                                              textProperties.Elements(),
+                                              range.Length(),
+                                              indices.Elements(),
+                                              glyphProperties.Elements(),
+                                              actualGlyphs,
+                                              font->GetFontFace(),
+                                              font->GetAdjustedSize(),
+                                              FALSE,
+                                              FALSE,
+                                              &runHead->mScript,
+                                              NULL,
+                                              NULL,
+                                              NULL,
+                                              0,
+                                              advances.Elements(),
+                                              glyphOffsets.Elements());
+        }
         if (FAILED(hr)) {
             NS_WARNING("Analyzer failed to get glyph placements.");
             result = PR_FALSE;
             break;
         }
 
         nsAutoTArray<gfxTextRun::DetailedGlyph,1> detailedGlyphs;
 
--- a/gfx/thebes/gfxImageSurface.cpp
+++ b/gfx/thebes/gfxImageSurface.cpp
@@ -144,34 +144,34 @@ gfxImageSurface::gfxImageSurface(cairo_s
 }
 
 gfxImageSurface::~gfxImageSurface()
 {
     if (mOwnsData)
         free(mData);
 }
 
-long
-gfxImageSurface::ComputeStride() const
+/*static*/ long
+gfxImageSurface::ComputeStride(const gfxIntSize& aSize, gfxImageFormat aFormat)
 {
     long stride;
 
-    if (mFormat == ImageFormatARGB32)
-        stride = mSize.width * 4;
-    else if (mFormat == ImageFormatRGB24)
-        stride = mSize.width * 4;
-    else if (mFormat == ImageFormatRGB16_565)
-        stride = mSize.width * 2;
-    else if (mFormat == ImageFormatA8)
-        stride = mSize.width;
-    else if (mFormat == ImageFormatA1) {
-        stride = (mSize.width + 7) / 8;
+    if (aFormat == ImageFormatARGB32)
+        stride = aSize.width * 4;
+    else if (aFormat == ImageFormatRGB24)
+        stride = aSize.width * 4;
+    else if (aFormat == ImageFormatRGB16_565)
+        stride = aSize.width * 2;
+    else if (aFormat == ImageFormatA8)
+        stride = aSize.width;
+    else if (aFormat == ImageFormatA1) {
+        stride = (aSize.width + 7) / 8;
     } else {
         NS_WARNING("Unknown format specified to gfxImageSurface!");
-        stride = mSize.width * 4;
+        stride = aSize.width * 4;
     }
 
     stride = ((stride + 3) / 4) * 4;
 
     return stride;
 }
 
 PRBool
--- a/gfx/thebes/gfxImageSurface.h
+++ b/gfx/thebes/gfxImageSurface.h
@@ -110,17 +110,19 @@ public:
 
     virtual PRBool SupportsSelfCopy() { return PR_FALSE; }
 
 protected:
     gfxImageSurface();
     void InitWithData(unsigned char *aData, const gfxIntSize& aSize,
                       long aStride, gfxImageFormat aFormat);
     void InitFromSurface(cairo_surface_t *csurf);
-    long ComputeStride() const;
+    long ComputeStride() const { return ComputeStride(mSize, mFormat); }
+
+    static long ComputeStride(const gfxIntSize&, gfxImageFormat);
 
     gfxIntSize mSize;
     PRBool mOwnsData;
     unsigned char *mData;
     gfxImageFormat mFormat;
     long mStride;
 };
 
--- a/gfx/thebes/gfxQtPlatform.cpp
+++ b/gfx/thebes/gfxQtPlatform.cpp
@@ -74,23 +74,17 @@
 #ifndef MOZ_PANGO
 #include <ft2build.h>
 #include FT_FREETYPE_H
 #endif
 
 #include "nsIPrefBranch.h"
 #include "nsIPrefService.h"
 
-// Because the QPainter backend has some problems with glyphs rendering
-// it is better to use image or xlib cairo backends by default
-#if (MOZ_PLATFORM_MAEMO == 6)
-#define DEFAULT_RENDER_MODE RENDER_BUFFERED
-#else
 #define DEFAULT_RENDER_MODE RENDER_DIRECT
-#endif
 
 static QPaintEngine::Type sDefaultQtPaintEngineType = QPaintEngine::X11;
 gfxFontconfigUtils *gfxQtPlatform::sFontconfigUtils = nsnull;
 static cairo_user_data_key_t cairo_qt_pixmap_key;
 static void do_qt_pixmap_unref (void *data)
 {
     QPixmap *pmap = (QPixmap*)data;
     delete pmap;
--- a/gfx/thebes/gfxSharedImageSurface.cpp
+++ b/gfx/thebes/gfxSharedImageSurface.cpp
@@ -37,91 +37,87 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "base/basictypes.h"
 #include "gfxSharedImageSurface.h"
 #include "cairo.h"
 
 #define MOZ_ALIGN_WORD(x) (((x) + 3) & ~3)
 
-using mozilla::ipc::SharedMemory;
+using namespace mozilla::ipc;
 
 static const cairo_user_data_key_t SHM_KEY = {0};
 
-typedef struct _SharedImageInfo
-{
+struct SharedImageInfo {
     PRInt32 width;
     PRInt32 height;
     PRInt32 format;
-} SharedImageInfo;
+};
 
 static SharedImageInfo*
-GetShmInfoPtr(const mozilla::ipc::Shmem &aShmem)
+GetShmInfoPtr(const Shmem& aShmem)
 {
     return reinterpret_cast<SharedImageInfo*>
         (aShmem.get<char>() + aShmem.Size<char>() - sizeof(SharedImageInfo));
 }
 
-size_t
-gfxSharedImageSurface::GetAlignedSize()
+gfxSharedImageSurface::~gfxSharedImageSurface()
 {
-   return MOZ_ALIGN_WORD(sizeof(SharedImageInfo) + mSize.height * mStride);
 }
 
-bool
-gfxSharedImageSurface::InitSurface(PRBool aUpdateShmemInfo)
+/*static*/ PRBool
+gfxSharedImageSurface::IsSharedImage(gfxASurface* aSurface)
 {
-    if (!CheckSurfaceSize(mSize))
-        return false;
+    return (aSurface
+            && aSurface->GetType() == gfxASurface::SurfaceTypeImage
+            && aSurface->GetData(&SHM_KEY));
+}
 
+gfxSharedImageSurface::gfxSharedImageSurface(const gfxIntSize& aSize,
+                                             gfxImageFormat aFormat,
+                                             const Shmem& aShmem)
+{
+    mSize = aSize;
+    mFormat = aFormat;
+    mStride = ComputeStride(aSize, aFormat);
+    mShmem = aShmem;
     cairo_surface_t *surface =
         cairo_image_surface_create_for_data(mShmem.get<unsigned char>(),
                                             (cairo_format_t)mFormat,
                                             mSize.width,
                                             mSize.height,
                                             mStride);
-
-    if (!surface)
-        return false;
-
-    cairo_surface_set_user_data(surface,
-                                &SHM_KEY,
-                                this, NULL);
-
-    if (aUpdateShmemInfo) {
-        SharedImageInfo *shmInfo = GetShmInfoPtr(mShmem);
-        shmInfo->width = mSize.width;
-        shmInfo->height = mSize.height;
-        shmInfo->format = mFormat;
+    if (surface) {
+        cairo_surface_set_user_data(surface, &SHM_KEY, this, NULL);
     }
-
-    InitFromSurface(surface);
-    return true;
+    Init(surface);
 }
 
-gfxSharedImageSurface::~gfxSharedImageSurface()
+void
+gfxSharedImageSurface::WriteShmemInfo()
 {
+    SharedImageInfo* shmInfo = GetShmInfoPtr(mShmem);
+    shmInfo->width = mSize.width;
+    shmInfo->height = mSize.height;
+    shmInfo->format = mFormat;
 }
 
-gfxSharedImageSurface::gfxSharedImageSurface()
+/*static*/ size_t
+gfxSharedImageSurface::GetAlignedSize(const gfxIntSize& aSize, long aStride)
 {
+   return MOZ_ALIGN_WORD(sizeof(SharedImageInfo) + aSize.height * aStride);
 }
 
-gfxSharedImageSurface::gfxSharedImageSurface(const mozilla::ipc::Shmem &aShmem)
+/*static*/ already_AddRefed<gfxSharedImageSurface>
+gfxSharedImageSurface::Open(const Shmem& aShmem)
 {
-    mShmem = aShmem;
-    SharedImageInfo *shmInfo = GetShmInfoPtr(aShmem);
-    mSize.width = shmInfo->width;
-    mSize.height = shmInfo->height;
-    mFormat = (gfxImageFormat)shmInfo->format;
-    mStride = ComputeStride();
+    SharedImageInfo* shmInfo = GetShmInfoPtr(aShmem);
+    gfxIntSize size(shmInfo->width, shmInfo->height);
+    if (!CheckSurfaceSize(size))
+        return nsnull;
 
-    if (!InitSurface(PR_FALSE))
-        NS_RUNTIMEABORT("Shared memory is bad");
+    nsRefPtr<gfxSharedImageSurface> s =
+        new gfxSharedImageSurface(size,
+                                  (gfxImageFormat)shmInfo->format,
+                                  aShmem);
+    // We didn't create this Shmem and so don't free it on errors
+    return (s->CairoStatus() != 0) ? nsnull : s.forget();
 }
-
-PRBool
-gfxSharedImageSurface::IsSharedImage(gfxASurface *aSurface)
-{
-    return (aSurface
-            && aSurface->GetType() == gfxASurface::SurfaceTypeImage
-            && aSurface->GetData(&SHM_KEY));
-}
--- a/gfx/thebes/gfxSharedImageSurface.h
+++ b/gfx/thebes/gfxSharedImageSurface.h
@@ -45,82 +45,98 @@
 #include "gfxASurface.h"
 #include "gfxImageSurface.h"
 
 class THEBES_API gfxSharedImageSurface : public gfxImageSurface {
     typedef mozilla::ipc::SharedMemory SharedMemory;
     typedef mozilla::ipc::Shmem Shmem;
 
 public:
-    /**
-     * Init must be called after ctor
-     */
-    gfxSharedImageSurface();
+    virtual ~gfxSharedImageSurface();
 
     /**
-     * Create shared image from external Shmem
-     * Shmem must be initialized by this class
-     */
-    gfxSharedImageSurface(const Shmem &aShmem);
-
-    ~gfxSharedImageSurface();
-
-    /**
-     * Initialize shared image surface
-     * @param aAllocator The pointer to protocol class which has AllocShmem method
-     * @param aSize The size of the buffer
-     * @param aFormat Format of the data
-     * @see gfxImageFormat
+     * Return a new gfxSharedImageSurface around a shmem segment newly
+     * allocated by this function.  |aAllocator| is the object used to
+     * allocate the new shmem segment.  Null is returned if creating
+     * the surface failed.
+     *
+     * NB: the *caller* is responsible for freeing the Shmem allocated
+     * by this function.
      */
     template<class ShmemAllocator>
-    bool Init(ShmemAllocator *aAllocator,
-              const gfxIntSize& aSize,
-              gfxImageFormat aFormat,
-              SharedMemory::SharedMemoryType aShmType = SharedMemory::TYPE_BASIC)
+    static already_AddRefed<gfxSharedImageSurface>
+    Create(ShmemAllocator* aAllocator,
+           const gfxIntSize& aSize,
+           gfxImageFormat aFormat,
+           SharedMemory::SharedMemoryType aShmType = SharedMemory::TYPE_BASIC)
     {
-        return Init<ShmemAllocator, false>(aAllocator, aSize, aFormat, aShmType);
+        return Create<ShmemAllocator, false>(aAllocator, aSize, aFormat, aShmType);
     }
 
+    /**
+     * Return a new gfxSharedImageSurface that wraps a shmem segment
+     * already created by the Create() above.  Bad things will happen
+     * if an attempt is made to wrap any other shmem segment.  Null is
+     * returned if creating the surface failed.
+     */
+    static already_AddRefed<gfxSharedImageSurface>
+    Open(const Shmem& aShmem);
+
     template<class ShmemAllocator>
-    bool InitUnsafe(ShmemAllocator *aAllocator,
-                    const gfxIntSize& aSize,
-                    gfxImageFormat aFormat,
-                    SharedMemory::SharedMemoryType aShmType = SharedMemory::TYPE_BASIC)
+    static already_AddRefed<gfxSharedImageSurface>
+    CreateUnsafe(ShmemAllocator* aAllocator,
+                 const gfxIntSize& aSize,
+                 gfxImageFormat aFormat,
+                 SharedMemory::SharedMemoryType aShmType = SharedMemory::TYPE_BASIC)
     {
-        return Init<ShmemAllocator, true>(aAllocator, aSize, aFormat, aShmType);
+        return Create<ShmemAllocator, true>(aAllocator, aSize, aFormat, aShmType);
     }
 
-    /* Gives Shmem data, which can be passed to IPDL interfaces */
     Shmem& GetShmem() { return mShmem; }
 
-    // This can be used for recognizing normal gfxImageSurface as SharedImage
     static PRBool IsSharedImage(gfxASurface *aSurface);
 
 private:
+    gfxSharedImageSurface(const gfxIntSize&, gfxImageFormat, const Shmem&);
+
+    void WriteShmemInfo();
+
+    static size_t GetAlignedSize(const gfxIntSize&, long aStride);
+
     template<class ShmemAllocator, bool Unsafe>
-    bool Init(ShmemAllocator *aAllocator,
-              const gfxIntSize& aSize,
-              gfxImageFormat aFormat,
-              SharedMemory::SharedMemoryType aShmType)
+    static already_AddRefed<gfxSharedImageSurface>
+    Create(ShmemAllocator* aAllocator,
+           const gfxIntSize& aSize,
+           gfxImageFormat aFormat,
+           SharedMemory::SharedMemoryType aShmType)
     {
-        mSize = aSize;
-        mFormat = aFormat;
-        mStride = ComputeStride();
+        if (!CheckSurfaceSize(aSize))
+            return nsnull;
+
+        Shmem shmem;
+        long stride = ComputeStride(aSize, aFormat);
+        size_t size = GetAlignedSize(aSize, stride);
         if (!Unsafe) {
-            if (!aAllocator->AllocShmem(GetAlignedSize(),
-                                        aShmType, &mShmem))
-                return false;
+            if (!aAllocator->AllocShmem(size, aShmType, &shmem))
+                return nsnull;
         } else {
-            if (!aAllocator->AllocUnsafeShmem(GetAlignedSize(),
-                                              aShmType, &mShmem))
-                return false;
+            if (!aAllocator->AllocUnsafeShmem(size, aShmType, &shmem))
+                return nsnull;
         }
 
-        return InitSurface(PR_TRUE);
+        nsRefPtr<gfxSharedImageSurface> s =
+            new gfxSharedImageSurface(aSize, aFormat, shmem);
+        if (s->CairoStatus() != 0) {
+            aAllocator->DeallocShmem(shmem);
+            return nsnull;
+        }
+        s->WriteShmemInfo();
+        return s.forget();
     }
 
-    size_t GetAlignedSize();
-    bool InitSurface(PRBool aUpdateShmemInfo);
+    Shmem mShmem;
 
-    Shmem mShmem;
+    // Calling these is very bad, disallow it
+    gfxSharedImageSurface(const gfxSharedImageSurface&);
+    gfxSharedImageSurface& operator=(const gfxSharedImageSurface&);
 };
 
 #endif /* GFX_SHARED_IMAGESURFACE_H */
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/gfxTeeSurface.cpp
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Thebes code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Robert O'Callahan <robert@ocallahan.org>
+ *
+ * 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
+ * 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 "gfxTeeSurface.h"
+
+#include "cairo.h"
+
+gfxTeeSurface::gfxTeeSurface(cairo_surface_t *csurf)
+{
+    Init(csurf, PR_TRUE);
+}
+
+gfxTeeSurface::gfxTeeSurface(gfxASurface **aSurfaces, PRInt32 aSurfaceCount)
+{
+    NS_ASSERTION(aSurfaceCount > 0, "Must have a least one surface");
+    cairo_surface_t *csurf = cairo_tee_surface_create(aSurfaces[0]->CairoSurface());
+    Init(csurf, PR_FALSE);
+
+    for (PRInt32 i = 1; i < aSurfaceCount; ++i) {
+        cairo_tee_surface_add(csurf, aSurfaces[i]->CairoSurface());
+    }
+}
+
+const gfxIntSize
+gfxTeeSurface::GetSize() const
+{
+    nsRefPtr<gfxASurface> master = Wrap(cairo_tee_surface_index(mSurface, 0));
+    return master->GetSize();
+}
+
+void
+gfxTeeSurface::GetSurfaces(nsTArray<nsRefPtr<gfxASurface> >* aSurfaces)
+{
+    for (PRInt32 i = 0; ; ++i) {
+        cairo_surface_t *csurf = cairo_tee_surface_index(mSurface, i);
+        if (cairo_surface_status(csurf))
+            break;
+        nsRefPtr<gfxASurface> *elem = aSurfaces->AppendElement();
+        if (!elem)
+            return;
+        *elem = Wrap(csurf);
+    }
+}
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/gfxTeeSurface.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Thebes code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Robert O'Callahan <robert@ocallahan.org>
+ *
+ * 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
+ * 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 ***** */
+
+#ifndef GFX_TEESURFACE_H
+#define GFX_TEESURFACE_H
+
+#include "gfxASurface.h"
+#include "nsTArray.h"
+
+/**
+ * Wraps a cairo_tee_surface. The first surface in the surface list is the
+ * primary surface, which answers all surface queries (including size).
+ * All drawing is performed on all the surfaces.
+ */
+class THEBES_API gfxTeeSurface : public gfxASurface {
+public:
+    gfxTeeSurface(cairo_surface_t *csurf);
+    gfxTeeSurface(gfxASurface **aSurfaces, PRInt32 aSurfaceCount);
+
+    virtual const gfxIntSize GetSize() const;
+
+    void GetSurfaces(nsTArray<nsRefPtr<gfxASurface> > *aSurfaces);
+};
+
+#endif /* GFX_TEESURFACE_H */
--- a/gfx/thebes/gfxUnicodeProperties.h
+++ b/gfx/thebes/gfxUnicodeProperties.h
@@ -34,18 +34,19 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef GFX_UNICODEPROPERTIES_H
 #define GFX_UNICODEPROPERTIES_H
 
 #include "prtypes.h"
+#include "gfxTypes.h"
 
-class gfxUnicodeProperties
+class THEBES_API gfxUnicodeProperties
 {
 public:
     static PRUint32 GetMirroredChar(PRUint32 aCh);
 
     static PRUint8 GetCombiningClass(PRUint32 aCh);
 
     static PRUint8 GetGeneralCategory(PRUint32 aCh);
 
--- a/ipc/glue/IPCMessageUtils.h
+++ b/ipc/glue/IPCMessageUtils.h
@@ -46,34 +46,38 @@
 #include "nsTArray.h"
 #include "gfx3DMatrix.h"
 #include "gfxColor.h"
 #include "gfxMatrix.h"
 #include "gfxPattern.h"
 #include "nsRect.h"
 #include "nsRegion.h"
 #include "gfxASurface.h"
+#include "Layers.h"
 
 #ifdef _MSC_VER
 #pragma warning( disable : 4800 )
 #endif
 
 #if !defined(OS_POSIX)
 // This condition must be kept in sync with the one in
 // ipc_message_utils.h, but this dummy definition of
 // base::FileDescriptor acts as a static assert that we only get one
 // def or the other (or neither, in which case code using
 // FileDescriptor fails to build)
 namespace base { class FileDescriptor { }; }
 #endif
 
+using mozilla::layers::LayerManager;
+
 namespace mozilla {
 
 typedef gfxPattern::GraphicsFilter GraphicsFilterType;
 typedef gfxASurface::gfxSurfaceType gfxSurfaceType;
+typedef LayerManager::LayersBackend LayersBackend;
 
 // XXX there are out of place and might be generally useful.  Could
 // move to nscore.h or something.
 struct void_t {
   bool operator==(const void_t&) const { return true; }
 };
 struct null_t {
   bool operator==(const null_t&) const { return true; }
@@ -493,16 +497,45 @@ struct ParamTraits<mozilla::gfxSurfaceTy
         filter < gfxASurface::SurfaceTypeMax) {
       *result = paramType(filter);
       return true;
     }
     return false;
   }
 };
 
+ template<>
+struct ParamTraits<mozilla::LayersBackend>
+{
+  typedef mozilla::LayersBackend paramType;
+
+  static void Write(Message* msg, const paramType& param)
+  {
+    if (LayerManager::LAYERS_NONE < param &&
+        param < LayerManager::LAYERS_LAST) {
+      WriteParam(msg, int32(param));
+      return;
+    }
+    NS_RUNTIMEABORT("surface type not reached");
+  }
+
+  static bool Read(const Message* msg, void** iter, paramType* result)
+  {
+    int32 type;
+    if (!ReadParam(msg, iter, &type))
+      return false;
+
+    if (LayerManager::LAYERS_NONE < type &&
+        type < LayerManager::LAYERS_LAST) {
+      *result = paramType(type);
+      return true;
+    }
+    return false;
+  }
+};
 
 template<>
 struct ParamTraits<gfxRGBA>
 {
   typedef gfxRGBA paramType;
 
   static void Write(Message* msg, const paramType& param)
   {
--- a/ipc/ipdl/test/cxx/app/Makefile.in
+++ b/ipc/ipdl/test/cxx/app/Makefile.in
@@ -42,16 +42,17 @@ VPATH = @srcdir@
 include $(DEPTH)/config/autoconf.mk
 
 MODULE = ipdlunittest
 PROGRAM = $(MODULE)$(BIN_SUFFIX)
 
 NSDISTMODE = copy
 
 LOCAL_INCLUDES += -I$(topsrcdir)/toolkit/xre
+LOCAL_INCLUDES += -I$(topsrcdir)/xpcom/base
 
 CPPSRCS = \
   TestIPDL.cpp \
   $(NULL)
 
 LIBS = \
   $(DIST)/lib/$(LIB_PREFIX)xpcomglue_s.$(LIB_SUFFIX) \
   $(LIBXUL_LIBS) \
--- a/js/jetpack/JetpackActorCommon.cpp
+++ b/js/jetpack/JetpackActorCommon.cpp
@@ -518,18 +518,19 @@ JetpackActorCommon::RecList::remove(jsva
   }
   if (!mHead)
     return;
   RecNode* prev = mHead, *node = prev->down;
   while (node) {
     if (node->value() == v) {
       prev->down = node->down;
       delete node;
-    }
-    node = (prev = node)->down;
+    } else
+      prev = node;
+    node = prev->down;
   }
 }
 
 void
 JetpackActorCommon::RecList::copyTo(nsTArray<jsval>& dst) const
 {
   dst.Clear();
   for (RecNode* node = mHe