Merge m-c to b2g-inbound.
authorRyan VanderMeulen <ryanvm@gmail.com>
Tue, 15 Apr 2014 23:29:32 -0400
changeset 179209 eb732605c93a2189df07b025df172a9356db2037
parent 179208 d8e8e9c6bf093866478bf268544a53c8a913e4b2 (current diff)
parent 179154 dd50745d7f35796adf7b037c2e60c3f6809a7347 (diff)
child 179210 899f33ff00e3d13453e74b744ae3900bacd8cb44
push id272
push userpvanderbeken@mozilla.com
push dateMon, 05 May 2014 16:31:18 +0000
milestone31.0a1
Merge m-c to b2g-inbound.
browser/themes/linux/devtools/ruleview.css
browser/themes/osx/devtools/ruleview.css
browser/themes/windows/devtools/ruleview.css
--- a/accessible/src/jsat/Utils.jsm
+++ b/accessible/src/jsat/Utils.jsm
@@ -592,17 +592,17 @@ PivotContext.prototype = {
    */
   _getAncestry: function _getAncestry(aAccessible) {
     let ancestry = [];
     let parent = aAccessible;
     try {
       while (parent && (parent = parent.parent)) {
        ancestry.push(parent);
       }
-    } catch (e) {
+    } catch (x) {
       // A defunct accessible will raise an exception geting parent.
       Logger.debug('Failed to get parent:', x);
     }
     return ancestry.reverse();
   },
 
   /**
    * A list of the old accessible's ancestry.
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -269,17 +269,16 @@ pref("editor.singleLine.pasteNewlines", 
 pref("ui.dragThresholdX", 25);
 pref("ui.dragThresholdY", 25);
 
 // Layers Acceleration.  We can only have nice things on gonk, because
 // they're not maintained anywhere else.
 pref("layers.offmainthreadcomposition.enabled", true);
 #ifndef MOZ_WIDGET_GONK
 pref("dom.ipc.tabs.disabled", true);
-pref("layers.acceleration.disabled", true);
 pref("layers.offmainthreadcomposition.async-animations", false);
 pref("layers.async-video.enabled", false);
 #else
 pref("dom.ipc.tabs.disabled", false);
 pref("layers.acceleration.disabled", false);
 pref("layers.offmainthreadcomposition.async-animations", true);
 pref("layers.async-video.enabled", true);
 pref("layers.async-pan-zoom.enabled", true);
--- a/b2g/config/mozconfigs/macosx64_gecko/debug
+++ b/b2g/config/mozconfigs/macosx64_gecko/debug
@@ -1,8 +1,9 @@
+. "$topsrcdir/b2g/config/mozconfigs/common"
 . $topsrcdir/build/macosx/mozconfig.common
 
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --enable-update-packaging
 ac_add_options --enable-signmar
 
 # Nightlies only since this has a cost in performance
 ac_add_options --enable-js-diagnostics
--- a/b2g/config/mozconfigs/macosx64_gecko/nightly
+++ b/b2g/config/mozconfigs/macosx64_gecko/nightly
@@ -1,8 +1,9 @@
+. "$topsrcdir/b2g/config/mozconfigs/common"
 . $topsrcdir/build/macosx/mozconfig.common
 
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --enable-update-packaging
 ac_add_options --enable-signmar
 
 # Nightlies only since this has a cost in performance
 ac_add_options --enable-js-diagnostics
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -4459,16 +4459,19 @@ var TabsInTitlebar = {
     for (let something in this._disallowed) {
       allowed = false;
       break;
     }
 
     let titlebar = $("titlebar");
     let titlebarContent = $("titlebar-content");
     let menubar = $("toolbar-menubar");
+#ifdef XP_MACOSX
+    let secondaryButtonsWidth = rect($("titlebar-secondary-buttonbox")).width;
+#endif
 
     if (allowed) {
       // We set the tabsintitlebar attribute first so that our CSS for
       // tabsintitlebar manifests before we do our measurements.
       document.documentElement.setAttribute("tabsintitlebar", "true");
       updateTitlebarDisplay();
 
       // Try to avoid reflows in this code by calculating dimensions first and
@@ -4476,17 +4479,16 @@ var TabsInTitlebar = {
 
       // Get the full height of the tabs toolbar:
       let tabsToolbar = $("TabsToolbar");
       let fullTabsHeight = rect(tabsToolbar).height;
       // Buttons first:
       let captionButtonsBoxWidth = rect($("titlebar-buttonbox-container")).width;
 
 #ifdef XP_MACOSX
-      let secondaryButtonsWidth = rect($("titlebar-secondary-buttonbox")).width;
       // No need to look up the menubar stuff on OS X:
       let menuHeight = 0;
       let fullMenuHeight = 0;
       // Instead, look up the titlebar padding:
       let titlebarPadding = parseInt(window.getComputedStyle(titlebar).paddingTop, 10);
 #else
       // Otherwise, get the height and margins separately for the menubar
       let menuHeight = rect(menubar).height;
@@ -4551,19 +4553,16 @@ var TabsInTitlebar = {
         titlebarContentHeight += extraMargin;
       }
 
       // Then we bring up the titlebar by the same amount, but we add any negative margin:
       titlebar.style.marginBottom = "-" + titlebarContentHeight + "px";
 
 
       // Finally, size the placeholders:
-#ifdef XP_MACOSX
-      this._sizePlaceholder("fullscreen-button", secondaryButtonsWidth);
-#endif
       this._sizePlaceholder("caption-buttons", captionButtonsBoxWidth);
 
       if (!this._draghandles) {
         this._draghandles = {};
         let tmp = {};
         Components.utils.import("resource://gre/modules/WindowDraggingUtils.jsm", tmp);
 
         let mouseDownCheck = function () {
@@ -4581,16 +4580,20 @@ var TabsInTitlebar = {
       updateTitlebarDisplay();
 
       // Reset the margins and padding that might have been modified:
       titlebarContent.style.marginTop = "";
       titlebarContent.style.marginBottom = "";
       titlebar.style.marginBottom = "";
       menubar.style.paddingBottom = "";
     }
+
+#ifdef XP_MACOSX
+    this._sizePlaceholder("fullscreen-button", secondaryButtonsWidth);
+#endif
   },
 
   _sizePlaceholder: function (type, width) {
     Array.forEach(document.querySelectorAll(".titlebar-placeholder[type='"+ type +"']"),
                   function (node) { node.width = width; });
   },
 #endif
 
--- a/browser/components/customizableui/src/CustomizableUI.jsm
+++ b/browser/components/customizableui/src/CustomizableUI.jsm
@@ -292,31 +292,37 @@ let CustomizableUIInternal = {
 
   registerArea: function(aName, aProperties, aInternalCaller) {
     if (typeof aName != "string" || !/^[a-z0-9-_]{1,}$/i.test(aName)) {
       throw new Error("Invalid area name");
     }
 
     let areaIsKnown = gAreas.has(aName);
     let props = areaIsKnown ? gAreas.get(aName) : new Map();
+    if (areaIsKnown && aProperties["type"] &&
+        props.get("type") != aProperties["type"]) {
+      throw new Error("An area cannot change types");
+    }
     for (let key in aProperties) {
       //XXXgijs for special items, we need to make sure they have an appropriate ID
       // so we aren't perpetually in a non-default state:
       if (key == "defaultPlacements" && Array.isArray(aProperties[key])) {
         props.set(key, aProperties[key].map(x => this.isSpecialWidget(x) ? this.ensureSpecialWidgetId(x) : x ));
       } else {
         props.set(key, aProperties[key]);
       }
     }
     // Default to a toolbar:
     if (!props.has("type")) {
       props.set("type", CustomizableUI.TYPE_TOOLBAR);
     }
     if (props.get("type") == CustomizableUI.TYPE_TOOLBAR) {
-      if (!aInternalCaller && props.has("defaultCollapsed")) {
+      // Check aProperties instead of props because this check is only interested
+      // in the passed arguments, not the state of a potentially pre-existing area.
+      if (!aInternalCaller && aProperties["defaultCollapsed"]) {
         throw new Error("defaultCollapsed is only allowed for default toolbars.")
       }
       if (!props.has("defaultCollapsed")) {
         props.set("defaultCollapsed", true);
       }
     } else if (props.has("defaultCollapsed")) {
       throw new Error("defaultCollapsed only applies for TYPE_TOOLBAR areas.");
     }
--- a/browser/components/customizableui/test/browser.ini
+++ b/browser/components/customizableui/test/browser.ini
@@ -100,10 +100,11 @@ skip-if = os == "linux"
 [browser_985815_propagate_setToolbarVisibility.js]
 [browser_981305_separator_insertion.js]
 [browser_987177_destroyWidget_xul.js]
 [browser_987177_xul_wrapper_updating.js]
 [browser_987492_window_api.js]
 [browser_992747_toggle_noncustomizable_toolbar.js]
 [browser_993322_widget_notoolbar.js]
 [browser_995164_registerArea_during_customize_mode.js]
+[browser_996364_defaultCollapsed.js]
 [browser_bootstrapped_custom_toolbar.js]
 [browser_panel_toggle.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/customizableui/test/browser_996364_defaultCollapsed.js
@@ -0,0 +1,56 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+// Calling CustomizableUI.registerArea twice with no
+// properties should not throw an exception.
+add_task(function() {
+  try {
+    CustomizableUI.registerArea("area-996364", {});
+    CustomizableUI.registerArea("area-996364", {});
+  } catch (ex) {
+    ok(false, ex.message);
+  }
+
+  CustomizableUI.unregisterArea("area-996364", true);
+});
+
+add_task(function() {
+  let exceptionThrown = false;
+  try {
+    CustomizableUI.registerArea("area-996364-2", {"type": CustomizableUI.TYPE_TOOLBAR, "defaultCollapsed": "false"});
+  } catch (ex) {
+    exceptionThrown = true;
+  }
+  ok(exceptionThrown, "defaultCollapsed is not allowed as an external property");
+
+  // No need to unregister the area because registration fails.
+});
+
+add_task(function() {
+  let exceptionThrown;
+  try {
+    CustomizableUI.registerArea("area-996364-3", {"type": CustomizableUI.TYPE_TOOLBAR});
+    CustomizableUI.registerArea("area-996364-3", {"type": CustomizableUI.TYPE_MENU_PANEL});
+  } catch (ex) {
+    exceptionThrown = ex;
+  }
+  ok(exceptionThrown, "Exception expected, an area cannot change types: " + (exceptionThrown ? exceptionThrown : "[no exception]"));
+
+  CustomizableUI.unregisterArea("area-996364-3", true);
+});
+
+add_task(function() {
+  let exceptionThrown;
+  try {
+    CustomizableUI.registerArea("area-996364-4", {"type": CustomizableUI.TYPE_MENU_PANEL});
+    CustomizableUI.registerArea("area-996364-4", {"type": CustomizableUI.TYPE_TOOLBAR});
+  } catch (ex) {
+    exceptionThrown = ex;
+  }
+  ok(exceptionThrown, "Exception expected, an area cannot change types: " + (exceptionThrown ? exceptionThrown : "[no exception]"));
+
+  CustomizableUI.unregisterArea("area-996364-4", true);
+});
--- a/browser/components/shell/src/nsWindowsShellService.cpp
+++ b/browser/components/shell/src/nsWindowsShellService.cpp
@@ -748,27 +748,29 @@ nsWindowsShellService::SetShouldCheckDef
   return prefs->SetBoolPref(PREF_CHECKDEFAULTBROWSER, aShouldCheck);
 }
 
 static nsresult
 WriteBitmap(nsIFile* aFile, imgIContainer* aImage)
 {
   nsresult rv;
 
-  nsRefPtr<gfxASurface> thebesSurface =
+  RefPtr<SourceSurface> surface =
     aImage->GetFrame(imgIContainer::FRAME_FIRST,
                      imgIContainer::FLAG_SYNC_DECODE);
-  NS_ENSURE_TRUE(thebesSurface, NS_ERROR_FAILURE);
+  NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE);
 
-  nsRefPtr<gfxImageSurface> thebesImageSurface =
-    thebesSurface->GetAsReadableARGB32ImageSurface();
-  NS_ENSURE_TRUE(thebesImageSurface, NS_ERROR_FAILURE);
+  // For either of the following formats we want to set the biBitCount member
+  // of the BITMAPINFOHEADER struct to 32, below. For that value the bitmap
+  // format defines that the A8/X8 WORDs in the bitmap byte stream be ignored
+  // for the BI_RGB value we use for the biCompression member.
+  MOZ_ASSERT(surface->GetFormat() == SurfaceFormat::B8G8R8A8 ||
+             surface->GetFormat() == SurfaceFormat::B8G8R8X8);
 
-  RefPtr<DataSourceSurface> dataSurface =
-    thebesImageSurface->CopyToB8G8R8A8DataSourceSurface();
+  RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface();
   NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
 
   int32_t width = dataSurface->GetSize().width;
   int32_t height = dataSurface->GetSize().height;
   int32_t bytesPerPixel = 4 * sizeof(uint8_t);
   uint32_t bytesPerRow = bytesPerPixel * width;
 
   // initialize these bitmap structs which we will later
--- a/browser/experiments/Experiments.jsm
+++ b/browser/experiments/Experiments.jsm
@@ -53,16 +53,17 @@ const OBSERVER_TOPIC            = "exper
 const MANIFEST_VERSION          = 1;
 const CACHE_VERSION             = 1;
 
 const KEEP_HISTORY_N_DAYS       = 180;
 const MIN_EXPERIMENT_ACTIVE_SECONDS = 60;
 
 const PREF_BRANCH               = "experiments.";
 const PREF_ENABLED              = "enabled"; // experiments.enabled
+const PREF_ACTIVE_EXPERIMENT    = "activeExperiment"; // whether we have an active experiment
 const PREF_LOGGING              = "logging";
 const PREF_LOGGING_LEVEL        = PREF_LOGGING + ".level"; // experiments.logging.level
 const PREF_LOGGING_DUMP         = PREF_LOGGING + ".dump"; // experiments.logging.dump
 const PREF_MANIFEST_URI         = "manifest.uri"; // experiments.logging.manifest.uri
 const PREF_MANIFEST_CHECKCERT   = "manifest.cert.checkAttributes"; // experiments.manifest.cert.checkAttributes
 const PREF_MANIFEST_REQUIREBUILTIN = "manifest.cert.requireBuiltin"; // experiments.manifest.cert.requireBuiltin
 const PREF_FORCE_SAMPLE = "force-sample-value"; // experiments.force-sample-value
 
@@ -970,16 +971,21 @@ Experiments.Experiments.prototype = {
 
       yield uninstallAddons(unknownAddons);
     }
 
     let activeExperiment = this._getActiveExperiment();
     let activeChanged = false;
     let now = this._policy.now();
 
+    if (!activeExperiment) {
+      // Avoid this pref staying out of sync if there were e.g. crashes.
+      gPrefs.set(PREF_ACTIVE_EXPERIMENT, false);
+    }
+
     if (activeExperiment) {
       this._pendingUninstall = activeExperiment._addonId;
       try {
         let wasStopped;
         if (this._terminateReason) {
           yield activeExperiment.stop(this._terminateReason);
           wasStopped = true;
         } else {
@@ -1479,16 +1485,17 @@ Experiments.ExperimentEntry.prototype = 
       let addons = yield installedExperimentAddons();
       if (addons.length > 0) {
         this._log.error("start() - there are already "
                         + addons.length + " experiment addons installed");
         yield uninstallAddons(addons);
       }
 
       yield this._installAddon();
+      gPrefs.set(PREF_ACTIVE_EXPERIMENT, true);
     }.bind(this));
   },
 
   // Async install of the addon for this experiment, part of the start task above.
   _installAddon: function* () {
     let deferred = Promise.defer();
 
     let install = yield addonInstallForURL(this._manifestData.xpiURL,
@@ -1599,16 +1606,18 @@ Experiments.ExperimentEntry.prototype = 
   stop: function (terminationKind, terminationReason) {
     this._log.trace("stop() - id=" + this.id + ", terminationKind=" + terminationKind);
     if (!this._enabled) {
       this._log.warning("stop() - experiment not enabled: " + id);
       return Promise.reject();
     }
 
     this._enabled = false;
+    gPrefs.set(PREF_ACTIVE_EXPERIMENT, false);
+
     let deferred = Promise.defer();
     let updateDates = () => {
       let now = this._policy.now();
       this._lastChangedDate = now;
       this._endDate = now;
     };
 
     this._getAddon().then((addon) => {
@@ -1846,18 +1855,16 @@ ExperimentsProvider.prototype = Object.f
     ExperimentsLastActiveMeasurement1,
   ],
 
   _OBSERVERS: [
     OBSERVER_TOPIC,
   ],
 
   postInit: function () {
-    this._experiments = Experiments.instance();
-
     for (let o of this._OBSERVERS) {
       Services.obs.addObserver(this, o, false);
     }
 
     return Promise.resolve();
   },
 
   onShutdown: function () {
@@ -1876,16 +1883,24 @@ ExperimentsProvider.prototype = Object.f
     }
   },
 
   collectDailyData: function () {
     return this.recordLastActiveExperiment();
   },
 
   recordLastActiveExperiment: function () {
+    if (!gExperimentsEnabled) {
+      return Promise.resolve();
+    }
+
+    if (!this._experiments) {
+      this._experiments = Experiments.instance();
+    }
+
     let m = this.getMeasurement(ExperimentsLastActiveMeasurement1.prototype.name,
                                 ExperimentsLastActiveMeasurement1.prototype.version);
 
     return this.enqueueStorageOperation(() => {
       return Task.spawn(function* recordTask() {
         let todayActive = yield this._experiments.lastActiveToday();
         if (!todayActive) {
           this._log.info("No active experiment on this day: " +
--- a/browser/experiments/ExperimentsService.js
+++ b/browser/experiments/ExperimentsService.js
@@ -3,59 +3,96 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {interfaces: Ci, utils: Cu} = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/Preferences.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Experiments",
                                   "resource:///modules/experiments/Experiments.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "OS",
                                   "resource://gre/modules/osfile.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "CommonUtils",
+                                  "resource://services-common/utils.js");
 
 const PREF_EXPERIMENTS_ENABLED  = "experiments.enabled";
+const PREF_ACTIVE_EXPERIMENT    = "experiments.activeExperiment"; // whether we have an active experiment
 const PREF_HEALTHREPORT_ENABLED = "datareporting.healthreport.service.enabled";
 const PREF_TELEMETRY_ENABLED    = "toolkit.telemetry.enabled";
+const DELAY_INIT_MS             = 30 * 1000;
+
+XPCOMUtils.defineLazyGetter(
+  this, "gPrefs", () => {
+    return new Preferences();
+  });
 
 XPCOMUtils.defineLazyGetter(
   this, "gExperimentsEnabled", () => {
-    try {
-      let prefs = Services.prefs;
-      return prefs.getBoolPref(PREF_EXPERIMENTS_ENABLED) &&
-             prefs.getBoolPref(PREF_TELEMETRY_ENABLED) &&
-             prefs.getBoolPref(PREF_HEALTHREPORT_ENABLED);
-    } catch (e) {
-      return false;
-    }
+    return gPrefs.get(PREF_EXPERIMENTS_ENABLED, false) &&
+           gPrefs.get(PREF_TELEMETRY_ENABLED, false) &&
+           gPrefs.get(PREF_HEALTHREPORT_ENABLED, false);
+  });
+
+XPCOMUtils.defineLazyGetter(
+  this, "gActiveExperiment", () => {
+    return gPrefs.get(PREF_ACTIVE_EXPERIMENT);
   });
 
 function ExperimentsService() {
+  this._initialized = false;
+  this._delayedInitTimer = null;
 }
 
 ExperimentsService.prototype = {
   classID: Components.ID("{f7800463-3b97-47f9-9341-b7617e6d8d49}"),
   QueryInterface: XPCOMUtils.generateQI([Ci.nsITimerCallback, Ci.nsIObserver]),
 
   notify: function (timer) {
     if (!gExperimentsEnabled) {
       return;
     }
     if (OS.Constants.Path.profileDir === undefined) {
       throw Error("Update timer fired before profile was initialized?");
     }
     Experiments.instance().updateManifest();
   },
 
+  _delayedInit: function () {
+    if (!this._initialized) {
+      this._initialized = true;
+      Experiments.instance(); // for side effects
+    }
+  },
+
   observe: function (subject, topic, data) {
     switch (topic) {
       case "profile-after-change":
         if (gExperimentsEnabled) {
-          Experiments.instance(); // for side effects
+          Services.obs.addObserver(this, "quit-application", false);
+          Services.obs.addObserver(this, "sessionstore-state-finalized", false);
+
+          if (gActiveExperiment) {
+            this._initialized = true;
+            Experiments.instance(); // for side effects
+          }
+        }
+        break;
+      case "sessionstore-state-finalized":
+        if (!this._initialized) {
+          CommonUtils.namedTimer(this._delayedInit, DELAY_INIT_MS, this, "_delayedInitTimer");
+        }
+        break;
+      case "quit-application":
+        Services.obs.removeObserver(this, "quit-application");
+        Services.obs.removeObserver(this, "sessionstore-state-finalized");
+        if (this._delayedInitTimer) {
+          this._delayedInitTimer.clear();
         }
         break;
     }
   },
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ExperimentsService]);
--- a/browser/experiments/test/xpcshell/head.js
+++ b/browser/experiments/test/xpcshell/head.js
@@ -7,16 +7,24 @@ Cu.import("resource://gre/modules/Servic
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Promise.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource://gre/modules/osfile.jsm");
 Cu.import("resource://services-sync/healthreport.jsm", this);
 Cu.import("resource://testing-common/services/healthreport/utils.jsm", this);
 Cu.import("resource://gre/modules/services/healthreport/providers.jsm");
 
+const PREF_EXPERIMENTS_ENABLED  = "experiments.enabled";
+const PREF_LOGGING_LEVEL        = "experiments.logging.level";
+const PREF_LOGGING_DUMP         = "experiments.logging.dump";
+const PREF_MANIFEST_URI         = "experiments.manifest.uri";
+const PREF_FETCHINTERVAL        = "experiments.manifest.fetchIntervalSeconds";
+const PREF_TELEMETRY_ENABLED    = "toolkit.telemetry.enabled";
+const PREF_HEALTHREPORT_ENABLED = "datareporting.healthreport.service.enabled";
+
 function getExperimentPath(base) {
   let p = do_get_cwd();
   p.append(base);
   return p.path;
 }
 
 function sha1File(path) {
   let f = Cc["@mozilla.org/file/local;1"]
--- a/browser/experiments/test/xpcshell/test_activate.js
+++ b/browser/experiments/test/xpcshell/test_activate.js
@@ -2,21 +2,16 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 Cu.import("resource://testing-common/httpd.js");
 Cu.import("resource:///modules/experiments/Experiments.jsm");
 
 const FILE_MANIFEST            = "experiments.manifest";
-const PREF_EXPERIMENTS_ENABLED = "experiments.enabled";
-const PREF_LOGGING_LEVEL       = "experiments.logging.level";
-const PREF_LOGGING_DUMP        = "experiments.logging.dump";
-const PREF_MANIFEST_URI        = "experiments.manifest.uri";
-
 const SEC_IN_ONE_DAY  = 24 * 60 * 60;
 const MS_IN_ONE_DAY   = SEC_IN_ONE_DAY * 1000;
 
 let gProfileDir = null;
 let gHttpServer = null;
 let gHttpRoot   = null;
 let gReporter   = null;
 let gPolicy     = null;
--- a/browser/experiments/test/xpcshell/test_api.js
+++ b/browser/experiments/test/xpcshell/test_api.js
@@ -5,22 +5,16 @@
 
 Cu.import("resource://testing-common/httpd.js");
 Cu.import("resource://testing-common/AddonManagerTesting.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Experiments",
   "resource:///modules/experiments/Experiments.jsm");
 
 const FILE_MANIFEST            = "experiments.manifest";
-const PREF_EXPERIMENTS_ENABLED = "experiments.enabled";
-const PREF_LOGGING_LEVEL       = "experiments.logging.level";
-const PREF_LOGGING_DUMP        = "experiments.logging.dump";
-const PREF_MANIFEST_URI        = "experiments.manifest.uri";
-const PREF_FETCHINTERVAL       = "experiments.manifest.fetchIntervalSeconds";
-
 const MANIFEST_HANDLER         = "manifests/handler";
 
 const SEC_IN_ONE_DAY  = 24 * 60 * 60;
 const MS_IN_ONE_DAY   = SEC_IN_ONE_DAY * 1000;
 
 let gProfileDir          = null;
 let gHttpServer          = null;
 let gHttpRoot            = null;
--- a/browser/experiments/test/xpcshell/test_cache.js
+++ b/browser/experiments/test/xpcshell/test_cache.js
@@ -3,22 +3,16 @@
 
 "use strict";
 
 Cu.import("resource://testing-common/httpd.js");
 XPCOMUtils.defineLazyModuleGetter(this, "Experiments",
   "resource:///modules/experiments/Experiments.jsm");
 
 const FILE_MANIFEST            = "experiments.manifest";
-const PREF_EXPERIMENTS_ENABLED = "experiments.enabled";
-const PREF_LOGGING_LEVEL       = "experiments.logging.level";
-const PREF_LOGGING_DUMP        = "experiments.logging.dump";
-const PREF_MANIFEST_URI        = "experiments.manifest.uri";
-const PREF_FETCHINTERVAL       = "experiments.manifest.fetchIntervalSeconds";
-
 const MANIFEST_HANDLER         = "manifests/handler";
 
 const SEC_IN_ONE_DAY  = 24 * 60 * 60;
 const MS_IN_ONE_DAY   = SEC_IN_ONE_DAY * 1000;
 
 let gProfileDir          = null;
 let gHttpServer          = null;
 let gHttpRoot            = null;
--- a/browser/experiments/test/xpcshell/test_conditions.js
+++ b/browser/experiments/test/xpcshell/test_conditions.js
@@ -2,21 +2,16 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 
 Cu.import("resource:///modules/experiments/Experiments.jsm");
 
 const FILE_MANIFEST            = "experiments.manifest";
-const PREF_EXPERIMENTS_ENABLED = "experiments.enabled";
-const PREF_LOGGING_LEVEL       = "experiments.logging.level";
-const PREF_LOGGING_DUMP        = "experiments.logging.dump";
-const PREF_MANIFEST_URI        = "experiments.manifest.uri";
-
 const SEC_IN_ONE_DAY = 24 * 60 * 60;
 const MS_IN_ONE_DAY  = SEC_IN_ONE_DAY * 1000;
 
 let gProfileDir = null;
 let gHttpServer = null;
 let gHttpRoot   = null;
 let gReporter   = null;
 let gPolicy     = null;
--- a/browser/experiments/test/xpcshell/test_disableExperiments.js
+++ b/browser/experiments/test/xpcshell/test_disableExperiments.js
@@ -5,22 +5,16 @@
 
 Cu.import("resource://testing-common/httpd.js");
 Cu.import("resource://testing-common/AddonManagerTesting.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Experiments",
   "resource:///modules/experiments/Experiments.jsm");
 
 const FILE_MANIFEST            = "experiments.manifest";
-const PREF_EXPERIMENTS_ENABLED = "experiments.enabled";
-const PREF_LOGGING_LEVEL       = "experiments.logging.level";
-const PREF_LOGGING_DUMP        = "experiments.logging.dump";
-const PREF_MANIFEST_URI        = "experiments.manifest.uri";
-const PREF_FETCHINTERVAL       = "experiments.manifest.fetchIntervalSeconds";
-
 const MANIFEST_HANDLER         = "manifests/handler";
 
 const SEC_IN_ONE_DAY  = 24 * 60 * 60;
 const MS_IN_ONE_DAY   = SEC_IN_ONE_DAY * 1000;
 
 let gProfileDir          = null;
 let gHttpServer          = null;
 let gHttpRoot            = null;
--- a/browser/experiments/test/xpcshell/test_fetch.js
+++ b/browser/experiments/test/xpcshell/test_fetch.js
@@ -3,22 +3,16 @@
 
 "use strict";
 
 Cu.import("resource://testing-common/httpd.js");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/osfile.jsm");
 Cu.import("resource:///modules/experiments/Experiments.jsm");
 
-const PREF_EXPERIMENTS_ENABLED = "experiments.enabled";
-const PREF_LOGGING_LEVEL       = "experiments.logging.level";
-const PREF_LOGGING_DUMP        = "experiments.logging.dump";
-const PREF_MANIFEST_URI        = "experiments.manifest.uri";
-
-
 let gProfileDir = null;
 let gHttpServer = null;
 let gHttpRoot   = null;
 let gPolicy     = new Experiments.Policy();
 
 function run_test() {
   loadAddonManager();
   gProfileDir = do_get_profile();
--- a/browser/experiments/test/xpcshell/test_healthreport.js
+++ b/browser/experiments/test/xpcshell/test_healthreport.js
@@ -22,20 +22,26 @@ function getStorageAndProvider(name) {
 function run_test() {
   run_next_test();
 }
 
 add_test(function setup() {
   do_get_profile();
   initTestLogging();
 
+  Services.prefs.setBoolPref(PREF_EXPERIMENTS_ENABLED, true);
+  Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, true);
+  Services.prefs.setBoolPref(PREF_HEALTHREPORT_ENABLED, true);
+
   run_next_test();
 });
 
 add_task(function test_constructor() {
+  Experiments.instance();
+  yield Experiments._mainTask;
   let provider = new ExperimentsProvider();
 });
 
 add_task(function* test_init() {
   let storage = yield Metrics.Storage("init");
   let provider = new ExperimentsProvider();
   yield provider.init(storage);
   yield provider.shutdown();
--- a/browser/experiments/test/xpcshell/test_telemetry.js
+++ b/browser/experiments/test/xpcshell/test_telemetry.js
@@ -5,22 +5,16 @@
 
 Cu.import("resource://testing-common/httpd.js");
 Cu.import("resource://gre/modules/TelemetryLog.jsm");
 Cu.import("resource://gre/modules/TelemetryPing.jsm");
 Cu.import("resource:///modules/experiments/Experiments.jsm");
 
 
 const FILE_MANIFEST            = "experiments.manifest";
-const PREF_EXPERIMENTS_ENABLED = "experiments.enabled";
-const PREF_LOGGING_LEVEL       = "experiments.logging.level";
-const PREF_LOGGING_DUMP        = "experiments.logging.dump";
-const PREF_MANIFEST_URI        = "experiments.manifest.uri";
-const PREF_FETCHINTERVAL       = "experiments.manifest.fetchIntervalSeconds";
-
 const MANIFEST_HANDLER         = "manifests/handler";
 
 const SEC_IN_ONE_DAY  = 24 * 60 * 60;
 const MS_IN_ONE_DAY   = SEC_IN_ONE_DAY * 1000;
 
 
 let gProfileDir          = null;
 let gHttpServer          = null;
--- a/browser/themes/linux/preferences/in-content/preferences.css
+++ b/browser/themes/linux/preferences/in-content/preferences.css
@@ -16,48 +16,40 @@ button[type="menu"] > .button-box > .but
 menulist:not([editable="true"]) > .menulist-dropmarker {
   display: -moz-box;
   margin-top: 6px;
   margin-bottom: 6px;
 }
 
 checkbox {
   -moz-binding: url("chrome://global/content/bindings/checkbox.xml#checkbox");
-}
-
-.checkbox-check {
-  max-height: 23px;
+  -moz-box-align: center;
 }
 
 checkbox:hover::before,
 checkbox[checked]::before {
-  max-height: 10px;
   margin-top: 7px;
   margin-bottom: 6px;
   -moz-margin-end: -19px;
   -moz-margin-start: 4px;
   background-repeat: no-repeat;
 }
 
 radio {
   -moz-binding: url("chrome://global/content/bindings/radio.xml#radio");
+  -moz-box-align: center;
   margin: 7px 0;
 }
 
-.radio-check {
-  max-height: 23px;
-}
-
 .radio-label-box {
   -moz-appearance: none;
 }
 
 radio:hover::before,
 radio[selected]::before {
-  max-height: 11px;
   margin-top: 6px;
   margin-bottom: 6px;
   -moz-margin-end: -17px;
   -moz-margin-start: 6px;
 }
 
 .numberbox-input-box {
   -moz-appearance: none;
--- a/content/base/src/moz.build
+++ b/content/base/src/moz.build
@@ -224,13 +224,14 @@ LOCAL_INCLUDES += [
     '/js/xpconnect/src',
     '/js/xpconnect/wrappers',
     '/layout/base',
     '/layout/generic',
     '/layout/style',
     '/layout/svg',
     '/layout/xul',
     '/netwerk/base/src',
+    '/xpcom/ds',
 ]
 
 if CONFIG['GNU_CC'] and CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
     # Work around bug 986928
     CXXFLAGS += ['-Wno-error=format']
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -119,16 +119,17 @@
 #include "nsIImageLoadingContent.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIIOService.h"
 #include "nsIJSRuntimeService.h"
 #include "nsILineBreaker.h"
 #include "nsILoadContext.h"
 #include "nsILoadGroup.h"
+#include "nsIMemoryReporter.h"
 #include "nsIMIMEService.h"
 #include "nsINode.h"
 #include "nsINodeInfo.h"
 #include "nsIObjectLoadingContent.h"
 #include "nsIObserver.h"
 #include "nsIObserverService.h"
 #include "nsIOfflineCacheUpdate.h"
 #include "nsIParser.h"
--- a/content/canvas/src/CanvasRenderingContext2D.cpp
+++ b/content/canvas/src/CanvasRenderingContext2D.cpp
@@ -95,19 +95,21 @@
 #include "nsGlobalWindow.h"
 #include "GLContext.h"
 #include "GLContextProvider.h"
 #include "SVGContentUtils.h"
 #include "nsIScreenManager.h"
 
 #undef free // apparently defined by some windows header, clashing with a free()
             // method in SkTypes.h
+#ifdef USE_SKIA
 #include "SkiaGLGlue.h"
 #include "SurfaceStream.h"
 #include "SurfaceTypes.h"
+#endif
 
 using mozilla::gl::GLContext;
 using mozilla::gl::SkiaGLGlue;
 using mozilla::gl::GLContextProvider;
 
 #ifdef XP_WIN
 #include "gfxWindowsPlatform.h"
 #endif
@@ -895,25 +897,27 @@ CanvasRenderingContext2D::EnsureTarget()
      if (layerManager) {
       if (gfxPlatform::GetPlatform()->UseAcceleratedSkiaCanvas() &&
           !mForceSoftware &&
           CheckSizeForSkiaGL(size)) {
         DemoteOldestContextIfNecessary();
 
         SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
 
+#if USE_SKIA
         if (glue && glue->GetGrContext() && glue->GetGLContext()) {
           mTarget = Factory::CreateDrawTargetSkiaWithGrContext(glue->GetGrContext(), size, format);
           if (mTarget) {
             mStream = gfx::SurfaceStream::CreateForType(SurfaceStreamType::TripleBuffer, glue->GetGLContext());
             AddDemotableContext(this);
           } else {
             printf_stderr("Failed to create a SkiaGL DrawTarget, falling back to software\n");
           }
         }
+#endif
         if (!mTarget) {
           mTarget = layerManager->CreateDrawTarget(size, format);
         }
       } else
         mTarget = layerManager->CreateDrawTarget(size, format);
      } else {
         mTarget = gfxPlatform::GetPlatform()->CreateOffscreenCanvasDrawTarget(size, format);
      }
@@ -4239,22 +4243,24 @@ CanvasRenderingContext2D::GetCanvasLayer
 
   if (!mResetLayer && aOldLayer) {
     CanvasRenderingContext2DUserData* userData =
       static_cast<CanvasRenderingContext2DUserData*>(
         aOldLayer->GetUserData(&g2DContextLayerUserData));
 
     CanvasLayer::Data data;
     if (mStream) {
+#ifdef USE_SKIA
       SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
 
       if (glue) {
         data.mGLContext = glue->GetGLContext();
         data.mStream = mStream.get();
       }
+#endif
     } else {
       data.mDrawTarget = mTarget;
     }
 
     if (userData && userData->IsForContext(this) && aOldLayer->IsDataValid(data)) {
       nsRefPtr<CanvasLayer> ret = aOldLayer;
       return ret.forget();
     }
@@ -4287,17 +4293,19 @@ CanvasRenderingContext2D::GetCanvasLayer
 
   CanvasLayer::Data data;
   if (mStream) {
     SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
 
     if (glue) {
       canvasLayer->SetPreTransactionCallback(
               CanvasRenderingContext2DUserData::PreTransactionCallback, userData);
+#if USE_SKIA
       data.mGLContext = glue->GetGLContext();
+#endif
       data.mStream = mStream.get();
       data.mTexID = (uint32_t)((uintptr_t)mTarget->GetNativeSurface(NativeSurfaceType::OPENGL_TEXTURE));
     }
   } else {
     data.mDrawTarget = mTarget;
   }
 
   data.mSize = nsIntSize(mWidth, mHeight);
--- a/content/html/content/src/HTMLInputElement.cpp
+++ b/content/html/content/src/HTMLInputElement.cpp
@@ -4669,34 +4669,25 @@ HTMLInputElement::GetValueAsDate(const n
  * -The day must be exactly 2 digit long, and 01 <= day <= maxday
  *  Where maxday is the number of days in the month 'month' and year 'year'
  */
 
   if (aValue.Length() < 10) {
     return false;
   }
 
-  uint32_t endOfYearOffset = 0;
-  for (; NS_IsAsciiDigit(aValue[endOfYearOffset]); ++endOfYearOffset);
-
-  // The year must be at least 4 digits long.
-  if (aValue[endOfYearOffset] != '-' || endOfYearOffset < 4) {
+  uint32_t endOfYearOffset = aValue.Length() - 6;
+  
+  if (aValue[endOfYearOffset]     != '-' ||
+      aValue[endOfYearOffset + 3] != '-') {
     return false;
   }
 
-  // Now, we know where is the next '-' and what should be the size of the
-  // string.
-  if (aValue[endOfYearOffset + 3] != '-' ||
-      aValue.Length() != 10 + (endOfYearOffset - 4)) {
-    return false;
-  }
-
-  nsresult ec;
-  *aYear = PromiseFlatString(StringHead(aValue, endOfYearOffset)).ToInteger(&ec);
-  if (NS_FAILED(ec) || *aYear == 0) {
+  if (!DigitSubStringToNumber(aValue, 0, endOfYearOffset, aYear) ||
+      *aYear < 1) {
     return false;
   }
 
   if (!DigitSubStringToNumber(aValue, endOfYearOffset + 1, 2, aMonth) ||
       *aMonth < 1 || *aMonth > 12) {
     return false;
   }
 
--- a/content/media/Latency.h
+++ b/content/media/Latency.h
@@ -8,17 +8,17 @@
 #define MOZILLA_LATENCY_H
 
 #include "mozilla/TimeStamp.h"
 #include "prlog.h"
 #include "nsCOMPtr.h"
 #include "nsIThread.h"
 #include "mozilla/Monitor.h"
 #include "nsISupportsImpl.h"
-#include "nsObserverService.h"
+#include "nsIObserver.h"
 
 class AsyncLatencyLogger;
 class LogEvent;
 
 PRLogModuleInfo* GetLatencyLog();
 
 // This class is a singleton. It is refcounted.
 class AsyncLatencyLogger : public nsIObserver
--- a/content/media/MediaTaskQueue.h
+++ b/content/media/MediaTaskQueue.h
@@ -18,21 +18,23 @@ namespace mozilla {
 
 class SharedThreadPool;
 
 // Abstracts executing runnables in order in a thread pool. The runnables
 // dispatched to the MediaTaskQueue will be executed in the order in which
 // they're received, and are guaranteed to not be executed concurrently.
 // They may be executed on different threads, and a memory barrier is used
 // to make this threadsafe for objects that aren't already threadsafe.
-class MediaTaskQueue : public AtomicRefCounted<MediaTaskQueue> {
+class MediaTaskQueue MOZ_FINAL {
+  ~MediaTaskQueue();
+
 public:
-  MOZ_DECLARE_REFCOUNTED_TYPENAME(MediaTaskQueue)
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaTaskQueue)
+
   MediaTaskQueue(TemporaryRef<SharedThreadPool> aPool);
-  ~MediaTaskQueue();
 
   nsresult Dispatch(nsIRunnable* aRunnable);
 
   // Removes all pending tasks from the task queue, and blocks until
   // the currently running task (if any) finishes.
   void Flush();
 
   // Blocks until all tasks finish executing, then shuts down the task queue
--- a/content/media/fmp4/PlatformDecoderModule.h
+++ b/content/media/fmp4/PlatformDecoderModule.h
@@ -138,20 +138,22 @@ public:
 //
 // All functions must be threadsafe, and be able to be called on an
 // arbitrary thread.
 //
 // Decoding is done asynchronously. Any async work can be done on the
 // MediaTaskQueue passed into the PlatformDecoderModules's Create*Decoder()
 // function. This may not be necessary for platforms with async APIs
 // for decoding.
-class MediaDataDecoder : public AtomicRefCounted<MediaDataDecoder> {
+class MediaDataDecoder {
+protected:
+  virtual ~MediaDataDecoder() {};
+
 public:
-  MOZ_DECLARE_REFCOUNTED_TYPENAME(MediaDataDecoder)
-  virtual ~MediaDataDecoder() {};
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDataDecoder)
 
   // Initialize the decoder. The decoder should be ready to decode after
   // this returns. The decoder should do any initialization here, rather
   // than in its constructor or PlatformDecoderModule::Create*Decoder(),
   // so that if the MP4Reader needs to shutdown during initialization,
   // it can call Shutdown() to cancel this operation. Any initialization
   // that requires blocking the calling thread in this function *must*
   // be done here so that it can be canceled by calling Shutdown()!
--- a/content/media/fmp4/wmf/MFTDecoder.h
+++ b/content/media/fmp4/wmf/MFTDecoder.h
@@ -9,21 +9,23 @@
 
 #include "WMF.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "nsIThread.h"
 
 namespace mozilla {
 
-class MFTDecoder : public AtomicRefCounted<MFTDecoder> {
+class MFTDecoder MOZ_FINAL {
+  ~MFTDecoder();
+
 public:
-  MOZ_DECLARE_REFCOUNTED_TYPENAME(MTFDecoder)
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MFTDecoder)
+
   MFTDecoder();
-  ~MFTDecoder();
 
   // Creates the MFT. First thing to do as part of setup.
   //
   // Params:
   //  - aMFTClsID the clsid used by CoCreateInstance to instantiate the
   //    decoder MFT.
   HRESULT Create(const GUID& aMFTClsID);
 
new file mode 100644
--- /dev/null
+++ b/content/media/gtest/TestVideoTrackEncoder.cpp
@@ -0,0 +1,296 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.0. If a copy of the MPL was not distributed with this
+* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+#include <algorithm>
+
+#include "mozilla/ArrayUtils.h"
+#include "VP8TrackEncoder.h"
+#include "ImageContainer.h"
+#include "MediaStreamGraph.h"
+#include "WebMWriter.h" // TODO: it's weird to include muxer header to get the class definition of VP8 METADATA
+
+using ::testing::TestWithParam;
+using ::testing::Values;
+
+using namespace mozilla::layers;
+using namespace mozilla;
+
+// A helper object to generate of different YUV planes.
+class YUVBufferGenerator {
+public:
+  YUVBufferGenerator() {}
+
+  void Init(const mozilla::gfx::IntSize &aSize)
+  {
+    mImageSize = aSize;
+
+    int yPlaneLen = aSize.width * aSize.height;
+    int cbcrPlaneLen = (yPlaneLen + 1) / 2;
+    int frameLen = yPlaneLen + cbcrPlaneLen;
+
+    // Generate source buffer.
+    mSourceBuffer.SetLength(frameLen);
+
+    // Fill Y plane.
+    memset(mSourceBuffer.Elements(), 0x10, yPlaneLen);
+
+    // Fill Cb/Cr planes.
+    memset(mSourceBuffer.Elements() + yPlaneLen, 0x80, cbcrPlaneLen);
+  }
+
+  mozilla::gfx::IntSize GetSize() const
+  {
+    return mImageSize;
+  }
+
+  void Generate(nsTArray<nsRefPtr<Image> > &aImages)
+  {
+    aImages.AppendElement(CreateI420Image());
+    aImages.AppendElement(CreateNV12Image());
+    aImages.AppendElement(CreateNV21Image());
+  }
+
+private:
+  Image *CreateI420Image()
+  {
+    PlanarYCbCrImage *image = new PlanarYCbCrImage(new BufferRecycleBin());
+    PlanarYCbCrData data;
+
+    const uint32_t yPlaneSize = mImageSize.width * mImageSize.height;
+    const uint32_t halfWidth = (mImageSize.width + 1) / 2;
+    const uint32_t halfHeight = (mImageSize.height + 1) / 2;
+    const uint32_t uvPlaneSize = halfWidth * halfHeight;
+
+    // Y plane.
+    uint8_t *y = mSourceBuffer.Elements();
+    data.mYChannel = y;
+    data.mYSize.width = mImageSize.width;
+    data.mYSize.height = mImageSize.height;
+    data.mYStride = mImageSize.width;
+    data.mYSkip = 0;
+
+    // Cr plane.
+    uint8_t *cr = y + yPlaneSize + uvPlaneSize;
+    data.mCrChannel = cr;
+    data.mCrSkip = 0;
+
+    // Cb plane
+    uint8_t *cb = y + yPlaneSize;
+    data.mCbChannel = cb;
+    data.mCbSkip = 0;
+
+    // CrCb plane vectors.
+    data.mCbCrStride = halfWidth;
+    data.mCbCrSize.width = halfWidth;
+    data.mCbCrSize.height = halfHeight;
+
+    image->SetData(data);
+    return image;
+  }
+
+  Image *CreateNV12Image()
+  {
+    PlanarYCbCrImage *image = new PlanarYCbCrImage(new BufferRecycleBin());
+    PlanarYCbCrData data;
+
+    const uint32_t yPlaneSize = mImageSize.width * mImageSize.height;
+    const uint32_t halfWidth = (mImageSize.width + 1) / 2;
+    const uint32_t halfHeight = (mImageSize.height + 1) / 2;
+
+    // Y plane.
+    uint8_t *y = mSourceBuffer.Elements();
+    data.mYChannel = y;
+    data.mYSize.width = mImageSize.width;
+    data.mYSize.height = mImageSize.height;
+    data.mYStride = mImageSize.width;
+    data.mYSkip = 0;
+
+    // Cr plane.
+    uint8_t *cr = y + yPlaneSize;
+    data.mCrChannel = cr;
+    data.mCrSkip = 1;
+
+    // Cb plane
+    uint8_t *cb = y + yPlaneSize + 1;
+    data.mCbChannel = cb;
+    data.mCbSkip = 1;
+
+    // 4:2:0.
+    data.mCbCrStride = mImageSize.width;
+    data.mCbCrSize.width = halfWidth;
+    data.mCbCrSize.height = halfHeight;
+
+    image->SetData(data);
+    return image;
+  }
+
+  Image *CreateNV21Image()
+  {
+    PlanarYCbCrImage *image = new PlanarYCbCrImage(new BufferRecycleBin());
+    PlanarYCbCrData data;
+
+    const uint32_t yPlaneSize = mImageSize.width * mImageSize.height;
+    const uint32_t halfWidth = (mImageSize.width + 1) / 2;
+    const uint32_t halfHeight = (mImageSize.height + 1) / 2;
+
+    // Y plane.
+    uint8_t *y = mSourceBuffer.Elements();
+    data.mYChannel = y;
+    data.mYSize.width = mImageSize.width;
+    data.mYSize.height = mImageSize.height;
+    data.mYStride = mImageSize.width;
+    data.mYSkip = 0;
+
+    // Cr plane.
+    uint8_t *cr = y + yPlaneSize + 1;
+    data.mCrChannel = cr;
+    data.mCrSkip = 1;
+
+    // Cb plane
+    uint8_t *cb = y + yPlaneSize;
+    data.mCbChannel = cb;
+    data.mCbSkip = 1;
+
+    // 4:2:0.
+    data.mCbCrStride = mImageSize.width;
+    data.mCbCrSize.width = halfWidth;
+    data.mCbCrSize.height = halfHeight;
+
+    image->SetData(data);
+    return image;
+  }
+
+private:
+  mozilla::gfx::IntSize mImageSize;
+  nsTArray<uint8_t> mSourceBuffer;
+};
+
+struct InitParam {
+  bool mShouldSucceed;  // This parameter should cause success or fail result
+  int  mWidth;          // frame width
+  int  mHeight;         // frame height
+  mozilla::TrackRate mTrackRate; // track rate. 90K is the most commond track rate.
+};
+
+class TestVP8TrackEncoder: public VP8TrackEncoder
+{
+public:
+  ::testing::AssertionResult TestInit(const InitParam &aParam)
+  {
+    nsresult result = Init(aParam.mWidth, aParam.mHeight, aParam.mWidth, aParam.mHeight, aParam.mTrackRate);
+
+    if (((NS_FAILED(result) && aParam.mShouldSucceed)) || (NS_SUCCEEDED(result) && !aParam.mShouldSucceed))
+    {
+      return ::testing::AssertionFailure()
+                << " width = " << aParam.mWidth
+                << " height = " << aParam.mHeight
+                << " TrackRate = " << aParam.mTrackRate << ".";
+    }
+    else
+    {
+      return ::testing::AssertionSuccess();
+    }
+  }
+};
+
+// Init test
+TEST(VP8VideoTrackEncoder, Initialization)
+{
+  InitParam params[] = {
+    // Failure cases.
+    { false, 640, 480, 0 },      // Trackrate should be larger than 1.
+    { false, 640, 480, -1 },     // Trackrate should be larger than 1.
+    { false, 0, 0, 90000 },      // Height/ width should be larger than 1.
+    { false, 0, 1, 90000 },      // Height/ width should be larger than 1.
+    { false, 1, 0, 90000},       // Height/ width should be larger than 1.
+
+    // Success cases
+    { true, 640, 480, 90000},    // Standard VGA
+    { true, 800, 480, 90000},    // Standard WVGA
+    { true, 960, 540, 90000},    // Standard qHD
+    { true, 1280, 720, 90000}    // Standard HD
+  };
+
+  for (size_t i = 0; i < ArrayLength(params); i++)
+  {
+    TestVP8TrackEncoder encoder;
+    EXPECT_TRUE(encoder.TestInit(params[i]));
+  }
+}
+
+// Get MetaData test
+TEST(VP8VideoTrackEncoder, FetchMetaData)
+{
+  InitParam params[] = {
+    // Success cases
+    { true, 640, 480, 90000},    // Standard VGA
+    { true, 800, 480, 90000},    // Standard WVGA
+    { true, 960, 540, 90000},    // Standard qHD
+    { true, 1280, 720, 90000}    // Standard HD
+  };
+
+  for (size_t i = 0; i < ArrayLength(params); i++)
+  {
+    TestVP8TrackEncoder encoder;
+    EXPECT_TRUE(encoder.TestInit(params[i]));
+
+    nsRefPtr<TrackMetadataBase> meta = encoder.GetMetadata();
+    nsRefPtr<VP8Metadata> vp8Meta(static_cast<VP8Metadata*>(meta.get()));
+
+    // METADATA should be depend on how to initiate encoder.
+    EXPECT_TRUE(vp8Meta->mWidth == params[i].mWidth);
+    EXPECT_TRUE(vp8Meta->mHeight == params[i].mHeight);
+  }
+}
+
+// Encode test
+TEST(VP8VideoTrackEncoder, FrameEncode)
+{
+  // Initiate VP8 encoder
+  TestVP8TrackEncoder encoder;
+  InitParam param = {true, 640, 480, 90000};
+  encoder.TestInit(param);
+
+  // Create YUV images as source.
+  nsTArray<nsRefPtr<Image>> images;
+  YUVBufferGenerator generator;
+  generator.Init(mozilla::gfx::IntSize(640, 480));
+  generator.Generate(images);
+
+  // Put generated YUV frame into video segment.
+  // Duration of each frame is 1 second.
+  VideoSegment segment;
+  for (nsTArray<nsRefPtr<Image>>::size_type i = 0; i < images.Length(); i++)
+  {
+    nsRefPtr<Image> image = images[i];
+    segment.AppendFrame(image.forget(), mozilla::TrackTicks(90000), generator.GetSize());
+  }
+
+  // track change notification.
+  encoder.NotifyQueuedTrackChanges(nullptr, 0, 0, 0, 0, segment);
+
+  // Pull Encoded Data back from encoder.
+  EncodedFrameContainer container;
+  EXPECT_TRUE(NS_SUCCEEDED(encoder.GetEncodedTrack(container)));
+}
+
+// EOS test
+TEST(VP8VideoTrackEncoder, EncodeComplete)
+{
+  // Initiate VP8 encoder
+  TestVP8TrackEncoder encoder;
+  InitParam param = {true, 640, 480, 90000};
+  encoder.TestInit(param);
+
+  // track end notification.
+  VideoSegment segment;
+  encoder.NotifyQueuedTrackChanges(nullptr, 0, 0, 0, MediaStreamListener::TRACK_EVENT_ENDED, segment);
+
+  // Pull Encoded Data back from encoder. Since we have sent
+  // EOS to encoder, encoder.GetEncodedTrack should return
+  // NS_OK immidiately.
+  EncodedFrameContainer container;
+  EXPECT_TRUE(NS_SUCCEEDED(encoder.GetEncodedTrack(container)));
+}
--- a/content/media/gtest/moz.build
+++ b/content/media/gtest/moz.build
@@ -6,16 +6,20 @@
 
 LIBRARY_NAME = 'media_gtest'
 
 UNIFIED_SOURCES += [
     'TestAudioCompactor.cpp',
     'TestTrackEncoder.cpp',
 ]
 
+if CONFIG['MOZ_WEBM_ENCODER']:
+    UNIFIED_SOURCES += ['TestVideoTrackEncoder.cpp',
+]
+
 EXPORT_LIBRARY = True
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 LOCAL_INCLUDES += [
     '/content/media/encoder',
 ]
 
--- a/content/svg/content/src/SVGFEImageElement.cpp
+++ b/content/svg/content/src/SVGFEImageElement.cpp
@@ -3,16 +3,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/SVGFEImageElement.h"
 
 #include "mozilla/EventStates.h"
 #include "mozilla/dom/SVGFEImageElementBinding.h"
 #include "mozilla/dom/SVGFilterElement.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/RefPtr.h"
 #include "nsContentUtils.h"
 #include "nsLayoutUtils.h"
 #include "nsSVGUtils.h"
 #include "nsNetUtil.h"
 #include "imgIContainer.h"
 #include "gfx2DGlue.h"
 
 NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(FEImage)
@@ -206,32 +208,26 @@ SVGFEImageElement::GetPrimitiveDescripti
   GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
              getter_AddRefs(currentRequest));
 
   nsCOMPtr<imgIContainer> imageContainer;
   if (currentRequest) {
     currentRequest->GetImage(getter_AddRefs(imageContainer));
   }
 
-  nsRefPtr<gfxASurface> currentFrame;
+  RefPtr<SourceSurface> image;
   if (imageContainer) {
-    currentFrame =
-      imageContainer->GetFrame(imgIContainer::FRAME_CURRENT,
-                               imgIContainer::FLAG_SYNC_DECODE);
+    image = imageContainer->GetFrame(imgIContainer::FRAME_CURRENT,
+                                     imgIContainer::FLAG_SYNC_DECODE);
   }
 
-  if (!currentFrame) {
+  if (!image) {
     return FilterPrimitiveDescription(PrimitiveType::Empty);
   }
 
-  gfxPlatform* platform = gfxPlatform::GetPlatform();
-  DrawTarget* dt = platform->ScreenReferenceDrawTarget();
-  RefPtr<SourceSurface> image =
-    platform->GetSourceSurfaceForSurface(dt, currentFrame);
-
   IntSize nativeSize;
   imageContainer->GetWidth(&nativeSize.width);
   imageContainer->GetHeight(&nativeSize.height);
 
   Matrix viewBoxTM =
     SVGContentUtils::GetViewBoxTransform(aFilterSubregion.width, aFilterSubregion.height,
                                          0, 0, nativeSize.width, nativeSize.height,
                                          mPreserveAspectRatio);
--- a/dom/apps/src/AppsUtils.jsm
+++ b/dom/apps/src/AppsUtils.jsm
@@ -4,27 +4,27 @@
 
 "use strict";
 
 const Cu = Components.utils;
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cr = Components.results;
 
-Cu.import("resource://gre/modules/FileUtils.jsm");
 Cu.import("resource://gre/modules/osfile.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
-Cu.import("resource://gre/modules/WebappOSUtils.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Promise.jsm");
 
-XPCOMUtils.defineLazyServiceGetter(this, "NetworkUtil",
-                                   "@mozilla.org/network/util;1",
-                                   "nsINetUtil");
+XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
+  "resource://gre/modules/FileUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "WebappOSUtils",
+  "resource://gre/modules/WebappOSUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
   "resource://gre/modules/NetUtil.jsm");
 
 // Shared code for AppsServiceChild.jsm, Webapps.jsm and Webapps.js
 
 this.EXPORTED_SYMBOLS = ["AppsUtils", "ManifestHelper", "isAbsoluteURI"];
 
@@ -337,17 +337,18 @@ this.AppsUtils = {
     }
     return true;
   },
 
   checkManifestContentType: function
      checkManifestContentType(aInstallOrigin, aWebappOrigin, aContentType) {
     let hadCharset = { };
     let charset = { };
-    let contentType = NetworkUtil.parseContentType(aContentType, charset, hadCharset);
+    let netutil = Cc["@mozilla.org/network/util;1"].getService(Ci.nsINetUtil);
+    let contentType = netutil.parseContentType(aContentType, charset, hadCharset);
     if (aInstallOrigin != aWebappOrigin &&
         contentType != "application/x-web-app-manifest+json") {
       return false;
     }
     return true;
   },
 
   /**
--- a/dom/bluetooth/BluetoothProfileController.h
+++ b/dom/bluetooth/BluetoothProfileController.h
@@ -3,17 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_bluetooth_bluetoothprofilecontroller_h__
 #define mozilla_dom_bluetooth_bluetoothprofilecontroller_h__
 
 #include "BluetoothUuid.h"
-#include "mozilla/RefPtr.h"
+#include "nsISupportsImpl.h"
 #include "nsAutoPtr.h"
 #include "nsITimer.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 /*
  * Class of Device(CoD): 32-bit unsigned integer
  *
@@ -52,20 +52,22 @@ BEGIN_BLUETOOTH_NAMESPACE
 
 // Pointing device: sub-field of minor device class (Bit 7)
 #define IS_POINTING_DEVICE(cod)      ((GET_MINOR_DEVICE_CLASS(cod) & 0x20) >> 5)
 
 class BluetoothProfileManagerBase;
 class BluetoothReplyRunnable;
 typedef void (*BluetoothProfileControllerCallback)();
 
-class BluetoothProfileController : public RefCounted<BluetoothProfileController>
+class BluetoothProfileController MOZ_FINAL
 {
+  ~BluetoothProfileController();
+
 public:
-  MOZ_DECLARE_REFCOUNTED_TYPENAME(BluetoothProfileController)
+  NS_INLINE_DECL_REFCOUNTING(BluetoothProfileController)
   /**
    * @param aConnect:       If it's a connect request, the value should be set
    *                        to true. For disconnect request, set it to false.
    * @param aDeviceAddress: The address of remote device.
    * @param aRunnable:      Once the controller has done, the runnable will be
    *                        replied. When all connection/disconnection attemps
    *                        have failed, an error is fired. In other words,
    *                        reply a success if any attemp successes.
@@ -78,17 +80,16 @@ public:
    *                        aCod or disconnect all connected profiles.
    */
   BluetoothProfileController(bool aConnect,
                              const nsAString& aDeviceAddress,
                              BluetoothReplyRunnable* aRunnable,
                              BluetoothProfileControllerCallback aCallback,
                              uint16_t aServiceUuid,
                              uint32_t aCod = 0);
-  ~BluetoothProfileController();
 
   /**
    * The controller starts connecting/disconnecting profiles one by one
    * according to the order in array mProfiles.
    */
   void StartSession();
 
   /**
--- a/dom/browser-element/BrowserElementPanning.js
+++ b/dom/browser-element/BrowserElementPanning.js
@@ -24,16 +24,37 @@ const ContentPanning = {
   // Are we listening to touch or mouse events?
   watchedEventsType: '',
 
   // Are mouse events being delivered to this content along with touch
   // events, in violation of spec?
   hybridEvents: false,
 
   init: function cp_init() {
+    // If APZ is enabled, we do active element handling in C++
+    // (see widget/xpwidgets/ActiveElementManager.h), and panning
+    // itself in APZ, so we don't need to handle any touch events here.
+    if (docShell.asyncPanZoomEnabled === false) {
+      this._setupListenersForPanning();
+    }
+
+    addEventListener("unload",
+		     this._unloadHandler.bind(this),
+		     /* useCapture = */ false,
+		     /* wantsUntrusted = */ false);
+
+    addMessageListener("Viewport:Change", this._recvViewportChange.bind(this));
+    addMessageListener("Gesture:DoubleTap", this._recvDoubleTap.bind(this));
+    addEventListener("visibilitychange", this._handleVisibilityChange.bind(this));
+    kObservedEvents.forEach((topic) => {
+      Services.obs.addObserver(this, topic, false);
+    });
+  },
+
+  _setupListenersForPanning: function cp_setupListenersForPanning() {
     var events;
     try {
       content.document.createEvent('TouchEvent');
       events = ['touchstart', 'touchend', 'touchmove'];
       this.watchedEventsType = 'touch';
 #ifdef MOZ_WIDGET_GONK
       // The gonk widget backend does not deliver mouse events per
       // spec.  Third-party content isn't exposed to this behavior,
@@ -55,28 +76,16 @@ const ContentPanning = {
 
     events.forEach(function(type) {
       // Using the system group for mouse/touch events to avoid
       // missing events if .stopPropagation() has been called.
       els.addSystemEventListener(global, type,
                                  this.handleEvent.bind(this),
                                  /* useCapture = */ false);
     }.bind(this));
-
-    addEventListener("unload",
-		     this._unloadHandler.bind(this),
-		     /* useCapture = */ false,
-		     /* wantsUntrusted = */ false);
-
-    addMessageListener("Viewport:Change", this._recvViewportChange.bind(this));
-    addMessageListener("Gesture:DoubleTap", this._recvDoubleTap.bind(this));
-    addEventListener("visibilitychange", this._handleVisibilityChange.bind(this));
-    kObservedEvents.forEach((topic) => {
-      Services.obs.addObserver(this, topic, false);
-    });
   },
 
   handleEvent: function cp_handleEvent(evt) {
     // Ignore events targeting a <iframe mozbrowser> since those will be
     // handle by the BrowserElementPanning.js instance of it.
     if (evt.target instanceof Ci.nsIMozBrowserFrame) {
       return;
     }
@@ -190,18 +199,17 @@ const ContentPanning = {
     }
 
     this.position.set(screenX, screenY);
     KineticPanning.reset();
     KineticPanning.record(new Point(0, 0), evt.timeStamp);
 
     // We prevent start events to avoid sending a focus event at the end of this
     // touch series. See bug 889717.
-    if (docShell.asyncPanZoomEnabled === false &&
-        (this.panning || this.preventNextClick)) {
+    if ((this.panning || this.preventNextClick)) {
       evt.preventDefault();
     }
   },
 
   onTouchEnd: function cp_onTouchEnd(evt) {
     let touch = null;
     if (!this.dragging ||
         (this.watchedEventsType == 'touch' &&
@@ -230,17 +238,17 @@ const ContentPanning = {
     }
 
     if (this.target && click && (this.panning || this.preventNextClick)) {
       if (this.hybridEvents) {
         let target = this.target;
         let view = target.ownerDocument ? target.ownerDocument.defaultView
                                         : target;
         view.addEventListener('click', this, true, true);
-      } else if (docShell.asyncPanZoomEnabled === false) {
+      } else {
         // We prevent end events to avoid sending a focus event. See bug 889717.
         evt.preventDefault();
       }
     } else if (this.target && click && !this.panning) {
       this.notify(this._activationTimer);
     }
 
     this._finishPanning();
@@ -279,28 +287,25 @@ const ContentPanning = {
       this._resetActive();
     }
 
     // There's no possibility of us panning anything.
     if (!this.scrollCallback) {
       return;
     }
 
-    // If the application is not managed by the AsyncPanZoomController, then
-    // scroll manually.
-    if (docShell.asyncPanZoomEnabled === false) {
-      this.scrollCallback(delta.scale(-1));
-    }
+    // Scroll manually.
+    this.scrollCallback(delta.scale(-1));
 
     if (!this.panning && isPan) {
       this.panning = true;
       this._activationTimer.cancel();
     }
 
-    if (this.panning && docShell.asyncPanZoomEnabled === false) {
+    if (this.panning) {
       // Only do this when we're actually executing a pan gesture.
       // Otherwise synthetic mouse events will be canceled.
       evt.stopPropagation();
       evt.preventDefault();
     }
   },
 
   // nsITimerCallback
@@ -587,19 +592,18 @@ const ContentPanning = {
     return (showing > 0.9 && (ratioW > 0.9 || ratioH > 0.9)); 
   },
 
   _finishPanning: function() {
     this.dragging = false;
     delete this.primaryPointerId;
     this._activationTimer.cancel();
 
-    // If there is a scroll action but the application is not managed by
-    // the AsyncPanZoom controller, let's do a manual kinetic panning action.
-    if (this.panning && docShell.asyncPanZoomEnabled === false) {
+    // If there is a scroll action, let's do a manual kinetic panning action.
+    if (this.panning) {
       KineticPanning.start(this);
     }
   },
 
   _unloadHandler: function() {
     kObservedEvents.forEach((topic) => {
       Services.obs.removeObserver(this, topic);
     });
--- a/dom/camera/CameraControlImpl.h
+++ b/dom/camera/CameraControlImpl.h
@@ -14,18 +14,16 @@
 #include "AutoRwLock.h"
 #include "nsIDOMDeviceStorage.h"
 #include "ICameraControl.h"
 #include "CameraCommon.h"
 #include "DeviceStorage.h"
 #include "DeviceStorageFileDescriptor.h"
 #include "CameraControlListener.h"
 
-class DeviceStorageFileDescriptor;
-
 namespace mozilla {
 
 namespace layers {
   class Image;
 }
 
 class RecorderProfileManager;
 
--- a/dom/camera/DOMCameraManager.cpp
+++ b/dom/camera/DOMCameraManager.cpp
@@ -4,17 +4,17 @@
 
 #include "DOMCameraManager.h"
 #include "nsDebug.h"
 #include "jsapi.h"
 #include "Navigator.h"
 #include "nsPIDOMWindow.h"
 #include "mozilla/Services.h"
 #include "nsContentPermissionHelper.h"
-#include "nsObserverService.h"
+#include "nsIObserverService.h"
 #include "nsIPermissionManager.h"
 #include "DOMCameraControl.h"
 #include "nsDOMClassInfo.h"
 #include "CameraCommon.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/CameraManagerBinding.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
 #include "mozilla/dom/TabChild.h"
--- a/dom/camera/ICameraControl.h
+++ b/dom/camera/ICameraControl.h
@@ -5,17 +5,17 @@
 #ifndef DOM_CAMERA_ICAMERACONTROL_H
 #define DOM_CAMERA_ICAMERACONTROL_H
 
 #include "nsCOMPtr.h"
 #include "nsString.h"
 #include "nsAutoPtr.h"
 #include "nsISupportsImpl.h"
 
-class DeviceStorageFileDescriptor;
+struct DeviceStorageFileDescriptor;
 
 class nsIFile;
 
 namespace mozilla {
 
 class CameraControlListener;
 class RecorderProfileManager;
 
--- a/dom/devicestorage/DeviceStorage.h
+++ b/dom/devicestorage/DeviceStorage.h
@@ -316,23 +316,17 @@ private:
   bool mIsWatchingFile;
   bool mAllowedToWatchFile;
 
   nsresult Notify(const char* aReason, class DeviceStorageFile* aFile);
 
   friend class WatchFileEvent;
   friend class DeviceStorageRequest;
 
-  class VolumeNameCache : public mozilla::RefCounted<VolumeNameCache>
-  {
-  public:
-    MOZ_DECLARE_REFCOUNTED_TYPENAME(VolumeNameCache)
-    nsTArray<nsString>  mVolumeNames;
-  };
-  static mozilla::StaticRefPtr<VolumeNameCache> sVolumeNameCache;
+  static mozilla::StaticAutoPtr<nsTArray<nsString>> sVolumeNameCache;
 
 #ifdef MOZ_WIDGET_GONK
   nsString mLastStatus;
   void DispatchMountChangeEvent(nsAString& aVolumeStatus);
 #endif
 
   // nsIDOMDeviceStorage.type
   enum {
--- a/dom/devicestorage/DeviceStorageFileDescriptor.h
+++ b/dom/devicestorage/DeviceStorageFileDescriptor.h
@@ -4,18 +4,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef DeviceStorageFileDescriptor_h
 #define DeviceStorageFileDescriptor_h
 
 #include "mozilla/ipc/FileDescriptor.h"
 
-class DeviceStorageFileDescriptor MOZ_FINAL
-  : public mozilla::RefCounted<DeviceStorageFileDescriptor>
+struct DeviceStorageFileDescriptor MOZ_FINAL
 {
-public:
-  MOZ_DECLARE_REFCOUNTED_TYPENAME(DeviceStorageFileDescriptor)
+  NS_INLINE_DECL_REFCOUNTING(DeviceStorageFileDescriptor)
   nsRefPtr<DeviceStorageFile> mDSFile;
   mozilla::ipc::FileDescriptor mFileDescriptor;
 };
 
 #endif
--- a/dom/devicestorage/DeviceStorageRequestChild.h
+++ b/dom/devicestorage/DeviceStorageRequestChild.h
@@ -4,17 +4,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_devicestorage_DeviceStorageRequestChild_h
 #define mozilla_dom_devicestorage_DeviceStorageRequestChild_h
 
 #include "mozilla/dom/devicestorage/PDeviceStorageRequestChild.h"
 
 class DeviceStorageFile;
-class DeviceStorageFileDescriptor;
+struct DeviceStorageFileDescriptor;
 
 namespace mozilla {
 namespace dom {
 
 class DOMRequest;
 
 namespace devicestorage {
 
--- a/dom/devicestorage/nsDeviceStorage.cpp
+++ b/dom/devicestorage/nsDeviceStorage.cpp
@@ -110,25 +110,26 @@ DeviceStorageUsedSpaceCache::CreateOrGet
 
   MOZ_ASSERT(NS_IsMainThread());
 
   sDeviceStorageUsedSpaceCache = new DeviceStorageUsedSpaceCache();
   ClearOnShutdown(&sDeviceStorageUsedSpaceCache);
   return sDeviceStorageUsedSpaceCache;
 }
 
-TemporaryRef<DeviceStorageUsedSpaceCache::CacheEntry>
+already_AddRefed<DeviceStorageUsedSpaceCache::CacheEntry>
 DeviceStorageUsedSpaceCache::GetCacheEntry(const nsAString& aStorageName)
 {
-  nsTArray<RefPtr<CacheEntry> >::size_type numEntries = mCacheEntries.Length();
-  nsTArray<RefPtr<CacheEntry> >::index_type i;
+  nsTArray<nsRefPtr<CacheEntry>>::size_type numEntries = mCacheEntries.Length();
+  nsTArray<nsRefPtr<CacheEntry>>::index_type i;
   for (i = 0; i < numEntries; i++) {
-    RefPtr<CacheEntry> cacheEntry = mCacheEntries[i];
+    nsRefPtr<CacheEntry>& cacheEntry = mCacheEntries[i];
     if (cacheEntry->mStorageName.Equals(aStorageName)) {
-      return cacheEntry;
+      nsRefPtr<CacheEntry> addRefedCacheEntry = cacheEntry;
+      return addRefedCacheEntry.forget();
     }
   }
   return nullptr;
 }
 
 static int64_t
 GetFreeBytes(const nsAString& aStorageName)
 {
@@ -144,17 +145,17 @@ GetFreeBytes(const nsAString& aStorageNa
 
 nsresult
 DeviceStorageUsedSpaceCache::AccumUsedSizes(const nsAString& aStorageName,
                                             uint64_t* aPicturesSoFar,
                                             uint64_t* aVideosSoFar,
                                             uint64_t* aMusicSoFar,
                                             uint64_t* aTotalSoFar)
 {
-  RefPtr<CacheEntry> cacheEntry = GetCacheEntry(aStorageName);
+  nsRefPtr<CacheEntry> cacheEntry = GetCacheEntry(aStorageName);
   if (!cacheEntry || cacheEntry->mDirty) {
     return NS_ERROR_NOT_AVAILABLE;
   }
   int64_t freeBytes = GetFreeBytes(cacheEntry->mStorageName);
   if (freeBytes != cacheEntry->mFreeBytes) {
     // Free space changed, so our cached results are no longer valid.
     return NS_ERROR_NOT_AVAILABLE;
   }
@@ -169,35 +170,35 @@ DeviceStorageUsedSpaceCache::AccumUsedSi
 
 void
 DeviceStorageUsedSpaceCache::SetUsedSizes(const nsAString& aStorageName,
                                           uint64_t aPictureSize,
                                           uint64_t aVideosSize,
                                           uint64_t aMusicSize,
                                           uint64_t aTotalUsedSize)
 {
-  RefPtr<CacheEntry> cacheEntry = GetCacheEntry(aStorageName);
+  nsRefPtr<CacheEntry> cacheEntry = GetCacheEntry(aStorageName);
   if (!cacheEntry) {
     cacheEntry = new CacheEntry;
     cacheEntry->mStorageName = aStorageName;
     mCacheEntries.AppendElement(cacheEntry);
   }
   cacheEntry->mFreeBytes = GetFreeBytes(cacheEntry->mStorageName);
 
   cacheEntry->mPicturesUsedSize = aPictureSize;
   cacheEntry->mVideosUsedSize = aVideosSize;
   cacheEntry->mMusicUsedSize = aMusicSize;
   cacheEntry->mTotalUsedSize = aTotalUsedSize;
   cacheEntry->mDirty = false;
 }
 
-class GlobalDirs : public RefCounted<GlobalDirs>
+class GlobalDirs
 {
 public:
-  MOZ_DECLARE_REFCOUNTED_TYPENAME(GlobalDirs)
+  NS_INLINE_DECL_REFCOUNTING(GlobalDirs)
 #if !defined(MOZ_WIDGET_GONK)
   nsCOMPtr<nsIFile> pictures;
   nsCOMPtr<nsIFile> videos;
   nsCOMPtr<nsIFile> music;
   nsCOMPtr<nsIFile> sdcard;
 #endif
   nsCOMPtr<nsIFile> apps;
   nsCOMPtr<nsIFile> crashes;
@@ -3174,26 +3175,25 @@ nsDOMDeviceStorage::Shutdown()
     UnregisterForSDCardChanges(this);
   }
 
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   obs->RemoveObserver(this, "file-watcher-update");
   obs->RemoveObserver(this, "disk-space-watcher");
 }
 
-StaticRefPtr<nsDOMDeviceStorage::VolumeNameCache>
-  nsDOMDeviceStorage::sVolumeNameCache;
+StaticAutoPtr<nsTArray<nsString>> nsDOMDeviceStorage::sVolumeNameCache;
 
 // static
 void
 nsDOMDeviceStorage::GetOrderedVolumeNames(
   nsDOMDeviceStorage::VolumeNameArray &aVolumeNames)
 {
-  if (sVolumeNameCache && sVolumeNameCache->mVolumeNames.Length() > 0) {
-    aVolumeNames.AppendElements(sVolumeNameCache->mVolumeNames);
+  if (sVolumeNameCache && sVolumeNameCache->Length() > 0) {
+    aVolumeNames.AppendElements(*sVolumeNameCache);
     return;
   }
 #ifdef MOZ_WIDGET_GONK
   nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
   if (vs) {
     vs->GetVolumeNames(aVolumeNames);
 
     // If the volume sdcard exists, then we want it to be first.
@@ -3204,18 +3204,18 @@ nsDOMDeviceStorage::GetOrderedVolumeName
       aVolumeNames.RemoveElementAt(sdcardIndex);
       aVolumeNames.InsertElementAt(0, NS_LITERAL_STRING("sdcard"));
     }
   }
 #endif
   if (aVolumeNames.IsEmpty()) {
     aVolumeNames.AppendElement(EmptyString());
   }
-  sVolumeNameCache = new VolumeNameCache;
-  sVolumeNameCache->mVolumeNames.AppendElements(aVolumeNames);
+  sVolumeNameCache = new nsTArray<nsString>;
+  sVolumeNameCache->AppendElements(aVolumeNames);
 }
 
 // static
 void
 nsDOMDeviceStorage::CreateDeviceStorageFor(nsPIDOMWindow* aWin,
                                            const nsAString &aType,
                                            nsDOMDeviceStorage** aStore)
 {
--- a/dom/devicestorage/nsDeviceStorage.h
+++ b/dom/devicestorage/nsDeviceStorage.h
@@ -24,17 +24,16 @@ class nsPIDOMWindow;
 #include "nsWeakPtr.h"
 #include "nsIDOMEventListener.h"
 #include "nsIObserver.h"
 #include "nsIStringBundle.h"
 #include "mozilla/Mutex.h"
 #include "prtime.h"
 #include "DeviceStorage.h"
 #include "mozilla/dom/devicestorage/DeviceStorageRequestChild.h"
-#include "mozilla/RefPtr.h"
 #include "mozilla/StaticPtr.h"
 
 namespace mozilla {
 class ErrorResult;
 } // namespace mozilla
 
 #define POST_ERROR_EVENT_FILE_EXISTS                 "NoModificationAllowedError"
 #define POST_ERROR_EVENT_FILE_DOES_NOT_EXIST         "NotFoundError"
@@ -75,17 +74,17 @@ public:
                          const nsAString& aStorageName)
         : mCache(aCache)
         , mStorageName(aStorageName) {}
 
       ~InvalidateRunnable() {}
 
       NS_IMETHOD Run() MOZ_OVERRIDE
       {
-        mozilla::RefPtr<DeviceStorageUsedSpaceCache::CacheEntry> cacheEntry;
+        nsRefPtr<DeviceStorageUsedSpaceCache::CacheEntry> cacheEntry;
         cacheEntry = mCache->GetCacheEntry(mStorageName);
         if (cacheEntry) {
           cacheEntry->mDirty = true;
         }
         return NS_OK;
       }
     private:
       DeviceStorageUsedSpaceCache* mCache;
@@ -115,31 +114,31 @@ public:
 
   void SetUsedSizes(const nsAString& aStorageName,
                     uint64_t aPictureSize, uint64_t aVideosSize,
                     uint64_t aMusicSize, uint64_t aTotalSize);
 
 private:
   friend class InvalidateRunnable;
 
-  class CacheEntry : public mozilla::RefCounted<CacheEntry> 
+  struct CacheEntry
   {
-  public:
-    MOZ_DECLARE_REFCOUNTED_TYPENAME(DeviceStorageUsedSpaceCache::CacheEntry)
+    NS_INLINE_DECL_REFCOUNTING(DeviceStorageUsedSpaceCache::CacheEntry)
+
     bool mDirty;
     nsString mStorageName;
     int64_t  mFreeBytes;
     uint64_t mPicturesUsedSize;
     uint64_t mVideosUsedSize;
     uint64_t mMusicUsedSize;
     uint64_t mTotalUsedSize;
   };
-  mozilla::TemporaryRef<CacheEntry> GetCacheEntry(const nsAString& aStorageName);
+  already_AddRefed<CacheEntry> GetCacheEntry(const nsAString& aStorageName);
 
-  nsTArray<mozilla::RefPtr<CacheEntry> > mCacheEntries;
+  nsTArray<nsRefPtr<CacheEntry>> mCacheEntries;
 
   nsCOMPtr<nsIThread> mIOThread;
 
   static mozilla::StaticAutoPtr<DeviceStorageUsedSpaceCache> sDeviceStorageUsedSpaceCache;
 };
 
 class DeviceStorageTypeChecker MOZ_FINAL
 {
--- a/dom/interfaces/devicestorage/nsIDOMDeviceStorage.idl
+++ b/dom/interfaces/devicestorage/nsIDOMDeviceStorage.idl
@@ -7,17 +7,17 @@
 interface nsIDOMBlob;
 interface nsIDOMDOMRequest;
 interface nsIDOMDOMCursor;
 interface nsIDOMDeviceStorageChangeEvent;
 interface nsIDOMEventListener;
 interface nsIFile;
 
 %{C++
-class DeviceStorageFileDescriptor;
+struct DeviceStorageFileDescriptor;
 %}
 [ptr] native DeviceStorageFdPtr(DeviceStorageFileDescriptor);
 
 [scriptable, uuid(8b724547-3c78-4244-969a-f00a1f4ae0c3), builtinclass]
 interface nsIDOMDeviceStorage : nsIDOMEventTarget
 {
     [implicit_jscontext] attribute jsval onchange;
     nsIDOMDOMRequest add(in nsIDOMBlob aBlob);
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -45,16 +45,17 @@ using class mozilla::WidgetSelectionEven
 using class mozilla::WidgetTextEvent from "ipc/nsGUIEventIPC.h";
 using class mozilla::WidgetTouchEvent from "ipc/nsGUIEventIPC.h";
 using struct mozilla::dom::RemoteDOMEvent from "mozilla/dom/TabMessageUtils.h";
 using mozilla::dom::ScreenOrientation from "mozilla/dom/ScreenOrientation.h";
 using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h";
 using mozilla::CSSPoint from "Units.h";
 using mozilla::CSSToScreenScale from "Units.h";
 using mozilla::CommandInt from "mozilla/EventForwards.h";
+using mozilla::layers::GeckoContentController::APZStateChange from "mozilla/layers/GeckoContentController.h";
 
 namespace mozilla {
 namespace dom {
 
 struct NativeKeyBinding
 {
   CommandInt[] singleLineCommands;
   CommandInt[] multiLineCommands;
@@ -405,21 +406,20 @@ child:
      * relative to the current scroll offset. In the case the "contextmenu"
      * event generated by the preceding HandleLongTap call was not handled,
      * this message is expected to generate a "mousedown" and "mouseup"
      * series of events
      */
     HandleLongTapUp(CSSPoint point, ScrollableLayerGuid aGuid);
 
     /**
-     * Notifies the child that the parent has begun or finished transforming
-     * the visible child content area. Useful for showing/hiding scrollbars.
+     * Notifies the child about various APZ state changes.
+     * See GeckoContentController::NotifyAPZStateChange() for details.
      */
-    NotifyTransformBegin(ViewID aViewId);
-    NotifyTransformEnd(ViewID aViewId);
+    NotifyAPZStateChange(ViewID aViewId, APZStateChange aChange, int aArg);
 
     /**
      * Sending an activate message moves focus to the child.
      */
     Activate();
 
     Deactivate();
 
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -69,16 +69,17 @@
 #include "nsViewportInfo.h"
 #include "JavaScriptChild.h"
 #include "APZCCallbackHelper.h"
 #include "nsILoadContext.h"
 #include "ipc/nsGUIEventIPC.h"
 #include "mozilla/gfx/Matrix.h"
 #include "UnitTransforms.h"
 #include "ClientLayerManager.h"
+#include "ActiveElementManager.h"
 
 #include "nsColorPickerProxy.h"
 
 #ifdef DEBUG
 #include "PCOMContentPermissionRequestChild.h"
 #endif /* DEBUG */
 
 #define BROWSER_ELEMENT_CHILD_SCRIPT \
@@ -682,16 +683,17 @@ TabChild::TabChild(ContentChild* aManage
   , mDidFakeShow(false)
   , mNotified(false)
   , mTriedBrowserInit(false)
   , mOrientation(eScreenOrientation_PortraitPrimary)
   , mUpdateHitRegion(false)
   , mContextMenuHandled(false)
   , mWaitingTouchListeners(false)
   , mIgnoreKeyPressEvent(false)
+  , mActiveElementManager(new ActiveElementManager())
 {
   if (!sActiveDurationMsSet) {
     Preferences::AddIntVarCache(&sActiveDurationMs,
                                 "ui.touch_activation.duration_ms",
                                 sActiveDurationMs);
     sActiveDurationMsSet = true;
   }
 }
@@ -1725,33 +1727,59 @@ TabChild::RecvHandleLongTapUp(const CSSP
     return true;
   }
 
   RecvHandleSingleTap(aPoint, aGuid);
   return true;
 }
 
 bool
-TabChild::RecvNotifyTransformBegin(const ViewID& aViewId)
+TabChild::RecvNotifyAPZStateChange(const ViewID& aViewId,
+                                   const APZStateChange& aChange,
+                                   const int& aArg)
 {
-  nsIScrollableFrame* sf = nsLayoutUtils::FindScrollableFrameFor(aViewId);
-  nsIScrollbarOwner* scrollbarOwner = do_QueryFrame(sf);
-  if (scrollbarOwner) {
-    scrollbarOwner->ScrollbarActivityStarted();
+  switch (aChange)
+  {
+  case APZStateChange::TransformBegin:
+  {
+    nsIScrollableFrame* sf = nsLayoutUtils::FindScrollableFrameFor(aViewId);
+    nsIScrollbarOwner* scrollbarOwner = do_QueryFrame(sf);
+    if (scrollbarOwner) {
+      scrollbarOwner->ScrollbarActivityStarted();
+    }
+    break;
   }
-  return true;
-}
-
-bool
-TabChild::RecvNotifyTransformEnd(const ViewID& aViewId)
-{
-  nsIScrollableFrame* sf = nsLayoutUtils::FindScrollableFrameFor(aViewId);
-  nsIScrollbarOwner* scrollbarOwner = do_QueryFrame(sf);
-  if (scrollbarOwner) {
-    scrollbarOwner->ScrollbarActivityStopped();
+  case APZStateChange::TransformEnd:
+  {
+    nsIScrollableFrame* sf = nsLayoutUtils::FindScrollableFrameFor(aViewId);
+    nsIScrollbarOwner* scrollbarOwner = do_QueryFrame(sf);
+    if (scrollbarOwner) {
+      scrollbarOwner->ScrollbarActivityStopped();
+    }
+    break;
+  }
+  case APZStateChange::StartTouch:
+  {
+    mActiveElementManager->HandleTouchStart(aArg);
+    break;
+  }
+  case APZStateChange::StartPanning:
+  {
+    mActiveElementManager->HandlePanStart();
+    break;
+  }
+  case APZStateChange::EndTouch:
+  {
+    mActiveElementManager->HandleTouchEnd(aArg);
+    break;
+  }
+  default:
+    // APZStateChange has a 'sentinel' value, and the compiler complains
+    // if an enumerator is not handled and there is no 'default' case.
+    break;
   }
   return true;
 }
 
 bool
 TabChild::RecvActivate()
 {
   nsCOMPtr<nsIWebBrowserFocus> browser = do_QueryInterface(WebNavigation());
@@ -1946,16 +1974,20 @@ TabChild::RecvRealTouchEvent(const Widge
 
   nsEventStatus status = DispatchWidgetEvent(localEvent);
 
   if (!IsAsyncPanZoomEnabled()) {
     UpdateTapState(localEvent, status);
     return true;
   }
 
+  if (aEvent.message == NS_TOUCH_START && localEvent.touches.Length() > 0) {
+    mActiveElementManager->SetTargetElement(localEvent.touches[0]->Target());
+  }
+
   nsCOMPtr<nsPIDOMWindow> outerWindow = do_GetInterface(WebNavigation());
   nsCOMPtr<nsPIDOMWindow> innerWindow = outerWindow->GetCurrentInnerWindow();
 
   if (!innerWindow || !innerWindow->HasTouchEventListeners()) {
     SendContentReceivedTouch(aGuid, false);
     return true;
   }
 
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -37,16 +37,20 @@
 class nsICachedFileDescriptorListener;
 class nsIDOMWindowUtils;
 
 namespace mozilla {
 namespace layout {
 class RenderFrameChild;
 }
 
+namespace widget {
+class ActiveElementManager;
+}
+
 namespace dom {
 
 class TabChild;
 class ClonedMessageData;
 class TabChildBase;
 
 class TabChildGlobal : public DOMEventTargetHelper,
                        public nsIContentFrameMessageManager,
@@ -223,16 +227,17 @@ class TabChild : public PBrowserChild,
                  public nsIObserver,
                  public TabContext,
                  public nsITooltipListener,
                  public TabChildBase
 {
     typedef mozilla::dom::ClonedMessageData ClonedMessageData;
     typedef mozilla::layout::RenderFrameChild RenderFrameChild;
     typedef mozilla::layout::ScrollingBehavior ScrollingBehavior;
+    typedef mozilla::widget::ActiveElementManager ActiveElementManager;
 
 public:
     /** 
      * This is expected to be called off the critical path to content
      * startup.  This is an opportunity to load things that are slow
      * on the critical path.
      */
     static void PreloadSlowThings();
@@ -291,18 +296,19 @@ public:
     virtual bool RecvHandleDoubleTap(const CSSPoint& aPoint,
                                      const mozilla::layers::ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
     virtual bool RecvHandleSingleTap(const CSSPoint& aPoint,
                                      const mozilla::layers::ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
     virtual bool RecvHandleLongTap(const CSSPoint& aPoint,
                                    const mozilla::layers::ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
     virtual bool RecvHandleLongTapUp(const CSSPoint& aPoint,
                                      const mozilla::layers::ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
-    virtual bool RecvNotifyTransformBegin(const ViewID& aViewId) MOZ_OVERRIDE;
-    virtual bool RecvNotifyTransformEnd(const ViewID& aViewId) MOZ_OVERRIDE;
+    virtual bool RecvNotifyAPZStateChange(const ViewID& aViewId,
+                                          const APZStateChange& aChange,
+                                          const int& aArg) MOZ_OVERRIDE;
     virtual bool RecvActivate() MOZ_OVERRIDE;
     virtual bool RecvDeactivate() MOZ_OVERRIDE;
     virtual bool RecvMouseEvent(const nsString& aType,
                                 const float&    aX,
                                 const float&    aY,
                                 const int32_t&  aButton,
                                 const int32_t&  aClickCount,
                                 const int32_t&  aModifiers,
@@ -539,16 +545,17 @@ private:
     bool mTriedBrowserInit;
     ScreenOrientation mOrientation;
     bool mUpdateHitRegion;
     bool mContextMenuHandled;
     bool mWaitingTouchListeners;
     void FireSingleTapEvent(LayoutDevicePoint aPoint);
 
     bool mIgnoreKeyPressEvent;
+    nsRefPtr<ActiveElementManager> mActiveElementManager;
 
     DISALLOW_EVIL_CONSTRUCTORS(TabChild);
 };
 
 }
 }
 
 #endif // mozilla_dom_TabChild_h
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -549,27 +549,22 @@ void TabParent::HandleLongTapUp(const CS
                                 int32_t aModifiers,
                                 const ScrollableLayerGuid &aGuid)
 {
   if (!mIsDestroyed) {
     unused << SendHandleLongTapUp(aPoint, aGuid);
   }
 }
 
-void TabParent::NotifyTransformBegin(ViewID aViewId)
+void TabParent::NotifyAPZStateChange(ViewID aViewId,
+                                     APZStateChange aChange,
+                                     int aArg)
 {
   if (!mIsDestroyed) {
-    unused << SendNotifyTransformBegin(aViewId);
-  }
-}
-
-void TabParent::NotifyTransformEnd(ViewID aViewId)
-{
-  if (!mIsDestroyed) {
-    unused << SendNotifyTransformEnd(aViewId);
+    unused << SendNotifyAPZStateChange(aViewId, aChange, aArg);
   }
 }
 
 void
 TabParent::Activate()
 {
   if (!mIsDestroyed) {
     unused << SendActivate();
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -207,18 +207,19 @@ public:
                          int32_t aModifiers,
                          const ScrollableLayerGuid& aGuid);
     void HandleLongTap(const CSSPoint& aPoint,
                        int32_t aModifiers,
                        const ScrollableLayerGuid& aGuid);
     void HandleLongTapUp(const CSSPoint& aPoint,
                          int32_t aModifiers,
                          const ScrollableLayerGuid& aGuid);
-    void NotifyTransformBegin(ViewID aViewId);
-    void NotifyTransformEnd(ViewID aViewId);
+    void NotifyAPZStateChange(ViewID aViewId,
+                              APZStateChange aChange,
+                              int aArg);
     void Activate();
     void Deactivate();
 
     bool MapEventCoordinatesForChildProcess(mozilla::WidgetEvent* aEvent);
     void MapEventCoordinatesForChildProcess(const LayoutDeviceIntPoint& aOffset,
                                             mozilla::WidgetEvent* aEvent);
 
     void SendMouseEvent(const nsAString& aType, float aX, float aY,
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -6,17 +6,17 @@
 #include "mozilla/Services.h"
 #include "mozilla/unused.h"
 #include "nsIMediaManager.h"
 
 #include "nsHashKeys.h"
 #include "nsGlobalWindow.h"
 #include "nsClassHashtable.h"
 #include "nsRefPtrHashtable.h"
-#include "nsObserverService.h"
+#include "nsIObserver.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
 
 #include "nsPIDOMWindow.h"
 #include "nsIDOMNavigatorUserMedia.h"
 #include "nsXULAppAPI.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Preferences.h"
--- a/dom/system/gonk/AutoMounter.cpp
+++ b/dom/system/gonk/AutoMounter.cpp
@@ -158,22 +158,22 @@ protected:
 private:
     const static int kMaxErrorCount = 3; // Max number of errors before we give up
 
     int   mErrorCount;
 };
 
 /***************************************************************************/
 
-class AutoMounter : public RefCounted<AutoMounter>
+class AutoMounter
 {
 public:
-  MOZ_DECLARE_REFCOUNTED_TYPENAME(AutoMounter)
+  NS_INLINE_DECL_REFCOUNTING(AutoMounter)
 
-  typedef nsTArray<RefPtr<Volume> > VolumeArray;
+  typedef nsTArray<RefPtr<Volume>> VolumeArray;
 
   AutoMounter()
     : mResponseCallback(new AutoMounterResponseCallback),
       mMode(AUTOMOUNTER_DISABLE)
   {
     VolumeManager::RegisterStateObserver(&mVolumeManagerStateObserver);
     Volume::RegisterObserver(&mVolumeEventObserver);
 
@@ -687,31 +687,31 @@ UsbCableEventIOThread()
 *
 *   Public API
 *
 *   Since the AutoMounter runs in IO Thread context, we need to switch
 *   to IOThread context before we can do anything.
 *
 **************************************************************************/
 
-class UsbCableObserver : public SwitchObserver,
-                         public RefCounted<UsbCableObserver>
+class UsbCableObserver MOZ_FINAL : public SwitchObserver
 {
+  ~UsbCableObserver()
+  {
+    UnregisterSwitchObserver(SWITCH_USB, this);
+  }
+
 public:
-  MOZ_DECLARE_REFCOUNTED_TYPENAME(UsbCableObserver)
+  NS_INLINE_DECL_REFCOUNTING(UsbCableObserver)
+
   UsbCableObserver()
   {
     RegisterSwitchObserver(SWITCH_USB, this);
   }
 
-  ~UsbCableObserver()
-  {
-    UnregisterSwitchObserver(SWITCH_USB, this);
-  }
-
   virtual void Notify(const SwitchEvent& aEvent)
   {
     DBG("UsbCable switch device: %d state: %s\n",
         aEvent.device(), SwitchStateStr(aEvent));
     XRE_GetIOMessageLoop()->PostTask(
         FROM_HERE,
         NewRunnableFunction(UsbCableEventIOThread));
   }
--- a/dom/system/gonk/Volume.h
+++ b/dom/system/gonk/Volume.h
@@ -4,35 +4,36 @@
 
 #ifndef mozilla_system_volume_h__
 #define mozilla_system_volume_h__
 
 #include "VolumeCommand.h"
 #include "nsIVolume.h"
 #include "nsString.h"
 #include "mozilla/Observer.h"
-#include "mozilla/RefPtr.h"
+#include "nsISupportsImpl.h"
 #include "nsWhitespaceTokenizer.h"
 
 namespace mozilla {
 namespace system {
 
 /***************************************************************************
 *
 *   There is an instance of the Volume class for each volume reported
 *   from vold.
 *
 *   Each volume originates from the /system/etv/vold.fstab file.
 *
 ***************************************************************************/
 
-class Volume : public RefCounted<Volume>
+class Volume MOZ_FINAL
 {
 public:
-  MOZ_DECLARE_REFCOUNTED_TYPENAME(Volume)
+  NS_INLINE_DECL_REFCOUNTING(Volume)
+
   Volume(const nsCSubstring& aVolumeName);
 
   typedef long STATE; // States are now defined in nsIVolume.idl
 
   static const char* StateStr(STATE aState) { return NS_VolumeStateStr(aState); }
   const char* StateStr() const  { return StateStr(mState); }
   STATE State() const           { return mState; }
 
--- a/dom/system/gonk/VolumeCommand.h
+++ b/dom/system/gonk/VolumeCommand.h
@@ -1,16 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_system_volumecommand_h__
 #define mozilla_system_volumecommand_h__
 
 #include "nsString.h"
+#include "nsISupportsImpl.h"
 #include "mozilla/RefPtr.h"
 #include <algorithm>
 #include <vold/ResponseCode.h>
 
 namespace mozilla {
 namespace system {
 
 class Volume;
@@ -27,25 +28,26 @@ class VolumeCommand;
 *   The responses from vold are all of the form:
 *
 *     <ResponseCode> <String>
 *
 *   Valid Response codes can be found in the vold/ResponseCode.h header.
 *
 ***************************************************************************/
 
-class VolumeResponseCallback : public RefCounted<VolumeResponseCallback>
+class VolumeResponseCallback
 {
+protected:
+  virtual ~VolumeResponseCallback() {}
+
 public:
-  MOZ_DECLARE_REFCOUNTED_TYPENAME(VolumeResponseCallback)
+  NS_INLINE_DECL_REFCOUNTING(VolumeResponseCallback)
   VolumeResponseCallback()
     : mResponseCode(0), mPending(false) {}
 
-  virtual ~VolumeResponseCallback() {}
-
   bool Done() const
   {
     // Response codes from the 200, 400, and 500 series all indicated that
     // the command has completed.
 
     return (mResponseCode >= ResponseCode::CommandOkay)
         && (mResponseCode < ResponseCode::UnsolicitedInformational);
   }
@@ -101,36 +103,38 @@ private:
 *   Commands sent to vold need an explicit null character so we add one
 *   to the command to ensure that it's included in the length.
 *
 *   All of these commands are asynchronous in nature, and the
 *   ResponseReceived callback will be called when a response is available.
 *
 ***************************************************************************/
 
-class VolumeCommand : public RefCounted<VolumeCommand>
+class VolumeCommand
 {
+protected:
+  virtual ~VolumeCommand() {}
+
 public:
-  MOZ_DECLARE_REFCOUNTED_TYPENAME(VolumeCommand)
+  NS_INLINE_DECL_REFCOUNTING(VolumeCommand)
+
   VolumeCommand(VolumeResponseCallback* aCallback)
     : mBytesConsumed(0),
       mCallback(aCallback)
   {
     SetCmd(NS_LITERAL_CSTRING(""));
   }
 
   VolumeCommand(const nsACString& aCommand, VolumeResponseCallback* aCallback)
     : mBytesConsumed(0),
       mCallback(aCallback)
   {
     SetCmd(aCommand);
   }
 
-  virtual ~VolumeCommand() {}
-
   void SetCmd(const nsACString& aCommand)
   {
     mCmd.Truncate();
 #if ANDROID_VERSION >= 17
     // JB requires a sequence number at the beginning of messages.
     // It doesn't matter what we use, so we use 0.
     mCmd = "0 ";
 #endif
--- a/dom/system/gonk/VolumeManager.h
+++ b/dom/system/gonk/VolumeManager.h
@@ -6,17 +6,17 @@
 #define mozilla_system_volumemanager_h__
 
 #include <vector>
 #include <queue>
 
 #include "base/message_loop.h"
 #include "mozilla/FileUtils.h"
 #include "mozilla/Observer.h"
-#include "mozilla/RefPtr.h"
+#include "nsISupportsImpl.h"
 #include "nsString.h"
 #include "nsTArray.h"
 
 #include "Volume.h"
 #include "VolumeCommand.h"
 
 namespace mozilla {
 namespace system {
@@ -68,26 +68,26 @@ namespace system {
 *   There is also a command line tool called vdc, which can be used to send
 *   the above commands to vold.
 *
 *   Currently, only the volume list, share/unshare, and mount/unmount
 *   commands are being used.
 *
 ***************************************************************************/
 
-class VolumeManager : public MessageLoopForIO::LineWatcher,
-                      public RefCounted<VolumeManager>
+class VolumeManager MOZ_FINAL : public MessageLoopForIO::LineWatcher
 {
+  virtual ~VolumeManager();
+
 public:
-  MOZ_DECLARE_REFCOUNTED_TYPENAME(VolumeManager)
+  NS_INLINE_DECL_REFCOUNTING(VolumeManager)
 
-  typedef nsTArray<RefPtr<Volume> > VolumeArray;
+  typedef nsTArray<RefPtr<Volume>> VolumeArray;
 
   VolumeManager();
-  virtual ~VolumeManager();
 
   //-----------------------------------------------------------------------
   //
   // State related methods.
   //
   // The VolumeManager starts off in the STARTING state. Once a connection
   // is established with vold, it asks for a list of volumes, and once the
   // volume list has been received, then the VolumeManager enters the
--- a/dom/system/gonk/VolumeServiceIOThread.h
+++ b/dom/system/gonk/VolumeServiceIOThread.h
@@ -14,23 +14,24 @@ namespace system {
 
 class nsVolumeService;
 
 /***************************************************************************
 * The nsVolumeServiceIOThread is a companion class to the nsVolumeService
 * class, but whose methods are called from IOThread.
 */
 class VolumeServiceIOThread : public VolumeManager::StateObserver,
-                              public Volume::EventObserver,
-                              public RefCounted<VolumeServiceIOThread>
+                              public Volume::EventObserver
 {
+  ~VolumeServiceIOThread();
+
 public:
-  MOZ_DECLARE_REFCOUNTED_TYPENAME(VolumeServiceIOThread)
+  NS_INLINE_DECL_REFCOUNTING(VolumeServiceIOThread)
+
   VolumeServiceIOThread(nsVolumeService* aVolumeService);
-  ~VolumeServiceIOThread();
 
 private:
   void  UpdateAllVolumes();
 
   virtual void Notify(const VolumeManager::StateChangedEvent& aEvent);
   virtual void Notify(Volume* const & aVolume);
 
   RefPtr<nsVolumeService>   mVolumeService;
--- a/dom/xbl/test/file_bug821850.xhtml
+++ b/dom/xbl/test/file_bug821850.xhtml
@@ -48,21 +48,22 @@ https://bugzilla.mozilla.org/show_bug.cg
              "Unexposed method should be visible to XBL");
           is(typeof bound.wrappedJSObject.unexposedMethod, 'undefined',
              "Unexposed method should not be defined in content");
           is(typeof bound.unexposedProperty, 'number',
              "Unexposed property should be visible to XBL");
           is(typeof bound.wrappedJSObject.unexposedProperty, 'undefined',
              "Unexposed property should not be defined in content");
 
-          // Check that here document.QueryInterface works
-          ok("QueryInterface" in document,
-             "Should have a document.QueryInterface here");
-          is(document.QueryInterface(Components.interfaces.nsIDOMDocument),
-             document, "Should be able to QI the document");
+          // Check that here HTMLImageElement.QueryInterface works
+          var img = document.querySelector("img");
+          ok("QueryInterface" in img,
+             "Should have a img.QueryInterface here");
+          is(img.QueryInterface(Components.interfaces.nsIImageLoadingContent),
+             img, "Should be able to QI the image");
 
           // Make sure standard constructors work right in the presence of
           // sandboxPrototype and Xray-resolved constructors.
           is(window.Function, XPCNativeWrapper(window.wrappedJSObject.Function),
              "window.Function comes from the window, not the global");
           ok(Function != window.Function, "Function constructors are distinct");
           is(Object.getPrototypeOf(Function.prototype), Object.getPrototypeOf({foo: 42}),
              "Function constructor is local");
@@ -284,13 +285,14 @@ https://bugzilla.mozilla.org/show_bug.cg
 </script>
 </head>
 <body onload="setup()">
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=821850">Mozilla Bug 821850</a>
 <p id="display"></p>
 <div id="content">
   <div id="bound">Bound element</div>
   <div id="bound2">Bound element</div>
+  <img/>
 </div>
 <pre id="test">
 </pre>
 </body>
 </html>
--- a/editor/libeditor/html/tests/browserscope/lib/richtext2/richtext2/tests/insert.py
+++ b/editor/libeditor/html/tests/browserscope/lib/richtext2/richtext2/tests/insert.py
@@ -160,55 +160,55 @@ INSERT_TESTS = {
       ]
     },
 
     { 'desc':       'insert <img>',
       'command':    'insertimage',
       'tests':      [
         { 'id':         'IIMG:url_TEXT-1_SC',
           'rte1-id':    'a-insertimage-0',
-          'desc':       'Insert image with URL "http://goo.gl/bar.png"',
-          'value':      'http://goo.gl/bar.png',
+          'desc':       'Insert image with URL "bar.png"',
+          'value':      'bar.png',
           'checkAttrs':  True,
           'pad':        'foo^bar',
-          'expected':   [ 'foo<img src="http://goo.gl/bar.png">|bar',
-                          'foo<img src="http://goo.gl/bar.png">^bar' ] },
+          'expected':   [ 'foo<img src="bar.png">|bar',
+                          'foo<img src="bar.png">^bar' ] },
 
         { 'id':         'IIMG:url_IMG-1_SO',
           'desc':       'Change existing image to new URL, selection on <img>',
-          'value':      'http://baz.com/quz.png',
+          'value':      'quz.png',
           'checkAttrs':  True,
-          'pad':        '<span>foo{<img src="http://goo.gl/bar.png">}bar</span>',
-          'expected':   [ '<span>foo<img src="http://baz.com/quz.png"/>|bar</span>',
-                          '<span>foo<img src="http://baz.com/quz.png"/>^bar</span>' ] },
+          'pad':        '<span>foo{<img src="bar.png">}bar</span>',
+          'expected':   [ '<span>foo<img src="quz.png"/>|bar</span>',
+                          '<span>foo<img src="quz.png"/>^bar</span>' ] },
 
         { 'id':         'IIMG:url_SPAN-IMG-1_SO',
           'desc':       'Change existing image to new URL, selection in text surrounding <img>',
-          'value':      'http://baz.com/quz.png',
+          'value':      'quz.png',
           'checkAttrs':  True,
-          'pad':        'foo[<img src="http://goo.gl/bar.png">]bar',
-          'expected':   [ 'foo<img src="http://baz.com/quz.png"/>|bar',
-                          'foo<img src="http://baz.com/quz.png"/>^bar' ] },
+          'pad':        'foo[<img src="bar.png">]bar',
+          'expected':   [ 'foo<img src="quz.png"/>|bar',
+                          'foo<img src="quz.png"/>^bar' ] },
 
         { 'id':         'IIMG:._SPAN-IMG-1_SO',
           'desc':       'Remove existing image or URL, selection on <img>',
           'value':      '',
           'checkAttrs':  True,
-          'pad':        '<span>foo{<img src="http://goo.gl/bar.png">}bar</span>',
+          'pad':        '<span>foo{<img src="bar.png">}bar</span>',
           'expected':   [ '<span>foo^bar</span>',
                           '<span>foo<img>|bar</span>',
                           '<span>foo<img>^bar</span>',
                           '<span>foo<img src="">|bar</span>',
                           '<span>foo<img src="">^bar</span>' ] },
 
         { 'id':         'IIMG:._IMG-1_SO',
           'desc':       'Remove existing image or URL, selection in text surrounding <img>',
           'value':      '',
           'checkAttrs':  True,
-          'pad':        'foo[<img src="http://goo.gl/bar.png">]bar',
+          'pad':        'foo[<img src="bar.png">]bar',
           'expected':   [ 'foo^bar',
                           'foo<img>|bar',
                           'foo<img>^bar',
                           'foo<img src="">|bar',
                           'foo<img src="">^bar' ] }
       ]
     },
 
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -411,17 +411,17 @@ public:
   }
 
   /*
    * Returns a DataSourceSurface with the same data as this one, but
    * guaranteed to have surface->GetType() == SurfaceType::DATA.
    */
   virtual TemporaryRef<DataSourceSurface> GetDataSurface();
 
-  DebugOnly<bool> mIsMapped;
+  bool mIsMapped;
 };
 
 /* This is an abstract object that accepts path segments. */
 class PathSink : public RefCounted<PathSink>
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathSink)
   virtual ~PathSink() {}
--- a/gfx/2d/DataSurfaceHelpers.h
+++ b/gfx/2d/DataSurfaceHelpers.h
@@ -20,27 +20,29 @@ ConvertBGRXToBGRA(uint8_t* aData, const 
  * contain |aSrcSize.width * aSrcSize.height * aBytesPerPixel| bytes.
  */
 void
 CopySurfaceDataToPackedArray(uint8_t* aSrc, uint8_t* aDst, IntSize aSrcSize,
                              int32_t aSrcStride, int32_t aBytesPerPixel);
 
 /**
  * Convert aSurface to a packed buffer in BGRA format. The pixel data is
- * returned in a buffer allocated with new uint8_t[].
+ * returned in a buffer allocated with new uint8_t[]. The caller then has
+ * ownership of the buffer and is responsible for delete[]'ing it.
  */
 uint8_t*
 SurfaceToPackedBGRA(DataSourceSurface *aSurface);
 
 /**
  * Convert aSurface to a packed buffer in BGR format. The pixel data is
- * returned in a buffer allocated with new uint8_t[].
+ * returned in a buffer allocated with new uint8_t[]. The caller then has
+ * ownership of the buffer and is responsible for delete[]'ing it.
  *
  * This function is currently only intended for use with surfaces of format
- * SurfaceFormat::B8G8R8X8 since the X components of the pixel data are simply
- * dropped (no attempt is made to un-pre-multiply alpha from the color
- * components).
+ * SurfaceFormat::B8G8R8X8 since the X components of the pixel data (if any)
+ * are simply dropped (no attempt is made to un-pre-multiply alpha from the
+ * color components).
  */
 uint8_t*
 SurfaceToPackedBGR(DataSourceSurface *aSurface);
 
 }
 }
--- a/gfx/ipc/GfxMessageUtils.h
+++ b/gfx/ipc/GfxMessageUtils.h
@@ -21,16 +21,17 @@
 #include "gfxRect.h"
 #include "nsRect.h"
 #include "nsRegion.h"
 #include "gfxTypes.h"
 #include "mozilla/layers/LayersTypes.h"
 #include "mozilla/layers/CompositorTypes.h"
 #include "FrameMetrics.h"
 #include "FilterSupport.h"
+#include "mozilla/layers/GeckoContentController.h"
 
 #ifdef _MSC_VER
 #pragma warning( disable : 4800 )
 #endif
 
 namespace mozilla {
 
 typedef gfxImageFormat PixelFormat;
@@ -1064,11 +1065,21 @@ struct ParamTraits<mozilla::gfx::FilterD
 
   static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
   {
     return (ReadParam(aMsg, aIter, &aResult->mFilterSpaceBounds) &&
             ReadParam(aMsg, aIter, &aResult->mPrimitives));
   }
 };
 
+typedef mozilla::layers::GeckoContentController::APZStateChange APZStateChange;
+
+template <>
+struct ParamTraits<APZStateChange>
+  : public ContiguousTypedEnumSerializer<
+             APZStateChange,
+             APZStateChange::TransformBegin,
+             APZStateChange::APZStateChangeSentinel>
+{};
+
 } /* namespace IPC */
 
 #endif /* __GFXMESSAGEUTILS_H__ */
--- a/gfx/layers/Compositor.h
+++ b/gfx/layers/Compositor.h
@@ -132,28 +132,22 @@ enum SurfaceInitMode
 {
   INIT_MODE_NONE,
   INIT_MODE_CLEAR
 };
 
 /**
  * A base class for a platform-dependent helper for use by TextureHost.
  */
-class CompositorBackendSpecificData : public RefCounted<CompositorBackendSpecificData>
+class CompositorBackendSpecificData
 {
-public:
-  MOZ_DECLARE_REFCOUNTED_TYPENAME(CompositorBackendSpecificData)
-  CompositorBackendSpecificData()
-  {
-    MOZ_COUNT_CTOR(CompositorBackendSpecificData);
-  }
-  virtual ~CompositorBackendSpecificData()
-  {
-    MOZ_COUNT_DTOR(CompositorBackendSpecificData);
-  }
+  NS_INLINE_DECL_REFCOUNTING(CompositorBackendSpecificData)
+
+protected:
+  virtual ~CompositorBackendSpecificData() {}
 };
 
 /**
  * Common interface for compositor backends.
  *
  * Compositor provides a cross-platform interface to a set of operations for
  * compositing quads. Compositor knows nothing about the layer tree. It must be
  * told everything about each composited quad - contents, location, transform,
@@ -190,31 +184,30 @@ public:
  *
  * By default, the compositor will render to the screen, to render to a target,
  * call SetTargetContext or SetRenderTarget, the latter with a target created
  * by CreateRenderTarget or CreateRenderTargetFromSource.
  *
  * The target and viewport methods can be called before any DrawQuad call and
  * affect any subsequent DrawQuad calls.
  */
-class Compositor : public RefCounted<Compositor>
+class Compositor
 {
+protected:
+  virtual ~Compositor() {}
+
 public:
-  MOZ_DECLARE_REFCOUNTED_TYPENAME(Compositor)
+  NS_INLINE_DECL_REFCOUNTING(Compositor)
+
   Compositor(PCompositorParent* aParent = nullptr)
     : mCompositorID(0)
     , mDiagnosticTypes(DIAGNOSTIC_NONE)
     , mParent(aParent)
     , mScreenRotation(ROTATION_0)
   {
-    MOZ_COUNT_CTOR(Compositor);
-  }
-  virtual ~Compositor()
-  {
-    MOZ_COUNT_DTOR(Compositor);
   }
 
   virtual TemporaryRef<DataTextureSource> CreateDataTextureSource(TextureFlags aFlags = 0) = 0;
   virtual bool Initialize() = 0;
   virtual void Destroy() = 0;
 
   /**
    * Return true if the effect type is supported.
--- a/gfx/layers/Effects.h
+++ b/gfx/layers/Effects.h
@@ -32,25 +32,28 @@ namespace layers {
  * - any way in which rendering can be changed, e.g., applying a mask layer.
  *
  * During the rendering process, an effect chain is created by the layer being
  * rendered and the primary effect is added by the compositable host. Secondary
  * effects may be added by the layer or compositable. The effect chain is passed
  * to the compositor by the compositable host as a parameter to DrawQuad.
  */
 
-struct Effect : public RefCounted<Effect>
+struct Effect
 {
-  MOZ_DECLARE_REFCOUNTED_TYPENAME(Effect)
+  NS_INLINE_DECL_REFCOUNTING(Effect)
+
   Effect(EffectTypes aType) : mType(aType) {}
 
   EffectTypes mType;
 
+  virtual void PrintInfo(nsACString& aTo, const char* aPrefix) = 0;
+
+protected:
   virtual ~Effect() {}
-  virtual void PrintInfo(nsACString& aTo, const char* aPrefix) =0;
 };
 
 // Render from a texture
 struct TexturedEffect : public Effect
 {
   TexturedEffect(EffectTypes aType,
                  TextureSource *aTexture,
                  bool aPremultiplied,
--- a/gfx/layers/client/CompositableClient.h
+++ b/gfx/layers/client/CompositableClient.h
@@ -63,24 +63,26 @@ class TextureClientData;
  * by this layer forwarder (the matching uses a global map on the compositor side,
  * see CompositableMap in ImageBridgeParent.cpp)
  *
  * Subclasses: Thebes layers use ContentClients, ImageLayers use ImageClients,
  * Canvas layers use CanvasClients (but ImageHosts). We have a different subclass
  * where we have a different way of interfacing with the textures - in terms of
  * drawing into the compositable and/or passing its contents to the compostior.
  */
-class CompositableClient : public AtomicRefCounted<CompositableClient>
+class CompositableClient
 {
+protected:
+  virtual ~CompositableClient();
+
 public:
-  MOZ_DECLARE_REFCOUNTED_TYPENAME(CompositableClient)
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositableClient)
+
   CompositableClient(CompositableForwarder* aForwarder, TextureFlags aFlags = 0);
 
-  virtual ~CompositableClient();
-
   virtual TextureInfo GetTextureInfo() const = 0;
 
   LayersBackend GetCompositorBackendType() const;
 
   TemporaryRef<BufferTextureClient>
   CreateBufferTextureClient(gfx::SurfaceFormat aFormat,
                             TextureFlags aFlags = TEXTURE_FLAGS_DEFAULT,
                             gfx::BackendType aMoz2dBackend = gfx::BackendType::NONE);
--- a/gfx/layers/client/SimpleTextureClientPool.h
+++ b/gfx/layers/client/SimpleTextureClientPool.h
@@ -14,31 +14,31 @@
 #include <stack>
 #include <list>
 
 namespace mozilla {
 namespace layers {
 
 class ISurfaceAllocator;
 
-class SimpleTextureClientPool : public RefCounted<SimpleTextureClientPool>
+class SimpleTextureClientPool
 {
-public:
-  MOZ_DECLARE_REFCOUNTED_TYPENAME(SimpleTextureClientPool)
-
-  SimpleTextureClientPool(gfx::SurfaceFormat aFormat, gfx::IntSize aSize,
-                          ISurfaceAllocator *aAllocator);
-
   ~SimpleTextureClientPool()
   {
     for (auto it = mOutstandingTextureClients.begin(); it != mOutstandingTextureClients.end(); ++it) {
       (*it)->ClearRecycleCallback();
     }
   }
 
+public:
+  NS_INLINE_DECL_REFCOUNTING(SimpleTextureClientPool)
+
+  SimpleTextureClientPool(gfx::SurfaceFormat aFormat, gfx::IntSize aSize,
+                          ISurfaceAllocator *aAllocator);
+
   /**
    * If a TextureClient is AutoRecycled, when the last reference is
    * released this object will be automatically return to the pool as
    * soon as the compositor informs us it is done with it.
    */
   TemporaryRef<TextureClient> GetTextureClient(bool aAutoRecycle = false);
   TemporaryRef<TextureClient> GetTextureClientWithAutoRecycle() { return GetTextureClient(true); }
 
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -70,33 +70,27 @@ namespace layers {
  * TextureChild is used to synchronize a texture client and its corresponding
  * TextureHost if needed (a TextureClient that is not shared with the compositor
  * does not have a TextureChild)
  *
  * During the deallocation phase, a TextureChild may hold its recently destroyed
  * TextureClient's data until the compositor side confirmed that it is safe to
  * deallocte or recycle the it.
  */
-class TextureChild : public PTextureChild
-                   , public AtomicRefCounted<TextureChild>
+class TextureChild MOZ_FINAL : public PTextureChild
 {
 public:
-  MOZ_DECLARE_REFCOUNTED_TYPENAME(TextureChild)
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TextureChild)
+
   TextureChild()
   : mForwarder(nullptr)
   , mTextureData(nullptr)
   , mTextureClient(nullptr)
   , mIPCOpen(false)
   {
-    MOZ_COUNT_CTOR(TextureChild);
-  }
-
-  ~TextureChild()
-  {
-    MOZ_COUNT_DTOR(TextureChild);
   }
 
   bool Recv__delete__() MOZ_OVERRIDE;
 
   bool RecvCompositorRecycle(const MaybeFenceHandle& aFence)
   {
     RECYCLE_LOG("Receive recycle %p (%p)\n", mTextureClient, mWaitForRecycle.get());
     if (aFence.type() != aFence.Tnull_t) {
--- a/gfx/layers/client/TextureClientPool.h
+++ b/gfx/layers/client/TextureClientPool.h
@@ -13,23 +13,25 @@
 #include "nsITimer.h"
 #include <stack>
 
 namespace mozilla {
 namespace layers {
 
 class ISurfaceAllocator;
 
-class TextureClientPool : public RefCounted<TextureClientPool>
+class TextureClientPool MOZ_FINAL
 {
+  ~TextureClientPool();
+
 public:
-  MOZ_DECLARE_REFCOUNTED_TYPENAME(TextureClientPool)
+  NS_INLINE_DECL_REFCOUNTING(TextureClientPool)
+
   TextureClientPool(gfx::SurfaceFormat aFormat, gfx::IntSize aSize,
                     ISurfaceAllocator *aAllocator);
-  ~TextureClientPool();
 
   /**
    * Gets an allocated TextureClient of size and format that are determined
    * by the initialisation parameters given to the pool. This will either be
    * a cached client that was returned to the pool, or a newly allocated
    * client if one isn't available.
    *
    * All clients retrieved by this method should be returned using the return
--- a/gfx/layers/client/TiledContentClient.h
+++ b/gfx/layers/client/TiledContentClient.h
@@ -42,20 +42,22 @@ namespace mozilla {
 namespace layers {
 
 class BasicTileDescriptor;
 class ClientTiledThebesLayer;
 class ClientLayerManager;
 
 
 // A class to help implement copy-on-write semantics for shared tiles.
-class gfxSharedReadLock : public AtomicRefCounted<gfxSharedReadLock> {
+class gfxSharedReadLock {
+protected:
+  virtual ~gfxSharedReadLock() {}
+
 public:
-  MOZ_DECLARE_REFCOUNTED_TYPENAME(gfxSharedReadLock)
-  virtual ~gfxSharedReadLock() {}
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(gfxSharedReadLock)
 
   virtual int32_t ReadLock() = 0;
   virtual int32_t ReadUnlock() = 0;
   virtual int32_t GetReadCount() = 0;
   virtual bool IsValid() const = 0;
 
   enum gfxSharedReadLockType {
     TYPE_MEMORY,
--- a/gfx/layers/composite/APZCTreeManager.cpp
+++ b/gfx/layers/composite/APZCTreeManager.cpp
@@ -774,17 +774,16 @@ APZCTreeManager::HandOffFling(AsyncPanZo
   ScreenPoint endPoint = startPoint + aVelocity;
   TransformDisplacement(this, aPrev, next, startPoint, endPoint);
   ScreenPoint transformedVelocity = endPoint - startPoint;
 
   // Tell |next| to start a fling with the transformed velocity.
   next->TakeOverFling(transformedVelocity);
 }
 
-
 bool
 APZCTreeManager::FlushRepaintsForOverscrollHandoffChain()
 {
   MonitorAutoLock lock(mTreeLock);  // to access mOverscrollHandoffChain
   if (mOverscrollHandoffChain.length() == 0) {
     return false;
   }
   for (uint32_t i = 0; i < mOverscrollHandoffChain.length(); i++) {
@@ -792,16 +791,40 @@ APZCTreeManager::FlushRepaintsForOverscr
     if (item) {
       item->FlushRepaintForOverscrollHandoff();
     }
   }
   return true;
 }
 
 bool
+APZCTreeManager::CanBePanned(AsyncPanZoomController* aApzc)
+{
+  MonitorAutoLock lock(mTreeLock);  // to access mOverscrollHandoffChain
+
+  // Find |aApzc| in the handoff chain.
+  uint32_t i;
+  for (i = 0; i < mOverscrollHandoffChain.length(); ++i) {
+    if (mOverscrollHandoffChain[i] == aApzc) {
+      break;
+    }
+  }
+
+  // See whether any APZC in the handoff chain starting from |aApzc|
+  // has room to be panned.
+  for (uint32_t j = i; j < mOverscrollHandoffChain.length(); ++j) {
+    if (mOverscrollHandoffChain[j]->IsPannable()) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+bool
 APZCTreeManager::HitTestAPZC(const ScreenIntPoint& aPoint)
 {
   MonitorAutoLock lock(mTreeLock);
   nsRefPtr<AsyncPanZoomController> target;
   // The root may have siblings, so check those too
   gfxPoint point(aPoint.x, aPoint.y);
   for (AsyncPanZoomController* apzc = mRootApzc; apzc; apzc = apzc->GetPrevSibling()) {
     target = GetAPZCAtPoint(apzc, point);
--- a/gfx/layers/composite/APZCTreeManager.h
+++ b/gfx/layers/composite/APZCTreeManager.h
@@ -209,16 +209,17 @@ public:
    */
   void GetAllowedTouchBehavior(WidgetInputEvent* aEvent,
                                nsTArray<TouchBehaviorFlags>& aOutValues);
 
   /**
    * Sets allowed touch behavior values for current touch-session for specific apzc (determined by guid).
    * Should be invoked by the widget. Each value of the aValues arrays corresponds to the different
    * touch point that is currently active.
+   * Must be called after receiving the TOUCH_START event that starts the touch-session.
    */
   void SetAllowedTouchBehavior(const ScrollableLayerGuid& aGuid,
                                const nsTArray<TouchBehaviorFlags>& aValues);
 
   /**
    * This is a callback for AsyncPanZoomController to call when it wants to
    * scroll in response to a touch-move event, or when it needs to hand off
    * overscroll to the next APZC. Note that because of scroll grabbing, the
@@ -266,16 +267,23 @@ public:
    * @param aApzc the APZC that is handing off the fling
    * @param aVelocity the current velocity of the fling, in |aApzc|'s screen
    *                  pixels per millisecond
    */
   void HandOffFling(AsyncPanZoomController* aApzc, ScreenPoint aVelocity);
 
   bool FlushRepaintsForOverscrollHandoffChain();
 
+  /**
+   * Determine whether |aApzc|, or any APZC along its overscroll handoff chain,
+   * has room to be panned.
+   * Expects the overscroll handoff chain to already be built.
+   */
+  bool CanBePanned(AsyncPanZoomController* aApzc);
+
 protected:
   // Protected destructor, to discourage deletion outside of Release():
   virtual ~APZCTreeManager();
 
   /**
    * Debug-build assertion that can be called to ensure code is running on the
    * compositor thread.
    */
--- a/gfx/layers/composite/AsyncCompositionManager.h
+++ b/gfx/layers/composite/AsyncCompositionManager.h
@@ -56,32 +56,31 @@ struct ViewTransform {
 /**
  * Manage async composition effects. This class is only used with OMTC and only
  * lives on the compositor thread. It is a layer on top of the layer manager
  * (LayerManagerComposite) which deals with elements of composition which are
  * usually dealt with by dom or layout when main thread rendering, but which can
  * short circuit that stuff to directly affect layers as they are composited,
  * for example, off-main thread animation, async video, async pan/zoom.
  */
-class AsyncCompositionManager MOZ_FINAL : public RefCounted<AsyncCompositionManager>
+class AsyncCompositionManager MOZ_FINAL
 {
   friend class AutoResolveRefLayers;
 public:
-  MOZ_DECLARE_REFCOUNTED_TYPENAME(AsyncCompositionManager)
+  NS_INLINE_DECL_REFCOUNTING(AsyncCompositionManager)
+
   AsyncCompositionManager(LayerManagerComposite* aManager)
     : mLayerManager(aManager)
     , mIsFirstPaint(false)
     , mLayersUpdated(false)
     , mReadyForCompose(true)
   {
-    MOZ_COUNT_CTOR(AsyncCompositionManager);
   }
   ~AsyncCompositionManager()
   {
-    MOZ_COUNT_DTOR(AsyncCompositionManager);
   }
 
   /**
    * This forces the is-first-paint flag to true. This is intended to
    * be called by the widget code when it loses its viewport information
    * (or for whatever reason wants to refresh the viewport information).
    * The information refresh happens because the compositor will call
    * SetFirstPaintViewport on the next frame of composition.
--- a/gfx/layers/composite/CompositableHost.h
+++ b/gfx/layers/composite/CompositableHost.h
@@ -51,28 +51,28 @@ class Compositor;
 class ISurfaceAllocator;
 class ThebesBufferData;
 class TiledLayerComposer;
 struct EffectChain;
 
 /**
  * A base class for doing CompositableHost and platform dependent task on TextureHost.
  */
-class CompositableBackendSpecificData : public RefCounted<CompositableBackendSpecificData>
+class CompositableBackendSpecificData
 {
+protected:
+  virtual ~CompositableBackendSpecificData() { }
+
 public:
-  MOZ_DECLARE_REFCOUNTED_TYPENAME(CompositableBackendSpecificData)
+  NS_INLINE_DECL_REFCOUNTING(CompositableBackendSpecificData)
+
   CompositableBackendSpecificData()
   {
-    MOZ_COUNT_CTOR(CompositableBackendSpecificData);
   }
-  virtual ~CompositableBackendSpecificData()
-  {
-    MOZ_COUNT_DTOR(CompositableBackendSpecificData);
-  }
+
   virtual void SetCompositor(Compositor* aCompositor) {}
   virtual void ClearData()
   {
     mCurrentReleaseFenceTexture = nullptr;
     ClearPendingReleaseFenceTextureList();
   }
 
   /**
@@ -119,24 +119,25 @@ protected:
  * the layers transaction to update the Compositbale's textures from the
  * content side. The actual update (and any syncronous upload) is done by the
  * TextureHost, but it is coordinated by the CompositableHost.
  *
  * Composite is called by the owning layer when it is composited. CompositableHost
  * will use its TextureHost(s) and call Compositor::DrawQuad to do the actual
  * rendering.
  */
-class CompositableHost : public RefCounted<CompositableHost>
+class CompositableHost
 {
+protected:
+  virtual ~CompositableHost();
+
 public:
-  MOZ_DECLARE_REFCOUNTED_TYPENAME(CompositableHost)
+  NS_INLINE_DECL_REFCOUNTING(CompositableHost)
   CompositableHost(const TextureInfo& aTextureInfo);
 
-  virtual ~CompositableHost();
-
   static TemporaryRef<CompositableHost> Create(const TextureInfo& aTextureInfo);
 
   virtual CompositableType GetType() = 0;
 
   virtual CompositableBackendSpecificData* GetCompositableBackendSpecificData()
   {
     return mBackendData;
   }
--- a/gfx/layers/composite/TextRenderer.h
+++ b/gfx/layers/composite/TextRenderer.h
@@ -2,27 +2,29 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef GFX_TextRenderer_H
 #define GFX_TextRenderer_H
 
 #include "mozilla/gfx/2D.h"
+#include "nsISupportsImpl.h"
 #include <string>
 
 namespace mozilla {
 namespace layers {
 
 class Compositor;
 
-class TextRenderer : public RefCounted<TextRenderer>
+class TextRenderer
 {
 public:
-  MOZ_DECLARE_REFCOUNTED_TYPENAME(TextRenderer)
+  NS_INLINE_DECL_REFCOUNTING(TextRenderer)
+
   TextRenderer(Compositor *aCompositor)
     : mCompositor(aCompositor)
   {
   }
 
   ~TextRenderer();
 
   void RenderText(const std::string& aText, const gfx::IntPoint& aOrigin,
--- a/gfx/layers/composite/TextureHost.h
+++ b/gfx/layers/composite/TextureHost.h
@@ -75,22 +75,25 @@ public:
  * TextureSource is the interface for texture objects that can be composited
  * by a given compositor backend. Since the drawing APIs are different
  * between backends, the TextureSource interface is split into different
  * interfaces (TextureSourceOGL, etc.), and TextureSource mostly provide
  * access to these interfaces.
  *
  * This class is used on the compositor side.
  */
-class TextureSource : public RefCounted<TextureSource>
+class TextureSource
 {
+protected:
+  virtual ~TextureSource();
+
 public:
-  MOZ_DECLARE_REFCOUNTED_TYPENAME(TextureSource)
+  NS_INLINE_DECL_REFCOUNTING(TextureSource)
+
   TextureSource();
-  virtual ~TextureSource();
 
   /**
    * Return the size of the texture in texels.
    * If this is a tile iterator, GetSize must return the size of the current tile.
    */
   virtual gfx::IntSize GetSize() const = 0;
 
   /**
--- a/gfx/layers/ipc/AsyncPanZoomController.cpp
+++ b/gfx/layers/ipc/AsyncPanZoomController.cpp
@@ -127,16 +127,17 @@ WidgetModifiersToDOMModifiers(mozilla::M
 }
 
 using namespace mozilla::css;
 
 namespace mozilla {
 namespace layers {
 
 typedef mozilla::layers::AllowedTouchBehavior AllowedTouchBehavior;
+typedef GeckoContentController::APZStateChange APZStateChange;
 
 /*
  * The following prefs are used to control the behaviour of the APZC.
  * The default values are provided in gfxPrefs.h.
  *
  * "apz.allow-checkerboarding"
  * Pref that allows or disallows checkerboarding
  *
@@ -415,19 +416,16 @@ AsyncPanZoomController::AsyncPanZoomCont
      mZoomConstraints(false, false, MIN_ZOOM, MAX_ZOOM),
      mLastSampleTime(GetFrameTime()),
      mState(NOTHING),
      mLastAsyncScrollTime(GetFrameTime()),
      mLastAsyncScrollOffset(0, 0),
      mCurrentAsyncScrollOffset(0, 0),
      mAsyncScrollTimeoutTask(nullptr),
      mHandlingTouchQueue(false),
-     mAllowedTouchBehaviorSet(false),
-     mPreventDefault(false),
-     mPreventDefaultSet(false),
      mTreeManager(aTreeManager),
      mScrollParentId(FrameMetrics::NULL_SCROLL_ID),
      mAPZCId(sAsyncPanZoomControllerCount++),
      mSharedFrameMetricsBuffer(nullptr),
      mSharedLock(nullptr)
 {
   MOZ_COUNT_CTOR(AsyncPanZoomController);
 
@@ -493,31 +491,33 @@ AsyncPanZoomController::GetTouchStartTol
 }
 
 /* static */AsyncPanZoomController::AxisLockMode AsyncPanZoomController::GetAxisLockMode()
 {
   return static_cast<AxisLockMode>(gfxPrefs::APZAxisLockMode());
 }
 
 nsEventStatus AsyncPanZoomController::ReceiveInputEvent(const InputData& aEvent) {
+  if (aEvent.mInputType == MULTITOUCH_INPUT &&
+      aEvent.AsMultiTouchInput().mType == MultiTouchInput::MULTITOUCH_START) {
+    // Starting a new touch block, clear old touch block state.
+    mTouchBlockState = TouchBlockState();
+  }
+
   // If we may have touch listeners and touch action property is enabled, we
   // enable the machinery that allows touch listeners to preventDefault any touch inputs
   // and also waits for the allowed touch behavior values to be received from the outside.
   // This should not happen unless there are actually touch listeners and touch-action property
   // enable as it introduces potentially unbounded lag because it causes a round-trip through
   // content.  Usually, if content is responding in a timely fashion, this only introduces a
   // nearly constant few hundred ms of lag.
   if (mFrameMetrics.mMayHaveTouchListeners && aEvent.mInputType == MULTITOUCH_INPUT &&
       (mState == NOTHING || mState == TOUCHING || IsPanningState(mState))) {
     const MultiTouchInput& multiTouchInput = aEvent.AsMultiTouchInput();
     if (multiTouchInput.mType == MultiTouchInput::MULTITOUCH_START) {
-      mAllowedTouchBehaviors.Clear();
-      mAllowedTouchBehaviorSet = false;
-      mPreventDefault = false;
-      mPreventDefaultSet = false;
       SetState(WAITING_CONTENT_RESPONSE);
     }
   }
 
   if (mState == WAITING_CONTENT_RESPONSE || mHandlingTouchQueue) {
     if (aEvent.mInputType == MULTITOUCH_INPUT) {
       const MultiTouchInput& multiTouchInput = aEvent.AsMultiTouchInput();
       mTouchQueue.AppendElement(multiTouchInput);
@@ -610,21 +610,28 @@ nsEventStatus AsyncPanZoomController::On
         RequestContentRepaint();
         ScheduleComposite();
         UpdateSharedCompositorFrameMetrics();
       }
       // Fall through.
     case FLING:
       CancelAnimation();
       // Fall through.
-    case NOTHING:
+    case NOTHING: {
       mX.StartTouch(point.x);
       mY.StartTouch(point.y);
+      APZCTreeManager* treeManagerLocal = mTreeManager;
+      if (treeManagerLocal) {
+        bool touchCanBePan = treeManagerLocal->CanBePanned(this);
+        mGeckoContentController->NotifyAPZStateChange(
+            GetGuid(), APZStateChange::StartTouch, touchCanBePan);
+      }
       SetState(TOUCHING);
       break;
+    }
     case TOUCHING:
     case PANNING:
     case PANNING_LOCKED_X:
     case PANNING_LOCKED_Y:
     case CROSS_SLIDING_X:
     case CROSS_SLIDING_Y:
     case PINCHING:
     case WAITING_CONTENT_RESPONSE:
@@ -693,16 +700,18 @@ nsEventStatus AsyncPanZoomController::On
   }
 
   return nsEventStatus_eConsumeNoDefault;
 }
 
 nsEventStatus AsyncPanZoomController::OnTouchEnd(const MultiTouchInput& aEvent) {
   APZC_LOG("%p got a touch-end in state %d\n", this, mState);
 
+  OnTouchEndOrCancel();
+
   // In case no touch behavior triggered previously we can avoid sending
   // scroll events or requesting content repaint. This condition is added
   // to make tests consistent - in case touch-action is NONE (and therefore
   // no pans/zooms can be performed) we expected neither scroll or repaint
   // events.
   if (mState != NOTHING) {
     ReentrantMonitorAutoEnter lock(mMonitor);
     SendAsyncScrollEvent();
@@ -759,16 +768,17 @@ nsEventStatus AsyncPanZoomController::On
     break;
   }
 
   return nsEventStatus_eConsumeNoDefault;
 }
 
 nsEventStatus AsyncPanZoomController::OnTouchCancel(const MultiTouchInput& aEvent) {
   APZC_LOG("%p got a touch-cancel in state %d\n", this, mState);
+  OnTouchEndOrCancel();
   SetState(NOTHING);
   return nsEventStatus_eConsumeNoDefault;
 }
 
 nsEventStatus AsyncPanZoomController::OnScaleBegin(const PinchGestureInput& aEvent) {
   APZC_LOG("%p got a scale-begin in state %d\n", this, mState);
 
   if (!TouchActionAllowZoom()) {
@@ -924,56 +934,56 @@ nsEventStatus AsyncPanZoomController::On
     if (ConvertToGecko(aEvent.mPoint, &geckoScreenPoint)) {
       controller->HandleLongTapUp(geckoScreenPoint, modifiers, GetGuid());
       return nsEventStatus_eConsumeNoDefault;
     }
   }
   return nsEventStatus_eIgnore;
 }
 
-nsEventStatus AsyncPanZoomController::OnSingleTapUp(const TapGestureInput& aEvent) {
-  APZC_LOG("%p got a single-tap-up in state %d\n", this, mState);
+nsEventStatus AsyncPanZoomController::GenerateSingleTap(const ScreenIntPoint& aPoint, mozilla::Modifiers aModifiers) {
   nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
-  // If mZoomConstraints.mAllowDoubleTapZoom is true we wait for a call to OnSingleTapConfirmed before
-  // sending event to content
-  if (controller && !mZoomConstraints.mAllowDoubleTapZoom) {
-    int32_t modifiers = WidgetModifiersToDOMModifiers(aEvent.modifiers);
+  if (controller) {
     CSSPoint geckoScreenPoint;
-    if (ConvertToGecko(aEvent.mPoint, &geckoScreenPoint)) {
+    if (ConvertToGecko(aPoint, &geckoScreenPoint)) {
+      int32_t modifiers = WidgetModifiersToDOMModifiers(aModifiers);
       // Because this may be being running as part of APZCTreeManager::ReceiveInputEvent,
       // calling controller->HandleSingleTap directly might mean that content receives
       // the single tap message before the corresponding touch-up. To avoid that we
       // schedule the singletap message to run on the next spin of the event loop.
       // See bug 965381 for the issue this was causing.
       controller->PostDelayedTask(
         NewRunnableMethod(controller.get(), &GeckoContentController::HandleSingleTap,
                           geckoScreenPoint, modifiers, GetGuid()),
         0);
+      mTouchBlockState.mSingleTapOccurred = true;
       return nsEventStatus_eConsumeNoDefault;
     }
   }
   return nsEventStatus_eIgnore;
 }
 
+void AsyncPanZoomController::OnTouchEndOrCancel() {
+  mGeckoContentController->NotifyAPZStateChange(
+      GetGuid(), APZStateChange::EndTouch, mTouchBlockState.mSingleTapOccurred);
+}
+
+nsEventStatus AsyncPanZoomController::OnSingleTapUp(const TapGestureInput& aEvent) {
+  APZC_LOG("%p got a single-tap-up in state %d\n", this, mState);
+  // If mZoomConstraints.mAllowDoubleTapZoom is true we wait for a call to OnSingleTapConfirmed before
+  // sending event to content
+  if (!mZoomConstraints.mAllowDoubleTapZoom) {
+    return GenerateSingleTap(aEvent.mPoint, aEvent.modifiers);
+  }
+  return nsEventStatus_eIgnore;
+}
+
 nsEventStatus AsyncPanZoomController::OnSingleTapConfirmed(const TapGestureInput& aEvent) {
   APZC_LOG("%p got a single-tap-confirmed in state %d\n", this, mState);
-  nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
-  if (controller) {
-    int32_t modifiers = WidgetModifiersToDOMModifiers(aEvent.modifiers);
-    CSSPoint geckoScreenPoint;
-    if (ConvertToGecko(aEvent.mPoint, &geckoScreenPoint)) {
-      // See comment in OnSingleTapUp as to why we do this in PostDelayedTask.
-      controller->PostDelayedTask(
-        NewRunnableMethod(controller.get(), &GeckoContentController::HandleSingleTap,
-                          geckoScreenPoint, modifiers, GetGuid()),
-        0);
-      return nsEventStatus_eConsumeNoDefault;
-    }
-  }
-  return nsEventStatus_eIgnore;
+  return GenerateSingleTap(aEvent.mPoint, aEvent.modifiers);
 }
 
 nsEventStatus AsyncPanZoomController::OnDoubleTap(const TapGestureInput& aEvent) {
   APZC_LOG("%p got a double-tap in state %d\n", this, mState);
   nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
   if (controller) {
     if (mZoomConstraints.mAllowDoubleTapZoom) {
       int32_t modifiers = WidgetModifiersToDOMModifiers(aEvent.modifiers);
@@ -1087,25 +1097,27 @@ nsEventStatus AsyncPanZoomController::St
   double angle = atan2(dy, dx); // range [-pi, pi]
   angle = fabs(angle); // range [0, pi]
 
   if (mTouchActionPropertyEnabled) {
     HandlePanningWithTouchAction(angle, GetTouchBehavior(0));
   } else {
     if (GetAxisLockMode() == FREE) {
       SetState(PANNING);
-      return nsEventStatus_eConsumeNoDefault;
+    } else {
+      HandlePanning(angle);
     }
-
-    HandlePanning(angle);
   }
 
+  if (IsPanningState(mState)) {
+    mGeckoContentController->NotifyAPZStateChange(GetGuid(), APZStateChange::StartPanning);
+    return nsEventStatus_eConsumeNoDefault;
+  }
   // Don't consume an event that didn't trigger a panning.
-  return IsPanningState(mState) ? nsEventStatus_eConsumeNoDefault
-                                : nsEventStatus_eIgnore;
+  return nsEventStatus_eIgnore;
 }
 
 void AsyncPanZoomController::UpdateWithTouchAtDevicePoint(const MultiTouchInput& aEvent) {
   ScreenIntPoint point = GetFirstTouchScreenPoint(aEvent);
   TimeDuration timeDelta = TimeDuration().FromMilliseconds(aEvent.mTime - mLastEventTime);
 
   // Probably a duplicate event, just throw it away.
   if (timeDelta.ToMilliseconds() <= EPSILON) {
@@ -1440,16 +1452,21 @@ void AsyncPanZoomController::ScheduleCom
 }
 
 void AsyncPanZoomController::FlushRepaintForOverscrollHandoff() {
   ReentrantMonitorAutoEnter lock(mMonitor);
   RequestContentRepaint();
   UpdateSharedCompositorFrameMetrics();
 }
 
+bool AsyncPanZoomController::IsPannable() const {
+  ReentrantMonitorAutoEnter lock(mMonitor);
+  return mX.HasRoomToPan() || mY.HasRoomToPan();
+}
+
 void AsyncPanZoomController::RequestContentRepaint() {
   RequestContentRepaint(mFrameMetrics);
 }
 
 void AsyncPanZoomController::RequestContentRepaint(FrameMetrics& aFrameMetrics) {
   aFrameMetrics.SetDisplayPortMargins(
     CalculatePendingDisplayPort(aFrameMetrics,
                                 GetVelocityVector(),
@@ -1885,50 +1902,50 @@ void AsyncPanZoomController::ZoomToRect(
 
     // Schedule a repaint now, so the new displayport will be painted before the
     // animation finishes.
     RequestContentRepaint(endZoomToMetrics);
   }
 }
 
 void AsyncPanZoomController::ContentReceivedTouch(bool aPreventDefault) {
-  mPreventDefaultSet = true;
-  mPreventDefault = aPreventDefault;
+  mTouchBlockState.mPreventDefaultSet = true;
+  mTouchBlockState.mPreventDefault = aPreventDefault;
   CheckContentResponse();
 }
 
 void AsyncPanZoomController::CheckContentResponse() {
   bool canProceedToTouchState = true;
 
   if (mFrameMetrics.mMayHaveTouchListeners) {
-    canProceedToTouchState &= mPreventDefaultSet;
+    canProceedToTouchState &= mTouchBlockState.mPreventDefaultSet;
   }
 
   if (mTouchActionPropertyEnabled) {
-    canProceedToTouchState &= mAllowedTouchBehaviorSet;
+    canProceedToTouchState &= mTouchBlockState.mAllowedTouchBehaviorSet;
   }
 
   if (!canProceedToTouchState) {
     return;
   }
 
   if (mContentResponseTimeoutTask) {
     mContentResponseTimeoutTask->Cancel();
     mContentResponseTimeoutTask = nullptr;
   }
 
   if (mState == WAITING_CONTENT_RESPONSE) {
-    if (!mPreventDefault) {
+    if (!mTouchBlockState.mPreventDefault) {
       SetState(NOTHING);
     }
 
     mHandlingTouchQueue = true;
 
     while (!mTouchQueue.IsEmpty()) {
-      if (!mPreventDefault) {
+      if (!mTouchBlockState.mPreventDefault) {
         HandleInputEvent(mTouchQueue[0]);
       }
 
       if (mTouchQueue[0].mType == MultiTouchInput::MULTITOUCH_END ||
           mTouchQueue[0].mType == MultiTouchInput::MULTITOUCH_CANCEL) {
         mTouchQueue.RemoveElementAt(0);
         break;
       }
@@ -1942,66 +1959,66 @@ void AsyncPanZoomController::CheckConten
 
 bool AsyncPanZoomController::TouchActionAllowZoom() {
   if (!mTouchActionPropertyEnabled) {
     return true;
   }
 
   // Pointer events specification implies all touch points to allow zoom
   // to perform it.
-  for (size_t i = 0; i < mAllowedTouchBehaviors.Length(); i++) {
-    if (!(mAllowedTouchBehaviors[i] & AllowedTouchBehavior::ZOOM)) {
+  for (size_t i = 0; i < mTouchBlockState.mAllowedTouchBehaviors.Length(); i++) {
+    if (!(mTouchBlockState.mAllowedTouchBehaviors[i] & AllowedTouchBehavior::ZOOM)) {
       return false;
     }
   }
 
   return true;
 }
 
 AsyncPanZoomController::TouchBehaviorFlags
 AsyncPanZoomController::GetTouchBehavior(uint32_t touchIndex) {
-  if (touchIndex < mAllowedTouchBehaviors.Length()) {
-    return mAllowedTouchBehaviors[touchIndex];
+  if (touchIndex < mTouchBlockState.mAllowedTouchBehaviors.Length()) {
+    return mTouchBlockState.mAllowedTouchBehaviors[touchIndex];
   }
   return DefaultTouchBehavior;
 }
 
 AsyncPanZoomController::TouchBehaviorFlags
 AsyncPanZoomController::GetAllowedTouchBehavior(ScreenIntPoint& aPoint) {
   // Here we need to perform a hit testing over the touch-action regions attached to the
   // layer associated with current apzc.
   // Currently they are in progress, for more info see bug 928833.
   return AllowedTouchBehavior::UNKNOWN;
 }
 
 void AsyncPanZoomController::SetAllowedTouchBehavior(const nsTArray<TouchBehaviorFlags>& aBehaviors) {
-  mAllowedTouchBehaviors.Clear();
-  mAllowedTouchBehaviors.AppendElements(aBehaviors);
-  mAllowedTouchBehaviorSet = true;
+  mTouchBlockState.mAllowedTouchBehaviors.Clear();
+  mTouchBlockState.mAllowedTouchBehaviors.AppendElements(aBehaviors);
+  mTouchBlockState.mAllowedTouchBehaviorSet = true;
   CheckContentResponse();
 }
 
 void AsyncPanZoomController::SetState(PanZoomState aNewState) {
 
   PanZoomState oldState;
 
   // Intentional scoping for mutex
   {
     ReentrantMonitorAutoEnter lock(mMonitor);
     oldState = mState;
     mState = aNewState;
   }
 
   if (mGeckoContentController) {
     if (!IsTransformingState(oldState) && IsTransformingState(aNewState)) {
-      mGeckoContentController->NotifyTransformBegin(
-        ScrollableLayerGuid(mLayersId, mFrameMetrics.mPresShellId, mFrameMetrics.GetScrollId()));
+      mGeckoContentController->NotifyAPZStateChange(
+          GetGuid(), APZStateChange::TransformBegin);
     } else if (IsTransformingState(oldState) && !IsTransformingState(aNewState)) {
-      mGeckoContentController->NotifyTransformEnd(
-        ScrollableLayerGuid(mLayersId, mFrameMetrics.mPresShellId, mFrameMetrics.GetScrollId()));
+      mGeckoContentController->NotifyAPZStateChange(
+          GetGuid(), APZStateChange::TransformEnd);
     }
   }
 }
 
 bool AsyncPanZoomController::IsTransformingState(PanZoomState aState) {
   return !(aState == NOTHING || aState == TOUCHING || aState == WAITING_CONTENT_RESPONSE);
 }
 
--- a/gfx/layers/ipc/AsyncPanZoomController.h
+++ b/gfx/layers/ipc/AsyncPanZoomController.h
@@ -310,16 +310,18 @@ public:
    * is done we should integrate its logic here.
    */
   TouchBehaviorFlags GetAllowedTouchBehavior(ScreenIntPoint& aPoint);
 
   /**
    * Sets allowed touch behavior for current touch session.
    * This method is invoked by the APZCTreeManager which in its turn invoked by
    * the widget after performing touch-action values retrieving.
+   * Must be called after receiving the TOUCH_START even that started the
+   * touch session.
    */
   void SetAllowedTouchBehavior(const nsTArray<TouchBehaviorFlags>& aBehaviors);
 
   /**
    * Returns whether this APZC is for an element marked with the 'scrollgrab'
    * attribute.
    */
   bool HasScrollgrab() const { return mFrameMetrics.mHasScrollgrab; }
@@ -327,16 +329,21 @@ public:
   /**
    * Set an extra offset for testing async scrolling.
    */
   void SetTestAsyncScrollOffset(const CSSPoint& aPoint)
   {
     mTestAsyncScrollOffset = aPoint;
   }
 
+  /**
+   * Returns whether this APZC has room to be panned (in any direction).
+   */
+  bool IsPannable() const;
+
 protected:
   // Protected destructor, to discourage deletion outside of Release():
   ~AsyncPanZoomController();
 
   /**
    * Helper method for touches beginning. Sets everything up for panning and any
    * multitouch gestures.
    */
@@ -552,16 +559,46 @@ private:
     PINCHING,                 /* nth touch-start, where n > 1. this mode allows pan and zoom */
     ANIMATING_ZOOM,           /* animated zoom to a new rect */
     WAITING_CONTENT_RESPONSE, /* a state halfway between NOTHING and TOUCHING - the user has
                                  put a finger down, but we don't yet know if a touch listener has
                                  prevented the default actions yet and the allowed touch behavior
                                  was not set yet. we still need to abort animations. */
   };
 
+  // State related to a single touch block. Does not persist across touch blocks.
+  struct TouchBlockState {
+
+    TouchBlockState()
+      :  mAllowedTouchBehaviorSet(false),
+         mPreventDefault(false),
+         mPreventDefaultSet(false),
+         mSingleTapOccurred(false)
+    {}
+
+    // Values of allowed touch behavior for touch points of this touch block.
+    // Since there are maybe a few current active touch points per time (multitouch case)
+    // and each touch point should have its own value of allowed touch behavior- we're
+    // keeping an array of allowed touch behavior values, not the single value.
+    nsTArray<TouchBehaviorFlags> mAllowedTouchBehaviors;
+
+    // Specifies whether mAllowedTouchBehaviors is set for this touch events block.
+    bool mAllowedTouchBehaviorSet;
+
+    // Flag used to specify that content prevented the default behavior of this
+    // touch events block.
+    bool mPreventDefault;
+
+    // Specifies whether mPreventDefault property is set for this touch events block.
+    bool mPreventDefaultSet;
+
+    // Specifies whether a single tap event was generated during this touch block.
+    bool mSingleTapOccurred;
+  };
+
   /*
    * Returns whether current touch behavior values allow zooming.
    */
   bool TouchActionAllowZoom();
 
   /*
    * Returns allowed touch behavior from the mAllowedTouchBehavior array.
    * In case apzc didn't receive touch behavior values within the timeout
@@ -612,16 +649,22 @@ private:
   // coordinates but the composition bounds is in parent layer coordinates.
   ParentLayerPoint ToParentLayerCoords(const ScreenPoint& aPoint);
 
   // Update mFrameMetrics.mTransformScale. This should be called whenever
   // our CSS transform or the non-transient part of our async transform
   // changes, as it corresponds to the scale portion of those transforms.
   void UpdateTransformScale();
 
+  // Helper function for OnSingleTapUp() and OnSingleTapConfirmed().
+  nsEventStatus GenerateSingleTap(const ScreenIntPoint& aPoint, mozilla::Modifiers aModifiers);
+
+  // Common processing at the end of a touch block.
+  void OnTouchEndOrCancel();
+
   uint64_t mLayersId;
   nsRefPtr<CompositorParent> mCompositorParent;
   PCompositorParent* mCrossProcessCompositorParent;
   TaskThrottler mPaintThrottler;
 
   /* Access to the following two fields is protected by the mRefPtrMonitor,
      since they are accessed on the UI thread but can be cleared on the
      compositor thread. */
@@ -638,17 +681,19 @@ protected:
   // monitor. Do not read from or modify either of them without locking.
   FrameMetrics mFrameMetrics;
 
   // Protects |mFrameMetrics|, |mLastContentPaintMetrics|, and |mState|.
   // Before manipulating |mFrameMetrics| or |mLastContentPaintMetrics|, the
   // monitor should be held. When setting |mState|, either the SetState()
   // function can be used, or the monitor can be held and then |mState| updated.
   // IMPORTANT: See the note about lock ordering at the top of APZCTreeManager.h.
-  ReentrantMonitor mMonitor;
+  // This is mutable to allow entering it from 'const' methods; doing otherwise
+  // would significantly limit what methods could be 'const'.
+  mutable ReentrantMonitor mMonitor;
 
   // Specifies whether we should use touch-action css property. Initialized from
   // the preferences. This property (in comparison with the global one) simplifies
   // testing apzc with (and without) touch-action property enabled concurrently
   // (e.g. with the gtest framework).
   bool mTouchActionPropertyEnabled;
 
 private:
@@ -713,31 +758,18 @@ private:
   CancelableTask* mAsyncScrollTimeoutTask;
 
   // Flag used to determine whether or not we should try to enter the
   // WAITING_LISTENERS state. This is used in the case that we are processing a
   // queued up event block. If set, this means that we are handling this queue
   // and we don't want to queue the events back up again.
   bool mHandlingTouchQueue;
 
-  // Values of allowed touch behavior for current touch points.
-  // Since there are maybe a few current active touch points per time (multitouch case)
-  // and each touch point should have its own value of allowed touch behavior- we're
-  // keeping an array of allowed touch behavior values, not the single value.
-  nsTArray<TouchBehaviorFlags> mAllowedTouchBehaviors;
-
-  // Specifies whether mAllowedTouchBehaviors is set for current touch events block.
-  bool mAllowedTouchBehaviorSet;
-
-  // Flag used to specify that content prevented the default behavior of the current
-  // touch events block.
-  bool mPreventDefault;
-
-  // Specifies whether mPreventDefault property is set for current touch events block.
-  bool mPreventDefaultSet;
+  // Stores information about the current touch block.
+  TouchBlockState mTouchBlockState;
 
   // Extra offset to add in SampleContentTransformForFrame for testing
   CSSPoint mTestAsyncScrollOffset;
 
   RefPtr<AsyncPanZoomAnimation> mAnimation;
 
   friend class Axis;
   friend class FlingAnimation;
--- a/gfx/layers/ipc/Axis.cpp
+++ b/gfx/layers/ipc/Axis.cpp
@@ -202,89 +202,95 @@ float Axis::ScaleWillOverscrollAmount(fl
 float Axis::GetVelocity() {
   return mAxisLocked ? 0 : mVelocity;
 }
 
 void Axis::SetVelocity(float aVelocity) {
   mVelocity = aVelocity;
 }
 
-float Axis::GetCompositionEnd() {
+float Axis::GetCompositionEnd() const {
   return GetOrigin() + GetCompositionLength();
 }
 
-float Axis::GetPageEnd() {
+float Axis::GetPageEnd() const {
   return GetPageStart() + GetPageLength();
 }
 
-float Axis::GetOrigin() {
+float Axis::GetOrigin() const {
   CSSPoint origin = mAsyncPanZoomController->GetFrameMetrics().GetScrollOffset();
   return GetPointOffset(origin);
 }
 
-float Axis::GetCompositionLength() {
+float Axis::GetCompositionLength() const {
   const FrameMetrics& metrics = mAsyncPanZoomController->GetFrameMetrics();
   return GetRectLength(metrics.CalculateCompositedRectInCssPixels());
 }
 
-float Axis::GetPageStart() {
+float Axis::GetPageStart() const {
   CSSRect pageRect = mAsyncPanZoomController->GetFrameMetrics().mScrollableRect;
   return GetRectOffset(pageRect);
 }
 
-float Axis::GetPageLength() {
+float Axis::GetPageLength() const {
   CSSRect pageRect = mAsyncPanZoomController->GetFrameMetrics().mScrollableRect;
   return GetRectLength(pageRect);
 }
 
 bool Axis::ScaleWillOverscrollBothSides(float aScale) {
   const FrameMetrics& metrics = mAsyncPanZoomController->GetFrameMetrics();
 
   CSSToParentLayerScale scale(metrics.GetZoomToParent().scale * aScale);
   CSSRect cssCompositionBounds = metrics.mCompositionBounds / scale;
 
   return GetRectLength(metrics.mScrollableRect) < GetRectLength(cssCompositionBounds);
 }
 
+bool Axis::HasRoomToPan() const {
+  return GetOrigin() > GetPageStart()
+      || GetCompositionEnd() < GetPageEnd();
+}
+
+
 AxisX::AxisX(AsyncPanZoomController* aAsyncPanZoomController)
   : Axis(aAsyncPanZoomController)
 {
 
 }
 
-float AxisX::GetPointOffset(const CSSPoint& aPoint)
+float AxisX::GetPointOffset(const CSSPoint& aPoint) const
 {
   return aPoint.x;
 }
 
-float AxisX::GetRectLength(const CSSRect& aRect)
+float AxisX::GetRectLength(const CSSRect& aRect) const
 {
   return aRect.width;
 }
 
-float AxisX::GetRectOffset(const CSSRect& aRect)
+float AxisX::GetRectOffset(const CSSRect& aRect) const
 {
   return aRect.x;
 }
 
 AxisY::AxisY(AsyncPanZoomController* aAsyncPanZoomController)
   : Axis(aAsyncPanZoomController)
 {
 
 }
 
-float AxisY::GetPointOffset(const CSSPoint& aPoint)
+float AxisY::GetPointOffset(const CSSPoint& aPoint) const
 {
   return aPoint.y;
 }
 
-float AxisY::GetRectLength(const CSSRect& aRect)
+float AxisY::GetRectLength(const CSSRect& aRect) const
 {
   return aRect.height;
 }
 
-float AxisY::GetRectOffset(const CSSRect& aRect)
+float AxisY::GetRectOffset(const CSSRect& aRect) const
 {
   return aRect.y;
 }
 
 }
 }
--- a/gfx/layers/ipc/Axis.h
+++ b/gfx/layers/ipc/Axis.h
@@ -165,50 +165,55 @@ public:
    * Checks if an axis will overscroll in both directions by computing the
    * content rect and checking that its height/width (depending on the axis)
    * does not overextend past the viewport.
    *
    * This gets called by ScaleWillOverscroll().
    */
   bool ScaleWillOverscrollBothSides(float aScale);
 
-  float GetOrigin();
-  float GetCompositionLength();
-  float GetPageStart();
-  float GetPageLength();
-  float GetCompositionEnd();
-  float GetPageEnd();
+  /**
+   * Returns whether there is room to pan on this axis in either direction.
+   */
+  bool HasRoomToPan() const;
+
+  float GetOrigin() const;
+  float GetCompositionLength() const;
+  float GetPageStart() const;
+  float GetPageLength() const;
+  float GetCompositionEnd() const;
+  float GetPageEnd() const;
 
   int32_t GetPos() const { return mPos; }
 
-  virtual float GetPointOffset(const CSSPoint& aPoint) = 0;
-  virtual float GetRectLength(const CSSRect& aRect) = 0;
-  virtual float GetRectOffset(const CSSRect& aRect) = 0;
+  virtual float GetPointOffset(const CSSPoint& aPoint) const = 0;
+  virtual float GetRectLength(const CSSRect& aRect) const = 0;
+  virtual float GetRectOffset(const CSSRect& aRect) const = 0;
 
 protected:
   int32_t mPos;
   int32_t mStartPos;
   float mVelocity;
   bool mAxisLocked;     // Whether movement on this axis is locked.
   AsyncPanZoomController* mAsyncPanZoomController;
   nsTArray<float> mVelocityQueue;
 };
 
 class AxisX : public Axis {
 public:
   AxisX(AsyncPanZoomController* mAsyncPanZoomController);
-  virtual float GetPointOffset(const CSSPoint& aPoint);
-  virtual float GetRectLength(const CSSRect& aRect);
-  virtual float GetRectOffset(const CSSRect& aRect);
+  virtual float GetPointOffset(const CSSPoint& aPoint) const;
+  virtual float GetRectLength(const CSSRect& aRect) const;
+  virtual float GetRectOffset(const CSSRect& aRect) const;
 };
 
 class AxisY : public Axis {
 public:
   AxisY(AsyncPanZoomController* mAsyncPanZoomController);
-  virtual float GetPointOffset(const CSSPoint& aPoint);
-  virtual float GetRectLength(const CSSRect& aRect);
-  virtual float GetRectOffset(const CSSRect& aRect);
+  virtual float GetPointOffset(const CSSPoint& aPoint) const;
+  virtual float GetRectLength(const CSSRect& aRect) const;
+  virtual float GetRectOffset(const CSSRect& aRect) const;
 };
 
 }
 }
 
 #endif
--- a/gfx/layers/ipc/GeckoContentController.h
+++ b/gfx/layers/ipc/GeckoContentController.h
@@ -108,27 +108,58 @@ public:
    * APZ can then get the correct touch-sensitive region for each frame
    * directly from the layer.
    */
   virtual bool GetTouchSensitiveRegion(CSSRect* aOutRegion)
   {
     return false;
   }
 
+  MOZ_BEGIN_NESTED_ENUM_CLASS(APZStateChange, int8_t)
+    /**
+     * APZ started modifying the view (including panning, zooming, and fling).
+     */
+    TransformBegin,
+    /**
+     * APZ finished modifying the view.
+     */
+    TransformEnd,
+    /**
+     * APZ started a touch.
+     * |aArg| is 1 if touch can be a pan, 0 otherwise.
+     */
+    StartTouch,
+    /**
+     * APZ started a pan.
+     */
+    StartPanning,
+    /**
+     * APZ finished processing a touch.
+     * |aArg| is 1 if touch was a click, 0 otherwise.
+     */
+    EndTouch,
+    APZStateChangeSentinel
+  MOZ_END_NESTED_ENUM_CLASS(APZStateChange)
+
   /**
-   * General tranformation notices for consumers. These fire any time
-   * the apzc is modifying the view, including panning, zooming, and
-   * fling.
+   * General notices of APZ state changes for consumers.
+   * |aGuid| identifies the APZC originating the state change.
+   * |aChange| identifies the type of state change
+   * |aArg| is used by some state changes to pass extra information (see
+   *        the documentation for each state change above)
    */
-  virtual void NotifyTransformBegin(const ScrollableLayerGuid& aGuid) {}
-  virtual void NotifyTransformEnd(const ScrollableLayerGuid& aGuid) {}
+  virtual void NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
+                                    APZStateChange aChange,
+                                    int aArg = 0) {}
 
   GeckoContentController() {}
 
 protected:
   // Protected destructor, to discourage deletion outside of Release():
   virtual ~GeckoContentController() {}
 };
 
+MOZ_FINISH_NESTED_ENUM_CLASS(GeckoContentController::APZStateChange)
+
 }
 }
 
 #endif // mozilla_layers_GeckoContentController_h
--- a/gfx/layers/ipc/ImageBridgeChild.cpp
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -326,38 +326,30 @@ static void ReleaseImageClientNow(ImageC
 {
   MOZ_ASSERT(InImageBridgeChildThread());
   aClient->Release();
 }
 
 // static
 void ImageBridgeChild::DispatchReleaseImageClient(ImageClient* aClient)
 {
-  if (!IsCreated()) {
-    return;
-  }
-
   sImageBridgeChildSingleton->GetMessageLoop()->PostTask(
     FROM_HERE,
     NewRunnableFunction(&ReleaseImageClientNow, aClient));
 }
 
 static void ReleaseTextureClientNow(TextureClient* aClient)
 {
   MOZ_ASSERT(InImageBridgeChildThread());
   aClient->Release();
 }
 
 // static
 void ImageBridgeChild::DispatchReleaseTextureClient(TextureClient* aClient)
 {
-  if (!IsCreated()) {
-    return;
-  }
-
   sImageBridgeChildSingleton->GetMessageLoop()->PostTask(
     FROM_HERE,
     NewRunnableFunction(&ReleaseTextureClientNow, aClient));
 }
 
 static void UpdateImageClientNow(ImageClient* aClient, ImageContainer* aContainer)
 {
   MOZ_ASSERT(aClient);
@@ -367,20 +359,16 @@ static void UpdateImageClientNow(ImageCl
   aClient->OnTransaction();
   sImageBridgeChildSingleton->EndTransaction();
 }
 
 //static
 void ImageBridgeChild::DispatchImageClientUpdate(ImageClient* aClient,
                                                  ImageContainer* aContainer)
 {
-  if (!IsCreated()) {
-    return;
-  }
-
   if (InImageBridgeChildThread()) {
     UpdateImageClientNow(aClient, aContainer);
     return;
   }
   sImageBridgeChildSingleton->GetMessageLoop()->PostTask(
     FROM_HERE,
     NewRunnableFunction<
       void (*)(ImageClient*, ImageContainer*),
@@ -395,20 +383,16 @@ static void FlushAllImagesSync(ImageClie
   ReentrantMonitorAutoEnter autoMon(*aBarrier);
   *aDone = true;
   aBarrier->NotifyAll();
 }
 
 //static
 void ImageBridgeChild::FlushAllImages(ImageClient* aClient, ImageContainer* aContainer, bool aExceptFront)
 {
-  if (!IsCreated()) {
-    return;
-  }
-
   if (InImageBridgeChildThread()) {
     FlushAllImagesNow(aClient, aContainer, aExceptFront);
     return;
   }
 
   ReentrantMonitor barrier("CreateImageClient Lock");
   ReentrantMonitorAutoEnter autoMon(barrier);
   bool done = false;
@@ -588,24 +572,25 @@ bool ImageBridgeChild::StartUpOnThread(T
     return true;
   } else {
     return false;
   }
 }
 
 void ImageBridgeChild::DestroyBridge()
 {
-  if (!IsCreated()) {
-    return;
-  }
   NS_ABORT_IF_FALSE(!InImageBridgeChildThread(),
                     "This method must not be called in this thread.");
   // ...because we are about to dispatch synchronous messages to the
   // ImageBridgeChild thread.
 
+  if (!IsCreated()) {
+    return;
+  }
+
   ReentrantMonitor barrier("ImageBridgeDestroyTask lock");
   ReentrantMonitorAutoEnter autoMon(barrier);
 
   bool done = false;
   sImageBridgeChildSingleton->GetMessageLoop()->PostTask(FROM_HERE,
                   NewRunnableFunction(&StopImageBridgeSync, &barrier, &done));
   while (!done) {
     barrier.Wait();
@@ -617,18 +602,17 @@ void ImageBridgeChild::DestroyBridge()
   while (!done) {
     barrier.Wait();
   }
 
 }
 
 bool InImageBridgeChildThread()
 {
-  return ImageBridgeChild::IsCreated() &&
-    sImageBridgeChildThread->thread_id() == PlatformThread::CurrentId();
+  return sImageBridgeChildThread->thread_id() == PlatformThread::CurrentId();
 }
 
 MessageLoop * ImageBridgeChild::GetMessageLoop() const
 {
   return sImageBridgeChildThread->message_loop();
 }
 
 void ImageBridgeChild::ConnectAsync(ImageBridgeParent* aParent)
--- a/gfx/layers/ipc/LayerTransactionChild.h
+++ b/gfx/layers/ipc/LayerTransactionChild.h
@@ -18,38 +18,35 @@ namespace mozilla {
 
 namespace layout {
 class RenderFrameChild;
 }
 
 namespace layers {
 
 class LayerTransactionChild : public PLayerTransactionChild
-                            , public AtomicRefCounted<LayerTransactionChild>
 {
 public:
-  MOZ_DECLARE_REFCOUNTED_TYPENAME(LayerTransactionChild)
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(LayerTransactionChild)
   /**
    * Clean this up, finishing with Send__delete__().
    *
    * It is expected (checked with an assert) that all shadow layers
    * created by this have already been destroyed and
    * Send__delete__()d by the time this method is called.
    */
   void Destroy();
 
   bool IPCOpen() const { return mIPCOpen; }
 
 protected:
   LayerTransactionChild()
     : mIPCOpen(false)
   {}
   ~LayerTransactionChild() { }
-  friend class AtomicRefCounted<LayerTransactionChild>;
-  friend class detail::RefCounted<LayerTransactionChild, detail::AtomicRefCount>;
 
   virtual PGrallocBufferChild*
   AllocPGrallocBufferChild(const IntSize&,
                            const uint32_t&, const uint32_t&,
                            MaybeMagicGrallocBufferHandle*) MOZ_OVERRIDE;
   virtual bool
   DeallocPGrallocBufferChild(PGrallocBufferChild* actor) MOZ_OVERRIDE;
 
--- a/gfx/layers/opengl/CompositorOGL.h
+++ b/gfx/layers/opengl/CompositorOGL.h
@@ -59,22 +59,23 @@ struct EffectChain;
  * Interface for pools of temporary gl textures for the compositor.
  * The textures are fully owned by the pool, so the latter is responsible
  * calling fDeleteTextures accordingly.
  * Users of GetTexture receive a texture that is only valid for the duration
  * of the current frame.
  * This is primarily intended for direct texturing APIs that need to attach
  * shared objects (such as an EGLImage) to a gl texture.
  */
-class CompositorTexturePoolOGL : public RefCounted<CompositorTexturePoolOGL>
+class CompositorTexturePoolOGL
 {
+protected:
+  virtual ~CompositorTexturePoolOGL() {}
+
 public:
-  MOZ_DECLARE_REFCOUNTED_TYPENAME(CompositorTexturePoolOGL)
-
-  virtual ~CompositorTexturePoolOGL() {}
+  NS_INLINE_DECL_REFCOUNTING(CompositorTexturePoolOGL)
 
   virtual void Clear() = 0;
 
   virtual GLuint GetTexture(GLenum aTarget, GLenum aEnum) = 0;
 
   virtual void EndFrame() = 0;
 };
 
--- a/gfx/src/FilterSupport.cpp
+++ b/gfx/src/FilterSupport.cpp
@@ -225,20 +225,20 @@ namespace FilterWrappers {
 // A class that wraps a FilterNode and handles conversion between different
 // color models. Create FilterCachedColorModels with your original filter and
 // the color model that this filter outputs in natively, and then call
 // ->ForColorModel(colorModel) in order to get a FilterNode which outputs to
 // the specified colorModel.
 // Internally, this is achieved by wrapping the original FilterNode with
 // conversion FilterNodes. These filter nodes are cached in such a way that no
 // repeated or back-and-forth conversions happen.
-class FilterCachedColorModels : public RefCounted<FilterCachedColorModels>
+class FilterCachedColorModels
 {
 public:
-  MOZ_DECLARE_REFCOUNTED_TYPENAME(FilterCachedColorModels)
+  NS_INLINE_DECL_REFCOUNTING(FilterCachedColorModels)
   // aFilter can be null. In that case, ForColorModel will return a non-null
   // completely transparent filter for all color models.
   FilterCachedColorModels(DrawTarget* aDT,
                           FilterNode* aFilter,
                           ColorModel aOriginalColorModel);
 
   // Get a FilterNode for the specified color model, guaranteed to be non-null.
   TemporaryRef<FilterNode> ForColorModel(ColorModel aColorModel);
--- a/gfx/src/nsRenderingContext.h
+++ b/gfx/src/nsRenderingContext.h
@@ -29,25 +29,24 @@ struct nsRect;
 
 typedef enum {
     nsLineStyle_kNone   = 0,
     nsLineStyle_kSolid  = 1,
     nsLineStyle_kDashed = 2,
     nsLineStyle_kDotted = 3
 } nsLineStyle;
 
-class nsRenderingContext
+class nsRenderingContext MOZ_FINAL
 {
     typedef mozilla::gfx::UserData UserData;
     typedef mozilla::gfx::UserDataKey UserDataKey;
     typedef mozilla::gfx::DrawTarget DrawTarget;
 
 public:
     nsRenderingContext() : mP2A(0.) {}
-    // ~nsRenderingContext() {}
 
     NS_INLINE_DECL_REFCOUNTING(nsRenderingContext)
 
     void Init(nsDeviceContext* aContext, gfxASurface* aThebesSurface);
     void Init(nsDeviceContext* aContext, gfxContext* aThebesContext);
     void Init(nsDeviceContext* aContext, DrawTarget* aDrawTarget);
 
     // These accessors will never return null.
@@ -125,17 +124,22 @@ public:
     }
     void *GetUserData(UserDataKey *key) {
       return mUserData.Get(key);
     }
     void *RemoveUserData(UserDataKey *key) {
       return mUserData.Remove(key);
     }
 
-protected:
+private:
+    // Private destructor, to discourage deletion outside of Release():
+    ~nsRenderingContext()
+    {
+    }
+
     int32_t GetMaxChunkLength();
 
     nsRefPtr<gfxContext> mThebes;
     nsRefPtr<nsDeviceContext> mDeviceContext;
     nsRefPtr<nsFontMetrics> mFontMetrics;
 
     double mP2A; // cached app units per device pixel value
 
--- a/gfx/tests/gtest/TestAsyncPanZoomController.cpp
+++ b/gfx/tests/gtest/TestAsyncPanZoomController.cpp
@@ -163,17 +163,24 @@ FrameMetrics TestFrameMetrics() {
   return fm;
 }
 
 /*
  * Dispatches mock touch events to the apzc and checks whether apzc properly
  * consumed them and triggered scrolling behavior.
  */
 static
-void ApzcPan(AsyncPanZoomController* apzc, TestAPZCTreeManager* aTreeManager, int& aTime, int aTouchStartY, int aTouchEndY, bool expectIgnoredPan = false, bool hasTouchListeners = false) {
+void ApzcPan(AsyncPanZoomController* apzc,
+             TestAPZCTreeManager* aTreeManager,
+             int& aTime,
+             int aTouchStartY,
+             int aTouchEndY,
+             bool expectIgnoredPan = false,
+             bool hasTouchListeners = false,
+             nsTArray<uint32_t>* aAllowedTouchBehaviors = nullptr) {
 
   const int TIME_BETWEEN_TOUCH_EVENT = 100;
   const int OVERCOME_TOUCH_TOLERANCE = 100;
   MultiTouchInput mti;
   nsEventStatus status;
 
   // Since we're passing inputs directly to the APZC instead of going through
   // the tree manager, we need to build the overscroll handoff chain explicitly
@@ -193,16 +200,21 @@ void ApzcPan(AsyncPanZoomController* apz
   mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, aTime, 0);
   aTime += TIME_BETWEEN_TOUCH_EVENT;
   // Make sure the move is large enough to not be handled as a tap
   mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, aTouchStartY+OVERCOME_TOUCH_TOLERANCE), ScreenSize(0, 0), 0, 0));
   status = apzc->ReceiveInputEvent(mti);
   EXPECT_EQ(status, touchStartStatus);
   // APZC should be in TOUCHING state
 
+  // Allowed touch behaviours must be set after sending touch-start.
+  if (aAllowedTouchBehaviors) {
+    apzc->SetAllowedTouchBehavior(*aAllowedTouchBehaviors);
+  }
+
   nsEventStatus touchMoveStatus;
   if (expectIgnoredPan) {
     // APZC should ignore panning, be in TOUCHING state and therefore return eIgnore.
     // The same applies to all consequent touch move events.
     touchMoveStatus = nsEventStatus_eIgnore;
   } else {
     // APZC should go into the panning state and therefore consume the event.
     touchMoveStatus = nsEventStatus_eConsumeNoDefault;
@@ -249,35 +261,33 @@ void DoPanTest(bool aShouldTriggerScroll
   }
 
   int time = 0;
   int touchStart = 50;
   int touchEnd = 10;
   ScreenPoint pointOut;
   ViewTransform viewTransformOut;
 
-  nsTArray<uint32_t> values;
-  values.AppendElement(aBehavior);
+  nsTArray<uint32_t> allowedTouchBehaviors;
+  allowedTouchBehaviors.AppendElement(aBehavior);
 
   // Pan down
-  apzc->SetAllowedTouchBehavior(values);
-  ApzcPan(apzc, tm, time, touchStart, touchEnd, !aShouldTriggerScroll);
+  ApzcPan(apzc, tm, time, touchStart, touchEnd, !aShouldTriggerScroll, false, &allowedTouchBehaviors);
   apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
 
   if (aShouldTriggerScroll) {
     EXPECT_EQ(pointOut, ScreenPoint(0, -(touchEnd-touchStart)));
     EXPECT_NE(viewTransformOut, ViewTransform());
   } else {
     EXPECT_EQ(pointOut, ScreenPoint());
     EXPECT_EQ(viewTransformOut, ViewTransform());
   }
 
   // Pan back
-  apzc->SetAllowedTouchBehavior(values);
-  ApzcPan(apzc, tm, time, touchEnd, touchStart, !aShouldTriggerScroll);
+  ApzcPan(apzc, tm, time, touchEnd, touchStart, !aShouldTriggerScroll, false, &allowedTouchBehaviors);
   apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
 
   EXPECT_EQ(pointOut, ScreenPoint());
   EXPECT_EQ(viewTransformOut, ViewTransform());
 
   apzc->Destroy();
 }
 
@@ -608,21 +618,20 @@ TEST_F(AsyncPanZoomControllerTester, Pan
 
   int time = 0;
   int touchStart = 50;
   int touchEnd = 10;
   ScreenPoint pointOut;
   ViewTransform viewTransformOut;
 
   // Pan down
-  nsTArray<uint32_t> values;
-  values.AppendElement(mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
+  nsTArray<uint32_t> allowedTouchBehaviors;
+  allowedTouchBehaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
   apzc->SetTouchActionEnabled(true);
-  apzc->SetAllowedTouchBehavior(values);
-  ApzcPan(apzc, tm, time, touchStart, touchEnd, true, true);
+  ApzcPan(apzc, tm, time, touchStart, touchEnd, true, true, &allowedTouchBehaviors);
 
   // Send the signal that content has handled and preventDefaulted the touch
   // events. This flushes the event queue.
   apzc->ContentReceivedTouch(true);
 
   apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
   EXPECT_EQ(pointOut, ScreenPoint());
   EXPECT_EQ(viewTransformOut, ViewTransform());
@@ -741,26 +750,28 @@ DoLongPressTest(bool aShouldUseTouchActi
   nsRefPtr<TestAPZCTreeManager> tm = new TestAPZCTreeManager();
   nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(
     0, mcc, tm, AsyncPanZoomController::USE_GESTURE_DETECTOR);
 
   apzc->SetFrameMetrics(TestFrameMetrics());
   apzc->NotifyLayersUpdated(TestFrameMetrics(), true);
   apzc->UpdateZoomConstraints(ZoomConstraints(false, false, CSSToScreenScale(1.0), CSSToScreenScale(1.0)));
 
-  nsTArray<uint32_t> values;
-  values.AppendElement(aBehavior);
   apzc->SetTouchActionEnabled(aShouldUseTouchAction);
-  apzc->SetAllowedTouchBehavior(values);
 
   int time = 0;
 
   nsEventStatus status = ApzcDown(apzc, 10, 10, time);
   EXPECT_EQ(nsEventStatus_eConsumeNoDefault, status);
 
+  // SetAllowedTouchBehavior() must be called after sending touch-start.
+  nsTArray<uint32_t> allowedTouchBehaviors;
+  allowedTouchBehaviors.AppendElement(aBehavior);
+  apzc->SetAllowedTouchBehavior(allowedTouchBehaviors);
+
   MockFunction<void(std::string checkPointName)> check;
 
   {
     InSequence s;
 
     EXPECT_CALL(check, Call("preHandleLongTap"));
     EXPECT_CALL(*mcc, HandleLongTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
     EXPECT_CALL(check, Call("postHandleLongTap"));
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -68,22 +68,24 @@
 #include "nsCRT.h"
 #include "GLContext.h"
 #include "GLContextProvider.h"
 
 #ifdef MOZ_WIDGET_ANDROID
 #include "TexturePoolOGL.h"
 #endif
 
+#include "mozilla/Hal.h"
 #ifdef USE_SKIA
-#include "mozilla/Hal.h"
 #include "skia/SkGraphics.h"
 
 #include "SkiaGLGlue.h"
-
+#else
+class mozilla::gl::SkiaGLGlue : public GenericAtomicRefCounted {
+};
 #endif
 
 #include "mozilla/Preferences.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Mutex.h"
 
 #include "nsIGfxInfo.h"
@@ -486,16 +488,22 @@ gfxPlatform::Shutdown()
     // most platforms.  Windows is a "special snowflake", though, and has three
     // context providers available, so we have to shut all of them down.
     // We should only support the default GL provider on Windows; then, this
     // could go away. Unfortunately, we currently support WGL (the default) for
     // WebGL on Optimus.
     mozilla::gl::GLContextProviderEGL::Shutdown();
 #endif
 
+    // This will block this thread untill the ImageBridge protocol is completely
+    // deleted.
+    ImageBridgeChild::ShutDown();
+
+    CompositorParent::ShutDown();
+
     delete gGfxPlatformPrefsLock;
 
     gfxPrefs::DestroySingleton();
     gfxFont::DestroySingletons();
 
     delete gPlatform;
     gPlatform = nullptr;
 }
@@ -917,17 +925,19 @@ gfxPlatform::InitializeSkiaCacheLimits()
         cacheSizeLimit = totalMemory / 16;
       }
     }
 
   #ifdef DEBUG
     printf_stderr("Determined SkiaGL cache limits: Size %i, Items: %i\n", cacheSizeLimit, cacheItemLimit);
   #endif
 
+#ifdef USE_SKIA_GPU
     mSkiaGlue->GetGrContext()->setTextureCacheLimits(cacheItemLimit, cacheSizeLimit);
+#endif
   }
 }
 
 mozilla::gl::SkiaGLGlue*
 gfxPlatform::GetSkiaGLGlue()
 {
 #ifdef USE_SKIA_GPU
   if (!mSkiaGlue) {
--- a/hal/HalWakeLock.cpp
+++ b/hal/HalWakeLock.cpp
@@ -7,17 +7,17 @@
 #include "mozilla/HalWakeLock.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/dom/ContentParent.h"
 #include "nsClassHashtable.h"
 #include "nsDataHashtable.h"
 #include "nsHashKeys.h"
 #include "nsIPropertyBag2.h"
-#include "nsObserverService.h"
+#include "nsIObserverService.h"
 
 using namespace mozilla;
 using namespace mozilla::hal;
 
 namespace {
 
 struct LockCount {
   LockCount()
--- a/hal/cocoa/CocoaBattery.cpp
+++ b/hal/cocoa/CocoaBattery.cpp
@@ -7,17 +7,18 @@
 #import <CoreFoundation/CoreFoundation.h>
 #import <IOKit/ps/IOPowerSources.h>
 #import <IOKit/ps/IOPSKeys.h>
 
 #include <mozilla/Hal.h>
 #include <mozilla/dom/battery/Constants.h>
 #include <mozilla/Services.h>
 
-#include <nsObserverService.h>
+#include <nsIObserverService.h>
+#include <nsIObserver.h>
 
 #include <dlfcn.h>
 
 #define IOKIT_FRAMEWORK_PATH "/System/Library/Frameworks/IOKit.framework/IOKit"
 
 #ifndef kIOPSTimeRemainingUnknown
   #define kIOPSTimeRemainingUnknown ((CFTimeInterval)-1.0)
 #endif
--- a/hal/gonk/GonkHal.cpp
+++ b/hal/gonk/GonkHal.cpp
@@ -289,21 +289,21 @@ public:
 
     hal::NotifyBatteryChange(info);
     return NS_OK;
   }
 };
 
 } // anonymous namespace
 
-class BatteryObserver : public IUeventObserver,
-                        public RefCounted<BatteryObserver>
+class BatteryObserver : public IUeventObserver
 {
 public:
-  MOZ_DECLARE_REFCOUNTED_TYPENAME(BatteryObserver)
+  NS_INLINE_DECL_REFCOUNTING(BatteryObserver)
+
   BatteryObserver()
     :mUpdater(new BatteryUpdater())
   {
   }
 
   virtual void Notify(const NetlinkEvent &aEvent)
   {
     // this will run on IO thread
--- a/hal/gonk/GonkSwitch.cpp
+++ b/hal/gonk/GonkSwitch.cpp
@@ -46,20 +46,21 @@ namespace hal_impl {
  *  change@/devices/virtual/switch/usb_configuration
  *    ACTION=change
  *    DEVPATH=/devices/virtual/switch/usb_configuration
  *    SUBSYSTEM=switch
  *    SWITCH_NAME=usb_configuration
  *    SWITCH_STATE=0
  *    SEQNUM=5038
  */
-class SwitchHandler : public RefCounted<SwitchHandler>
+class SwitchHandler
 {
 public:
-  MOZ_DECLARE_REFCOUNTED_TYPENAME(SwitchHandler)
+  NS_INLINE_DECL_REFCOUNTING(SwitchHandler)
+
   SwitchHandler(const char* aDevPath, SwitchDevice aDevice)
     : mDevPath(aDevPath),
       mState(SWITCH_STATE_UNKNOWN),
       mDevice(aDevice)
   {
     GetInitialState();
   }
 
@@ -225,31 +226,30 @@ public:
   {
     NotifySwitchChange(mEvent);
     return NS_OK;
   }
 private:
   SwitchEvent mEvent;
 };
 
-class SwitchEventObserver : public IUeventObserver,
-                            public RefCounted<SwitchEventObserver>
+class SwitchEventObserver MOZ_FINAL : public IUeventObserver
 {
+  ~SwitchEventObserver()
+  {
+    mHandler.Clear();
+  }
+
 public:
-  MOZ_DECLARE_REFCOUNTED_TYPENAME(SwitchEventObserver)
+  NS_INLINE_DECL_REFCOUNTING(SwitchEventObserver)
   SwitchEventObserver() : mEnableCount(0)
   {
     Init();
   }
 
-  ~SwitchEventObserver()
-  {
-    mHandler.Clear();
-  }
-
   int GetEnableCount()
   {
     return mEnableCount;
   }
 
   void EnableSwitch(SwitchDevice aDevice)
   {
     mEventInfo[aDevice].mEnabled = true;
--- a/image/public/imgIContainer.idl
+++ b/image/public/imgIContainer.idl
@@ -8,16 +8,18 @@
 
 %{C++
 #include "gfxImageSurface.h"
 #include "gfxContext.h"
 #include "gfxMatrix.h"
 #include "gfxRect.h"
 #include "GraphicsFilter.h"
 #include "gfxASurface.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/RefPtr.h"
 #include "nsRect.h"
 #include "nsSize.h"
 #include "limits.h"
 
 namespace mozilla {
 namespace layers {
 class LayerManager;
 class ImageContainer;
@@ -50,27 +52,27 @@ native gfxGraphicsFilter(GraphicsFilter)
 [ref] native nsIntSize(nsIntSize);
 native nsSize(nsSize);
 [ptr] native nsIFrame(nsIFrame);
 [ptr] native ImageContainer(mozilla::layers::ImageContainer);
 [ptr] native LayerManager(mozilla::layers::LayerManager);
 native Orientation(mozilla::image::Orientation);
 [ref] native TimeStamp(mozilla::TimeStamp);
 [ptr] native SVGImageContext(mozilla::SVGImageContext);
-native already_AddRefed_gfxASurface(already_AddRefed<gfxASurface>);
+native TempRefSourceSurface(mozilla::TemporaryRef<mozilla::gfx::SourceSurface>);
 
 
 /**
  * imgIContainer is the interface that represents an image. It allows
  * access to frames as Thebes surfaces. It also allows drawing of images
  * onto Thebes contexts.
  *
  * Internally, imgIContainer also manages animation of images.
  */
-[scriptable, builtinclass, uuid(8b7db7dd-bfe9-40d3-9114-3a79c0658afd)]
+[scriptable, builtinclass, uuid(503a830c-734d-4362-91f6-73f83ac59646)]
 interface imgIContainer : nsISupports
 {
   /**
    * The width of the container rectangle.  In the case of any error,
    * zero is returned, and an exception will be thrown.
    */
   readonly attribute int32_t width;
 
@@ -145,16 +147,17 @@ interface imgIContainer : nsISupports
    */
 
   const long FLAG_NONE            = 0x0;
   const long FLAG_SYNC_DECODE     = 0x1;
   const long FLAG_DECODE_NO_PREMULTIPLY_ALPHA = 0x2;
   const long FLAG_DECODE_NO_COLORSPACE_CONVERSION = 0x4;
   const long FLAG_CLAMP           = 0x8;
   const long FLAG_HIGH_QUALITY_SCALING = 0x10;
+  const long FLAG_WANT_DATA_SURFACE = 0x20;
 
   /**
     * Constants for specifying various "special" frames.
     *
     * FRAME_FIRST: The first frame
     * FRAME_CURRENT: The current frame
     *
     * FRAME_MAX_VALUE should be set to the value of the maximum constant above,
@@ -168,19 +171,18 @@ interface imgIContainer : nsISupports
    * Get a surface for the given frame. This may be a platform-native,
    * optimized surface, so you cannot inspect its pixel data. If you
    * need that, use gfxASurface::GetAsReadableARGB32ImageSurface or
    * gfxASurface::CopyToARGB32ImageSurface.
    *
    * @param aWhichFrame Frame specifier of the FRAME_* variety.
    * @param aFlags Flags of the FLAG_* variety
    */
-  [noscript, notxpcom] already_AddRefed_gfxASurface
-                         getFrame(in uint32_t aWhichFrame,
-                                  in uint32_t aFlags);
+  [noscript, notxpcom] TempRefSourceSurface getFrame(in uint32_t aWhichFrame,
+                                                     in uint32_t aFlags);
 
   /**
    * Whether the given frame is opaque; that is, needs the background painted
    * behind it.
    *
    * @param aWhichFrame Frame specifier of the FRAME_* variety.
    */
   [notxpcom] boolean frameIsOpaque(in uint32_t aWhichFrame);
--- a/image/src/ClippedImage.cpp
+++ b/image/src/ClippedImage.cpp
@@ -1,31 +1,35 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "gfxDrawable.h"
 #include "gfxPlatform.h"
 #include "gfxUtils.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/RefPtr.h"
 
 #include "ClippedImage.h"
 #include "Orientation.h"
 #include "SVGImageContext.h"
 
+using namespace mozilla;
+using namespace mozilla::gfx;
 using mozilla::layers::LayerManager;
 using mozilla::layers::ImageContainer;
 
 namespace mozilla {
 namespace image {
 
 class ClippedImageCachedSurface
 {
 public:
-  ClippedImageCachedSurface(mozilla::gfx::DrawTarget* aSurface,
+  ClippedImageCachedSurface(TemporaryRef<SourceSurface> aSurface,
                             const nsIntSize& aViewportSize,
                             const SVGImageContext* aSVGContext,
                             float aFrame,
                             uint32_t aFlags)
     : mSurface(aSurface)
     , mViewportSize(aViewportSize)
     , mFrame(aFrame)
     , mFlags(aFlags)
@@ -44,23 +48,22 @@ public:
     bool matchesSVGContext = (!aSVGContext && mSVGContext.empty()) ||
                              *aSVGContext == mSVGContext.ref();
     return mViewportSize == aViewportSize &&
            matchesSVGContext &&
            mFrame == aFrame &&
            mFlags == aFlags;
   }
 
-  already_AddRefed<gfxASurface> Surface() {
-    nsRefPtr<gfxASurface> surf = gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mSurface);
-    return surf.forget();
+  TemporaryRef<SourceSurface> Surface() {
+    return mSurface;
   }
 
 private:
-  nsRefPtr<mozilla::gfx::DrawTarget> mSurface;
+  RefPtr<SourceSurface>              mSurface;
   const nsIntSize                    mViewportSize;
   Maybe<SVGImageContext>             mSVGContext;
   const float                        mFrame;
   const uint32_t                     mFlags;
 };
 
 class DrawSingleTileCallback : public gfxDrawingCallback
 {
@@ -204,62 +207,60 @@ ClippedImage::GetIntrinsicRatio(nsSize* 
   if (!ShouldClip()) {
     return InnerImage()->GetIntrinsicRatio(aRatio);
   }
 
   *aRatio = nsSize(mClip.width, mClip.height);
   return NS_OK;
 }
 
-NS_IMETHODIMP_(already_AddRefed<gfxASurface>)
+NS_IMETHODIMP_(TemporaryRef<SourceSurface>)
 ClippedImage::GetFrame(uint32_t aWhichFrame,
                        uint32_t aFlags)
 {
   return GetFrameInternal(mClip.Size(), nullptr, aWhichFrame, aFlags);
 }
 
-already_AddRefed<gfxASurface>
+TemporaryRef<SourceSurface>
 ClippedImage::GetFrameInternal(const nsIntSize& aViewportSize,
                                const SVGImageContext* aSVGContext,
                                uint32_t aWhichFrame,
                                uint32_t aFlags)
 {
   if (!ShouldClip()) {
     return InnerImage()->GetFrame(aWhichFrame, aFlags);
   }
 
   float frameToDraw = InnerImage()->GetFrameIndex(aWhichFrame);
   if (!mCachedSurface || !mCachedSurface->Matches(aViewportSize,
                                                   aSVGContext,
                                                   frameToDraw,
                                                   aFlags)) {
     // Create a surface to draw into.
-    mozilla::RefPtr<mozilla::gfx::DrawTarget> target;
-    nsRefPtr<gfxContext> ctx;
+    RefPtr<DrawTarget> target = gfxPlatform::GetPlatform()->
+      CreateOffscreenContentDrawTarget(IntSize(mClip.width, mClip.height),
+                                       SurfaceFormat::B8G8R8A8);
 
-    target = gfxPlatform::GetPlatform()->
-      CreateOffscreenContentDrawTarget(gfx::IntSize(mClip.width, mClip.height),
-                                       gfx::SurfaceFormat::B8G8R8A8);
-    ctx = new gfxContext(target);
+    nsRefPtr<gfxContext> ctx = new gfxContext(target);
 
     // Create our callback.
     nsRefPtr<gfxDrawingCallback> drawTileCallback =
       new DrawSingleTileCallback(this, mClip, aViewportSize, aSVGContext, aWhichFrame, aFlags);
     nsRefPtr<gfxDrawable> drawable =
       new gfxCallbackDrawable(drawTileCallback, mClip.Size());
 
     // Actually draw. The callback will end up invoking DrawSingleTile.
     gfxRect imageRect(0, 0, mClip.width, mClip.height);
     gfxUtils::DrawPixelSnapped(ctx, drawable, gfxMatrix(),
                                imageRect, imageRect, imageRect, imageRect,
                                gfxImageFormat::ARGB32,
                                GraphicsFilter::FILTER_FAST);
 
     // Cache the resulting surface.
-    mCachedSurface = new ClippedImageCachedSurface(target,
+    mCachedSurface = new ClippedImageCachedSurface(target->Snapshot(),
                                                    aViewportSize,
                                                    aSVGContext,
                                                    frameToDraw,
                                                    aFlags);
   }
 
   MOZ_ASSERT(mCachedSurface, "Should have a cached surface now");
   return mCachedSurface->Surface();
@@ -317,17 +318,17 @@ ClippedImage::Draw(gfxContext* aContext,
   }
 
   // Check for tiling. If we need to tile then we need to create a
   // gfxCallbackDrawable to handle drawing for us.
   gfxRect sourceRect = aUserSpaceToImageSpace.Transform(aFill);
   if (MustCreateSurface(aContext, aUserSpaceToImageSpace, sourceRect, aSubimage, aFlags)) {
     // Create a temporary surface containing a single tile of this image.
     // GetFrame will call DrawSingleTile internally.
-    nsRefPtr<gfxASurface> surface =
+    RefPtr<SourceSurface> surface =
       GetFrameInternal(aViewportSize, aSVGContext, aWhichFrame, aFlags);
     NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE);
 
     // Create a drawable from that surface.
     nsRefPtr<gfxSurfaceDrawable> drawable =
       new gfxSurfaceDrawable(surface, gfxIntSize(mClip.width, mClip.height));
 
     // Draw.
--- a/image/src/ClippedImage.h
+++ b/image/src/ClippedImage.h
@@ -2,46 +2,50 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_IMAGELIB_CLIPPEDIMAGE_H_
 #define MOZILLA_IMAGELIB_CLIPPEDIMAGE_H_
 
 #include "ImageWrapper.h"
+#include "mozilla/gfx/2D.h"
 #include "mozilla/Maybe.h"
+#include "mozilla/RefPtr.h"
 
 namespace mozilla {
 namespace image {
 
 class ClippedImageCachedSurface;
 class DrawSingleTileCallback;
 
 /**
  * An Image wrapper that clips an image against a rectangle. Right now only
  * absolute coordinates in pixels are supported.
  *
  * XXX(seth): There a known (performance, not correctness) issue with
  * GetImageContainer. See the comments for that method for more information.
  */
 class ClippedImage : public ImageWrapper
 {
+  typedef mozilla::gfx::SourceSurface SourceSurface;
+
 public:
   NS_DECL_ISUPPORTS
 
   virtual ~ClippedImage();
 
   virtual nsIntRect FrameRect(uint32_t aWhichFrame) MOZ_OVERRIDE;
 
   NS_IMETHOD GetWidth(int32_t* aWidth) MOZ_OVERRIDE;
   NS_IMETHOD GetHeight(int32_t* aHeight) MOZ_OVERRIDE;
   NS_IMETHOD GetIntrinsicSize(nsSize* aSize) MOZ_OVERRIDE;
   NS_IMETHOD GetIntrinsicRatio(nsSize* aRatio) MOZ_OVERRIDE;
-  NS_IMETHOD_(already_AddRefed<gfxASurface>) GetFrame(uint32_t aWhichFrame,
-                                                      uint32_t aFlags) MOZ_OVERRIDE;
+  NS_IMETHOD_(mozilla::TemporaryRef<SourceSurface>)
+    GetFrame(uint32_t aWhichFrame, uint32_t aFlags) MOZ_OVERRIDE;
   NS_IMETHOD GetImageContainer(mozilla::layers::LayerManager* aManager,
                                mozilla::layers::ImageContainer** _retval) MOZ_OVERRIDE;
   NS_IMETHOD Draw(gfxContext* aContext,
                   GraphicsFilter aFilter,
                   const gfxMatrix& aUserSpaceToImageSpace,
                   const gfxRect& aFill,
                   const nsIntRect& aSubimage,
                   const nsIntSize& aViewportSize,
@@ -50,20 +54,21 @@ public:
                   uint32_t aFlags) MOZ_OVERRIDE;
   NS_IMETHOD RequestDiscard() MOZ_OVERRIDE;
   NS_IMETHOD_(Orientation) GetOrientation() MOZ_OVERRIDE;
 
 protected:
   ClippedImage(Image* aImage, nsIntRect aClip);
 
 private:
-  already_AddRefed<gfxASurface> GetFrameInternal(const nsIntSize& aViewportSize,
-                                                 const SVGImageContext* aSVGContext,
-                                                 uint32_t aWhichFrame,
-                                                 uint32_t aFlags);
+  mozilla::TemporaryRef<SourceSurface>
+    GetFrameInternal(const nsIntSize& aViewportSize,
+                     const SVGImageContext* aSVGContext,
+                     uint32_t aWhichFrame,
+                     uint32_t aFlags);
   bool ShouldClip();
   bool MustCreateSurface(gfxContext* aContext,
                          const gfxMatrix& aTransform,
                          const gfxRect& aSourceRect,
                          const nsIntRect& aSubimage,
                          const uint32_t aFlags) const;
   gfxFloat ClampFactor(const gfxFloat aToClamp, const int aReference) const;
   nsresult DrawSingleTile(gfxContext* aContext,
--- a/image/src/FrozenImage.cpp
+++ b/image/src/FrozenImage.cpp
@@ -1,15 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "FrozenImage.h"
 
+using namespace mozilla::gfx;
+
 namespace mozilla {
 namespace image {
 
 NS_IMPL_ISUPPORTS1(FrozenImage, imgIContainer)
 
 nsIntRect
 FrozenImage::FrameRect(uint32_t /* aWhichFrame - ignored */)
 {
@@ -35,17 +37,17 @@ FrozenImage::GetAnimated(bool* aAnimated
   bool dummy;
   nsresult rv = InnerImage()->GetAnimated(&dummy);
   if (NS_SUCCEEDED(rv)) {
     *aAnimated = false;
   }
   return rv;
 }
 
-NS_IMETHODIMP_(already_AddRefed<gfxASurface>)
+NS_IMETHODIMP_(TemporaryRef<SourceSurface>)
 FrozenImage::GetFrame(uint32_t aWhichFrame,
                       uint32_t aFlags)
 {
   return InnerImage()->GetFrame(FRAME_FIRST, aFlags);
 }
 
 NS_IMETHODIMP_(bool)
 FrozenImage::FrameIsOpaque(uint32_t aWhichFrame)
--- a/image/src/FrozenImage.h
+++ b/image/src/FrozenImage.h
@@ -2,16 +2,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_IMAGELIB_FROZENIMAGE_H_
 #define MOZILLA_IMAGELIB_FROZENIMAGE_H_
 
 #include "ImageWrapper.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/RefPtr.h"
 
 namespace mozilla {
 namespace image {
 
 /**
  * An Image wrapper that disables animation, freezing the image at its first
  * frame. It does this using two strategies. If this is the only instance of the
  * image, animation will never start, because IncrementAnimationConsumers is
@@ -19,28 +21,30 @@ namespace image {
  * because any imgIContainer method that is affected by animation gets its
  * aWhichFrame argument set to FRAME_FIRST when it passes through FrozenImage.
  *
  * XXX(seth): There a known (performance, not correctness) issue with
  * GetImageContainer. See the comments for that method for more information.
  */
 class FrozenImage : public ImageWrapper
 {
+  typedef mozilla::gfx::SourceSurface SourceSurface;
+
 public:
   NS_DECL_ISUPPORTS
 
   virtual ~FrozenImage() { }
 
   virtual nsIntRect FrameRect(uint32_t aWhichFrame) MOZ_OVERRIDE;
   virtual void IncrementAnimationConsumers() MOZ_OVERRIDE;
   virtual void DecrementAnimationConsumers() MOZ_OVERRIDE;
 
   NS_IMETHOD GetAnimated(bool* aAnimated) MOZ_OVERRIDE;
-  NS_IMETHOD_(already_AddRefed<gfxASurface>) GetFrame(uint32_t aWhichFrame,
-                                                      uint32_t aFlags) MOZ_OVERRIDE;
+  NS_IMETHOD_(TemporaryRef<SourceSurface>)
+    GetFrame(uint32_t aWhichFrame, uint32_t aFlags) MOZ_OVERRIDE;
   NS_IMETHOD_(bool) FrameIsOpaque(uint32_t aWhichFrame) MOZ_OVERRIDE;
   NS_IMETHOD GetImageContainer(layers::LayerManager* aManager,
                                layers::ImageContainer** _retval) MOZ_OVERRIDE;
   NS_IMETHOD Draw(gfxContext* aContext,
                   GraphicsFilter aFilter,
                   const gfxMatrix& aUserSpaceToImageSpace,
                   const gfxRect& aFill,
                   const nsIntRect& aSubimage,
--- a/image/src/ImageWrapper.cpp
+++ b/image/src/ImageWrapper.cpp
@@ -1,18 +1,22 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ImageWrapper.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/RefPtr.h"
 #include "Orientation.h"
 
 #include "mozilla/MemoryReporting.h"
 
+using mozilla::gfx::DataSourceSurface;
+using mozilla::gfx::SourceSurface;
 using mozilla::layers::LayerManager;
 using mozilla::layers::ImageContainer;
 
 namespace mozilla {
 namespace image {
 
 // Inherited methods from Image.
 
@@ -191,17 +195,17 @@ ImageWrapper::GetType()
 }
 
 NS_IMETHODIMP
 ImageWrapper::GetAnimated(bool* aAnimated)
 {
   return mInnerImage->GetAnimated(aAnimated);
 }
 
-NS_IMETHODIMP_(already_AddRefed<gfxASurface>)
+NS_IMETHODIMP_(TemporaryRef<SourceSurface>)
 ImageWrapper::GetFrame(uint32_t aWhichFrame,
                        uint32_t aFlags)
 {
   return mInnerImage->GetFrame(aWhichFrame, aFlags);
 }
 
 NS_IMETHODIMP_(bool)
 ImageWrapper::FrameIsOpaque(uint32_t aWhichFrame)
--- a/image/src/OrientedImage.cpp
+++ b/image/src/OrientedImage.cpp
@@ -6,16 +6,18 @@
 #include <algorithm>
 
 #include "gfxDrawable.h"
 #include "gfxPlatform.h"
 #include "gfxUtils.h"
 
 #include "OrientedImage.h"
 
+using namespace mozilla::gfx;
+
 using std::swap;
 using mozilla::layers::LayerManager;
 using mozilla::layers::ImageContainer;
 
 namespace mozilla {
 namespace image {
 
 NS_IMPL_ISUPPORTS1(OrientedImage, imgIContainer)
@@ -70,71 +72,68 @@ OrientedImage::GetIntrinsicRatio(nsSize*
 
   if (mOrientation.SwapsWidthAndHeight()) {
     swap(aRatio->width, aRatio->height);
   }
 
   return rv;
 }
 
-NS_IMETHODIMP_(already_AddRefed<gfxASurface>)
+NS_IMETHODIMP_(TemporaryRef<SourceSurface>)
 OrientedImage::GetFrame(uint32_t aWhichFrame,
                         uint32_t aFlags)
 {
   nsresult rv;
 
   if (mOrientation.IsIdentity()) {
     return InnerImage()->GetFrame(aWhichFrame, aFlags);
   }
 
   // Get the underlying dimensions.
   int32_t width, height;
+  rv = InnerImage()->GetWidth(&width);
+  NS_ENSURE_SUCCESS(rv, nullptr);
+  rv = InnerImage()->GetHeight(&height);
+  NS_ENSURE_SUCCESS(rv, nullptr);
   if (mOrientation.SwapsWidthAndHeight()) {
-    rv = InnerImage()->GetWidth(&height);
-    rv = NS_FAILED(rv) ? rv : InnerImage()->GetHeight(&width);
-  } else {
-    rv = InnerImage()->GetWidth(&width);
-    rv = NS_FAILED(rv) ? rv : InnerImage()->GetHeight(&height);
+    swap(width, height);
   }
   NS_ENSURE_SUCCESS(rv, nullptr);
 
   // Determine an appropriate format for the surface.
   gfx::SurfaceFormat surfaceFormat;
   gfxImageFormat imageFormat;
   if (InnerImage()->FrameIsOpaque(aWhichFrame)) {
     surfaceFormat = gfx::SurfaceFormat::B8G8R8X8;
     imageFormat = gfxImageFormat::ARGB32;
   } else {
     surfaceFormat = gfx::SurfaceFormat::B8G8R8A8;
     imageFormat = gfxImageFormat::ARGB32;
   }
 
   // Create a surface to draw into.
-  mozilla::RefPtr<mozilla::gfx::DrawTarget> target;
-  target = gfxPlatform::GetPlatform()->
-    CreateOffscreenContentDrawTarget(gfx::IntSize(width, height), surfaceFormat);
+  mozilla::RefPtr<DrawTarget> target =
+    gfxPlatform::GetPlatform()->
+      CreateOffscreenContentDrawTarget(IntSize(width, height), surfaceFormat);
 
   // Create our drawable.
-  nsRefPtr<gfxASurface> innerSurface =
+  RefPtr<SourceSurface> innerSurface =
     InnerImage()->GetFrame(aWhichFrame, aFlags);
   NS_ENSURE_TRUE(innerSurface, nullptr);
   nsRefPtr<gfxDrawable> drawable =
     new gfxSurfaceDrawable(innerSurface, gfxIntSize(width, height));
 
   // Draw.
   nsRefPtr<gfxContext> ctx = new gfxContext(target);
   gfxRect imageRect(0, 0, width, height);
   gfxUtils::DrawPixelSnapped(ctx, drawable, OrientationMatrix(nsIntSize(width, height)),
                              imageRect, imageRect, imageRect, imageRect,
                              imageFormat, GraphicsFilter::FILTER_FAST);
   
-  nsRefPtr<gfxASurface> surface = gfxPlatform::GetPlatform()->
-    GetThebesSurfaceForDrawTarget(target);
-
-  return surface.forget();
+  return target->Snapshot();
 }
 
 NS_IMETHODIMP
 OrientedImage::GetImageContainer(LayerManager* aManager, ImageContainer** _retval)
 {
   // XXX(seth): We currently don't have a way of orienting the result of
   // GetImageContainer. We work around this by always returning null, but if it
   // ever turns out that OrientedImage is widely used on codepaths that can
--- a/image/src/OrientedImage.h
+++ b/image/src/OrientedImage.h
@@ -2,43 +2,47 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_IMAGELIB_ORIENTEDIMAGE_H_
 #define MOZILLA_IMAGELIB_ORIENTEDIMAGE_H_
 
 #include "ImageWrapper.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/RefPtr.h"
 #include "Orientation.h"
 
 namespace mozilla {
 namespace image {
 
 /**
  * An Image wrapper that rotates and/or flips an image according to a specified
  * Orientation.
  *
  * XXX(seth): There a known (performance, not correctness) issue with
  * GetImageContainer. See the comments for that method for more information.
  */
 class OrientedImage : public ImageWrapper
 {
+  typedef mozilla::gfx::SourceSurface SourceSurface;
+
 public:
   NS_DECL_ISUPPORTS
 
   virtual ~OrientedImage() { }
 
   virtual nsIntRect FrameRect(uint32_t aWhichFrame) MOZ_OVERRIDE;
 
   NS_IMETHOD GetWidth(int32_t* aWidth) MOZ_OVERRIDE;
   NS_IMETHOD GetHeight(int32_t* aHeight) MOZ_OVERRIDE;
   NS_IMETHOD GetIntrinsicSize(nsSize* aSize) MOZ_OVERRIDE;
   NS_IMETHOD GetIntrinsicRatio(nsSize* aRatio) MOZ_OVERRIDE;
-  NS_IMETHOD_(already_AddRefed<gfxASurface>) GetFrame(uint32_t aWhichFrame,
-                                                      uint32_t aFlags) MOZ_OVERRIDE;
+  NS_IMETHOD_(mozilla::TemporaryRef<SourceSurface>)
+    GetFrame(uint32_t aWhichFrame, uint32_t aFlags) MOZ_OVERRIDE;
   NS_IMETHOD GetImageContainer(mozilla::layers::LayerManager* aManager,
                                mozilla::layers::ImageContainer** _retval) MOZ_OVERRIDE;
   NS_IMETHOD Draw(gfxContext* aContext,
                   GraphicsFilter aFilter,
                   const gfxMatrix& aUserSpaceToImageSpace,
                   const gfxRect& aFill,
                   const nsIntRect& aSubimage,
                   const nsIntSize& aViewportSize,
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -1,20 +1,24 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+// Must #include ImageLogging.h before any IPDL-generated files or other files that #include prlog.h
+#include "ImageLogging.h"
+
+#include "RasterImage.h"
+
 #include "base/histogram.h"
-#include "ImageLogging.h"
+#include "gfxPlatform.h"
 #include "nsComponentManagerUtils.h"
 #include "imgDecoderObserver.h"
 #include "nsError.h"
 #include "Decoder.h"
-#include "RasterImage.h"
 #include "nsAutoPtr.h"
 #include "prenv.h"
 #include "prsystem.h"
 #include "ImageContainer.h"
 #include "Layers.h"
 #include "nsPresContext.h"
 #include "nsIThreadPool.h"
 #include "nsXPCOMCIDInternal.h"
@@ -25,16 +29,18 @@
 #include "nsGIFDecoder2.h"
 #include "nsJPEGDecoder.h"
 #include "nsBMPDecoder.h"
 #include "nsICODecoder.h"
 #include "nsIconDecoder.h"
 
 #include "gfxContext.h"
 
+#include "mozilla/gfx/2D.h"
+#include "mozilla/RefPtr.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Services.h"
 #include "mozilla/Preferences.h"
 #include <stdint.h>
 #include "mozilla/Telemetry.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/gfx/Scale.h"
@@ -43,16 +49,17 @@
 #include "gfx2DGlue.h"
 #include <algorithm>
 
 #ifdef MOZ_NUWA_PROCESS
 #include "ipc/Nuwa.h"
 #endif
 
 using namespace mozilla;
+using namespace mozilla::gfx;
 using namespace mozilla::image;
 using namespace mozilla::layers;
 
 // a mask for flags that will affect the decoding
 #define DECODE_FLAGS_MASK (imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA | imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION)
 #define DECODE_FLAGS_DEFAULT 0
 
 /* Accounting for compressed data */
@@ -875,19 +882,19 @@ RasterImage::CopyFrame(uint32_t aWhichFr
   ctx.SetPattern(pattern);
   ctx.Fill();
 
   imgsurface.forget(_retval);
   return NS_OK;
 }
 
 //******************************************************************************
-/* [noscript] gfxASurface getFrame(in uint32_t aWhichFrame,
- *                                 in uint32_t aFlags); */
-NS_IMETHODIMP_(already_AddRefed<gfxASurface>)
+/* [noscript] SourceSurface getFrame(in uint32_t aWhichFrame,
+ *                                   in uint32_t aFlags); */
+NS_IMETHODIMP_(TemporaryRef<SourceSurface>)
 RasterImage::GetFrame(uint32_t aWhichFrame,
                       uint32_t aFlags)
 {
   MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE);
 
   if (aWhichFrame > FRAME_MAX_VALUE)
     return nullptr;
 
@@ -946,48 +953,63 @@ RasterImage::GetFrame(uint32_t aWhichFra
   // The image doesn't have a surface because it's been optimized away. Create
   // one.
   if (!framesurf) {
     nsRefPtr<gfxImageSurface> imgsurf;
     CopyFrame(aWhichFrame, aFlags, getter_AddRefs(imgsurf));
     framesurf = imgsurf;
   }
 
-  return framesurf.forget();
+  RefPtr<SourceSurface> result;
+
+  // As far as Moz2D is concerned, SourceSurface contains premultiplied alpha.
+  // If we're abusing it to contain non-premultiplied alpha then we want to
+  // avoid having Moz2D do any conversions on it (like copy to another
+  // surface). Hence why we try to wrap framesurf's data here for
+  // FLAG_DECODE_NO_PREMULTIPLY_ALPHA.
+  if ((aFlags & FLAG_WANT_DATA_SURFACE) != 0 ||
+      (aFlags & FLAG_DECODE_NO_PREMULTIPLY_ALPHA) != 0) {
+    result = gfxPlatform::GetPlatform()->GetWrappedDataSourceSurface(framesurf);
+  }
+  if (!result) {
+    result = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(nullptr,
+                                                                    framesurf);
+  }
+  return result.forget();
 }
 
 already_AddRefed<layers::Image>
 RasterImage::GetCurrentImage()
 {
   if (!mDecoded) {
     // We can't call StartDecoding because that can synchronously notify
     // which can cause DOM modification
     RequestDecodeCore(ASYNCHRONOUS);
     return nullptr;
   }
 
-  nsRefPtr<gfxASurface> imageSurface = GetFrame(FRAME_CURRENT, FLAG_NONE);
-  if (!imageSurface) {
+  RefPtr<SourceSurface> surface = GetFrame(FRAME_CURRENT, FLAG_NONE);
+  if (!surface) {
     // The OS threw out some or all of our buffer. Start decoding again.
     // GetFrame will only return null in the case that the image was
     // discarded. We already checked that the image is decoded, so other
     // error paths are not possible.
     ForceDiscard();
     RequestDecodeCore(ASYNCHRONOUS);
     return nullptr;
   }
 
   if (!mImageContainer) {
     mImageContainer = LayerManager::CreateImageContainer();
   }
 
   CairoImage::Data cairoData;
   GetWidth(&cairoData.mSize.width);
   GetHeight(&cairoData.mSize.height);
-  cairoData.mSourceSurface = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(nullptr, imageSurface);
+  cairoData.mSourceSurface = surface;
 
   nsRefPtr<layers::Image> image = mImageContainer->CreateImage(ImageFormat::CAIRO_SURFACE);
   NS_ASSERTION(image, "Failed to create Image");
 
   static_cast<CairoImage*>(image.get())->SetData(cairoData);
 
   return image.forget();
 }
--- a/image/src/SurfaceCache.cpp
+++ b/image/src/SurfaceCache.cpp
@@ -105,20 +105,21 @@ private:
   CachedSurface* mSurface;
   Cost           mCost;
 };
 
 /*
  * A CachedSurface associates a surface with a key that uniquely identifies that
  * surface.
  */
-class CachedSurface : public RefCounted<CachedSurface>
+class CachedSurface
 {
 public:
-  MOZ_DECLARE_REFCOUNTED_TYPENAME(CachedSurface)
+  NS_INLINE_DECL_REFCOUNTING(CachedSurface)
+
   CachedSurface(DrawTarget*       aTarget,
                 const IntSize     aTargetSize,
                 const Cost        aCost,
                 const ImageKey    aImageKey,
                 const SurfaceKey& aSurfaceKey)
     : mTarget(aTarget)
     , mTargetSize(aTargetSize)
     , mCost(aCost)
@@ -151,20 +152,21 @@ private:
 };
 
 /*
  * An ImageSurfaceCache is a per-image surface cache. For correctness we must be
  * able to remove all surfaces associated with an image when the image is
  * destroyed or invalidated. Since this will happen frequently, it makes sense
  * to make it cheap by storing the surfaces for each image separately.
  */
-class ImageSurfaceCache : public RefCounted<ImageSurfaceCache>
+class ImageSurfaceCache
 {
 public:
-  MOZ_DECLARE_REFCOUNTED_TYPENAME(ImageSurfaceCache)
+  NS_INLINE_DECL_REFCOUNTING(ImageSurfaceCache)
+
   typedef nsRefPtrHashtable<nsGenericHashKey<SurfaceKey>, CachedSurface> SurfaceTable;
 
   bool IsEmpty() const { return mSurfaces.Count() == 0; }
   
   void Insert(const SurfaceKey& aKey, CachedSurface* aSurface)
   {
     MOZ_ASSERT(aSurface, "Should have a surface");
     mSurfaces.Put(aKey, aSurface);
--- a/image/src/VectorImage.cpp
+++ b/image/src/VectorImage.cpp
@@ -644,19 +644,19 @@ VectorImage::FrameIsOpaque(uint32_t aWhi
 {
   if (aWhichFrame > FRAME_MAX_VALUE)
     NS_WARNING("aWhichFrame outside valid range!");
 
   return false; // In general, SVG content is not opaque.
 }
 
 //******************************************************************************
-/* [noscript] gfxASurface getFrame(in uint32_t aWhichFrame,
- *                                 in uint32_t aFlags; */
-NS_IMETHODIMP_(already_AddRefed<gfxASurface>)
+/* [noscript] SourceSurface getFrame(in uint32_t aWhichFrame,
+ *                                   in uint32_t aFlags; */
+NS_IMETHODIMP_(TemporaryRef<SourceSurface>)
 VectorImage::GetFrame(uint32_t aWhichFrame,
                       uint32_t aFlags)
 {
   MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE);
 
   if (aWhichFrame > FRAME_MAX_VALUE)
     return nullptr;
 
@@ -669,45 +669,32 @@ VectorImage::GetFrame(uint32_t aWhichFra
   if (!mSVGDocumentWrapper->GetWidthOrHeight(SVGDocumentWrapper::eWidth,
                                              imageIntSize.width) ||
       !mSVGDocumentWrapper->GetWidthOrHeight(SVGDocumentWrapper::eHeight,
                                              imageIntSize.height)) {
     // We'll get here if our SVG doc has a percent-valued width or height.
     return nullptr;
   }
 
-  // Create a surface that we'll ultimately return
-  // ---------------------------------------------
   // Make our surface the size of what will ultimately be drawn to it.
   // (either the full image size, or the restricted region)
-  gfxIntSize surfaceSize(imageIntSize.width, imageIntSize.height);
-
-  nsRefPtr<gfxImageSurface> surface =
-    new gfxImageSurface(surfaceSize, gfxImageFormat::ARGB32);
+  RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()->
+    CreateOffscreenContentDrawTarget(IntSize(imageIntSize.width,
+                                             imageIntSize.height),
+                                     SurfaceFormat::B8G8R8A8);
+  nsRefPtr<gfxContext> context = new gfxContext(dt);
 
-  RefPtr<DrawTarget> drawTarget =
-    Factory::CreateDrawTargetForData(BackendType::CAIRO,
-                                     surface->Data(),
-                                     IntSize(imageIntSize.width,
-                                             imageIntSize.height),
-                                     surface->Stride(),
-                                     SurfaceFormat::B8G8R8A8);
-
-  nsRefPtr<gfxContext> context = new gfxContext(drawTarget);
-
-  // Draw to our surface!
-  // --------------------
   nsresult rv = Draw(context, GraphicsFilter::FILTER_NEAREST, gfxMatrix(),
                      gfxRect(gfxPoint(0,0), gfxIntSize(imageIntSize.width,
                                                        imageIntSize.height)),
                      nsIntRect(nsIntPoint(0,0), imageIntSize),
                      imageIntSize, nullptr, aWhichFrame, aFlags);
 
   NS_ENSURE_SUCCESS(rv, nullptr);
-  return surface.forget();
+  return dt->Snapshot();
 }
 
 //******************************************************************************
 /* [noscript] ImageContainer getImageContainer(); */
 NS_IMETHODIMP
 VectorImage::GetImageContainer(LayerManager* aManager,
                                mozilla::layers::ImageContainer** _retval)
 {
--- a/image/src/imgTools.cpp
+++ b/image/src/imgTools.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "imgTools.h"
 
+#include "gfxUtils.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/RefPtr.h"
 #include "nsCOMPtr.h"
 #include "nsIDocument.h"
 #include "nsIDOMDocument.h"
 #include "nsError.h"
 #include "imgLoader.h"
 #include "imgICache.h"
@@ -92,30 +93,16 @@ NS_IMETHODIMP imgTools::DecodeImage(nsII
   rv = image->OnImageDataComplete(nullptr, nullptr, NS_OK, true);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // All done.
   NS_ADDREF(*aContainer = image.get());
   return NS_OK;
 }
 
-static TemporaryRef<SourceSurface>
-GetFirstImageFrame(imgIContainer *aContainer)
-{
-  nsRefPtr<gfxASurface> frame =
-    aContainer->GetFrame(imgIContainer::FRAME_FIRST,
-                         imgIContainer::FLAG_SYNC_DECODE);
-  NS_ENSURE_TRUE(frame, nullptr);
-
-  nsRefPtr<gfxImageSurface> imageSurface = frame->CopyToARGB32ImageSurface();
-  NS_ENSURE_TRUE(imageSurface, nullptr);
-
-  return imageSurface->CopyToB8G8R8A8DataSourceSurface();
-}
-
 /**
  * This takes a DataSourceSurface rather than a SourceSurface because some
  * of the callers have a DataSourceSurface and we don't want to call
  * GetDataSurface on such surfaces since that may incure a conversion to
  * SurfaceType::DATA which we don't need.
  */
 static nsresult EncodeImageData(DataSourceSurface* aDataSurface,
                                 const nsACString& aMimeType,
@@ -156,20 +143,32 @@ static nsresult EncodeImageData(DataSour
 }
 
 NS_IMETHODIMP imgTools::EncodeImage(imgIContainer *aContainer,
                                     const nsACString& aMimeType,
                                     const nsAString& aOutputOptions,
                                     nsIInputStream **aStream)
 {
   // Use frame 0 from the image container.
-  RefPtr<SourceSurface> frame = GetFirstImageFrame(aContainer);
+  RefPtr<SourceSurface> frame =
+    aContainer->GetFrame(imgIContainer::FRAME_FIRST,
+                         imgIContainer::FLAG_SYNC_DECODE);
   NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
 
-  RefPtr<DataSourceSurface> dataSurface = frame->GetDataSurface();
+  RefPtr<DataSourceSurface> dataSurface;
+
+  if (frame->GetFormat() == SurfaceFormat::B8G8R8A8) {
+    dataSurface = frame->GetDataSurface();
+  } else {
+    // Convert format to SurfaceFormat::B8G8R8A8
+    dataSurface = gfxUtils::
+      CopySurfaceToDataSourceSurfaceWithFormat(frame,
+                                               SurfaceFormat::B8G8R8A8);
+  }
+
   NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
 
   return EncodeImageData(dataSurface, aMimeType, aOutputOptions, aStream);
 }
 
 NS_IMETHODIMP imgTools::EncodeScaledImage(imgIContainer *aContainer,
                                           const nsACString& aMimeType,
                                           int32_t aScaledWidth,
@@ -181,17 +180,19 @@ NS_IMETHODIMP imgTools::EncodeScaledImag
 
   // If no scaled size is specified, we'll just encode the image at its
   // original size (no scaling).
   if (aScaledWidth == 0 && aScaledHeight == 0) {
     return EncodeImage(aContainer, aMimeType, aOutputOptions, aStream);
   }
 
   // Use frame 0 from the image container.
-  RefPtr<SourceSurface> frame = GetFirstImageFrame(aContainer);
+  RefPtr<SourceSurface> frame =
+    aContainer->GetFrame(imgIContainer::FRAME_FIRST,
+                         imgIContainer::FLAG_SYNC_DECODE);
   NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
 
   int32_t frameWidth = frame->GetSize().width;
   int32_t frameHeight = frame->GetSize().height;
 
   // If the given width or height is zero we'll replace it with the image's
   // original dimensions.
   if (aScaledWidth == 0) {
@@ -242,17 +243,19 @@ NS_IMETHODIMP imgTools::EncodeCroppedIma
 
   // If no size is specified then we'll preserve the image's original dimensions
   // and don't need to crop.
   if (aWidth == 0 && aHeight == 0) {
     return EncodeImage(aContainer, aMimeType, aOutputOptions, aStream);
   }
 
   // Use frame 0 from the image container.
-  RefPtr<SourceSurface> frame = GetFirstImageFrame(aContainer);
+  RefPtr<SourceSurface> frame =
+    aContainer->GetFrame(imgIContainer::FRAME_FIRST,
+                         imgIContainer::FLAG_SYNC_DECODE);
   NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
 
   int32_t frameWidth = frame->GetSize().width;
   int32_t frameHeight = frame->GetSize().height;
 
   // If the given width or height is zero we'll replace it with the image's
   // original dimensions.
   if (aWidth == 0) {
--- a/ipc/netd/Netd.cpp
+++ b/ipc/netd/Netd.cpp
@@ -11,16 +11,17 @@
 #include "cutils/properties.h"
 #include "android/log.h"
 
 #include "nsWhitespaceTokenizer.h"
 #include "nsXULAppAPI.h"
 #include "nsAutoPtr.h"
 #include "nsString.h"
 #include "nsThreadUtils.h"
+#include "mozilla/RefPtr.h"
 
 
 #define CHROMIUM_LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "Gonk", args)
 #define ICS_SYS_USB_RNDIS_MAC "/sys/class/android_usb/android0/f_rndis/ethaddr"
 #define INVALID_SOCKET -1
 #define MAX_RECONNECT_TIMES 10
 
 namespace {
--- a/ipc/netd/Netd.h
+++ b/ipc/netd/Netd.h
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_system_netd_h__
 #define mozilla_system_netd_h__
 
-#include "mozilla/RefPtr.h"
+#include "nsISupportsImpl.h"
 #include "nsAutoPtr.h"
 #include "base/message_loop.h"
 #include "mozilla/FileUtils.h"
 
 #define MAX_COMMAND_SIZE  4096
 
 namespace mozilla {
 namespace ipc {
@@ -21,38 +21,41 @@ namespace ipc {
 struct NetdCommand
 {
   uint8_t mData[MAX_COMMAND_SIZE];
 
   // Number of octets in mData.
   size_t mSize;
 };
 
-class NetdConsumer : public mozilla::RefCounted<NetdConsumer>
+class NetdConsumer
 {
+protected:
+  virtual ~NetdConsumer() { }
+
 public:
-  MOZ_DECLARE_REFCOUNTED_TYPENAME(NetdConsumer)
-  virtual ~NetdConsumer() { }
+  NS_INLINE_DECL_REFCOUNTING(NetdConsumer)
+
   virtual void MessageReceived(NetdCommand* aMessage) = 0;
 };
 
 class NetdWriteTask : public Task
 {
   virtual void Run();
 };
 
-class NetdClient : public MessageLoopForIO::LineWatcher,
-                   public RefCounted<NetdClient>
+class NetdClient : public MessageLoopForIO::LineWatcher
 {
+  virtual ~NetdClient();
+
 public:
-  MOZ_DECLARE_REFCOUNTED_TYPENAME(NetdClient)
+  NS_INLINE_DECL_REFCOUNTING(NetdClient)
   typedef std::queue<NetdCommand*> NetdCommandQueue;
 
   NetdClient();
-  virtual ~NetdClient();
   static void Start();
   static void SendNetdCommandIOThread(NetdCommand* aMessage);
 
 private:
   void WriteNetdCommand();
   virtual void OnError();
   virtual void OnLineRead(int aFd, nsDependentCSubstring& aMessage);
   virtual void OnFileCanWriteWithoutBlocking(int aFd);
--- a/ipc/unixsocket/UnixSocket.h
+++ b/ipc/unixsocket/UnixSocket.h
@@ -130,24 +130,26 @@ public:
 
 enum SocketConnectionStatus {
   SOCKET_DISCONNECTED = 0,
   SOCKET_LISTENING = 1,
   SOCKET_CONNECTING = 2,
   SOCKET_CONNECTED = 3
 };
 
-class UnixSocketConsumer : public AtomicRefCounted<UnixSocketConsumer>
+class UnixSocketConsumer
 {
+protected:
+  virtual ~UnixSocketConsumer();
+
 public:
-  MOZ_DECLARE_REFCOUNTED_TYPENAME(UnixSocketConsumer)
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(UnixSocketConsumer)
+
   UnixSocketConsumer();
 
-  virtual ~UnixSocketConsumer();
-
   SocketConnectionStatus GetConnectionStatus() const
   {
     MOZ_ASSERT(NS_IsMainThread());
     return mConnectionStatus;
   }
 
   int GetSuggestedConnectDelayMs() const
   {
--- a/js/ipc/JavaScriptChild.cpp
+++ b/js/ipc/JavaScriptChild.cpp
@@ -434,16 +434,18 @@ JavaScriptChild::AnswerIsExtensible(cons
     JSAutoRequest request(cx);
 
     *result = false;
 
     RootedObject obj(cx, findObject(objId));
     if (!obj)
         return false;
 
+    JSAutoCompartment comp(cx, obj);
+
     bool extensible;
     if (!JS_IsExtensible(cx, obj, &extensible))
         return fail(cx, rs);
 
     *result = !!extensible;
     return ok(rs);
 }
 
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug892794.js
@@ -0,0 +1,13 @@
+// |jit-test| ion-eager
+
+function test0(v) {
+  return (2147483648-Math.max(1.1,-(((2<<(-v|v))-3)|0)))|0;
+}
+assertEq(test0(1.6), 2147483645);
+assertEq(test0(437348122.9), 2147483646);
+
+function test1(v) {
+  return (2147483648+Math.min(v,0))|0;
+}
+assertEq(test1(2.1), -2147483648)
+assertEq(test1(-0.1), 2147483647)
--- a/js/src/jit-test/tests/proxy/testDirectProxyConstruct2.js
+++ b/js/src/jit-test/tests/proxy/testDirectProxyConstruct2.js
@@ -1,15 +1,18 @@
+load(libdir + "asserts.js");
 /*
  * Call the trap with the handler as the this value, the target as the first
  * argument, and the original arguments as the third argument.
+ *
+ * Hooks that don't return an object must throw.
  */
 var target = function () {};
 var handler = {
     construct: function (target1, args) {
         assertEq(this, handler);
         assertEq(target1, target);
         assertEq(args.length, 2);
         assertEq(args[0], 2);
         assertEq(args[1], 3);
     }
 }
-assertEq(new (new Proxy(target, handler))(2, 3), undefined);
+assertThrowsInstanceOf(function () {new (new Proxy(target, handler))(2, 3)}, TypeError);
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -4369,16 +4369,17 @@ ICGetElemNativeCompiler::generateStubCod
             emitCallNative(masm, objReg);
 
         } else {
             JS_ASSERT(acctype_ == ICGetElemNativeStub::ScriptedGetter);
 
             // Load function in scratchReg and ensure that it has a jit script.
             masm.loadPtr(Address(BaselineStubReg, ICGetElemNativeGetterStub::offsetOfGetter()),
                          scratchReg);
+            masm.branchIfFunctionHasNoScript(scratchReg, popR1 ? &failurePopR1 : &failure);
             masm.loadPtr(Address(scratchReg, JSFunction::offsetOfNativeOrScript()), scratchReg);
             masm.loadBaselineOrIonRaw(scratchReg, scratchReg, SequentialExecution,
                                       popR1 ? &failurePopR1 : &failure);
 
             // At this point, we are guaranteed to successfully complete.
             if (popR1)
                 masm.addPtr(Imm32(sizeof(size_t)), BaselineStackReg);
 
@@ -4801,34 +4802,47 @@ ICGetElem_Arguments::Compiler::generateS
     return true;
 }
 
 //
 // SetElem_Fallback
 //
 
 static bool
+SetElemDenseAddHasSameShapes(ICSetElem_DenseAdd *stub, JSObject *obj)
+{
+    size_t numShapes = stub->protoChainDepth() + 1;
+    for (size_t i = 0; i < numShapes; i++) {
+        static const size_t MAX_DEPTH = ICSetElem_DenseAdd::MAX_PROTO_CHAIN_DEPTH;
+        if (obj->lastProperty() != stub->toImplUnchecked<MAX_DEPTH>()->shape(i))
+            return false;
+        obj = obj->getProto();
+        if (!obj && i != numShapes - 1)
+            return false;
+    }
+
+    return true;
+}
+
+static bool
 DenseSetElemStubExists(JSContext *cx, ICStub::Kind kind, ICSetElem_Fallback *stub, HandleObject obj)
 {
     JS_ASSERT(kind == ICStub::SetElem_Dense || kind == ICStub::SetElem_DenseAdd);
 
     for (ICStubConstIterator iter = stub->beginChainConst(); !iter.atEnd(); iter++) {
         if (kind == ICStub::SetElem_Dense && iter->isSetElem_Dense()) {
             ICSetElem_Dense *dense = iter->toSetElem_Dense();
             if (obj->lastProperty() == dense->shape() && obj->getType(cx) == dense->type())
                 return true;
         }
 
         if (kind == ICStub::SetElem_DenseAdd && iter->isSetElem_DenseAdd()) {
             ICSetElem_DenseAdd *dense = iter->toSetElem_DenseAdd();
-            if (obj->lastProperty() == dense->toImplUnchecked<0>()->shape(0) &&
-                obj->getType(cx) == dense->type())
-            {
+            if (obj->getType(cx) == dense->type() && SetElemDenseAddHasSameShapes(dense, obj))
                 return true;
-            }
         }
     }
     return false;
 }
 
 static bool
 TypedArraySetElemStubExists(ICSetElem_Fallback *stub, HandleObject obj, bool expectOOB)
 {
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -3550,16 +3550,17 @@ class ICSetElem_DenseAdd : public ICUpda
     static size_t offsetOfType() {
         return offsetof(ICSetElem_DenseAdd, type_);
     }
 
     HeapPtrTypeObject &type() {
         return type_;
     }
     size_t protoChainDepth() const {
+        MOZ_ASSERT(extra_ <= MAX_PROTO_CHAIN_DEPTH);
         return extra_;
     }
 
     template <size_t ProtoChainDepth>
     ICSetElem_DenseAddImpl<ProtoChainDepth> *toImplUnchecked() {
         return static_cast<ICSetElem_DenseAddImpl<ProtoChainDepth> *>(this);
     }
 
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -3011,53 +3011,43 @@ jit::TraceIonScripts(JSTracer* trc, JSSc
 
 AutoDebugModeInvalidation::~AutoDebugModeInvalidation()
 {
     MOZ_ASSERT(!!comp_ != !!zone_);
 
     if (needInvalidation_ == NoNeed)
         return;
 
-    // Invalidate the stack if any compartments toggled from on->off, because
-    // we allow scripts to be on stack when turning off debug mode.
-    bool invalidateStack = needInvalidation_ == ToggledOff;
     Zone *zone = zone_ ? zone_ : comp_->zone();
     JSRuntime *rt = zone->runtimeFromMainThread();
     FreeOp *fop = rt->defaultFreeOp();
 
     if (comp_) {
         StopAllOffThreadCompilations(comp_);
     } else {
         for (CompartmentsInZoneIter comp(zone_); !comp.done(); comp.next())
             StopAllOffThreadCompilations(comp);
     }
 
-    if (invalidateStack) {
-        jit::MarkActiveBaselineScripts(zone);
-
-        for (JitActivationIterator iter(rt); !iter.done(); ++iter) {
-            JSCompartment *comp = iter.activation()->compartment();
-            if ((comp_ && comp_ == comp) ||
-                (zone_ && zone_ == comp->zone() && comp->principals))
-            {
-                IonContext ictx(CompileRuntime::get(rt));
-                AutoFlushCache afc("AutoDebugModeInvalidation", rt->jitRuntime());
-                IonSpew(IonSpew_Invalidate, "Invalidating frames for debug mode toggle");
-                InvalidateActivation(fop, iter.jitTop(), true);
-            }
+    jit::MarkActiveBaselineScripts(zone);
+
+    for (JitActivationIterator iter(rt); !iter.done(); ++iter) {
+        JSCompartment *comp = iter.activation()->compartment();
+        if ((comp_ && comp_ == comp) || (zone_ && zone_ == comp->zone())) {
+            IonContext ictx(CompileRuntime::get(rt));
+            AutoFlushCache afc("AutoDebugModeInvalidation", rt->jitRuntime());
+            IonSpew(IonSpew_Invalidate, "Invalidating frames for debug mode toggle");
+            InvalidateActivation(fop, iter.jitTop(), true);
         }
     }
 
     for (gc::CellIter i(zone, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
         JSScript *script = i.get<JSScript>();
-        if ((comp_ && script->compartment() == comp_) ||
-            (zone_ && script->compartment()->principals))
-        {
+        if ((comp_ && script->compartment() == comp_) || zone_) {
             FinishInvalidation<SequentialExecution>(fop, script);
             FinishInvalidation<ParallelExecution>(fop, script);
             FinishDiscardBaselineScript(fop, script);
-            // script->clearAnalysis();
             script->resetUseCount();
         } else if (script->hasBaselineScript()) {
             script->baselineScript()->resetActive();
         }
     }
 }
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -432,8 +432,10 @@ MSG_DEF(JSMSG_RC_AFTER_EXPORT_SPEC_LIST,
 MSG_DEF(JSMSG_NO_EXPORT_NAME,           378, 0, JSEXN_SYNTAXERR, "missing export name")
 MSG_DEF(JSMSG_DECLARATION_AFTER_EXPORT, 379, 0, JSEXN_SYNTAXERR, "missing declaration after 'export' keyword")
 MSG_DEF(JSMSG_INVALID_PROTOTYPE,        380, 0, JSEXN_TYPEERR, "prototype field is not an object")
 MSG_DEF(JSMSG_TYPEDOBJECT_HANDLE_TO_UNSIZED, 381, 0, JSEXN_TYPEERR, "cannot create a handle to an unsized type")
 MSG_DEF(JSMSG_SETPROTOTYPEOF_FAIL,      382, 1, JSEXN_TYPEERR, "[[SetPrototypeOf]] failed on {0}")
 MSG_DEF(JSMSG_INVALID_ARG_TYPE,         383, 3, JSEXN_TYPEERR, "Invalid type: {0} can't be a{1} {2}")
 MSG_DEF(JSMSG_TERMINATED,               384, 1, JSEXN_ERR, "Script terminated by timeout at:\n{0}")
 MSG_DEF(JSMSG_NO_SUCH_SELF_HOSTED_PROP, 385, 1, JSEXN_ERR, "No such property on self-hosted object: {0}")
+MSG_DEF(JSMSG_PROXY_EXTENSIBILITY,      386, 0, JSEXN_TYPEERR, "proxy must report same extensiblitity as target")
+MSG_DEF(JSMSG_PROXY_CONSTRUCT_OBJECT,   387, 0, JSEXN_TYPEERR, "proxy [[Construct]] must return an object")
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -1072,22 +1072,16 @@ ScriptedIndirectProxyHandler::fun_toStri
         return nullptr;
     }
     RootedObject obj(cx, &proxy->as<ProxyObject>().extra(0).toObject().getReservedSlot(0).toObject());
     return fun_toStringHelper(cx, obj, indent);
 }
 
 ScriptedIndirectProxyHandler ScriptedIndirectProxyHandler::singleton;
 
-static JSObject *
-GetDirectProxyHandlerObject(JSObject *proxy)
-{
-    return proxy->as<ProxyObject>().extra(0).toObjectOrNull();
-}
-
 /* Derived class for all scripted direct proxy handlers. */
 class ScriptedDirectProxyHandler : public DirectProxyHandler {
   public:
     ScriptedDirectProxyHandler();
     virtual ~ScriptedDirectProxyHandler();
 
     /* ES5 Harmony fundamental proxy traps. */
     virtual bool preventExtensions(JSContext *cx, HandleObject proxy) MOZ_OVERRIDE;
@@ -1109,16 +1103,19 @@ class ScriptedDirectProxyHandler : publi
     virtual bool get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
                      MutableHandleValue vp) MOZ_OVERRIDE;
     virtual bool set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
                      bool strict, MutableHandleValue vp) MOZ_OVERRIDE;
     virtual bool keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) MOZ_OVERRIDE;
     virtual bool iterate(JSContext *cx, HandleObject proxy, unsigned flags,
                          MutableHandleValue vp) MOZ_OVERRIDE;
 
+    /* ES6 Harmony traps */
+    virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) MOZ_OVERRIDE;
+
     /* Spidermonkey extensions. */
     virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) MOZ_OVERRIDE;
     virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) MOZ_OVERRIDE;
     virtual bool isScripted() MOZ_OVERRIDE { return true; }
 
     static ScriptedDirectProxyHandler singleton;
 };
 
@@ -1360,16 +1357,26 @@ IdToValue(JSContext *cx, HandleId id, Mu
     value.set(IdToValue(id)); // Re-use out-param to avoid Rooted overhead.
     JSString *name = ToString<CanGC>(cx, value);
     if (!name)
         return false;
     value.set(StringValue(name));
     return true;
 }
 
+// Get the [[ProxyHandler]] of a scripted direct proxy.
+//
+// NB: This *must* stay synched with proxy().
+static JSObject *
+GetDirectProxyHandlerObject(JSObject *proxy)
+{
+    JS_ASSERT(proxy->as<ProxyObject>().handler() == &ScriptedDirectProxyHandler::singleton);
+    return proxy->as<ProxyObject>().extra(0).toObjectOrNull();
+}
+
 // TrapGetOwnProperty(O, P)
 static bool
 TrapGetOwnProperty(JSContext *cx, HandleObject proxy, HandleId id, MutableHandleValue rval)
 {
     // step 1
     RootedObject handler(cx, GetDirectProxyHandlerObject(proxy));
 
     // step 2
@@ -2220,16 +2227,52 @@ ScriptedDirectProxyHandler::keys(JSConte
                              v, js::NullPtr(), bytes.ptr());
         return false;
     }
 
     // steps g-n are shared
     return ArrayToIdVector(cx, proxy, target, trapResult, props, JSITER_OWNONLY, cx->names().keys);
 }
 
+// ES6 (5 April, 2014) 9.5.3 Proxy.[[IsExtensible]](P)
+bool
+ScriptedDirectProxyHandler::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible)
+{
+    RootedObject handler(cx, GetDirectProxyHandlerObject(proxy));
+
+    RootedObject target(cx, proxy->as<ProxyObject>().target());
+
+    RootedValue trap(cx);
+    if (!JSObject::getProperty(cx, handler, handler, cx->names().isExtensible, &trap))
+        return false;
+
+    if (trap.isUndefined())
+        return DirectProxyHandler::isExtensible(cx, proxy, extensible);
+
+    Value argv[] = {
+        ObjectValue(*target)
+    };
+    RootedValue trapResult(cx);
+    if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult))
+        return false;
+
+    bool booleanTrapResult = ToBoolean(trapResult);
+    bool targetResult;
+    if (!JSObject::isExtensible(cx, target, &targetResult))
+        return false;
+
+    if (targetResult != booleanTrapResult) {
+       JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_EXTENSIBILITY);
+       return false;
+    }
+
+    *extensible = booleanTrapResult;
+    return true;
+}
+
 bool
 ScriptedDirectProxyHandler::iterate(JSContext *cx, HandleObject proxy, unsigned flags,
                                     MutableHandleValue vp)
 {
     // FIXME: Provide a proper implementation for this trap, see bug 787004
     return DirectProxyHandler::iterate(cx, proxy, flags, vp);
 }
 
@@ -2300,17 +2343,23 @@ ScriptedDirectProxyHandler::construct(JS
         return DirectProxyHandler::construct(cx, proxy, args);
 
     // step 6
     Value constructArgv[] = {
         ObjectValue(*target),
         ObjectValue(*argsArray)
     };
     RootedValue thisValue(cx, ObjectValue(*handler));
-    return Invoke(cx, thisValue, trap, ArrayLength(constructArgv), constructArgv, args.rval());
+    if (!Invoke(cx, thisValue, trap, ArrayLength(constructArgv), constructArgv, args.rval()))
+        return false;
+    if (!args.rval().isObject()) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_CONSTRUCT_OBJECT);
+        return false;
+    }
+    return true;
 }
 
 ScriptedDirectProxyHandler ScriptedDirectProxyHandler::singleton;
 
 #define INVOKE_ON_PROTOTYPE(cx, handler, proxy, protoCall)                   \
     JS_BEGIN_MACRO                                                           \
         RootedObject proto(cx);                                              \
         if (!JSObject::getProto(cx, proxy, &proto))                          \
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Proxy/proxy-constructNonObject.js
@@ -0,0 +1,18 @@
+function bogusConstruct(target) { return 4; }
+function bogusConstructUndefined(target) { }
+
+var handler = { construct: bogusConstruct }
+
+function callable() {}
+
+var p = new Proxy(callable, handler);
+
+assertThrowsInstanceOf(function () { new p(); }, TypeError,
+                       "[[Construct must throw if an object is not returned.");
+
+handler.construct = bogusConstructUndefined;
+assertThrowsInstanceOf(function () { new p(); }, TypeError,
+                       "[[Construct must throw if an object is not returned.");
+
+if (typeof reportCompare === "function")
+    reportCompare(0,0, "OK");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Proxy/proxy-isExtensible.js
@@ -0,0 +1,94 @@
+// Test ES6 Proxy trap compliance for Object.isExtensible() on exotic proxy
+// objects.
+var unsealed = {};
+var sealed = Object.seal({});
+var handler = {};
+
+assertEq(Object.isExtensible(unsealed), true);
+assertEq(Object.isExtensible(sealed), false);
+
+var targetSealed = new Proxy(sealed, handler);
+var targetUnsealed = new Proxy(unsealed, handler);
+
+var handlerCalled = false;
+
+// without traps, forward to the target
+// First, make sure we get the obvious answer on a non-exotic target.
+assertEq(Object.isExtensible(targetSealed), false, "Must forward to target without hook.");
+assertEq(Object.isExtensible(targetUnsealed), true, "Must forward to target without hook.");
+
+// Now, keep everyone honest. What if the target itself is a proxy?
+function ensureCalled() { handlerCalled = true; return true; }
+var proxyTarget = new Proxy({}, { isExtensible : ensureCalled });
+assertEq(Object.isExtensible(new Proxy(proxyTarget, {})), true, "Must forward to target without hook.");
+assertEq(handlerCalled, true, "Must forward to target without hook.");
+
+// with traps, makes sure that the handler is called, and that we throw if the
+// trap disagrees with the target
+function testExtensible(obj, shouldThrow, expectedResult)
+{
+    handlerCalled = false;
+    if (shouldThrow)
+        assertThrowsInstanceOf(function () { Object.isExtensible(obj); },
+                               TypeError, "Must throw if handler and target disagree.");
+    else
+        assertEq(Object.isExtensible(obj), expectedResult, "Must return the correct value.");
+    assertEq(handlerCalled, true, "Must call handler trap if present");
+}
+
+// What if the trap says it's necessarily sealed?
+function fakeSealed() { handlerCalled = true; return false; }
+handler.isExtensible = fakeSealed;
+testExtensible(targetSealed, false, false);
+testExtensible(targetUnsealed, true);
+
+// What if the trap says it's never sealed?
+function fakeUnsealed() { handlerCalled = true; return true; }
+handler.isExtensible = fakeUnsealed;
+testExtensible(targetSealed, true);
+testExtensible(targetUnsealed, false, true);
+
+// make sure we are able to prevent further extensions mid-flight and throw if the
+// hook tries to lie.
+function makeSealedTruth(target) { handlerCalled = true; Object.preventExtensions(target); return false; }
+function makeSealedLie(target) { handlerCalled = true; Object.preventExtensions(target); return true; }
+handler.isExtensible = makeSealedTruth;
+testExtensible(new Proxy({}, handler), false, false);
+handler.isExtensible = makeSealedLie;
+testExtensible(new Proxy({}, handler), true);
+
+// What if the trap doesn't directly return a boolean?
+function falseyNonBool() { handlerCalled = true; return undefined; }
+handler.isExtensible = falseyNonBool;
+testExtensible(targetSealed, false, false);
+testExtensible(targetUnsealed, true);
+
+function truthyNonBool() { handlerCalled = true; return {}; }
+handler.isExtensible = truthyNonBool;
+testExtensible(targetSealed, true);
+testExtensible(targetUnsealed, false, true);
+
+// What if the trap throws?
+function ExtensibleError() { }
+ExtensibleError.prototype = new Error();
+ExtensibleError.prototype.constructor = ExtensibleError;
+function throwFromTrap() { throw new ExtensibleError(); }
+handler.isExtensible = throwFromTrap;
+
+// exercise some other code paths and make sure that they invoke the trap and
+// can handle the ensuing error.
+assertThrowsInstanceOf(function () { Object.isExtensible(targetSealed); },
+                       ExtensibleError, "Must throw if the trap does.");
+assertThrowsInstanceOf(function () { Object.isFrozen(targetSealed); },
+                       ExtensibleError, "Must throw if the trap does.");
+assertThrowsInstanceOf(function () { Object.isSealed(targetSealed); },
+                       ExtensibleError, "Must throw if the trap does.");
+
+
+// What if the trap likes to re-ask old questions?
+function recurse() { return Object.isExtensible(targetSealed); }
+handler.isExtensible = recurse;
+assertThrowsInstanceOf(function () { Object.isExtensible(targetSealed); },
+                       InternalError, "Should allow and detect infinite recurison.");
+
+reportCompare(0, 0, "OK");
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -98,16 +98,17 @@
     macro(isFinite, isFinite, "isFinite") \
     macro(isNaN, isNaN, "isNaN") \
     macro(isPrototypeOf, isPrototypeOf, "isPrototypeOf") \
     macro(iterate, iterate, "iterate") \
     macro(Infinity, Infinity, "Infinity") \
     macro(int8, int8, "int8") \
     macro(int16, int16, "int16") \
     macro(int32, int32, "int32") \
+    macro(isExtensible, isExtensible, "isExtensible") \
     macro(iterator, iterator, "iterator") \
     macro(iteratorIntrinsic, iteratorIntrinsic, "__iterator__") \
     macro(join, join, "join") \
     macro(keys, keys, "keys") \
     macro(lastIndex, lastIndex, "lastIndex") \
     macro(length, length, "length") \
     macro(let, let, "let") \
     macro(line, line, "line") \
--- a/js/src/vm/Opcodes.h
+++ b/js/src/vm/Opcodes.h
@@ -281,17 +281,17 @@ 1234567890123456789012345678901234567890
     macro(JSOP_UNUSED47,  47, "unused47",   NULL,         1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED48,  48, "unused48",   NULL,         1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED49,  49, "unused49",   NULL,         1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED50,  50, "unused50",   NULL,         1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED51,  51, "unused51",   NULL,         1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED52,  52, "unused52",   NULL,         1,  0,  0,  JOF_BYTE) \
     \
     macro(JSOP_GETPROP,   53, "getprop",    NULL,         5,  1,  1, JOF_ATOM|JOF_PROP|JOF_TYPESET|JOF_TMPSLOT3) \
-    macro(JSOP_SETPROP,   54, "setprop",    NULL,         5,  2,  1, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING|JOF_TMPSLOT) \
+    macro(JSOP_SETPROP,   54, "setprop",    NULL,         5,  2,  1, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) \
     macro(JSOP_GETELEM,   55, "getelem",    NULL,         1,  2,  1, JOF_BYTE |JOF_ELEM|JOF_TYPESET|JOF_LEFTASSOC) \
     macro(JSOP_SETELEM,   56, "setelem",    NULL,         1,  3,  1, JOF_BYTE |JOF_ELEM|JOF_SET|JOF_DETECTING) \
     macro(JSOP_UNUSED57,  57, "unused57",   NULL,         1,  0,  0, JOF_BYTE) \
     macro(JSOP_CALL,      58, "call",       NULL,         3, -1,  1, JOF_UINT16|JOF_INVOKE|JOF_TYPESET) \
     macro(JSOP_NAME,      59, "name",       NULL,         5,  0,  1, JOF_ATOM|JOF_NAME|JOF_TYPESET) \
     macro(JSOP_DOUBLE,    60, "double",     NULL,         5,  0,  1, JOF_DOUBLE) \
     macro(JSOP_STRING,    61, "string",     NULL,         5,  0,  1, JOF_ATOM) \
     macro(JSOP_ZERO,      62, "zero",       "0",          1,  0,  1, JOF_BYTE) \
--- a/layout/base/FrameLayerBuilder.h
+++ b/layout/base/FrameLayerBuilder.h
@@ -41,19 +41,20 @@ enum LayerState {
   // when the layer has rounded rect clips.
   LAYER_ACTIVE_FORCE,
   // Special layer that is metadata only.
   LAYER_ACTIVE_EMPTY,
   // Inactive style layer for rendering SVG effects.
   LAYER_SVG_EFFECTS
 };
 
-class RefCountedRegion : public RefCounted<RefCountedRegion> {
+class RefCountedRegion {
 public:
-  MOZ_DECLARE_REFCOUNTED_TYPENAME(RefCountedRegion)
+  NS_INLINE_DECL_REFCOUNTING(RefCountedRegion)
+
   RefCountedRegion() : mIsInfinite(false) {}
   nsRegion mRegion;
   bool mIsInfinite;
 };
 
 struct ContainerLayerParameters {
   ContainerLayerParameters() :
     mXScale(1), mYScale(1), mAncestorClipRect(nullptr),
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -5392,31 +5392,22 @@ nsLayoutUtils::SurfaceFromElement(nsIIma
 
   int32_t imgWidth, imgHeight;
   rv = imgContainer->GetWidth(&imgWidth);
   nsresult rv2 = imgContainer->GetHeight(&imgHeight);
   if (NS_FAILED(rv) || NS_FAILED(rv2))
     return result;
 
   if (!noRasterize || imgContainer->GetType() == imgIContainer::TYPE_RASTER) {
-    bool wantImageSurface = (aSurfaceFlags & SFE_WANT_IMAGE_SURFACE) != 0;
-    if (aSurfaceFlags & SFE_NO_PREMULTIPLY_ALPHA) {
-      wantImageSurface = true;
+    if (aSurfaceFlags & SFE_WANT_IMAGE_SURFACE) {
+      frameFlags |= imgIContainer::FLAG_WANT_DATA_SURFACE;
     }
-    
-    nsRefPtr<gfxASurface> gfxsurf =
-      imgContainer->GetFrame(whichFrame, frameFlags);
-    if (!gfxsurf)
+    result.mSourceSurface = imgContainer->GetFrame(whichFrame, frameFlags);
+    if (!result.mSourceSurface) {
       return result;
-
-    if (wantImageSurface) {
-      result.mSourceSurface = gfxPlatform::GetPlatform()->GetWrappedDataSourceSurface(gfxsurf);
-    }
-    if (!result.mSourceSurface) {
-      result.mSourceSurface = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(aTarget, gfxsurf);
     }
   } else {
     result.mDrawInfo.mImgContainer = imgContainer;
     result.mDrawInfo.mWhichFrame = whichFrame;
     result.mDrawInfo.mDrawingFlags = frameFlags;
   }
 
   int32_t corsmode;
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -1079,17 +1079,17 @@ static const mozilla::Module::ContractID
   { "@mozilla.org/layout/xul-boxobject-tree;1", &kNS_TREEBOXOBJECT_CID },
 #endif // MOZ_XUL
 #ifdef MOZ_XUL
   { "@mozilla.org/inspector/dom-view;1", &kIN_DOMVIEW_CID },
 #endif
   { "@mozilla.org/inspector/deep-tree-walker;1", &kIN_DEEPTREEWALKER_CID },
   { "@mozilla.org/inspector/flasher;1", &kIN_FLASHER_CID },
   { "@mozilla.org/inspector/search;1?type=cssvalue", &kIN_CSSVALUESEARCH_CID },
-  { "@mozilla.org/inspector/dom-utils;1", &kIN_DOMUTILS_CID },
+  { IN_DOMUTILS_CONTRACTID, &kIN_DOMUTILS_CID },
   { "@mozilla.org/xml/xml-document;1", &kNS_XMLDOCUMENT_CID },
   { "@mozilla.org/svg/svg-document;1", &kNS_SVGDOCUMENT_CID },
   { NS_DOMMULTIPARTBLOB_CONTRACTID, &kNS_DOMMULTIPARTBLOB_CID },
   { NS_DOMMULTIPARTFILE_CONTRACTID, &kNS_DOMMULTIPARTFILE_CID },
   { "@mozilla.org/content/dom-selection;1", &kNS_DOMSELECTION_CID },
   { "@mozilla.org/content/post-content-iterator;1", &kNS_CONTENTITERATOR_CID },
   { "@mozilla.org/content/pre-content-iterator;1", &kNS_PRECONTENTITERATOR_CID },
   { "@mozilla.org/content/subtree-content-iterator;1", &kNS_SUBTREEITERATOR_CID },
--- a/layout/generic/nsCanvasFrame.cpp
+++ b/layout/generic/nsCanvasFrame.cpp
@@ -210,17 +210,17 @@ static void BlitSurface(DrawTarget* aDes
 void
 nsDisplayCanvasBackgroundImage::Paint(nsDisplayListBuilder* aBuilder,
                                       nsRenderingContext* aCtx)
 {
   nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame);
   nsPoint offset = ToReferenceFrame();
   nsRect bgClipRect = frame->CanvasArea() + offset;
 
-  nsRenderingContext context;
+  nsRefPtr<nsRenderingContext> context;
   nsRefPtr<gfxContext> dest = aCtx->ThebesContext();
   nsRefPtr<gfxASurface> surf;
   RefPtr<DrawTarget> dt;
   nsRefPtr<gfxContext> ctx;
   gfxRect destRect;
 #ifndef MOZ_GFX_OPTIMIZE_MOBILE
   if (IsSingleFixedPositionImage(aBuilder, bgClipRect, &destRect) &&
       aBuilder->IsPaintingToWindow() && !aBuilder->IsCompositingCheap() &&
@@ -249,23 +249,24 @@ nsDisplayCanvasBackgroundImage::Paint(ns
     }
     if (surf || dt) {
       if (surf) {
         ctx = new gfxContext(surf);
       } else {
         ctx = new gfxContext(dt);
       }
       ctx->Translate(-gfxPoint(destRect.x, destRect.y));
-      context.Init(aCtx->DeviceContext(), ctx);
+      context = new nsRenderingContext();
+      context->Init(aCtx->DeviceContext(), ctx);
     }
   }
 #endif
 
   PaintInternal(aBuilder,
-                (surf || dt) ? &context : aCtx,
+                (surf || dt) ? context.get() : aCtx,
                 (surf || dt) ? bgClipRect: mVisibleRect,
                 &bgClipRect);
 
   if (surf) {
     BlitSurface(dest, destRect, surf);
     frame->Properties().Set(nsIFrame::CachedBackgroundImage(),
                             surf.forget().take());
   }
--- a/layout/inspector/inDOMView.cpp
+++ b/layout/inspector/inDOMView.cpp
@@ -19,16 +19,17 @@
 #include "nsIDOMMutationEvent.h"
 #include "nsBindingManager.h"
 #include "nsNameSpaceManager.h"
 #include "nsIDocument.h"
 #include "nsIServiceManager.h"
 #include "nsITreeColumns.h"
 #include "nsITreeBoxObject.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/Services.h"
 
 #ifdef ACCESSIBILITY
 #include "nsIAccessible.h"
 #include "nsIAccessibilityService.h"
 #endif
 
 using namespace mozilla;
 
@@ -785,17 +786,17 @@ inDOMView::ContentInserted(nsIDocument *
 {
   if (!mTree)
     return;
 
   nsresult rv;
   nsCOMPtr<nsIDOMNode> childDOMNode(do_QueryInterface(aChild));
   nsCOMPtr<nsIDOMNode> parent;
   if (!mDOMUtils) {
-    mDOMUtils = do_GetService("@mozilla.org/inspector/dom-utils;1");
+    mDOMUtils = services::GetInDOMUtils();
     if (!mDOMUtils) {
       return;
     }
   }
   mDOMUtils->GetParentForNode(childDOMNode, mShowAnonymous,
                               getter_AddRefs(parent));
 
   // find the inDOMViewNode for the parent of the inserted content
@@ -1184,17 +1185,17 @@ inDOMView::GetChildNodesFor(nsIDOMNode* 
         AppendAttrsToArray(attrs, aResult);
       }
     }
   }
 
   if (mWhatToShow & nsIDOMNodeFilter::SHOW_ELEMENT) {
     nsCOMPtr<nsIDOMNodeList> kids;
     if (!mDOMUtils) {
-      mDOMUtils = do_GetService("@mozilla.org/inspector/dom-utils;1");
+      mDOMUtils = services::GetInDOMUtils();
       if (!mDOMUtils) {
         return NS_ERROR_FAILURE;
       }
     }
 
     mDOMUtils->GetChildrenForNode(aNode, mShowAnonymous,
                                   getter_AddRefs(kids));
 
@@ -1229,17 +1230,17 @@ inDOMView::AppendKidsToArray(nsIDOMNodeL
 {
   uint32_t l = 0;
   aKids->GetLength(&l);
   nsCOMPtr<nsIDOMNode> kid;
   uint16_t nodeType = 0;
 
   // Try and get DOM Utils in case we don't have one yet.
   if (!mShowWhitespaceNodes && !mDOMUtils) {
-    mDOMUtils = do_CreateInstance("@mozilla.org/inspector/dom-utils;1");
+    mDOMUtils = services::GetInDOMUtils();
   }
 
   for (uint32_t i = 0; i < l; ++i) {
     aKids->Item(i, getter_AddRefs(kid));
     kid->GetNodeType(&nodeType);
 
     NS_ASSERTION(nodeType && nodeType <= nsIDOMNode::NOTATION_NODE,
                  "Unknown node type. "
--- a/layout/inspector/inIDOMUtils.idl
+++ b/layout/inspector/inIDOMUtils.idl
@@ -99,8 +99,12 @@ interface inIDOMUtils : nsISupports
    * Parse CSS and update the style sheet in place.
    *
    * @param DOMCSSStyleSheet aSheet
    * @param DOMString aInput
    *        The new source string for the style sheet.
    */
   void parseStyleSheet(in nsIDOMCSSStyleSheet aSheet, in DOMString aInput);
 };
+
+%{ C++
+#define IN_DOMUTILS_CONTRACTID "@mozilla.org/inspector/dom-utils;1"
+%}
--- a/layout/ipc/RenderFrameParent.cpp
+++ b/layout/ipc/RenderFrameParent.cpp
@@ -653,43 +653,30 @@ public:
   {
     if (mTouchSensitiveRegion.IsEmpty())
       return false;
 
     *aOutRegion = CSSRect::FromAppUnits(mTouchSensitiveRegion.GetBounds());
     return true;
   }
 
-  virtual void NotifyTransformBegin(const ScrollableLayerGuid& aGuid)
+  virtual void NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
+                                    APZStateChange aChange,
+                                    int aArg)
   {
     if (MessageLoop::current() != mUILoop) {
       mUILoop->PostTask(
         FROM_HERE,
-        NewRunnableMethod(this, &RemoteContentController::NotifyTransformBegin,
-                          aGuid));
+        NewRunnableMethod(this, &RemoteContentController::NotifyAPZStateChange,
+                          aGuid, aChange, aArg));
       return;
     }
     if (mRenderFrame) {
       TabParent* browser = static_cast<TabParent*>(mRenderFrame->Manager());
-      browser->NotifyTransformBegin(aGuid.mScrollId);
-    }
-  }
-
-  virtual void NotifyTransformEnd(const ScrollableLayerGuid& aGuid)
-  {
-    if (MessageLoop::current() != mUILoop) {
-      mUILoop->PostTask(
-        FROM_HERE,
-        NewRunnableMethod(this, &RemoteContentController::NotifyTransformEnd,
-                          aGuid));
-      return;
-    }
-    if (mRenderFrame) {
-      TabParent* browser = static_cast<TabParent*>(mRenderFrame->Manager());
-      browser->NotifyTransformEnd(aGuid.mScrollId);
+      browser->NotifyAPZStateChange(aGuid.mScrollId, aChange, aArg);
     }
   }
 
   // Methods used by RenderFrameParent to set fields stored here.
 
   void SaveZoomConstraints(const ZoomConstraints& aConstraints)
   {
     mHaveZoomConstraints = true;
--- a/layout/svg/nsFilterInstance.cpp
+++ b/layout/svg/nsFilterInstance.cpp
@@ -313,21 +313,21 @@ nsFilterInstance::BuildSourcePaint(Sourc
     if (!offscreenDT) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
     ctx = new gfxContext(offscreenDT);
   }
 
   ctx->Translate(-neededRect.TopLeft());
 
-  nsRenderingContext tmpCtx;
-  tmpCtx.Init(mTargetFrame->PresContext()->DeviceContext(), ctx);
+  nsRefPtr<nsRenderingContext> tmpCtx(new nsRenderingContext());
+  tmpCtx->Init(mTargetFrame->PresContext()->DeviceContext(), ctx);
 
   gfxMatrix deviceToFilterSpace = GetFilterSpaceToDeviceSpaceTransform().Invert();
-  gfxContext *gfx = tmpCtx.ThebesContext();
+  gfxContext *gfx = tmpCtx->ThebesContext();
   gfx->Multiply(deviceToFilterSpace);
 
   gfx->Save();
 
   gfxMatrix matrix =
     nsSVGUtils::GetCanvasTM(mTargetFrame, nsISVGChildFrame::FOR_PAINTING,
                             mTransformRoot);
   if (!matrix.IsSingular()) {
@@ -396,18 +396,18 @@ nsFilterInstance::BuildSourceImage(gfxAS
     if (!offscreenDT) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
     ctx = new gfxContext(offscreenDT);
   }
 
   ctx->Translate(-neededRect.TopLeft());
 
-  nsRenderingContext tmpCtx;
-  tmpCtx.Init(mTargetFrame->PresContext()->DeviceContext(), ctx);
+  nsRefPtr<nsRenderingContext> tmpCtx(new nsRenderingContext());
+  tmpCtx->Init(mTargetFrame->PresContext()->DeviceContext(), ctx);
 
   gfxRect r = FilterSpaceToUserSpace(neededRect);
   r.RoundOut();
   nsIntRect dirty;
   if (!gfxUtils::GfxRectToIntRect(r, &dirty))
     return NS_ERROR_FAILURE;
 
   // SVG graphics paint to device space, so we need to set an initial device
@@ -417,18 +417,18 @@ nsFilterInstance::BuildSourceImage(gfxAS
   // (In theory it would be better to minimize error by having filtered SVG
   // graphics temporarily paint to user space when painting the sources and
   // only set a user space to filter space transform on the gfxContext
   // (since that would eliminate the transform multiplications from user
   // space to device space and back again). However, that would make the
   // code more complex while being hard to get right without introducing
   // subtle bugs, and in practice it probably makes no real difference.)
   gfxMatrix deviceToFilterSpace = GetFilterSpaceToDeviceSpaceTransform().Invert();
-  tmpCtx.ThebesContext()->Multiply(deviceToFilterSpace);
-  mPaintCallback->Paint(&tmpCtx, mTargetFrame, &dirty, mTransformRoot);
+  tmpCtx->ThebesContext()->Multiply(deviceToFilterSpace);
+  mPaintCallback->Paint(tmpCtx, mTargetFrame, &dirty, mTransformRoot);
 
   RefPtr<SourceSurface> sourceGraphicSource;
 
   if (offscreenSurface) {
     sourceGraphicSource =
       gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(aTargetDT, offscreenSurface);
   } else {
     sourceGraphicSource = offscreenDT->Snapshot();
--- a/layout/svg/nsSVGIntegrationUtils.cpp
+++ b/layout/svg/nsSVGIntegrationUtils.cpp
@@ -600,18 +600,18 @@ PaintFrameCallback::operator()(gfxContex
                                const GraphicsFilter& aFilter,
                                const gfxMatrix& aTransform)
 {
   if (mFrame->GetStateBits() & NS_FRAME_DRAWING_AS_PAINTSERVER)
     return false;
 
   mFrame->AddStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER);
 
-  nsRenderingContext context;
-  context.Init(mFrame->PresContext()->DeviceContext(), aContext);
+  nsRefPtr<nsRenderingContext> context(new nsRenderingContext());
+  context->Init(mFrame->PresContext()->DeviceContext(), aContext);
   aContext->Save();
 
   // Clip to aFillRect so that we don't paint outside.
   aContext->NewPath();
   aContext->Rectangle(aFillRect);
   aContext->Clip();
 
   aContext->Multiply(gfxMatrix(aTransform).Invert());
@@ -639,17 +639,17 @@ PaintFrameCallback::operator()(gfxContex
   nsRect dirty(-offset.x, -offset.y,
                mPaintServerSize.width, mPaintServerSize.height);
 
   uint32_t flags = nsLayoutUtils::PAINT_IN_TRANSFORM |
                    nsLayoutUtils::PAINT_ALL_CONTINUATIONS;
   if (mFlags & nsSVGIntegrationUtils::FLAG_SYNC_DECODE_IMAGES) {
     flags |= nsLayoutUtils::PAINT_SYNC_DECODE_IMAGES;
   }
-  nsLayoutUtils::PaintFrame(&context, mFrame,
+  nsLayoutUtils::PaintFrame(context, mFrame,
                             dirty, NS_RGBA(0, 0, 0, 0),
                             flags);
 
   aContext->Restore();
 
   mFrame->RemoveStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER);
 
   return true;
--- a/layout/svg/nsSVGMaskFrame.cpp
+++ b/layout/svg/nsSVGMaskFrame.cpp
@@ -221,35 +221,35 @@ nsSVGMaskFrame::ComputeMaskAlpha(nsRende
   // 'image'. However, we need to set the same matrix on the temporary context
   // and pattern that we create below as is currently set on 'gfx'.
   // Unfortunately, any device offset set by SetDeviceOffset() is affected by
   // the transform passed to the SetMatrix() calls, so to avoid that we account
   // for the device offset in the transform rather than use SetDeviceOffset().
   gfxMatrix matrix =
     gfx->CurrentMatrix() * gfxMatrix().Translate(-clipExtents.TopLeft());
 
-  nsRenderingContext tmpCtx;
-  tmpCtx.Init(this->PresContext()->DeviceContext(), image);
-  tmpCtx.ThebesContext()->SetMatrix(matrix);
+  nsRefPtr<nsRenderingContext> tmpCtx(new nsRenderingContext);
+  tmpCtx->Init(this->PresContext()->DeviceContext(), image);
+  tmpCtx->ThebesContext()->SetMatrix(matrix);
 
   mMaskParent = aParent;
   if (mMaskParentMatrix) {
     *mMaskParentMatrix = aMatrix;
   } else {
     mMaskParentMatrix = new gfxMatrix(aMatrix);
   }
 
   for (nsIFrame* kid = mFrames.FirstChild(); kid;
        kid = kid->GetNextSibling()) {
     // The CTM of each frame referencing us can be different
     nsISVGChildFrame* SVGFrame = do_QueryFrame(kid);
     if (SVGFrame) {
       SVGFrame->NotifySVGChanged(nsISVGChildFrame::TRANSFORM_CHANGED);
     }
-    nsSVGUtils::PaintFrameWithEffects(&tmpCtx, nullptr, kid);
+    nsSVGUtils::PaintFrameWithEffects(tmpCtx, nullptr, kid);
   }
 
   uint8_t *data   = image->Data();
   int32_t  stride = image->Stride();
   nsIntRect rect(0, 0, surfaceSize.width, surfaceSize.height);
 
   if (StyleSVGReset()->mMaskType == NS_STYLE_MASK_TYPE_LUMINANCE) {
     if (StyleSVG()->mColorInterpolation ==
--- a/layout/svg/nsSVGPatternFrame.cpp
+++ b/layout/svg/nsSVGPatternFrame.cpp
@@ -367,19 +367,19 @@ nsSVGPatternFrame::PaintPattern(gfxASurf
   }
 
   nsRefPtr<gfxASurface> tmpSurface =
     gfxPlatform::GetPlatform()->CreateOffscreenSurface(surfaceSize,
                                                        gfxContentType::COLOR_ALPHA);
   if (!tmpSurface || tmpSurface->CairoStatus())
     return NS_ERROR_FAILURE;
 
-  nsRenderingContext context;
-  context.Init(aSource->PresContext()->DeviceContext(), tmpSurface);
-  gfxContext* gfx = context.ThebesContext();
+  nsRefPtr<nsRenderingContext> context(new nsRenderingContext());
+  context->Init(aSource->PresContext()->DeviceContext(), tmpSurface);
+  gfxContext* gfx = context->ThebesContext();
 
   // Fill with transparent black
   gfx->SetOperator(gfxContext::OPERATOR_CLEAR);
   gfx->Paint();
   gfx->SetOperator(gfxContext::OPERATOR_OVER);
 
   if (aGraphicOpacity != 1.0f) {
     gfx->Save();
@@ -401,17 +401,17 @@ nsSVGPatternFrame::PaintPattern(gfxASurf
     patternFrame->AddStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER);
     for (nsIFrame* kid = firstKid; kid;
          kid = kid->GetNextSibling()) {
       // The CTM of each frame referencing us can be different
       nsISVGChildFrame* SVGFrame = do_QueryFrame(kid);
       if (SVGFrame) {
         SVGFrame->NotifySVGChanged(nsISVGChildFrame::TRANSFORM_CHANGED);
       }
-      nsSVGUtils::PaintFrameWithEffects(&context, nullptr, kid);
+      nsSVGUtils::PaintFrameWithEffects(context, nullptr, kid);
     }
     patternFrame->RemoveStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER);
   }
 
   patternFrame->mSource = nullptr;
 
   if (aGraphicOpacity != 1.0f) {
     gfx->PopGroupToSource();
--- a/layout/svg/nsSVGUtils.cpp
+++ b/layout/svg/nsSVGUtils.cpp
@@ -1466,21 +1466,22 @@ nsSVGUtils::PaintSVGGlyph(Element* aElem
                           DrawMode aDrawMode,
                           gfxTextContextPaint* aContextPaint)
 {
   nsIFrame* frame = aElement->GetPrimaryFrame();
   nsISVGChildFrame* svgFrame = do_QueryFrame(frame);
   if (!svgFrame) {
     return false;
   }
-  nsRenderingContext context;
-  context.Init(frame->PresContext()->DeviceContext(), aContext);
-  context.AddUserData(&gfxTextContextPaint::sUserDataKey, aContextPaint, nullptr);
+  nsRefPtr<nsRenderingContext> context(new nsRenderingContext());
+  context->Init(frame->PresContext()->DeviceContext(), aContext);
+  context->AddUserData(&gfxTextContextPaint::sUserDataKey, aContextPaint,
+                       nullptr);
   svgFrame->NotifySVGChanged(nsISVGChildFrame::TRANSFORM_CHANGED);
-  nsresult rv = svgFrame->PaintSVG(&context, nullptr, frame);
+  nsresult rv = svgFrame->PaintSVG(context, nullptr, frame);
   return NS_SUCCEEDED(rv);
 }
 
 bool
 nsSVGUtils::GetSVGGlyphExtents(Element* aElement,
                                const gfxMatrix& aSVGToAppSpace,
                                gfxRect* aResult)
 {
--- a/media/webrtc/signaling/src/sipcc/core/sdp/sdp_token.c
+++ b/media/webrtc/signaling/src/sipcc/core/sdp/sdp_token.c
@@ -1573,17 +1573,17 @@ sdp_result_e sdp_build_media (sdp_t *sdp
                 flex_string_sprintf(fs, " %s",
                                  sdp_get_payload_name((sdp_payload_e)mca_p->payload_type[i]));
             } else {
                 flex_string_sprintf(fs, " %u", mca_p->payload_type[i]);
             }
         }
     } else {
         /* Add port to SDP if transport is DTLS/SCTP */
-    	flex_string_sprintf(fs, " %u ", (u32)mca_p->sctpport);
+    	flex_string_sprintf(fs, " %u", (u32)mca_p->sctpport);
     }
 
     flex_string_sprintf(fs, "\r\n");
 
     if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
         SDP_PRINT("%s Built m= media line", sdp_p->debug_str);
     }
     return (SDP_SUCCESS);
--- a/media/webrtc/signaling/test/signaling_unittests.cpp
+++ b/media/webrtc/signaling/test/signaling_unittests.cpp
@@ -2812,17 +2812,17 @@ TEST_F(SignalingTest, missingUfrag)
     "a=rtpmap:120 VP8/90000\r\n"
     "a=recvonly\r\n"
     "a=candidate:0 1 UDP 2113601791 192.168.178.20 49929 typ host\r\n"
     "a=candidate:1 1 UDP 1694236671 77.9.79.167 49929 typ srflx raddr "
       "192.168.178.20 rport 49929\r\n"
     "a=candidate:0 2 UDP 2113601790 192.168.178.20 50769 typ host\r\n"
     "a=candidate:1 2 UDP 1694236670 77.9.79.167 50769 typ srflx raddr "
       "192.168.178.20 rport 50769\r\n"
-    "m=application 54054 DTLS/SCTP 5000 \r\n"
+    "m=application 54054 DTLS/SCTP 5000\r\n"
     "c=IN IP4 77.9.79.167\r\n"
     "a=fmtp:HuRUu]Dtcl\\zM,7(OmEU%O$gU]x/z\tD protocol=webrtc-datachannel;"
       "streams=16\r\n"
     "a=sendrecv\r\n";
 
   // Need to create an offer, since that's currently required by our
   // FSM. This may change in the future.
   a1_->CreateOffer(constraints, OFFER_AV, SHOULD_SENDRECV_AV);
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/BrowserLocaleManager.java
@@ -0,0 +1,285 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.util.Log;
+
+import java.util.Locale;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * This class manages persistence, application, and otherwise handling of
+ * user-specified locales.
+ *
+ * Of note:
+ *
+ * * It's a singleton, because its scope extends to that of the application,
+ *   and definitionally all changes to the locale of the app must go through
+ *   this.
+ * * It's lazy.
+ * * It has ties into the Gecko event system, because it has to tell Gecko when
+ *   to switch locale.
+ * * It relies on using the SharedPreferences file owned by the browser (in
+ *   Fennec's case, "GeckoApp") for performance.
+ */
+public class BrowserLocaleManager implements LocaleManager {
+    private static final String LOG_TAG = "GeckoLocales";
+
+    private static final String EVENT_LOCALE_CHANGED = "Locale:Changed";
+    private static final String PREF_LOCALE = "locale";
+
+    // This is volatile because we don't impose restrictions
+    // over which thread calls our methods.
+    private volatile Locale currentLocale = null;
+
+    private AtomicBoolean inited = new AtomicBoolean(false);
+    private boolean systemLocaleDidChange = false;
+    private BroadcastReceiver receiver;
+
+    private static AtomicReference<LocaleManager> instance = new AtomicReference<LocaleManager>();
+
+    public static LocaleManager getInstance() {
+        LocaleManager localeManager = instance.get();
+        if (localeManager != null) {
+            return localeManager;
+        }
+
+        localeManager = new BrowserLocaleManager();
+        if (instance.compareAndSet(null, localeManager)) {
+            return localeManager;
+        } else {
+            return instance.get();
+        }
+    }
+
+    /**
+     * Gecko uses locale codes like "es-ES", whereas a Java {@link Locale}
+     * stringifies as "es_ES".
+     *
+     * This method approximates the Java 7 method <code>Locale#toLanguageTag()</code>.
+     *
+     * @return a locale string suitable for passing to Gecko.
+     */
+    public static String getLanguageTag(final Locale locale) {
+        // If this were Java 7:
+        // return locale.toLanguageTag();
+
+        String language = locale.getLanguage();  // Can, but should never be, an empty string.
+        // Modernize certain language codes.
+        if (language.equals("iw")) {
+            language = "he";
+        } else if (language.equals("in")) {
+            language = "id";
+        } else if (language.equals("ji")) {
+            language = "yi";
+        }
+
+        String country = locale.getCountry();    // Can be an empty string.
+        if (country.equals("")) {
+            return language;
+        }
+        return language + "-" + country;
+    }
+
+    private static Locale parseLocaleCode(final String localeCode) {
+        int index;
+        if ((index = localeCode.indexOf('-')) != -1 ||
+            (index = localeCode.indexOf('_')) != -1) {
+            final String langCode = localeCode.substring(0, index);
+            final String countryCode = localeCode.substring(index + 1);
+            return new Locale(langCode, countryCode);
+        } else {
+            return new Locale(localeCode);
+        }
+    }
+
+    /**
+     * Ensure that you call this early in your application startup,
+     * and with a context that's sufficiently long-lived (typically
+     * the application context).
+     *
+     * Calling multiple times is harmless.
+     */
+    @Override
+    public void initialize(final Context context) {
+        if (!inited.compareAndSet(false, true)) {
+            return;
+        }
+
+        receiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                systemLocaleDidChange = true;
+            }
+        };
+        context.registerReceiver(receiver, new IntentFilter(Intent.ACTION_LOCALE_CHANGED));
+    }
+
+    @Override
+    public boolean systemLocaleDidChange() {
+        return systemLocaleDidChange;
+    }
+
+    /**
+     * Every time the system gives us a new configuration, it
+     * carries the external locale. Fix it.
+     */
+    @Override
+    public void correctLocale(Context context, Resources res, Configuration config) {
+        final Locale current = getCurrentLocale(context);
+        if (current == null) {
+            return;
+        }
+
+        // I know it's tempting to short-circuit here if the config seems to be
+        // up-to-date, but the rest is necessary.
+
+        config.locale = current;
+
+        // The following two lines are heavily commented in case someone
+        // decides to chase down performance improvements and decides to
+        // question what's going on here.
+        // Both lines should be cheap, *but*...
+
+        // This is unnecessary for basic string choice, but it almost
+        // certainly comes into play when rendering numbers, deciding on RTL,
+        // etc. Take it out if you can prove that's not the case.
+        Locale.setDefault(current);
+
+        // This seems to be a no-op, but every piece of documentation under the
+        // sun suggests that it's necessary, and it certainly makes sense.
+        res.updateConfiguration(config, res.getDisplayMetrics());
+    }
+
+    @Override
+    public String getAndApplyPersistedLocale(Context context) {
+        initialize(context);
+
+        final long t1 = android.os.SystemClock.uptimeMillis();
+        final String localeCode = getPersistedLocale(context);
+        if (localeCode == null) {
+            return null;
+        }
+
+        // Note that we don't tell Gecko about this. We notify Gecko when the
+        // locale is set, not when we update Java.
+        final String resultant = updateLocale(context, localeCode);
+
+        if (resultant == null) {
+            // Update the configuration anyway.
+            updateConfiguration(context, currentLocale);
+        }
+
+        final long t2 = android.os.SystemClock.uptimeMillis();
+        Log.i(LOG_TAG, "Locale read and update took: " + (t2 - t1) + "ms.");
+        return resultant;
+    }
+
+    /**
+     * Returns the set locale if it changed.
+     *
+     * Always persists and notifies Gecko.
+     */
+    @Override
+    public String setSelectedLocale(Context context, String localeCode) {
+        final String resultant = updateLocale(context, localeCode);
+
+        // We always persist and notify Gecko, even if nothing seemed to
+        // change. This might happen if you're picking a locale that's the same
+        // as the current OS locale. The OS locale might change next time we
+        // launch, and we need the Gecko pref and persisted locale to have been
+        // set by the time that happens.
+        persistLocale(context, localeCode);
+
+        // Tell Gecko.
+        GeckoEvent ev = GeckoEvent.createBroadcastEvent(EVENT_LOCALE_CHANGED, BrowserLocaleManager.getLanguageTag(getCurrentLocale(context)));
+        GeckoAppShell.sendEventToGecko(ev);
+
+        return resultant;
+    }
+
+    /**
+     * This is public to allow for an activity to force the
+     * current locale to be applied if necessary (e.g., when
+     * a new activity launches).
+     */
+    @Override
+    public void updateConfiguration(Context context, Locale locale) {
+        Resources res = context.getResources();
+        Configuration config = res.getConfiguration();
+        config.locale = locale;
+        res.updateConfiguration(config, res.getDisplayMetrics());
+    }
+
+    private SharedPreferences getSharedPreferences(Context context) {
+        return GeckoSharedPrefs.forApp(context);
+    }
+
+    private String getPersistedLocale(Context context) {
+        final SharedPreferences settings = getSharedPreferences(context);
+        final String locale = settings.getString(PREF_LOCALE, "");
+
+        if ("".equals(locale)) {
+            return null;
+        }
+        return locale;
+    }
+
+    private void persistLocale(Context context, String localeCode) {
+        final SharedPreferences settings = getSharedPreferences(context);
+        settings.edit().putString(PREF_LOCALE, localeCode).commit();
+    }
+
+    private Locale getCurrentLocale(Context context) {
+        if (currentLocale != null) {
+            return currentLocale;
+        }
+
+        final String current = getPersistedLocale(context);
+        if (current == null) {
+            return null;
+        }
+        return currentLocale = parseLocaleCode(current);
+    }
+
+    /**
+     * Updates the Java locale and the Android configuration.
+     *
+     * Returns the persisted locale if it differed.
+     *
+     * Does not notify Gecko.
+     */
+    private String updateLocale(Context context, String localeCode) {
+        // Fast path.
+        final Locale defaultLocale = Locale.getDefault();
+        if (defaultLocale.toString().equals(localeCode)) {
+            return null;
+        }
+
+        final Locale locale = parseLocaleCode(localeCode);
+
+        // Fast path.
+        if (defaultLocale.equals(locale)) {
+            return null;
+        }
+
+        Locale.setDefault(locale);
+        currentLocale = locale;
+
+        // Update resources.
+        updateConfiguration(context, locale);
+
+        return locale.toString();
+    }
+}
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -1253,17 +1253,17 @@ public abstract class GeckoApp
             Log.e(LOGTAG, "Exception starting favicon cache. Corrupt resources?", e);
         }
 
         // Did the OS locale change while we were backgrounded? If so,
         // we need to die so that Gecko will re-init add-ons that touch
         // the UI.
         // This is using a sledgehammer to crack a nut, but it'll do for
         // now.
-        if (LocaleManager.systemLocaleDidChange()) {
+        if (BrowserLocaleManager.getInstance().systemLocaleDidChange()) {
             Log.i(LOGTAG, "System locale changed. Restarting.");
             doRestart();
             GeckoAppShell.systemExit();
             return;
         }
 
         if (GeckoThread.isCreated()) {
             // This happens when the GeckoApp activity is destroyed by Android
@@ -1331,18 +1331,18 @@ public abstract class GeckoApp
 
         // Perform background initialization.
         ThreadUtils.postToBackgroundThread(new Runnable() {
             @Override
             public void run() {
                 final SharedPreferences prefs = GeckoApp.this.getSharedPreferences();
 
                 // Wait until now to set this, because we'd rather throw an exception than 
-                // have a caller of LocaleManager regress startup.
-                LocaleManager.initialize(getApplicationContext());
+                // have a caller of BrowserLocaleManager regress startup.
+                BrowserLocaleManager.getInstance().initialize(getApplicationContext());
 
                 SessionInformation previousSession = SessionInformation.fromSharedPrefs(prefs);
                 if (previousSession.wasKilled()) {
                     Telemetry.HistogramAdd("FENNEC_WAS_KILLED", 1);
                 }
 
                 SharedPreferences.Editor editor = prefs.edit();
                 editor.putBoolean(GeckoApp.PREFS_OOM_EXCEPTION, false);
@@ -1356,17 +1356,17 @@ public abstract class GeckoApp
                 // The lifecycle of mHealthRecorder is "shortly after onCreate"
                 // through "onDestroy" -- essentially the same as the lifecycle
                 // of the activity itself.
                 final String profilePath = getProfile().getDir().getAbsolutePath();
                 final EventDispatcher dispatcher = GeckoAppShell.getEventDispatcher();
                 Log.i(LOGTAG, "Creating HealthRecorder.");
 
                 final String osLocale = Locale.getDefault().toString();
-                String appLocale = LocaleManager.getAndApplyPersistedLocale(GeckoApp.this);
+                String appLocale = BrowserLocaleManager.getInstance().getAndApplyPersistedLocale(GeckoApp.this);
                 Log.d(LOGTAG, "OS locale is " + osLocale + ", app locale is " + appLocale);
 
                 if (appLocale == null) {
                     appLocale = osLocale;
                 }
 
                 mHealthRecorder = GeckoApp.this.createHealthRecorder(GeckoApp.this,
                                                                      profilePath,
@@ -2204,17 +2204,17 @@ public abstract class GeckoApp
         for (File file : files) {
             file.delete();
         }
     }
 
     @Override
     public void onConfigurationChanged(Configuration newConfig) {
         Log.d(LOGTAG, "onConfigurationChanged: " + newConfig.locale);
-        LocaleManager.correctLocale(this, getResources(), newConfig);
+        BrowserLocaleManager.getInstance().correctLocale(this, getResources(), newConfig);
 
         // onConfigurationChanged is not called for 180 degree orientation changes,
         // we will miss such rotations and the screen orientation will not be
         // updated.
         if (GeckoScreenOrientation.getInstance().update(newConfig.orientation)) {
             if (mFormAssistPopup != null)
                 mFormAssistPopup.hide();
             refreshChrome();
@@ -2796,24 +2796,24 @@ public abstract class GeckoApp
         // return (flags & android.content.pm.ApplicationInfo.FLAG_DEBUGGABLE) != 0;
     }
 
     // FHR reason code for a session end prior to a restart for a
     // locale change.
     private static final String SESSION_END_LOCALE_CHANGED = "L";
 
     /**
-     * Use LocaleManager to change our persisted and current locales,
+     * Use BrowserLocaleManager to change our persisted and current locales,
      * and poke HealthRecorder to tell it of our changed state.
      */
     private void setLocale(final String locale) {
         if (locale == null) {
             return;
         }
-        final String resultant = LocaleManager.setSelectedLocale(this, locale);
+        final String resultant = BrowserLocaleManager.getInstance().setSelectedLocale(this, locale);
         if (resultant == null) {
             return;
         }
 
         final boolean startNewSession = true;
         final boolean shouldRestart = false;
 
         // If the HealthRecorder is not yet initialized (unlikely), the locale change won't
--- a/mobile/android/base/GeckoApplication.java
+++ b/mobile/android/base/GeckoApplication.java
@@ -62,19 +62,19 @@ public class GeckoApplication extends Ap
         if (mInBackground) {
             super.onConfigurationChanged(config);
             return;
         }
 
         // Otherwise, correct the locale. This catches some cases that GeckoApp
         // doesn't get a chance to.
         try {
-            LocaleManager.correctLocale(this, getResources(), config);
+            BrowserLocaleManager.getInstance().correctLocale(this, getResources(), config);
         } catch (IllegalStateException ex) {
-            // GeckoApp hasn't started, so we have no ContextGetter in LocaleManager.
+            // GeckoApp hasn't started, so we have no ContextGetter in BrowserLocaleManager.
             Log.w(LOG_TAG, "Couldn't correct locale.", ex);
         }
 
         super.onConfigurationChanged(config);
     }
 
     public void onActivityPause(GeckoActivityStatus activity) {
         mInBackground = true;
--- a/mobile/android/base/LocaleManager.java
+++ b/mobile/android/base/LocaleManager.java
@@ -1,248 +1,20 @@
-/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
- * This Source Code Form is subject to the terms of the Mozilla Public
+/* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko;
 
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.SharedPreferences;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.util.Log;
-
 import java.util.Locale;
 
-/**
- * This class manages persistence, application, and otherwise handling of
- * user-specified locales.
- *
- * Of note:
- * 
- * * It's a singleton, because its scope extends to that of the application,
- *   and definitionally all changes to the locale of the app must go through
- *   this.
- * * It's lazy.
- * * It has ties into the Gecko event system, because it has to tell Gecko when
- *   to switch locale.
- * * It relies on using the SharedPreferences file owned by the browser (in
- *   Fennec's case, "GeckoApp") for performance.
- */
-public class LocaleManager {
-    private static final String LOG_TAG = "GeckoLocales";
-    private static final String PREF_LOCALE = "locale";
-
-    // This is volatile because we don't impose restrictions
-    // over which thread calls our methods.
-    private static volatile Locale currentLocale = null;
-
-    private static volatile boolean inited = false;
-    private static boolean systemLocaleDidChange = false;
-    private static BroadcastReceiver receiver;
-
-    /**
-     * Ensure that you call this early in your application startup,
-     * and with a context that's sufficiently long-lived (typically
-     * the application context).
-     *
-     * Calling multiple times is harmless.
-     */
-    public static void initialize(final Context context) {
-        if (inited) {
-            return;
-        }
-
-        receiver = new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                systemLocaleDidChange = true;
-            }
-        };
-        context.registerReceiver(receiver, new IntentFilter(Intent.ACTION_LOCALE_CHANGED));
-        inited = true;
-    }
-
-    public static boolean systemLocaleDidChange() {
-        return systemLocaleDidChange;
-    }
-
-    /**
-     * Every time the system gives us a new configuration, it
-     * carries the external locale. Fix it.
-     */
-    public static void correctLocale(Context context, Resources res, Configuration config) {
-        final Locale current = getCurrentLocale(context);
-        if (current == null) {
-            return;
-        }
-
-        // I know it's tempting to short-circuit here if the config seems to be
-        // up-to-date, but the rest is necessary.
-
-        config.locale = current;
-
-        // The following two lines are heavily commented in case someone
-        // decides to chase down performance improvements and decides to
-        // question what's going on here.
-        // Both lines should be cheap, *but*...
-
-        // This is unnecessary for basic string choice, but it almost
-        // certainly comes into play when rendering numbers, deciding on RTL,
-        // etc. Take it out if you can prove that's not the case.
-        Locale.setDefault(current);
-
-        // This seems to be a no-op, but every piece of documentation under the
-        // sun suggests that it's necessary, and it certainly makes sense.
-        res.updateConfiguration(config, res.getDisplayMetrics());
-    }
-
-    private static Locale parseLocaleCode(final String localeCode) {
-        int index;
-        if ((index = localeCode.indexOf('-')) != -1 ||
-            (index = localeCode.indexOf('_')) != -1) {
-            final String langCode = localeCode.substring(0, index);
-            final String countryCode = localeCode.substring(index + 1);
-            return new Locale(langCode, countryCode);
-        } else {
-            return new Locale(localeCode);
-        }
-    }
-
-    /**
-     * Gecko uses locale codes like "es-ES", whereas a Java {@link Locale}
-     * stringifies as "es_ES".
-     *
-     * This method approximates the Java 7 method <code>Locale#toLanguageTag()</code>.
-     *
-     * @return a locale string suitable for passing to Gecko.
-     */
-    public static String getLanguageTag(final Locale locale) {
-        // If this were Java 7:
-        // return locale.toLanguageTag();
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
 
-        String language = locale.getLanguage();  // Can, but should never be, an empty string.
-        // Modernize certain language codes.
-        if (language.equals("iw")) {
-            language = "he";
-        } else if (language.equals("in")) {
-            language = "id";
-        } else if (language.equals("ji")) {
-            language = "yi";
-        }
-
-        String country = locale.getCountry();    // Can be an empty string.
-        if (country.equals("")) {
-            return language;
-        }
-        return language + "-" + country;
-    }
-
-    public static Locale getCurrentLocale(Context context) {
-        if (currentLocale != null) {
-            return currentLocale;
-        }
-
-        final String current = getPersistedLocale(context);
-        if (current == null) {
-            return null;
-        }
-        return currentLocale = parseLocaleCode(current);
-    }
-
-    /**
-     * Updates the Java locale and the Android configuration.
-     *
-     * Returns the persisted locale if it differed.
-     *
-     * Does not notify Gecko.
-     */
-    private static String updateLocale(Context context, String localeCode) {
-        // Fast path.
-        final Locale defaultLocale = Locale.getDefault();
-        if (defaultLocale.toString().equals(localeCode)) {
-            return null;
-        }
-
-        final Locale locale = parseLocaleCode(localeCode);
-
-        // Fast path.
-        if (defaultLocale.equals(locale)) {
-            return null;
-        }
-
-        Locale.setDefault(locale);
-        currentLocale = locale;
-
-        // Update resources.
-        Resources res = context.getResources();
-        Configuration config = res.getConfiguration();
-        config.locale = locale;
-        res.updateConfiguration(config, res.getDisplayMetrics());
-
-        return locale.toString();
-    }
-
-    public static void notifyGeckoOfLocaleChange(Locale locale) {
-        // Tell Gecko.
-        GeckoEvent ev = GeckoEvent.createBroadcastEvent("Locale:Changed", getLanguageTag(locale));
-        GeckoAppShell.sendEventToGecko(ev);
-    }
-
-    public static String getPersistedLocale(Context context) {
-        final SharedPreferences settings = getSharedPreferences(context);
-        final String locale = settings.getString(PREF_LOCALE, "");
-
-        if ("".equals(locale)) {
-            return null;
-        }
-        return locale;
-    }
-
-    private static void persistLocale(Context context, String localeCode) {
-        final SharedPreferences settings = getSharedPreferences(context);
-        settings.edit().putString(PREF_LOCALE, localeCode).commit();
-    }
-
-    private static SharedPreferences getSharedPreferences(Context context) {
-        // TODO: this should be per-profile, but we don't want to pay the price.
-        return GeckoSharedPrefs.forApp(context);
-    }
-
-    public static String getAndApplyPersistedLocale(Context context) {
-        final long t1 = android.os.SystemClock.uptimeMillis();
-        final String localeCode = getPersistedLocale(context);
-        if (localeCode == null) {
-            return null;
-        }
-
-        // Note that we don't tell Gecko about this. We notify Gecko when the
-        // locale is set, not when we update Java.
-        final String resultant = updateLocale(context, localeCode);
-
-        final long t2 = android.os.SystemClock.uptimeMillis();
-        Log.i(LOG_TAG, "Locale read and update took: " + (t2 - t1) + "ms.");
-        return resultant;
-    }
-
-    /**
-     * Returns the set locale if it changed.
-     *
-     * Always persists and notifies Gecko.
-     */
-    public static String setSelectedLocale(Context context, String localeCode) {
-        final String resultant = updateLocale(context, localeCode);
-
-        // We always persist and notify Gecko, even if nothing seemed to
-        // change. This might happen if you're picking a locale that's the same
-        // as the current OS locale. The OS locale might change next time we
-        // launch, and we need the Gecko pref and persisted locale to have been
-        // set by the time that happens.
-        persistLocale(context, localeCode);
-        notifyGeckoOfLocaleChange(getCurrentLocale(context));
-        return resultant;
-    }
+public interface LocaleManager {
+    void initialize(Context context);
+    String getAndApplyPersistedLocale(Context context);
+    void correctLocale(Context context, Resources resources, Configuration newConfig);
+    void updateConfiguration(Context context, Locale locale);
+    String setSelectedLocale(Context context, String localeCode);
+    boolean systemLocaleDidChange();
 }
-
--- a/mobile/android/base/android-services.mozbuild
+++ b/mobile/android/base/android-services.mozbuild
@@ -769,16 +769,17 @@ sync_java_files = [
     'sync/repositories/StoreFailedException.java',
     'sync/repositories/StoreTracker.java',
     'sync/repositories/StoreTrackingRepositorySession.java',
     'sync/Server11PreviousPostFailedException.java',
     'sync/Server11RecordPostFailedException.java',
     'sync/setup/activities/AccountActivity.java',
     'sync/setup/activities/ActivityUtils.java',
     'sync/setup/activities/ClientRecordArrayAdapter.java',
+    'sync/setup/activities/LocaleAware.java',
     'sync/setup/activities/RedirectToSetupActivity.java',
     'sync/setup/activities/SendTabActivity.java',
     'sync/setup/activities/SendTabData.java',
     'sync/setup/activities/SetupFailureActivity.java',
     'sync/setup/activities/SetupSuccessActivity.java',
     'sync/setup/activities/SetupSyncActivity.java',
     'sync/setup/activities/SyncActivity.java',
     'sync/setup/activities/WebURLFinder.java',
--- a/mobile/android/base/background/announcements/AnnouncementsService.java
+++ b/mobile/android/base/background/announcements/AnnouncementsService.java
@@ -5,16 +5,17 @@
 package org.mozilla.gecko.background.announcements;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.net.URI;
 import java.util.List;
 import java.util.Locale;
 
+import org.mozilla.gecko.BrowserLocaleManager;
 import org.mozilla.gecko.background.BackgroundService;
 import org.mozilla.gecko.background.common.GlobalConstants;
 import org.mozilla.gecko.background.common.log.Logger;
 
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.os.IBinder;
 
@@ -123,16 +124,20 @@ public class AnnouncementsService extend
       return;
     }
 
     if (!shouldFetchAnnouncements()) {
       Logger.debug(LOG_TAG, "Not fetching.");
       return;
     }
 
+    // Ensure that our locale is up to date, so that the fetcher's
+    // Accept-Language header is, too.
+    BrowserLocaleManager.getInstance().getAndApplyPersistedLocale(getApplicationContext());
+
     // Otherwise, grab our announcements URL and process the contents.
     AnnouncementsFetcher.fetchAndProcessAnnouncements(getLastLaunch(), this);
   }
 
   @Override
   public IBinder onBind(Intent intent) {
     return null;
   }
--- a/mobile/android/base/background/healthreport/HealthReportGenerator.java
+++ b/mobile/android/base/background/healthreport/HealthReportGenerator.java
@@ -31,22 +31,26 @@ public class HealthReportGenerator {
   }
 
   @SuppressWarnings("static-method")
   protected long now() {
     return System.currentTimeMillis();
   }
 
   /**
+   * Ensure that you have initialized the Locale to your satisfaction
+   * prior to calling this method.
+   *
    * @return null if no environment could be computed, or else the resulting document.
    * @throws JSONException if there was an error adding environment data to the resulting document.
    */
   public JSONObject generateDocument(long since, long lastPingTime, String profilePath) throws JSONException {
     Logger.info(LOG_TAG, "Generating FHR document from " + since + "; last ping " + lastPingTime);
     Logger.pii(LOG_TAG, "Generating for profile " + profilePath);
+
     ProfileInformationCache cache = new ProfileInformationCache(profilePath);
     if (!cache.restoreUnlessInitialized()) {
       Logger.warn(LOG_TAG, "Not enough profile information to compute current environment.");
       return null;
     }
     Environment current = EnvironmentBuilder.getCurrentEnvironment(cache);
     return generateDocument(since, lastPingTime, current);
   }
--- a/mobile/android/base/background/healthreport/upload/AndroidSubmissionClient.java
+++ b/mobile/android/base/background/healthreport/upload/AndroidSubmissionClient.java
@@ -7,16 +7,17 @@ package org.mozilla.gecko.background.hea
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.Collection;
 
 import org.json.JSONException;
 import org.json.JSONObject;
+import org.mozilla.gecko.BrowserLocaleManager;
 import org.mozilla.gecko.background.bagheera.BagheeraClient;
 import org.mozilla.gecko.background.bagheera.BagheeraRequestDelegate;
 import org.mozilla.gecko.background.common.GlobalConstants;
 import org.mozilla.gecko.background.common.log.Logger;
 import org.mozilla.gecko.background.healthreport.Environment;
 import org.mozilla.gecko.background.healthreport.EnvironmentBuilder;
 import org.mozilla.gecko.background.healthreport.HealthReportConstants;
 import org.mozilla.gecko.background.healthreport.HealthReportDatabaseStorage;
@@ -394,16 +395,20 @@ public class AndroidSubmissionClient imp
     public class TrackingGenerator extends HealthReportGenerator {
       public TrackingGenerator() {
         super(storage);
       }
 
       @Override
       public JSONObject generateDocument(long since, long lastPingTime,
           String generationProfilePath) throws JSONException {
+
+        // Let's make sure we have an accurate locale.
+        BrowserLocaleManager.getInstance().getAndApplyPersistedLocale(context);
+
         final JSONObject document;
         // If the given profilePath matches the one we cached for the tracker, use the cached env.
         if (profilePath != null && profilePath.equals(generationProfilePath)) {
           final Environment environment = getCurrentEnvironment();
           document = super.generateDocument(since, lastPingTime, environment);
         } else {
           document = super.generateDocument(since, lastPingTime, generationProfilePath);
         }
--- a/mobile/android/base/db/BrowserDB.java
+++ b/mobile/android/base/db/BrowserDB.java
@@ -2,28 +2,25 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.db;
 
 import java.util.List;
 
-import org.mozilla.gecko.db.BrowserContract.Bookmarks;
 import org.mozilla.gecko.db.BrowserContract.ExpirePriority;
 import org.mozilla.gecko.favicons.decoders.LoadFaviconResult;
 import org.mozilla.gecko.mozglue.RobocopTarget;
 
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.database.ContentObserver;
 import android.database.Cursor;
-import android.database.CursorWrapper;
 import android.graphics.drawable.BitmapDrawable;
-import android.util.SparseArray;
 
 public class BrowserDB {
     private static boolean sAreContentProvidersEnabled = true;
 
     public static interface URLColumns {
         public static String URL = "url";
         public static String TITLE = "title";
         public static String FAVICON = "favicon";
@@ -353,188 +350,9 @@ public class BrowserDB {
 
     public static boolean areContentProvidersDisabled() {
         return sAreContentProvidersEnabled;
     }
 
     public static void setEnableContentProviders(boolean enableContentProviders) {
         sAreContentProvidersEnabled = enableContentProviders;
     }
-
-    public static class PinnedSite {
-        public String title = "";
-        public String url = "";
-
-        public PinnedSite(String aTitle, String aUrl) {
-            title = aTitle;
-            url = aUrl;
-        }
-    }
-
-    /* Cursor wrapper that forces top sites to contain at least
-     * mNumberOfTopSites entries. For rows outside the wrapped cursor
-     * will return empty strings and zero.
-     */
-    public static class TopSitesCursorWrapper extends CursorWrapper {
-        int mIndex = -1; // Current position of the cursor
-        Cursor mCursor = null;
-        int mSize = 0;
-        private SparseArray<PinnedSite> mPinnedSites = null;
-
-        public TopSitesCursorWrapper(Cursor pinnedCursor, Cursor normalCursor, int minSize) {
-            super(normalCursor);
-
-            setPinnedSites(pinnedCursor);
-            mCursor = normalCursor;
-            mSize = Math.max(minSize, mPinnedSites.size() + mCursor.getCount());
-        }
-
-        public void setPinnedSites(Cursor c) {
-            mPinnedSites = new SparseArray<PinnedSite>();
-
-            if (c == null) {
-                return;
-            }
-
-            try {
-                if (c.getCount() <= 0) {
-                    return;
-                }
-                c.moveToPosition(0);
-                do {
-                    int pos = c.getInt(c.getColumnIndex(Bookmarks.POSITION));
-                    String url = c.getString(c.getColumnIndex(URLColumns.URL));
-                    String title = c.getString(c.getColumnIndex(URLColumns.TITLE));
-                    mPinnedSites.put(pos, new PinnedSite(title, url));
-                } while (c.moveToNext());
-            } finally {
-                c.close();
-            }
-        }
-
-        public boolean hasPinnedSites() {
-            return mPinnedSites != null && mPinnedSites.size() > 0;
-        }
-
-        public PinnedSite getPinnedSite(int position) {
-            if (!hasPinnedSites()) {
-                return null;
-            }
-            return mPinnedSites.get(position);
-        }
-
-        public boolean isPinned() {
-            return mPinnedSites.get(mIndex) != null;
-        }
-
-        private int getPinnedBefore(int position) {
-            int numFound = 0;
-            if (!hasPinnedSites()) {
-                return numFound;
-            }
-
-            for (int i = 0; i < position; i++) {
-                if (mPinnedSites.get(i) != null) {
-                    numFound++;
-                }
-            }
-
-            return numFound;
-        }
-
-        @Override
-        public int getPosition() { return mIndex; }
-        @Override
-        public int getCount() { return mSize; }
-        @Override
-        public boolean isAfterLast() { return mIndex >= mSize; }
-        @Override
-        public boolean isBeforeFirst() { return mIndex < 0; }
-        @Override
-        public boolean isLast() { return mIndex == mSize - 1; }
-        @Override
-        public boolean moveToNext() { return moveToPosition(mIndex + 1); }
-        @Override
-        public boolean moveToPrevious() { return moveToPosition(mIndex - 1); }
-
-        @Override
-        public boolean moveToPosition(int position) {
-            mIndex = position;
-
-            // Move the real cursor as if we were stepping through it to this position.
-            // Account for pinned sites, and be careful to update its position to the
-            // minimum or maximum position, even if we're moving beyond its bounds.
-            int before = getPinnedBefore(position);
-            int p2 = position - before;
-            if (p2 <= -1) {
-                super.moveToPosition(-1);
-            } else if (p2 >= mCursor.getCount()) {
-                super.moveToPosition(mCursor.getCount());
-            } else {
-                super.moveToPosition(p2);
-            }
-
-            return !(isBeforeFirst() || isAfterLast());
-        }
-
-        @Override
-        public long getLong(int columnIndex) {
-            if (hasPinnedSites()) {
-                PinnedSite site = getPinnedSite(mIndex);
-                if (site != null) {
-                    return 0;
-                }
-            }
-
-            if (!super.isBeforeFirst() && !super.isAfterLast())
-                return super.getLong(columnIndex);
-            return 0;
-        }
-
-        @Override
-        public int getInt(int columnIndex) {
-            if (hasPinnedSites()) {
-                PinnedSite site = getPinnedSite(mIndex);
-                if (site != null) {
-                    return 0;
-                }
-            }
-
-            if (!super.isBeforeFirst() && !super.isAfterLast())
-                return super.getInt(columnIndex);
-            return 0;
-        }
-
-        @Override
-        public String getString(int columnIndex) {
-            if (hasPinnedSites()) {
-                PinnedSite site = getPinnedSite(mIndex);
-                if (site != null) {
-                    if (columnIndex == mCursor.getColumnIndex(URLColumns.URL)) {
-                        return site.url;
-                    } else if (columnIndex == mCursor.getColumnIndex(URLColumns.TITLE)) {
-                        return site.title;
-                    }
-                    return "";
-                }
-            }
-
-            if (!super.isBeforeFirst() && !super.isAfterLast())
-                return super.getString(columnIndex);
-            return "";
-        }
-
-        @Override
-        public boolean move(int offset) {
-            return moveToPosition(mIndex + offset);
-        }
-
-        @Override
-        public boolean moveToFirst() {
-            return moveToPosition(0);
-        }
-
-        @Override
-        public boolean moveToLast() {
-            return moveToPosition(mSize-1);
-        }
-    }
 }
--- a/mobile/android/base/db/HomeProvider.java
+++ b/mobile/android/base/db/HomeProvider.java
@@ -1,23 +1,23 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.db;
 
 import java.io.IOException;
-import java.io.InputStream;
 
 import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.db.BrowserContract.HomeItems;
 import org.mozilla.gecko.sqlite.SQLiteBridge;
+import org.mozilla.gecko.util.RawResource;
 
 import android.content.ContentValues;
 import android.content.UriMatcher;
 import android.database.Cursor;
 import android.database.MatrixCursor;
 import android.net.Uri;
 import android.util.Log;
 
@@ -78,17 +78,18 @@ public class HomeProvider extends SQLite
     }
 
     /**
      * Returns a cursor populated with static fake data.
      */
     private Cursor queryFakeItems(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
         JSONArray items = null;
         try {
-            items = new JSONArray(getRawFakeItems());
+            final String jsonString = RawResource.get(getContext(), R.raw.fake_home_items);
+            items = new JSONArray(jsonString);
         } catch (IOException e) {
             Log.e(LOGTAG, "Error getting fake home items", e);
             return null;
         } catch (JSONException e) {
             Log.e(LOGTAG, "Error parsing fake_home_items.json", e);
             return null;
         }
 
@@ -117,28 +118,16 @@ public class HomeProvider extends SQLite
                 });
             } catch (JSONException e) {
                 Log.e(LOGTAG, "Error creating cursor row for fake home item", e);
             }
         }
         return c;
     }
 
-    private String getRawFakeItems() throws IOException {
-        final InputStream inputStream = getContext().getResources().openRawResource(R.raw.fake_home_items);
-        final byte[] buffer = new byte[1024];
-        StringBuilder s = new StringBuilder();
-        int count;
-
-        while ((count = inputStream.read(buffer)) != -1) {
-            s.append(new String(buffer, 0, count));
-        }
-        return s.toString();
-    }
-
     /**
      * SQLiteBridgeContentProvider implementation
      */
 
     @Override
     protected String getDBName(){
         return DB_FILENAME;
     }
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/db/TopSitesCursorWrapper.java
@@ -0,0 +1,231 @@
+package org.mozilla.gecko.db;
+
+import android.database.Cursor;
+import android.database.CursorWrapper;
+import android.util.SparseArray;
+
+import org.mozilla.gecko.db.BrowserContract.Bookmarks;
+import org.mozilla.gecko.db.BrowserDB.URLColumns;
+
+/**
+ * {@TopSitesCursorWrapper} is a cursor wrapper that merges
+ * the top and pinned sites cursors into one. It ensures the
+ * cursor will contain at least a given minimum number of
+ * entries.
+ */
+public class TopSitesCursorWrapper extends CursorWrapper {
+
+    private static class PinnedSite {
+        public final String title;
+        public final String url;
+
+        public PinnedSite(String title, String url) {
+            this.title = (title == null ? "" : title);
+            this.url = (url == null ? "" : url);
+        }
+    }
+
+    // The cursor for the top sites query
+    private final Cursor topCursor;
+
+    // Associates pinned sites and their respective positions
+    private SparseArray<PinnedSite> pinnedSites;
+
+    // Current position of the cursor
+    private int currentPosition = -1;
+
+    // The size of the cursor wrapper
+    private final int count;
+
+    public TopSitesCursorWrapper(Cursor pinnedCursor, Cursor topCursor, int minSize) {
+        super(topCursor);
+
+        setPinnedSites(pinnedCursor);
+        this.topCursor = topCursor;
+
+        count = Math.max(minSize, pinnedSites.size() + topCursor.getCount());
+    }
+
+    public void setPinnedSites(Cursor c) {
+        pinnedSites = new SparseArray<PinnedSite>();
+
+        if (c == null) {
+            return;
+        }
+
+        try {
+            if (c.getCount() <= 0) {
+                return;
+            }
+
+            c.moveToPosition(0);
+            do {
+                final int pos = c.getInt(c.getColumnIndex(Bookmarks.POSITION));
+                final String url = c.getString(c.getColumnIndex(URLColumns.URL));
+                final String title = c.getString(c.getColumnIndex(URLColumns.TITLE));
+                pinnedSites.put(pos, new PinnedSite(title, url));
+            } while (c.moveToNext());
+        } finally {
+            c.close();
+        }
+    }
+
+    public boolean hasPinnedSites() {
+        return (pinnedSites != null && pinnedSites.size() > 0);
+    }
+
+    public PinnedSite getPinnedSite(int position) {
+        if (!hasPinnedSites()) {
+            return null;
+        }
+
+        return pinnedSites.get(position);
+    }
+
+    public boolean isPinned() {
+        return (pinnedSites.get(currentPosition) != null);
+    }
+
+    private int getPinnedBefore(int position) {
+        int numFound = 0;
+        if (!hasPinnedSites()) {
+            return numFound;
+        }
+
+        for (int i = 0; i < position; i++) {
+            if (pinnedSites.get(i) != null) {
+                numFound++;
+            }
+        }
+
+        return numFound;
+    }
+
+    @Override
+    public int getPosition() {
+        return currentPosition;
+    }
+
+    @Override
+    public int getCount() {
+        return count;
+    }
+
+    @Override
+    public boolean isAfterLast() {
+        return (currentPosition >= count);
+    }
+
+    @Override
+    public boolean isBeforeFirst() {
+        return (currentPosition < 0);
+    }
+
+    @Override
+    public boolean isLast() {
+        return (currentPosition == count - 1);
+    }
+
+    @Override
+    public boolean moveToNext() {
+        return moveToPosition(currentPosition + 1);
+    }
+
+    @Override
+    public boolean moveToPrevious() {
+        return moveToPosition(currentPosition - 1);
+    }
+
+    @Override
+    public boolean move(int offset) {
+        return moveToPosition(currentPosition + offset);
+    }
+
+    @Override
+    public boolean moveToFirst() {
+        return moveToPosition(0);
+    }
+
+    @Override
+    public boolean moveToLast() {
+        return moveToPosition(count - 1);
+    }
+
+    @Override
+    public boolean moveToPosition(int position) {
+        currentPosition = position;
+
+        // Move the real cursor as if we were stepping through it to this position.
+        // Account for pinned sites, and be careful to update its position to the
+        // minimum or maximum position, even if we're moving beyond its bounds.
+        final int before = getPinnedBefore(position);
+        final int p2 = position - before;
+
+        if (p2 <= -1) {
+            super.moveToPosition(-1);
+        } else if (p2 >= topCursor.getCount()) {
+            super.moveToPosition(topCursor.getCount());
+        } else {
+            super.moveToPosition(p2);
+        }
+
+        return (!isBeforeFirst() && !isAfterLast());
+    }
+
+    @Override
+    public long getLong(int columnIndex) {
+        if (hasPinnedSites()) {
+            final PinnedSite site = getPinnedSite(currentPosition);
+
+            if (site != null) {
+                return 0;
+            }
+        }
+
+        if (!super.isBeforeFirst() && !super.isAfterLast()) {
+            return super.getLong(columnIndex);
+        }
+
+        return 0;
+    }
+
+    @Override
+    public int getInt(int columnIndex) {
+        if (hasPinnedSites()) {
+            final PinnedSite site = getPinnedSite(currentPosition);
+
+            if (site != null) {
+                return 0;
+            }
+        }
+
+        if (!super.isBeforeFirst() && !super.isAfterLast()) {
+            return super.getInt(columnIndex);
+        }
+
+        return 0;
+    }
+
+    @Override
+    public String getString(int columnIndex) {
+        if (hasPinnedSites()) {
+            final PinnedSite site = getPinnedSite(currentPosition);
+
+            if (site != null) {
+                if (columnIndex == topCursor.getColumnIndex(URLColumns.URL)) {
+                    return site.url;
+                } else if (columnIndex == topCursor.getColumnIndex(URLColumns.TITLE)) {
+                    return site.title;
+                }
+
+                return "";
+            }
+        }
+
+        if (!super.isBeforeFirst() && !super.isAfterLast()) {
+            return super.getString(columnIndex);
+        }
+
+        return "";
+    }
+}
--- a/mobile/android/base/fxa/activities/FxAccountAbstractActivity.java
+++ b/mobile/android/base/fxa/activities/FxAccountAbstractActivity.java
@@ -4,26 +4,27 @@
 
 package org.mozilla.gecko.fxa.activities;
 
 import org.mozilla.gecko.background.common.log.Logger;
 import org.mozilla.gecko.background.fxa.FxAccountAgeLockoutHelper;
 import org.mozilla.gecko.fxa.FirefoxAccounts;
 import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
 import org.mozilla.gecko.sync.setup.activities.ActivityUtils;
+import org.mozilla.gecko.sync.setup.activities.LocaleAware.LocaleAwareActivity;
 
 import android.accounts.Account;
 import android.app.Activity;
 import android.content.Intent;
 import android.os.SystemClock;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.widget.TextView;
 
-public abstract class FxAccountAbstractActivity extends Activity {
+public abstract class FxAccountAbstractActivity extends LocaleAwareActivity {
   private static final String LOG_TAG = FxAccountAbstractActivity.class.getSimpleName();
 
   protected final boolean cannotResumeWhenAccountsExist;
   protected final boolean cannotResumeWhenNoAccountsExist;
   protected final boolean cannotResumeWhenLockedOut;
 
   public static final int CAN_ALWAYS_RESUME = 0;
   public static final int CANNOT_RESUME_WHEN_ACCOUNTS_EXIST = 1 << 0;
--- a/mobile/android/base/fxa/activities/FxAccountGetStartedActivity.java
+++ b/mobile/android/base/fxa/activities/FxAccountGetStartedActivity.java
@@ -9,16 +9,17 @@ import java.util.Locale;
 import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.background.common.log.Logger;
 import org.mozilla.gecko.background.fxa.FxAccountAgeLockoutHelper;
 import org.mozilla.gecko.fxa.FirefoxAccounts;
 import org.mozilla.gecko.fxa.FxAccountConstants;
 import org.mozilla.gecko.sync.Utils;
 import org.mozilla.gecko.sync.setup.activities.ActivityUtils;
+import org.mozilla.gecko.sync.setup.activities.LocaleAware;
 
 import android.accounts.AccountAuthenticatorActivity;
 import android.content.Intent;
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.widget.TextView;
@@ -34,17 +35,20 @@ public class FxAccountGetStartedActivity
   /**
    * {@inheritDoc}
    */
   @Override
   public void onCreate(Bundle icicle) {
     Logger.setThreadLogTag(FxAccountConstants.GLOBAL_LOG_TAG);
     Logger.debug(LOG_TAG, "onCreate(" + icicle + ")");
 
+    LocaleAware.initializeLocale(getApplicationContext());
+
     super.onCreate(icicle);
+
     setContentView(R.layout.fxaccount_get_started);
 
     linkifyOldFirefoxLink();
 
     View button = findViewById(R.id.get_started_button);
     button.setOnClickListener(new OnClickListener() {
       @Override
       public void onClick(View v) {
@@ -102,15 +106,15 @@ public class FxAccountGetStartedActivity
     }
   }
 
   protected void linkifyOldFirefoxLink() {
     TextView oldFirefox = (TextView) findViewById(R.id.old_firefox);
     String text = getResources().getString(R.string.fxaccount_getting_started_old_firefox);
     String VERSION = AppConstants.MOZ_APP_VERSION;
     String OS = AppConstants.OS_TARGET;
-    // We'll need to adjust this when we have active locale switching.
+
     String LOCALE = Utils.getLanguageTag(Locale.getDefault());
     String url = getResources().getString(R.string.fxaccount_link_old_firefox, VERSION, OS, LOCALE);
     FxAccountConstants.pii(LOG_TAG, "Old Firefox url is: " + url); // Don't want to leak locale in particular.
     ActivityUtils.linkTextView(oldFirefox, text, url);
   }
 }
--- a/mobile/android/base/fxa/activities/FxAccountStatusActivity.java
+++ b/mobile/android/base/fxa/activities/FxAccountStatusActivity.java
@@ -2,30 +2,30 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.fxa.activities;
 
 import org.mozilla.gecko.background.common.log.Logger;
 import org.mozilla.gecko.fxa.FirefoxAccounts;
 import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
+import org.mozilla.gecko.sync.setup.activities.LocaleAware.LocaleAwareFragmentActivity;
 
 import android.accounts.Account;
 import android.annotation.TargetApi;
 import android.app.ActionBar;
 import android.content.Intent;
 import android.os.Build;
 import android.os.Bundle;
-import android.support.v4.app.FragmentActivity;
 import android.view.MenuItem;
 
 /**
  * Activity which displays account status.
  */
-public class FxAccountStatusActivity extends FragmentActivity {
+public class FxAccountStatusActivity extends LocaleAwareFragmentActivity {
   private static final String LOG_TAG = FxAccountStatusActivity.class.getSimpleName();
 
   protected FxAccountStatusFragment statusFragment;
 
   @Override
   protected void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
 
--- a/mobile/android/base/fxa/sync/FxAccountNotificationManager.java
+++ b/mobile/android/base/fxa/sync/FxAccountNotificationManager.java
@@ -1,14 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.fxa.sync;
 
+import org.mozilla.gecko.BrowserLocaleManager;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.background.common.log.Logger;
 import org.mozilla.gecko.fxa.FxAccountConstants;
 import org.mozilla.gecko.fxa.activities.FxAccountStatusActivity;
 import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
 import org.mozilla.gecko.fxa.login.State;
 import org.mozilla.gecko.fxa.login.State.Action;
 
@@ -29,16 +30,19 @@ import android.support.v4.app.Notificati
  * <li>messages from other clients.</li>
  * </ul>
  */
 public class FxAccountNotificationManager {
   private static final String LOG_TAG = FxAccountNotificationManager.class.getSimpleName();
 
   protected final int notificationId;
 
+  // We're lazy about updating our locale info, because most syncs don't notify.
+  private volatile boolean localeUpdated;
+
   public FxAccountNotificationManager(int notificationId) {
     this.notificationId = notificationId;
   }
 
   /**
    * Reflect new Firefox Account state to the notification manager: show or hide
    * notifications reflecting the state of a Firefox Account.
    *
@@ -53,16 +57,21 @@ public class FxAccountNotificationManage
     final State state = fxAccount.getState();
     final Action action = state.getNeededAction();
     if (action == Action.None) {
       Logger.info(LOG_TAG, "State " + state.getStateLabel() + " needs no action; cancelling any existing notification.");
       notificationManager.cancel(notificationId);
       return;
     }
 
+    if (!localeUpdated) {
+      localeUpdated = true;
+      BrowserLocaleManager.getInstance().getAndApplyPersistedLocale(context);
+    }
+
     final String title = context.getResources().getString(R.string.fxaccount_sync_sign_in_error_notification_title);
     final String text = context.getResources().getString(R.string.fxaccount_sync_sign_in_error_notification_text, state.email);
     Logger.info(LOG_TAG, "State " + state.getStateLabel() + " needs action; offering notification with title: " + title);
     FxAccountConstants.pii(LOG_TAG, "And text: " + text);
 
     final Intent notificationIntent = new Intent(context, FxAccountStatusActivity.class);
     final PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0);
 
--- a/mobile/android/base/home/DynamicPanel.java
+++ b/mobile/android/base/home/DynamicPanel.java
@@ -408,18 +408,21 @@ public class DynamicPanel extends HomeFr
             } else {
                 selection = DBUtils.concatenateWhere(HomeItems.DATASET_ID + " = ?", HomeItems.FILTER + " = ?");
                 selectionArgs = new String[] { mRequest.getDatasetId(), mRequest.getFilter() };
             }
 
             // XXX: You can use CONTENT_FAKE_URI for development to pull items from fake_home_items.json.
             final Cursor c = cr.query(HomeItems.CONTENT_URI, null, selection, selectionArgs, null);
 
-            final Uri notificationUri = getDatasetNotificationUri(mRequest.getDatasetId());
-            c.setNotificationUri(cr, notificationUri);
+            // SQLiteBridgeContentProvider may return a null Cursor if the database hasn't been created yet.
+            if (c != null) {
+                final Uri notificationUri = getDatasetNotificationUri(mRequest.getDatasetId());
+                c.setNotificationUri(cr, notificationUri);
+            }
 
             return c;
         }
     }
 
     /**
      * LoaderCallbacks implementation that interacts with the LoaderManager.
      */
--- a/mobile/android/base/home/HomeConfig.java
+++ b/mobile/android/base/home/HomeConfig.java
@@ -607,70 +607,83 @@ public final class HomeConfig {
     public static class ViewConfig implements Parcelable {
         private final int mIndex;
         private final ViewType mType;
         private final String mDatasetId;
         private final ItemType mItemType;
         private final ItemHandler mItemHandler;
         private final String mBackImageUrl;
         private final String mFilter;
+        private final EmptyViewConfig mEmptyViewConfig;
 
         private static final String JSON_KEY_TYPE = "type";
         private static final String JSON_KEY_DATASET = "dataset";
         private static final String JSON_KEY_ITEM_TYPE = "itemType";
         private static final String JSON_KEY_ITEM_HANDLER = "itemHandler";
         private static final String JSON_KEY_BACK_IMAGE_URL = "backImageUrl";
         private static final String JSON_KEY_FILTER = "filter";
+        private static final String JSON_KEY_EMPTY = "empty";
 
         public ViewConfig(int index, JSONObject json) throws JSONException, IllegalArgumentException {
             mIndex = index;
             mType = ViewType.fromId(json.getString(JSON_KEY_TYPE));
             mDatasetId = json.getString(JSON_KEY_DATASET);
             mItemType = ItemType.fromId(json.getString(JSON_KEY_ITEM_TYPE));
             mItemHandler = ItemHandler.fromId(json.getString(JSON_KEY_ITEM_HANDLER));
             mBackImageUrl = json.optString(JSON_KEY_BACK_IMAGE_URL, null);
             mFilter = json.optString(JSON_KEY_FILTER, null);
 
+            final JSONObject jsonEmptyViewConfig = json.optJSONObject(JSON_KEY_EMPTY);
+            if (jsonEmptyViewConfig != null) {
+                mEmptyViewConfig = new EmptyViewConfig(jsonEmptyViewConfig);
+            } else {
+                mEmptyViewConfig = null;
+            }
+
             validate();
         }
 
         @SuppressWarnings("unchecked")
         public ViewConfig(Parcel in) {
             mIndex = in.readInt();
             mType = (ViewType) in.readParcelable(getClass().getClassLoader());
             mDatasetId = in.readString();
             mItemType = (ItemType) in.readParcelable(getClass().getClassLoader());
             mItemHandler = (ItemHandler) in.readParcelable(getClass().getClassLoader());
             mBackImageUrl = in.readString();
             mFilter = in.readString();
+            mEmptyViewConfig = (EmptyViewConfig) in.readParcelable(getClass().getClassLoader());
 
             validate();
         }
 
         public ViewConfig(ViewConfig viewConfig) {
             mIndex = viewConfig.mIndex;
             mType = viewConfig.mType;
             mDatasetId = viewConfig.mDatasetId;
             mItemType = viewConfig.mItemType;
             mItemHandler = viewConfig.mItemHandler;
             mBackImageUrl = viewConfig.mBackImageUrl;
             mFilter = viewConfig.mFilter;
+            mEmptyViewConfig = viewConfig.mEmptyViewConfig;
 
             validate();
         }
 
         public ViewConfig(int index, ViewType type, String datasetId, ItemType itemType,
-                          ItemHandler itemHandler, String backImageUrl, String filter) {
+                          ItemHandler itemHandler, String backImageUrl, String filter,
+                          EmptyViewConfig emptyViewConfig) {
             mIndex = index;
             mType = type;
             mDatasetId = datasetId;
             mItemType = itemType;
             mItemHandler = itemHandler;
             mBackImageUrl = backImageUrl;
             mFilter = filter;
+            mEmptyViewConfig = emptyViewConfig;
 
             validate();
         }
 
         private void validate() {
             if (mType == null) {
                 throw new IllegalArgumentException("Can't create ViewConfig with null type");
             }
@@ -711,32 +724,40 @@ public final class HomeConfig {
         public String getBackImageUrl() {
             return mBackImageUrl;
         }
 
         public String getFilter() {
             return mFilter;
         }
 
+        public EmptyViewConfig getEmptyViewConfig() {
+            return mEmptyViewConfig;
+        }
+
         public JSONObject toJSON() throws JSONException {
             final JSONObject json = new JSONObject();
 
             json.put(JSON_KEY_TYPE, mType.toString());
             json.put(JSON_KEY_DATASET, mDatasetId);
             json.put(JSON_KEY_ITEM_TYPE, mItemType.toString());
             json.put(JSON_KEY_ITEM_HANDLER, mItemHandler.toString());
 
             if (!TextUtils.isEmpty(mBackImageUrl)) {
                 json.put(JSON_KEY_BACK_IMAGE_URL, mBackImageUrl);
             }
 
             if (!TextUtils.isEmpty(mFilter)) {
                 json.put(JSON_KEY_FILTER, mFilter);
             }
 
+            if (mEmptyViewConfig != null) {
+                json.put(JSON_KEY_EMPTY, mEmptyViewConfig.toJSON());
+            }
+
             return json;
         }
 
         @Override
         public int describeContents() {
             return 0;
         }
 
@@ -744,31 +765,101 @@ public final class HomeConfig {
         public void writeToParcel(Parcel dest, int flags) {
             dest.writeInt(mIndex);
             dest.writeParcelable(mType, 0);
             dest.writeString(mDatasetId);
             dest.writeParcelable(mItemType, 0);
             dest.writeParcelable(mItemHandler, 0);
             dest.writeString(mBackImageUrl);
             dest.writeString(mFilter);
+            dest.writeParcelable(mEmptyViewConfig, 0);
         }
 
         public static final Creator<ViewConfig> CREATOR = new Creator<ViewConfig>() {
             @Override
             public ViewConfig createFromParcel(final Parcel in) {
                 return new ViewConfig(in);
             }
 
             @Override
             public ViewConfig[] newArray(final int size) {
                 return new ViewConfig[size];
             }
         };
     }
 
+    public static class EmptyViewConfig implements Parcelable {
+        private final String mText;
+        private final String mImageUrl;
+
+        private static final String JSON_KEY_TEXT = "text";
+        private static final String JSON_KEY_IMAGE_URL = "imageUrl";
+
+        public EmptyViewConfig(JSONObject json) throws JSONException, IllegalArgumentException {
+            mText = json.optString(JSON_KEY_TEXT, null);
+            mImageUrl = json.optString(JSON_KEY_IMAGE_URL, null);
+        }
+
+        @SuppressWarnings("unchecked")
+        public EmptyViewConfig(Parcel in) {
+            mText = in.readString();
+            mImageUrl = in.readString();
+        }
+
+        public EmptyViewConfig(EmptyViewConfig emptyViewConfig) {
+            mText = emptyViewConfig.mText;
+            mImageUrl = emptyViewConfig.mImageUrl;
+        }
+
+        public EmptyViewConfig(String text, String imageUrl) {
+            mText = text;
+            mImageUrl = imageUrl;
+        }
+
+        public String getText() {
+            return mText;
+        }
+
+        public String getImageUrl() {
+            return mImageUrl;
+        }
+
+        public JSONObject toJSON() throws JSONException {
+            final JSONObject json = new JSONObject();
+
+            json.put(JSON_KEY_TEXT, mText);
+            json.put(JSON_KEY_IMAGE_URL, mImageUrl);
+
+            return json;
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeString(mText);
+            dest.writeString(mImageUrl);
+        }
+
+        public static final Creator<EmptyViewConfig> CREATOR = new Creator<EmptyViewConfig>() {
+            @Override
+            public EmptyViewConfig createFromParcel(final Parcel in) {
+                return new EmptyViewConfig(in);
+            }
+
+            @Override
+            public EmptyViewConfig[] newArray(final int size) {
+                return new EmptyViewConfig[size];
+            }
+        };
+    }
+
     public static class AuthConfig implements Parcelable {
         private final String mMessageText;
         private final String mButtonText;
         private final String mImageUrl;
 
         private static final String JSON_KEY_MESSAGE_TEXT = "messageText";
         private static final String JSON_KEY_BUTTON_TEXT = "buttonText";
         private static final String JSON_KEY_IMAGE_URL = "imageUrl";
--- a/mobile/android/base/home/PanelLayout.java
+++ b/mobile/android/base/home/PanelLayout.java
@@ -2,38 +2,46 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.home;
 
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
+import org.mozilla.gecko.home.HomeConfig.EmptyViewConfig;
 import org.mozilla.gecko.home.HomeConfig.ItemHandler;
 import org.mozilla.gecko.home.HomeConfig.PanelConfig;
 import org.mozilla.gecko.home.HomeConfig.ViewConfig;
 import org.mozilla.gecko.util.StringUtils;
 
 import android.content.Context;
 import android.database.Cursor;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.KeyEvent;
+import android.view.LayoutInflater;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
 
 import java.lang.ref.SoftReference;
 import java.util.EnumSet;
 import java.util.LinkedList;
 import java.util.Map;
 import java.util.WeakHashMap;
 
+import com.squareup.picasso.Picasso;
+
 /**
  * {@code PanelLayout} is the base class for custom layouts to be
  * used in {@code DynamicPanel}. It provides the basic framework
  * that enables custom layouts to request and reset datasets and
  * create panel views. Furthermore, it automates most of the process
  * of binding panel views with their respective datasets.
  *
  * {@code PanelLayout} abstracts the implemention details of how
@@ -259,19 +267,34 @@ abstract class PanelLayout extends Frame
             case FILTER_PUSH:
                 viewState.pushFilter(request.getFilterDetail());
                 break;
             case FILTER_POP:
                 viewState.popFilter();
                 break;
         }
 
-        final View view = viewState.getView();
-        if (view != null) {
-            maybeSetDataset(view, cursor);
+        final View activeView = viewState.getActiveView();
+        if (activeView == null) {
+            throw new IllegalStateException("No active view for view state: " + viewState.getIndex());
+        }
+
+        final ViewConfig viewConfig = viewState.getViewConfig();
+
+        final View newView;
+        if (cursor == null || cursor.getCount() == 0) {
+            newView = createEmptyView(viewConfig);
+            maybeSetDataset(activeView, null);
+        } else {
+            newView = createPanelView(viewConfig);
+            maybeSetDataset(newView, cursor);
+        }
+
+        if (activeView != newView) {
+            replacePanelView(activeView, newView);
         }
     }
 
     /**
      * Releases any references to the given dataset from all
      * existing panel views.
      */
     public final void releaseDataset(int viewIndex) {
@@ -378,49 +401,126 @@ abstract class PanelLayout extends Frame
 
     private void maybeSetDataset(View view, Cursor cursor) {
         if (view instanceof DatasetBacked) {
             final DatasetBacked dsb = (DatasetBacked) view;
             dsb.setDataset(cursor);
         }
     }
 
+    private View createEmptyView(ViewConfig viewConfig) {
+        Log.d(LOGTAG, "Creating empty view: " + viewConfig.getType());
+
+        ViewState viewState = mViewStates.get(viewConfig.getIndex());
+        if (viewState == null) {
+            throw new IllegalStateException("No view state found for view index: " + viewConfig.getIndex());
+        }
+
+        View view = viewState.getEmptyView();
+        if (view == null) {
+            view = LayoutInflater.from(getContext()).inflate(R.layout.home_empty_panel, null);
+
+            final EmptyViewConfig emptyViewConfig = viewConfig.getEmptyViewConfig();
+
+            // XXX: Refactor this into a custom view (bug 985134)
+            final String text = (emptyViewConfig == null) ? null : emptyViewConfig.getText();
+            final TextView textView = (TextView) view.findViewById(R.id.home_empty_text);
+            if (TextUtils.isEmpty(text)) {
+                textView.setText(R.string.home_default_empty);
+            } else {
+                textView.setText(text);
+            }
+
+            final String imageUrl = (emptyViewConfig == null) ? null : emptyViewConfig.getImageUrl();
+            final ImageView imageView = (ImageView) view.findViewById(R.id.home_empty_image);
+
+            if (imageUrl == null) {
+                Picasso.with(getContext())
+                       .load(R.drawable.icon_home_empty_firefox)
+                       .into(imageView);
+            } else {
+                Picasso.with(getContext())
+                       .load(imageUrl)
+                       .error(R.drawable.icon_home_empty_firefox)
+                       .into(imageView);
+            }
+
+            viewState.setEmptyView(view);
+        }
+
+        return view;
+    }
+
+    private void replacePanelView(View currentView, View newView) {
+        final ViewGroup parent = (ViewGroup) currentView.getParent();
+        parent.addView(newView, parent.indexOfChild(currentView), currentView.getLayoutParams());
+        parent.removeView(currentView);
+    }
+
     /**
      * Must be implemented by {@code PanelLayout} subclasses to define
      * what happens then the layout is first loaded. Should set initial
      * UI state and request any necessary datasets.
      */
     public abstract void load();
 
     /**
      * Represents a 'live' instance of a panel view associated with
      * the {@code PanelLayout}. Is responsible for tracking the history stack of filters.
      */
     protected class ViewState {
         private final ViewConfig mViewConfig;
         private SoftReference<View> mView;
+        private SoftReference<View> mEmptyView;
         private LinkedList<FilterDetail> mFilterStack;
 
         public ViewState(ViewConfig viewConfig) {
             mViewConfig = viewConfig;
             mView = new SoftReference<View>(null);
+            mEmptyView = new SoftReference<View>(null);
+        }
+
+        public ViewConfig getViewConfig() {
+            return mViewConfig;
         }
 
         public int getIndex() {
             return mViewConfig.getIndex();
         }
 
         public View getView() {
             return mView.get();
         }
 
         public void setView(View view) {
             mView = new SoftReference<View>(view);
         }
 
+        public View getEmptyView() {
+            return mEmptyView.get();
+        }
+
+        public void setEmptyView(View view) {
+            mEmptyView = new SoftReference<View>(view);
+        }
+
+        public View getActiveView() {
+            final View view = getView();
+            if (view != null && view.getParent() != null) {
+                return view;
+            }
+
+            final View emptyView = getEmptyView();
+            if (emptyView != null && emptyView.getParent() != null) {
+                return emptyView;
+            }
+
+            return null;
+        }
+
         public String getDatasetId() {
             return mViewConfig.getDatasetId();
         }
 
         public ItemHandler getItemHandler() {
             return mViewConfig.getItemHandler();
         }
 
--- a/mobile/android/base/home/TopSitesGridView.java
+++ b/mobile/android/base/home/TopSitesGridView.java
@@ -4,18 +4,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.home;
 
 import java.util.EnumSet;
 
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.ThumbnailHelper;
-import org.mozilla.gecko.db.BrowserDB.TopSitesCursorWrapper;
 import org.mozilla.gecko.db.BrowserDB.URLColumns;
+import org.mozilla.gecko.db.TopSitesCursorWrapper;
 import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
 
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.database.Cursor;
 import android.graphics.Rect;
 import android.text.TextUtils;
 import android.util.AttributeSet;
--- a/mobile/android/base/home/TopSitesPanel.java
+++ b/mobile/android/base/home/TopSitesPanel.java
@@ -11,18 +11,18 @@ import java.util.HashMap;
 import java.util.Map;
 
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.Telemetry;
 import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.db.BrowserContract.Combined;
 import org.mozilla.gecko.db.BrowserContract.Thumbnails;
 import org.mozilla.gecko.db.BrowserDB;
-import org.mozilla.gecko.db.BrowserDB.TopSitesCursorWrapper;
 import org.mozilla.gecko.db.BrowserDB.URLColumns;
+import org.mozilla.gecko.db.TopSitesCursorWrapper;
 import org.mozilla.gecko.favicons.Favicons;
 import org.mozilla.gecko.favicons.OnFaviconLoadedListener;
 import org.mozilla.gecko.gfx.BitmapUtils;
 import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
 import org.mozilla.gecko.home.PinSiteDialog.OnSiteSelectedListener;
 import org.mozilla.gecko.home.TopSitesGridView.OnEditPinnedSiteListener;
 import org.mozilla.gecko.home.TopSitesGridView.TopSitesGridContextMenuInfo;
 import org.mozilla.gecko.util.ThreadUtils;
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -61,16 +61,17 @@ gujar.sources += [
     'util/INISection.java',
     'util/JSONUtils.java',
     'util/MenuUtils.java',
     'util/NativeEventListener.java',
     'util/NativeJSContainer.java',
     'util/NativeJSObject.java',
     'util/NonEvictingLruCache.java',
     'util/ProxySelector.java',
+    'util/RawResource.java',
     'util/StringUtils.java',
     'util/ThreadUtils.java',
     'util/UiAsyncTask.java',
 ]
 gujar.extra_jars = [
     'gecko-mozglue.jar'
 ]
 gujar.javac_flags += ['-Xlint:all,-deprecation']
@@ -112,16 +113,17 @@ gbjar.sources += [
     'animation/HeightChangeAnimation.java',
     'animation/PropertyAnimator.java',
     'animation/Rotate3DAnimation.java',
     'animation/ViewHelper.java',
     'ANRReporter.java',
     'AppNotificationClient.java',
     'BaseGeckoInterface.java',
     'BrowserApp.java',
+    'BrowserLocaleManager.java',
     'ContactService.java',
     'ContextGetter.java',
     'CustomEditText.java',
     'DataReportingNotification.java',
     'db/AbstractPerProfileDatabaseProvider.java',
     'db/AbstractTransactionalProvider.java',
     'db/BrowserContract.java',
     'db/BrowserDatabaseHelper.java',
@@ -133,16 +135,17 @@ gbjar.sources += [
     'db/LocalBrowserDB.java',
     'db/PasswordsProvider.java',
     'db/PerProfileDatabaseProvider.java',
     'db/PerProfileDatabases.java',
     'db/ReadingListProvider.java',
     'db/SharedBrowserDatabaseProvider.java',
     'db/SQLiteBridgeContentProvider.java',
     'db/TabsProvider.java',
+    'db/TopSitesCursorWrapper.java',
     'Distribution.java',
     'DoorHangerPopup.java',
     'DynamicToolbar.java',
     'EditBookmarkDialog.java',
     'EventDispatcher.java',
     'favicons/cache/FaviconCache.java',
     'favicons/cache/FaviconCacheElement.java',
     'favicons/cache/FaviconsForURL.java',
--- a/mobile/android/base/resources/menu-large-v11/browser_app_menu.xml
+++ b/mobile/android/base/resources/menu-large-v11/browser_app_menu.xml
@@ -51,16 +51,19 @@
           android:title="@string/page"
           android:icon="@drawable/ic_menu_tools">
 
         <menu>
 
             <item android:id="@+id/subscribe"
                   android:title="@string/contextmenu_subscribe"/>
 
+            <item android:id="@+id/save_as_pdf"
+                  android:title="@string/save_as_pdf"/>
+
             <item android:id="@+id/add_search_engine"
                   android:title="@string/contextmenu_add_search_engine"/>
 
             <item android:id="@+id/site_settings"
                   android:title="@string/contextmenu_site_settings" />
 
             <item android:id="@+id/add_to_launcher"
                   android:title="@string/contextmenu_add_to_launcher"/>
@@ -70,20 +73,16 @@
     </item>
 
     <item android:id="@+id/tools"
           android:title="@string/tools"
           android:icon="@drawable/ic_menu_tools">
 
         <menu>
 
-            <item android:id="@+id/save_as_pdf"
-                  android:icon="@drawable/ic_menu_save_as_pdf"
-                  android:title="@string/save_as_pdf" />
-
             <item android:id="@+id/downloads"
                   android:icon="@drawable/ic_menu_downloads"
                   android:title="@string/downloads"/>
 
             <item android:id="@+id/addons"
                   android:icon="@drawable/ic_menu_addons"
                   android:title="@string/addons"/>
 
--- a/mobile/android/base/resources/menu-v11/browser_app_menu.xml
+++ b/mobile/android/base/resources/menu-v11/browser_app_menu.xml
@@ -51,16 +51,19 @@
           android:title="@string/page"
           android:icon="@drawable/ic_menu_tools">
 
         <menu>
 
             <item android:id="@+id/subscribe"
                   android:title="@string/contextmenu_subscribe"/>
 
+            <item android:id="@+id/save_as_pdf"
+                  android:title="@string/save_as_pdf"/>
+
             <item android:id="@+id/add_search_engine"
                   android:title="@string/contextmenu_add_search_engine"/>
 
             <item android:id="@+id/site_settings"
                   android:title="@string/contextmenu_site_settings" />
 
             <item android:id="@+id/add_to_launcher"
                   android:title="@string/contextmenu_add_to_launcher"/>
@@ -70,20 +73,16 @@
     </item>
 
     <item android:id="@+id/tools"
           android:title="@string/tools"
           android:icon="@drawable/ic_menu_tools">
 
         <menu>
 
-            <item android:id="@+id/save_as_pdf"
-                  android:icon="@drawable/ic_menu_save_as_pdf"
-                  android:title="@string/save_as_pdf" />
-
             <item android:id="@+id/downloads"
                   android:icon="@drawable/ic_menu_downloads"
                   android:title="@string/downloads"/>
 
             <item android:id="@+id/addons"
                   android:icon="@drawable/ic_menu_addons"
                   android:title="@string/addons"/>
 
--- a/mobile/android/base/resources/menu-xlarge-v11/browser_app_menu.xml
+++ b/mobile/android/base/resources/menu-xlarge-v11/browser_app_menu.xml
@@ -52,16 +52,19 @@
           android:title="@string/page"
           android:icon="@drawable/ic_menu_tools">
 
         <menu>
 
             <item android:id="@+id/subscribe"
                   android:title="@string/contextmenu_subscribe"/>
 
+            <item android:id="@+id/save_as_pdf"
+                  android:title="@string/save_as_pdf"/>
+
             <item android:id="@+id/add_search_engine"
                   android:title="@string/contextmenu_add_search_engine"/>
 
             <item android:id="@+id/site_settings"
                   android:title="@string/contextmenu_site_settings" />
 
             <item android:id="@+id/add_to_launcher"
                   android:title="@string/contextmenu_add_to_launcher"/>
@@ -71,20 +74,16 @@
     </item>
 
     <item android:id="@+id/tools"
           android:title="@string/tools"
           android:icon="@drawable/ic_menu_tools">
 
         <menu>
 
-            <item android:id="@+id/save_as_pdf"
-                  android:icon="@drawable/ic_menu_save_as_pdf"
-                  android:title="@string/save_as_pdf" />
-
             <item android:id="@+id/downloads"
                   android:icon="@drawable/ic_menu_downloads"
                   android:title="@string/downloads"/>
 
             <item android:id="@+id/addons"
                   android:icon="@drawable/ic_menu_addons"
                   android:title="@string/addons"/>
 
--- a/mobile/android/base/sync/CommandProcessor.java
+++ b/mobile/android/base/sync/CommandProcessor.java
@@ -7,16 +7,17 @@ package org.mozilla.gecko.sync;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import org.json.simple.JSONArray;
 import org.json.simple.JSONObject;
+import org.mozilla.gecko.BrowserLocaleManager;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.background.common.log.Logger;
 import org.mozilla.gecko.sync.repositories.NullCursorException;
 import org.mozilla.gecko.sync.repositories.android.ClientsDatabaseAccessor;
 import org.mozilla.gecko.sync.repositories.domain.ClientRecord;
 
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -234,33 +235,42 @@ public class CommandProcessor {
       db.store(clientID, command);
     } catch (NullCursorException e) {
       Logger.error(LOG_TAG, "NullCursorException: Unable to send command.");
     } finally {
       db.close();
     }
   }
 
+  private static volatile boolean didUpdateLocale = false;
+
   @SuppressWarnings("deprecation")
   public static void displayURI(final List<String> args, final Context context) {
     // We trust the client sender that these exist.
     final String uri = args.get(0);
     final String clientId = args.get(1);
 
     Logger.pii(LOG_TAG, "Received a URI for display: " + uri + " from " + clientId);
 
     String title = null;
     if (args.size() == 3) {
       title = args.get(2);
     }
 
+    // We don't care too much about races, but let's try to avoid
+    // unnecessary work.
+    if (!didUpdateLocale) {
+      BrowserLocaleManager.getInstance().getAndApplyPersistedLocale(context);
+      didUpdateLocale = true;
+    }
+
     final String ns = Context.NOTIFICATION_SERVICE;
     final NotificationManager notificationManager = (NotificationManager) context.getSystemService(ns);
 
-    // Create a Notificiation.
+    // Create a Notification.
     final int icon = R.drawable.icon;
     String notificationTitle = context.getString(R.string.sync_new_tab);
     if (title != null) {
       notificationTitle = notificationTitle.concat(": " + title);
     }
 
     final long when = System.currentTimeMillis();
     Notification notification = new Notification(icon, notificationTitle, when);
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/sync/setup/activities/LocaleAware.java
@@ -0,0 +1,59 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko.sync.setup.activities;
+
+import org.mozilla.gecko.BrowserLocaleManager;
+import org.mozilla.gecko.LocaleManager;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.content.Context;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.StrictMode;
+import android.support.v4.app.FragmentActivity;
+
+/**
+ * This is a helper class to do typical locale switching operations
+ * without hitting StrictMode errors or adding boilerplate to common
+ * activity subclasses.
+ *
+ * Either call {@link LocaleAware#initializeLocale(Context)} in your
+ * <code>onCreate</code> method, or inherit from <code>LocaleAwareFragmentActivity</code>
+ * or <code>LocaleAwareActivity</code>.
+ */
+public class LocaleAware {
+  @TargetApi(Build.VERSION_CODES.GINGERBREAD)
+  public static void initializeLocale(Context context) {
+    final LocaleManager localeManager = BrowserLocaleManager.getInstance();
+    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.GINGERBREAD) {
+      localeManager.getAndApplyPersistedLocale(context);
+    } else {
+      final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
+      StrictMode.allowThreadDiskWrites();
+      try {
+        localeManager.getAndApplyPersistedLocale(context);
+      } finally {
+        StrictMode.setThreadPolicy(savedPolicy);
+      }
+    }
+  }
+
+  public static class LocaleAwareFragmentActivity extends FragmentActivity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+      LocaleAware.initializeLocale(getApplicationContext());
+      super.onCreate(savedInstanceState);
+    }
+  }
+
+  public static class LocaleAwareActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+      LocaleAware.initializeLocale(getApplicationContext());
+      super.onCreate(savedInstanceState);
+    }
+  }
+}
--- a/mobile/android/base/sync/setup/activities/SendTabActivity.java
+++ b/mobile/android/base/sync/setup/activities/SendTabActivity.java
@@ -22,33 +22,34 @@ import org.mozilla.gecko.sync.CommandPro
 import org.mozilla.gecko.sync.CommandRunner;
 import org.mozilla.gecko.sync.GlobalSession;
 import org.mozilla.gecko.sync.SyncConfiguration;
 import org.mozilla.gecko.sync.SyncConstants;
 import org.mozilla.gecko.sync.repositories.NullCursorException;
 import org.mozilla.gecko.sync.repositories.android.ClientsDatabaseAccessor;
 import org.mozilla.gecko.sync.repositories.domain.ClientRecord;
 import org.mozilla.gecko.sync.setup.SyncAccounts;
+import org.mozilla.gecko.sync.setup.activities.LocaleAware.LocaleAwareActivity;
 import org.mozilla.gecko.sync.stage.SyncClientsEngineStage;
 import org.mozilla.gecko.sync.syncadapter.SyncAdapter;
 
 import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.view.View;
 import android.widget.ListView;
 import android.widget.TextView;
 import android.widget.Toast;
 
-public class SendTabActivity extends Activity {
+public class SendTabActivity extends LocaleAwareActivity {
   private interface TabSender {
     static final String[] CLIENTS_STAGE = new String[] { SyncClientsEngineStage.COLLECTION_NAME };
 
     /**
      * @return Return null if the account isn't correctly initialized. Return
      *         the account GUID otherwise.
      */
     String getAccountGUID();
--- a/mobile/android/base/toolbar/ToolbarDisplayLayout.java
+++ b/mobile/android/base/toolbar/ToolbarDisplayLayout.java
@@ -439,24 +439,24 @@ public class ToolbarDisplayLayout extend
 
     private void setSiteSecurityVisibility(boolean visible, EnumSet<UpdateFlags> flags) {
         if (visible == mSiteSecurityVisible) {
             return;
         }
 
         mSiteSecurityVisible = visible;
 
+        mTitle.clearAnimation();
+        mSiteSecurity.clearAnimation();
+
         if (flags.contains(UpdateFlags.DISABLE_ANIMATIONS)) {
             mSiteSecurity.setVisibility(visible ? View.VISIBLE : View.GONE);
             return;
         }
 
-        mTitle.clearAnimation();
-        mSiteSecurity.clearAnimation();
-
         // If any of these animations were cancelled as a result of the
         // clearAnimation() calls above, we need to reset them.
         mLockFadeIn.reset();
         mTitleSlideLeft.reset();
         mTitleSlideRight.reset();
 
         if (mForwardAnim != null) {
             long delay = mForwardAnim.getRemainingTime();
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/util/RawResource.java
@@ -0,0 +1,43 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko.util;
+
+import android.content.Context;
+import android.content.res.Resources;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.StringWriter;
+
+public final class RawResource {
+    public static String get(Context context, int id) throws IOException {
+        InputStreamReader reader = null;
+
+        try {
+            final Resources res = context.getResources();
+            final InputStream is = res.openRawResource(id);
+            if (is == null) {
+                return null;
+            }
+
+            reader = new InputStreamReader(is);
+
+            final char[] buffer = new char[1024];
+            final StringWriter s = new StringWriter();
+
+            int n;
+            while ((n = reader.read(buffer, 0, buffer.length)) != -1) {
+                s.write(buffer, 0, n);
+            }
+
+            return s.toString();
+        } finally {
+            if (reader != null) {
+                reader.close();
+            }
+        }
+    }
+}
\ No newline at end of file
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -126,17 +126,17 @@ XPCOMUtils.defineLazyModuleGetter(this, 
     Services.obs.addObserver(function(s, t, d) {
         window[name].observe(s, t, d)
     }, aNotification, false);
   });
 });
 
 // Lazily-loaded JS modules that use observer notifications
 [
-  ["Home", ["HomePanels:Get", "HomePanels:Authenticate",
+  ["Home", ["HomeBanner:Get", "HomePanels:Get", "HomePanels:Authenticate",
             "HomePanels:Installed", "HomePanels:Uninstalled"], "resource://gre/modules/Home.jsm"],
 ].forEach(module => {
   let [name, notifications, resource] = module;
   XPCOMUtils.defineLazyModuleGetter(this, name, resource);
   notifications.forEach(notification => {
     Services.obs.addObserver((s,t,d) => {
       this[name].observe(s,t,d)
     }, notification, false);
--- a/mobile/android/modules/Home.jsm
+++ b/mobile/android/modules/Home.jsm
@@ -46,33 +46,53 @@ function BannerMessage(options) {
 
   if ("onclick" in options && typeof options.onclick === "function")
     this.onclick = options.onclick;
 
   if ("ondismiss" in options && typeof options.ondismiss === "function")
     this.ondismiss = options.ondismiss;
 }
 
+// We need this object to have access to the HomeBanner
+// private members without leaking it outside Home.jsm.
+let HomeBannerMessageHandlers;
+
 let HomeBanner = (function () {
+  // Whether there is a "HomeBanner:Get" request we couldn't fulfill.
+  let _pendingRequest = false;
+
+  // Functions used to handle messages sent from Java.
+  HomeBannerMessageHandlers = {
+    "HomeBanner:Get": function handleBannerGet(data) {
+      if (!_sendBannerData()) {
+        _pendingRequest = true;
+      }
+    }
+  };
+
   // Holds the messages that will rotate through the banner.
   let _messages = {};
 
+  let _sendBannerData = function() {
+    let keys = Object.keys(_messages);
+    if (!keys.length) {
+      return false;
+    }
 
-  let _handleGet = function() {
     // Choose a message at random.
-    let keys = Object.keys(_messages);
     let randomId = keys[Math.floor(Math.random() * keys.length)];
     let message = _messages[randomId];
 
     sendMessageToJava({
       type: "HomeBanner:Data",
       id: message.id,
       text: message.text,
       iconURI: message.iconURI
     });
+    return true;
   };
 
   let _handleShown = function(id) {
     let message = _messages[id];
     if (message.onshown)
       message.onshown();
   };
 
@@ -86,20 +106,16 @@ let HomeBanner = (function () {
     let message = _messages[id];
     if (message.ondismiss)
       message.ondismiss();
   };
 
   return Object.freeze({
     observe: function(subject, topic, data) {
       switch(topic) {
-        case "HomeBanner:Get":
-          _handleGet();
-          break;
-
         case "HomeBanner:Shown":
           _handleShown(data);
           break;
 
         case "HomeBanner:Click":
           _handleClick(data);
           break;
 
@@ -116,24 +132,25 @@ let HomeBanner = (function () {
      */
     add: function(options) {
       let message = new BannerMessage(options);
       _messages[message.id] = message;
 
       // If this is the first message we're adding, add
       // observers to listen for requests from the Java UI.
       if (Object.keys(_messages).length == 1) {
-        Services.obs.addObserver(this, "HomeBanner:Get", false);
         Services.obs.addObserver(this, "HomeBanner:Shown", false);
         Services.obs.addObserver(this, "HomeBanner:Click", false);
         Services.obs.addObserver(this, "HomeBanner:Dismiss", false);
 
-        // Send a message to Java, in case there's an active HomeBanner
-        // waiting for a response.
-        _handleGet();
+        // Send a message to Java if there's a pending "HomeBanner:Get" request.
+        if (_pendingRequest) {
+          _pendingRequest = false;
+          _sendBannerData();
+        }
       }
 
       return message.id;
     },
 
     /**
      * Removes a banner message from the rotation.
      *
@@ -143,17 +160,16 @@ let HomeBanner = (function () {
       if (!(id in _messages)) {
         throw "Home.banner: Can't remove message that doesn't exist: id = " + id;
       }
 
       delete _messages[id];
 
       // If there are no more messages, remove the observers.
       if (Object.keys(_messages).length == 0) {
-        Services.obs.removeObserver(this, "HomeBanner:Get");
         Services.obs.removeObserver(this, "HomeBanner:Shown");
         Services.obs.removeObserver(this, "HomeBanner:Click");
         Services.obs.removeObserver(this, "HomeBanner:Dismiss");
       }
     }
   });
 })();
 
@@ -407,15 +423,17 @@ let HomePanels = (function () {
 
 // Public API
 this.Home = Object.freeze({
   banner: HomeBanner,
   panels: HomePanels,
 
   // Lazy notification observer registered in browser.js
   observe: function(subject, topic, data) {
-    if (topic in HomePanelsMessageHandlers) {
+    if (topic in HomeBannerMessageHandlers) {
+      HomeBannerMessageHandlers[topic](data);
+    } else if (topic in HomePanelsMessageHandlers) {
       HomePanelsMessageHandlers[topic](data);
     } else {
       Cu.reportError("Home.observe: message handler not found for topic: " + topic);
     }
   }
 });
--- a/mobile/android/services/manifests/SyncAndroidManifest_activities.xml.in
+++ b/mobile/android/services/manifests/SyncAndroidManifest_activities.xml.in
@@ -72,24 +72,21 @@
             <intent-filter>
                 <action android:name="@MOZ_ANDROID_SHARED_ACCOUNT_TYPE@.accounts.SYNC_ACCOUNT_DELETED_ACTION"/>
             </intent-filter>
         </receiver>
 
         <activity
             android:theme="@style/SyncTheme"
             android:excludeFromRecents="true"
-            android:exported="true"
             android:icon="@drawable/icon"
             android:label="@string/sync_app_name"
             android:configChanges="keyboardHidden|orientation|screenSize"
             android:windowSoftInputMode="adjustResize|stateHidden"
             android:taskAffinity="org.mozilla.gecko.sync.setup"
             android:name="org.mozilla.gecko.sync.setup.activities.SendTabActivity" >
 
-#ifndef RELEASE_BUILD
             <intent-filter>
                 <action android:name="android.intent.action.SEND" />
                 <category android:name="android.intent.category.DEFAULT" />
                 <data android:mimeType="text/plain" />
             </intent-filter>
-#endif
         </activity>
--- a/mobile/android/tests/browser/junit3/moz.build
+++ b/mobile/android/tests/browser/junit3/moz.build
@@ -8,16 +8,17 @@ DEFINES['ANDROID_PACKAGE_NAME'] = CONFIG
 
 jar = add_java_jar('browser-junit3')
 jar.sources += [
     'src/harness/BrowserInstrumentationTestRunner.java',
     'src/harness/BrowserTestListener.java',
     'src/tests/BrowserTestCase.java',
     'src/tests/TestGeckoSharedPrefs.java',
     'src/tests/TestJarReader.java',
+    'src/tests/TestTopSitesCursorWrapper.java',
 ]
 jar.generated_sources = [] # None yet -- try to keep it this way.
 jar.javac_flags += ['-Xlint:all,-unchecked']
 
 # Android Eclipse project.
 main = add_android_eclipse_project('BrowserInstrumentationTests', OBJDIR + '/AndroidManifest.xml')
 # The package name doesn't really matter, but it looks nicest if the
 # generated classes (org.mozilla.gecko.browser.tests.{BuildConfig,R})
new file mode 100644
--- /dev/null
+++ b/mobile/android/tests/browser/junit3/src/tests/TestTopSitesCursorWrapper.java
@@ -0,0 +1,197 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+package org.mozilla.gecko.browser.tests;
+
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.database.MatrixCursor.RowBuilder;
+import android.text.TextUtils;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.mozilla.gecko.db.BrowserContract.Bookmarks;
+import org.mozilla.gecko.db.BrowserContract.Combined;
+import org.mozilla.gecko.db.TopSitesCursorWrapper;
+
+public class TestTopSitesCursorWrapper extends BrowserTestCase {
+
+    private String[] TOP_SITES_COLUMNS = new String[] { Combined._ID,
+                                                        Combined.URL,
+                                                        Combined.TITLE,
+                                                        Combined.DISPLAY,
+                                                        Combined.BOOKMARK_ID,
+                                                        Combined.HISTORY_ID };
+
+    private String[] PINNED_SITES_COLUMNS = new String[] { Bookmarks._ID,
+                                                           Bookmarks.URL,
+                                                           Bookmarks.TITLE,
+                                                           Bookmarks.POSITION };
+
+    private final int MIN_COUNT = 6;
+
+    private final String TOP_PREFIX = "top-";
+    private final String PINNED_PREFIX = "pinned-";
+
+    private Cursor createTopSitesCursor(int count) {
+        MatrixCursor c = new MatrixCursor(TOP_SITES_COLUMNS);
+
+        for (int i = 0; i < count; i++) {
+            RowBuilder row = c.newRow();
+            row.add(-1);
+            row.add(TOP_PREFIX + "url" + i);
+            row.add(TOP_PREFIX + "title" + i);
+            row.add(Combined.DISPLAY_NORMAL);
+            row.add(i);
+            row.add(i);
+        }
+
+        return c;
+    }
+
+    private Cursor createPinnedSitesCursor(Integer[] positions) {
+        MatrixCursor c = new MatrixCursor(PINNED_SITES_COLUMNS);
+
+        if (positions == null) {
+            return c;
+        }
+
+        for (int i = 0; i < positions.length; i++) {
+            int position = positions[i];
+
+            RowBuilder row = c.newRow();
+            row.add(-1);
+            row.add(PINNED_PREFIX + "url" + i);
+            row.add(PINNED_PREFIX + "title" + i);
+            row.add(position);
+        }
+
+        return c;
+    }
+
+    private TopSitesCursorWrapper createTopSitesCursorWrapper(int topSitesCount, Integer[] pinnedPositions) {
+        Cursor pinnedSites = createPinnedSitesCursor(pinnedPositions);
+        Cursor topSites = createTopSitesCursor(topSitesCount);
+
+        return new TopSitesCursorWrapper(pinnedSites, topSites, MIN_COUNT);
+    }
+
+    private void assertUrlAndTitle(Cursor c, String prefix, int index) {
+        String url = c.getString(c.getColumnIndex(Combined.URL));
+        String title = c.getString(c.getColumnIndex(Combined.TITLE));
+
+        assertEquals(prefix + "url" + index, url);
+        assertEquals(prefix + "title" + index, title);
+    }
+
+    private void assertBlank(Cursor c) {
+        String url = c.getString(c.getColumnIndex(Combined.URL));
+        String title = c.getString(c.getColumnIndex(Combined.TITLE));
+
+        assertTrue(TextUtils.isEmpty(url));
+        assertTrue(TextUtils.isEmpty(title));
+    }
+
+    public void testCount() {
+        // The sum of top and pinned sites is smaller than MIN_COUNT
+        Cursor c = createTopSitesCursorWrapper(2, new Integer[] { 0, 1 });
+        assertEquals(MIN_COUNT, c.getCount());
+        c.close();
+
+        // No top sites, some pinned sites, still smaller than MIN_COUNT
+        c = createTopSitesCursorWrapper(0, new Integer[] { 0, 1, 2 });
+        assertEquals(MIN_COUNT, c.getCount());
+        c.close();
+
+        // Some top sites, no pinned sites, still smaller than MIN_COUNT
+        c = createTopSitesCursorWrapper(3, null);
+        assertEquals(MIN_COUNT, c.getCount());
+        c.close();
+
+        // The sum of top and pinned sites is greater than MIN_COUNT
+        c = createTopSitesCursorWrapper(10, new Integer[] { 0, 1, 2 });
+        assertEquals(13, c.getCount());
+        c.close();
+    }
+
+    public void testClosedPinnedSites() {
+        Cursor pinnedSites = createPinnedSitesCursor(new Integer[] { 0, 1 });
+        Cursor topSites = createTopSitesCursor(2);
+        Cursor wrapper = new TopSitesCursorWrapper(pinnedSites, topSites, MIN_COUNT);
+
+        // The pinned sites cursor is closed immediately
+        // when a TopSitesCursorWrapper is created.
+        assertTrue(pinnedSites.isClosed());
+
+        // But not the topsites cursor, of course.
+        assertFalse(topSites.isClosed());
+
+        wrapper.close();
+
+        // Closing wrapper closes wrapped cursor
+        assertTrue(topSites.isClosed());
+    }
+
+    public void testIsPinned() {
+        Integer[] pinnedPositions = new Integer[] { 0, 1 };
+        TopSitesCursorWrapper c = createTopSitesCursorWrapper(2, pinnedPositions);
+
+        List<Integer> pinnedList = Arrays.asList(pinnedPositions);
+
+        c.moveToPosition(-1);
+        while (c.moveToNext()) {
+            boolean isPinnedPosition = pinnedList.contains(c.getPosition());
+            assertEquals(isPinnedPosition, c.isPinned());
+        }
+
+        c.close();
+    }
+
+    public void testBlankPositions() {
+        Integer[] pinnedPositions = new Integer[] { 0, 1, 4 };
+        TopSitesCursorWrapper c = createTopSitesCursorWrapper(0, pinnedPositions);
+
+        c.moveToPosition(-1);
+        while (c.moveToNext()) {
+            if (!c.isPinned()) {
+                assertBlank(c);
+            }
+        }
+
+        c.close();
+    }
+
+    public void testColumnValues() {
+        Integer[] pinnedPositions = new Integer[] { 0, 1, 4 };
+        TopSitesCursorWrapper c = createTopSitesCursorWrapper(2, pinnedPositions);
+
+        int topIndex = 0;
+        int pinnedIndex = 0;
+
+        c.moveToPosition(-1);
+        while (c.moveToNext()) {
+            int position = c.getPosition();
+
+            // Last position should be blank
+            if (position == MIN_COUNT - 1) {
+                assertBlank(c);
+            } else {
+                int index;
+                String prefix;
+
+                if (c.isPinned()) {
+                    index = pinnedIndex++;
+                    prefix = PINNED_PREFIX;
+                } else {
+                    index = topIndex++;
+                    prefix = TOP_PREFIX;
+                }
+
+                assertUrlAndTitle(c, prefix, index);
+            }
+        }
+
+        c.close();
+    }
+}
\ No newline at end of file
--- a/netwerk/base/src/NetworkActivityMonitor.cpp
+++ b/netwerk/base/src/NetworkActivityMonitor.cpp
@@ -146,47 +146,54 @@ nsNetMon_AcceptRead(PRFileDesc *listenSo
   if (ret > 0)
     NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kDownload);
   return ret;
 }
 
 
 class NotifyNetworkActivity : public nsRunnable {
 public:
-  NotifyNetworkActivity(nsIObserverService* aObs,
-                        NetworkActivityMonitor::Direction aDirection)
-    : mObs(aObs)
-    , mDirection(aDirection)
+  NotifyNetworkActivity(NetworkActivityMonitor::Direction aDirection)
+    : mDirection(aDirection)
   {}
   NS_IMETHOD Run()
   {
-    mObs->NotifyObservers(nullptr,
-                          mDirection == NetworkActivityMonitor::kUpload
-                            ? NS_NETWORK_ACTIVITY_BLIP_UPLOAD_TOPIC
-                            : NS_NETWORK_ACTIVITY_BLIP_DOWNLOAD_TOPIC,
-                          nullptr);
+    MOZ_ASSERT(NS_IsMainThread());
+
+    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+    if (!obs)
+      return NS_ERROR_FAILURE;
+
+    obs->NotifyObservers(nullptr,
+                         mDirection == NetworkActivityMonitor::kUpload
+                           ? NS_NETWORK_ACTIVITY_BLIP_UPLOAD_TOPIC
+                           : NS_NETWORK_ACTIVITY_BLIP_DOWNLOAD_TOPIC,
+                         nullptr);
     return NS_OK;
   }
 private:
   nsCOMPtr<nsIObserverService>      mObs;
   NetworkActivityMonitor::Direction mDirection;
 };
 
 NetworkActivityMonitor * NetworkActivityMonitor::gInstance = nullptr;
 
 NetworkActivityMonitor::NetworkActivityMonitor()
   : mLayerIdentity(PR_INVALID_IO_LAYER)
   , mBlipInterval(PR_INTERVAL_NO_TIMEOUT)
 {
+  MOZ_COUNT_CTOR(NetworkActivityMonitor);
+
   NS_ASSERTION(gInstance==nullptr,
                "multiple NetworkActivityMonitor instances!");
 }
 
 NetworkActivityMonitor::~NetworkActivityMonitor()
 {
+  MOZ_COUNT_DTOR(NetworkActivityMonitor);
   gInstance = nullptr;
 }
 
 nsresult
 NetworkActivityMonitor::Init(int32_t blipInterval)
 {
   nsresult rv;
 
@@ -224,20 +231,16 @@ NetworkActivityMonitor::Init_Internal(in
   mLayerMethods.write      = nsNetMon_Write;
   mLayerMethods.writev     = nsNetMon_Writev;
   mLayerMethods.recv       = nsNetMon_Recv;
   mLayerMethods.send       = nsNetMon_Send;
   mLayerMethods.recvfrom   = nsNetMon_RecvFrom;
   mLayerMethods.sendto     = nsNetMon_SendTo;
   mLayerMethods.acceptread = nsNetMon_AcceptRead;
 
-  mObserverService = mozilla::services::GetObserverService();
-  if (!mObserverService)
-    return NS_ERROR_FAILURE;
-
   mBlipInterval = PR_MillisecondsToInterval(blipInterval);
   // Set the last notification times to time that has just expired, so any
   // activity even right now will trigger notification.
   mLastNotificationTime[kUpload] = PR_IntervalNow() - mBlipInterval;
   mLastNotificationTime[kDownload] = mLastNotificationTime[kUpload];
 
   return NS_OK;
 }
@@ -282,12 +285,11 @@ NetworkActivityMonitor::DataInOut(Direct
   }
 
   return NS_OK;
 }
 
 void
 NetworkActivityMonitor::PostNotification(Direction direction)
 {
-  nsRefPtr<nsIRunnable> ev = new NotifyNetworkActivity(mObserverService,
-                                                       direction);
+  nsRefPtr<nsIRunnable> ev = new NotifyNetworkActivity(direction);
   NS_DispatchToMainThread(ev);
 }
--- a/netwerk/base/src/NetworkActivityMonitor.h
+++ b/netwerk/base/src/NetworkActivityMonitor.h
@@ -2,22 +2,21 @@
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef NetworkActivityMonitor_h___
 #define NetworkActivityMonitor_h___
 
-#include "nsCOMPtr.h"
+#include <stdint.h>
+#include "nscore.h"
 #include "prio.h"
 #include "prinrval.h"
 
-class nsIObserverService;
-
 namespace mozilla { namespace net {
 
 class NetworkActivityMonitor
 {
 public:
   enum Direction {
     kUpload   = 0,
     kDownload = 1
@@ -36,14 +35,13 @@ private:
   nsresult Init_Internal(int32_t blipInterval);
   void PostNotification(Direction direction);
 
   static NetworkActivityMonitor * gInstance;
   PRDescIdentity                  mLayerIdentity;
   PRIOMethods                     mLayerMethods;
   PRIntervalTime                  mBlipInterval;
   PRIntervalTime                  mLastNotificationTime[2];
-  nsCOMPtr<nsIObserverService>    mObserverService;
 };
 
 }} // namespace mozilla::net
 
 #endif /* NetworkActivityMonitor_h___ */
index 4e5cc647ab24d7038d524c1b5c5d610507949e9c..75caa046e715eb63c24749cfaf688bf3efad0325
GIT binary patch
literal 121856
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