Merge m-c to b2g-inbound. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Tue, 03 Jun 2014 15:22:54 -0400
changeset 205644 552a79dac6423358e75625e68d0de0690f74795d
parent 205643 2b6b0ced08ef6ee04038df0e0913d2dc02889ea1 (current diff)
parent 205605 298b39b50ff7b6165f9ba27193258f73a2527649 (diff)
child 205645 2b95d41095ae647b3539468c413ed3d16ceb7e2c
push id3741
push userasasaki@mozilla.com
push dateMon, 21 Jul 2014 20:25:18 +0000
treeherdermozilla-beta@4d6f46f5af68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone32.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to b2g-inbound. a=merge
b2g/installer/package-manifest.in
browser/themes/shared/devtools/images/dropmarker.png
dom/datastore/DataStoreImpl.jsm
dom/datastore/DataStoreService.js
dom/datastore/DataStoreServiceInternal.jsm
dom/system/gonk/RadioInterfaceLayer.js
dom/telephony/Telephony.cpp
dom/telephony/gonk/TelephonyService.js
layout/build/nsLayoutModule.cpp
testing/tps/INSTALL.sh
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -808,17 +808,17 @@ bin/components/@DLL_PREFIX@nkgnomevfs@DL
 #endif
 
 #ifdef MOZ_SERVICES_FXACCOUNTS
 @BINPATH@/components/FxAccountsUIGlue.js
 @BINPATH@/components/services_fxaccounts.xpt
 #endif
 
 @BINPATH@/components/DataStore.manifest
-@BINPATH@/components/DataStoreService.js
+@BINPATH@/components/DataStoreImpl.js
 @BINPATH@/components/dom_datastore.xpt
 
 #ifdef MOZ_WEBSPEECH
 @BINPATH@/components/dom_webspeechsynth.xpt
 #endif
 
 #ifdef XP_MACOSX
 @BINPATH@/@DLL_PREFIX@plugin_child_interpose@DLL_SUFFIX@
--- a/browser/components/customizableui/test/browser_995164_registerArea_during_customize_mode.js
+++ b/browser/components/customizableui/test/browser_995164_registerArea_during_customize_mode.js
@@ -80,38 +80,44 @@ add_task(function*() {
 
   simulateItemDrag(syncButton, toolbar);
   ok(CustomizableUI.getPlacementOfWidget("sync-button"), "Button moved out of palette");
   is(CustomizableUI.getPlacementOfWidget("sync-button").area, TOOLBARID, "Button's back on toolbar");
   ok(toolbar.querySelector("#sync-button"), "Sync button really is on toolbar.");
   ok(otherTB.querySelector("#sync-button"), "Sync button is on other toolbar, too.");
 
   let wasInformedCorrectlyOfAreaDisappearing = false;
-  let windowClosed = null;
+  //XXXgijs So we could be using promiseWindowClosed here. However, after
+  // repeated random oranges, I'm instead relying on onWindowClosed below to
+  // fire appropriately - it is linked to an unload event as well, and so
+  // reusing it prevents a potential race between unload handlers where the
+  // one from promiseWindowClosed could fire before the onWindowClosed
+  // (and therefore onAreaNodeRegistered) one, causing the test to fail.
+  let windowCloseDeferred = Promise.defer();
   listener = {
     onAreaNodeUnregistered: function(aArea, aNode, aReason) {
       if (aArea == TOOLBARID) {
         is(aNode, otherTB, "Should be informed about other toolbar");
         is(aReason, CustomizableUI.REASON_WINDOW_CLOSED, "Reason should be correct.");
         wasInformedCorrectlyOfAreaDisappearing = (aReason === CustomizableUI.REASON_WINDOW_CLOSED);
       }
     },
     onWindowClosed: function(aWindow) {
       if (aWindow == otherWin) {
-        info("Got window closed notification for correct window.");
-        windowClosed = aWindow;
+        windowCloseDeferred.resolve(aWindow);
       } else {
         info("Other window was closed!");
         info("Other window title: " + (aWindow.document && aWindow.document.title));
         info("Our window title: " + (otherWin.document && otherWin.document.title));
       }
     },
   };
   CustomizableUI.addListener(listener);
-  yield promiseWindowClosed(otherWin);
+  otherWin.close();
+  let windowClosed = yield windowCloseDeferred.promise;
 
   is(windowClosed, otherWin, "Window should have sent onWindowClosed notification.");
   ok(wasInformedCorrectlyOfAreaDisappearing, "Should be told about window closing.");
   // Closing the other window should not be counted against this window's customize mode:
   is(syncButton.parentNode.localName, "toolbarpaletteitem", "Sync button's parent node should still be a wrapper.");
   isnot(gCustomizeMode.areas.indexOf(toolbar), -1, "Toolbar should still be a customizable area for this customize mode instance.");
 
   yield gCustomizeMode.reset();
--- a/browser/components/customizableui/test/head.js
+++ b/browser/components/customizableui/test/head.js
@@ -244,18 +244,23 @@ function openAndLoadWindow(aOptions, aWa
       win.removeEventListener("load", onLoad);
       deferred.resolve(win);
     });
   }
   return deferred.promise;
 }
 
 function promiseWindowClosed(win) {
+  let deferred = Promise.defer();
+  win.addEventListener("unload", function onunload() {
+    win.removeEventListener("unload", onunload);
+    deferred.resolve();
+  });
   win.close();
-  return waitForCondition(() => win.closed);
+  return deferred.promise;
 }
 
 function promisePanelShown(win) {
   let panelEl = win.PanelUI.panel;
   return promisePanelElementShown(win, panelEl);
 }
 
 function promiseOverflowShown(win) {
--- a/browser/components/preferences/permissions.js
+++ b/browser/components/preferences/permissions.js
@@ -198,16 +198,21 @@ var gPermissionManager = {
                        .getService(Components.interfaces.nsIObserverService);
     os.removeObserver(this, "perm-changed");
   },
   
   observe: function (aSubject, aTopic, aData)
   {
     if (aTopic == "perm-changed") {
       var permission = aSubject.QueryInterface(Components.interfaces.nsIPermission);
+
+      // Ignore unrelated permission types.
+      if (permission.type != this._type)
+        return;
+
       if (aData == "added") {
         this._addPermissionToList(permission);
         ++this._view._rowCount;
         this._tree.treeBoxObject.rowCountChanged(this._view.rowCount - 1, 1);        
         // Re-do the sort, since we inserted this new item at the end. 
         gTreeUtils.sort(this._tree, this._view, this._permissions,
                         this._permissionsComparator,
                         this._lastPermissionSortColumn, 
--- a/browser/components/translation/BingTranslator.jsm
+++ b/browser/components/translation/BingTranslator.jsm
@@ -3,16 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 this.EXPORTED_SYMBOLS = [ "BingTranslation" ];
 
+Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://gre/modules/Promise.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource://services-common/utils.js");
 Cu.import("resource://services-common/rest.js");
 
 // The maximum amount of net data allowed per request on Bing's API.
 const MAX_REQUEST_DATA = 5000; // Documentation says 10000 but anywhere
@@ -322,24 +323,26 @@ let BingTokenManager = {
    *                     string once it is obtained.
    */
   _getNewToken: function() {
     let request = new RESTRequest("https://datamarket.accesscontrol.windows.net/v2/OAuth2-13");
     request.setHeader("Content-type", "application/x-www-form-urlencoded");
     let params = [
       "grant_type=client_credentials",
       "scope=" + encodeURIComponent("http://api.microsofttranslator.com"),
-      "client_id=",
-      "client_secret="
+      "client_id=" +
+      getAuthTokenParam("%BING_API_CLIENTID%", "browser.translation.bing.clientIdOverride"),
+      "client_secret=" +
+      getAuthTokenParam("%BING_API_KEY%", "browser.translation.bing.apiKeyOverride")
     ];
 
     let deferred = Promise.defer();
     this._pendingRequest = deferred.promise;
     request.post(params.join("&"), function(err) {
-      this._pendingRequest = null;
+      BingTokenManager._pendingRequest = null;
 
       if (err) {
         deferred.reject(err);
       }
 
       try {
         let json = JSON.parse(this.response.body);
         let token = json.access_token;
@@ -362,8 +365,21 @@ let BingTokenManager = {
 function escapeXML(aStr) {
   return aStr.toString()
              .replace("&", "&amp;", "g")
              .replace('"', "&quot;", "g")
              .replace("'", "&apos;", "g")
              .replace("<", "&lt;", "g")
              .replace(">", "&gt;", "g");
 }
+
+/**
+ * Fetch an auth token (clientID or client secret), which may be overridden by
+ * a pref if it's set.
+ */
+function getAuthTokenParam(key, prefName) {
+  let val;
+  try {
+    val = Services.prefs.getCharPref(prefName);
+  } catch(ex) {}
+
+  return encodeURIComponent(Services.urlFormatter.formatURL(val || key));
+}
--- a/browser/components/translation/translation-infobar.xml
+++ b/browser/components/translation/translation-infobar.xml
@@ -37,37 +37,39 @@
                           anonid="translate"
                           oncommand="document.getBindingParent(this).translate();"/>
               <xul:button class="translate-infobar-element"
                           label="&translation.notNow.button;" anonid="notNow"
                           oncommand="document.getBindingParent(this).close();"/>
             </xul:hbox>
 
             <!-- translating -->
-            <xul:hbox class="translating-box" pack="center">
+            <xul:vbox class="translating-box" pack="center">
               <xul:label class="translate-infobar-element"
                          value="&translation.translatingContent.label;"/>
-            </xul:hbox>
+            </xul:vbox>
 
             <!-- translated -->
             <xul:hbox class="translated-box" align="center">
               <xul:label class="translate-infobar-element"
                          value="&translation.translatedFrom.label;"/>
               <xul:menulist class="translate-infobar-element"
                             anonid="fromLanguage"
                             oncommand="document.getBindingParent(this).translate()">
                 <xul:menupopup/>
               </xul:menulist>
               <xul:label class="translate-infobar-element"
                          value="&translation.translatedTo.label;"/>
-              <xul:menulist anonid="toLanguage"
+              <xul:menulist class="translate-infobar-element"
+                            anonid="toLanguage"
                             oncommand="document.getBindingParent(this).translate()">
                 <xul:menupopup/>
               </xul:menulist>
-              <xul:label value="&translation.translatedToSuffix.label;"/>
+              <xul:label class="translate-infobar-element"
+                         value="&translation.translatedToSuffix.label;"/>
               <xul:button anonid="showOriginal"
                           class="translate-infobar-element"
                           label="&translation.showOriginal.button;"
                           oncommand="document.getBindingParent(this).showOriginal();"/>
               <xul:button anonid="showTranslation"
                           class="translate-infobar-element"
                           label="&translation.showTranslation.button;"
                           oncommand="document.getBindingParent(this).showTranslation();"/>
--- a/browser/devtools/debugger/test/browser.ini
+++ b/browser/devtools/debugger/test/browser.ini
@@ -108,27 +108,30 @@ skip-if = os == 'win' # bug 1005274
 [browser_dbg_break-on-dom-01.js]
 [browser_dbg_break-on-dom-02.js]
 [browser_dbg_break-on-dom-03.js]
 [browser_dbg_break-on-dom-04.js]
 [browser_dbg_break-on-dom-05.js]
 [browser_dbg_break-on-dom-06.js]
 [browser_dbg_break-on-dom-07.js]
 [browser_dbg_break-on-dom-08.js]
+[browser_dbg_break-on-dom-event.js]
+skip-if = os == "mac" || e10s # Bug 895426
 [browser_dbg_breakpoints-actual-location.js]
 [browser_dbg_breakpoints-break-on-last-line-of-script-on-reload.js]
 [browser_dbg_breakpoints-button-01.js]
 [browser_dbg_breakpoints-button-02.js]
 [browser_dbg_breakpoints-contextmenu-add.js]
 [browser_dbg_breakpoints-contextmenu.js]
 [browser_dbg_breakpoints-disabled-reload.js]
 [browser_dbg_breakpoints-editor.js]
 [browser_dbg_breakpoints-highlight.js]
 [browser_dbg_breakpoints-new-script.js]
 [browser_dbg_breakpoints-pane.js]
+[browser_dbg_chrome-create.js]
 [browser_dbg_chrome-debugging.js]
 [browser_dbg_clean-exit-window.js]
 skip-if = true # Bug 933950 (leaky test)
 [browser_dbg_clean-exit.js]
 [browser_dbg_closure-inspection.js]
 [browser_dbg_cmd-blackbox.js]
 [browser_dbg_cmd-break.js]
 [browser_dbg_cmd-dbg.js]
@@ -160,16 +163,18 @@ skip-if = true # Bug 933950 (leaky test)
 [browser_dbg_location-changes-01-simple.js]
 [browser_dbg_location-changes-02-blank.js]
 [browser_dbg_location-changes-03-new.js]
 [browser_dbg_location-changes-04-breakpoint.js]
 [browser_dbg_multiple-windows.js]
 [browser_dbg_navigation.js]
 [browser_dbg_no-page-sources.js]
 [browser_dbg_on-pause-highlight.js]
+[browser_dbg_on-pause-raise.js]
+skip-if = os == "linux" || e10s # Bug 888811 & bug 891176
 [browser_dbg_optimized-out-vars.js]
 [browser_dbg_panel-size.js]
 [browser_dbg_parser-01.js]
 [browser_dbg_parser-02.js]
 [browser_dbg_parser-03.js]
 [browser_dbg_parser-04.js]
 [browser_dbg_parser-05.js]
 [browser_dbg_parser-06.js]
@@ -293,14 +298,8 @@ skip-if = (os == 'mac' || os == 'win') &
 [browser_dbg_variables-view-popup-15.js]
 [browser_dbg_variables-view-popup-16.js]
 [browser_dbg_variables-view-reexpand-01.js]
 [browser_dbg_variables-view-reexpand-02.js]
 [browser_dbg_variables-view-reexpand-03.js]
 [browser_dbg_variables-view-webidl.js]
 [browser_dbg_watch-expressions-01.js]
 [browser_dbg_watch-expressions-02.js]
-[browser_dbg_chrome-create.js]
-skip-if = true # Test doesn't clean up after itself (bug 918507), but also bug 847558 on Linux
-[browser_dbg_on-pause-raise.js]
-skip-if = os == "linux" || e10s # Bug 888811 & bug 891176
-[browser_dbg_break-on-dom-event.js]
-skip-if = os == "mac" || e10s # Bug 895426
--- a/browser/devtools/debugger/test/head.js
+++ b/browser/devtools/debugger/test/head.js
@@ -46,17 +46,20 @@ registerCleanupFunction(function() {
 
   // Debugger tests use a lot of memory, so force a GC to help fragmentation.
   info("Forcing GC after debugger test.");
   Cu.forceGC();
 });
 
 // Import the GCLI test helper
 let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
-Services.scriptloader.loadSubScript(testDir + "../../../commandline/test/helpers.js", this);
+testDir = testDir.replace(/\/\//g, '/');
+testDir = testDir.replace("chrome:/mochitest", "chrome://mochitest");
+let helpersjs = testDir + "/../../commandline/test/helpers.js";
+Services.scriptloader.loadSubScript(helpersjs, this);
 
 // Redeclare dbg_assert with a fatal behavior.
 function dbg_assert(cond, e) {
   if (!cond) {
     throw e;
   }
 }
 
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -852,17 +852,17 @@ bin/libfreebl_32int64_3.so
 @BINPATH@/metro/chrome/pdfjs/*
 #endif
 @BINPATH@/metro/components
 @BINPATH@/metro/defaults
 @BINPATH@/metro/modules
 #endif
 
 @BINPATH@/components/DataStore.manifest
-@BINPATH@/components/DataStoreService.js
+@BINPATH@/components/DataStoreImpl.js
 @BINPATH@/components/dom_datastore.xpt
 
 
 #ifdef MOZ_ASAN
 #ifdef CLANG_CXX
 @BINPATH@/llvm-symbolizer
 #endif
 #endif
--- a/browser/locales/en-US/chrome/browser/preferences/colors.dtd
+++ b/browser/locales/en-US/chrome/browser/preferences/colors.dtd
@@ -6,21 +6,21 @@
 <!ENTITY  window.width                    "38em">
 <!ENTITY  window.macWidth                 "41em">
 
 <!ENTITY  allowPagesToUse.label           "Allow pages to choose their own colors, instead of my selections above">
 <!ENTITY  allowPagesToUse.accesskey       "A">
 
 <!ENTITY  color                           "Text and Background">
 <!ENTITY  textColor.label                 "Text:">
-<!ENTITY  textColor.accesskey             "t">
+<!ENTITY  textColor.accesskey             "T">
 <!ENTITY  backgroundColor.label           "Background:">
-<!ENTITY  backgroundColor.accesskey       "b">
+<!ENTITY  backgroundColor.accesskey       "B">
 <!ENTITY  useSystemColors.label           "Use system colors">
 <!ENTITY  useSystemColors.accesskey       "s">
 
 <!ENTITY  underlineLinks.label            "Underline links">
-<!ENTITY  underlineLinks.accesskey        "u">
+<!ENTITY  underlineLinks.accesskey        "U">
 <!ENTITY  links                           "Link Colors">
 <!ENTITY  linkColor.label                 "Unvisited Links:">
-<!ENTITY  linkColor.accesskey             "l">
+<!ENTITY  linkColor.accesskey             "L">
 <!ENTITY  visitedLinkColor.label          "Visited Links:">
-<!ENTITY  visitedLinkColor.accesskey      "v">
+<!ENTITY  visitedLinkColor.accesskey      "V">
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -245,17 +245,17 @@ browser.jar:
   skin/classic/browser/devtools/magnifying-glass-light@2x.png (../shared/devtools/images/magnifying-glass-light@2x.png)
   skin/classic/browser/devtools/itemToggle.png         (../shared/devtools/images/itemToggle.png)
   skin/classic/browser/devtools/itemToggle@2x.png      (../shared/devtools/images/itemToggle@2x.png)
   skin/classic/browser/devtools/itemArrow-dark-rtl.svg (../shared/devtools/images/itemArrow-dark-rtl.svg)
   skin/classic/browser/devtools/itemArrow-dark-ltr.svg (../shared/devtools/images/itemArrow-dark-ltr.svg)
   skin/classic/browser/devtools/itemArrow-rtl.svg      (../shared/devtools/images/itemArrow-rtl.svg)
   skin/classic/browser/devtools/itemArrow-ltr.svg      (../shared/devtools/images/itemArrow-ltr.svg)
   skin/classic/browser/devtools/noise.png             (../shared/devtools/images/noise.png)
-  skin/classic/browser/devtools/dropmarker.png        (../shared/devtools/images/dropmarker.png)
+  skin/classic/browser/devtools/dropmarker.svg        (../shared/devtools/images/dropmarker.svg)
   skin/classic/browser/devtools/layoutview.css         (../shared/devtools/layoutview.css)
   skin/classic/browser/devtools/debugger-collapse.png  (../shared/devtools/images/debugger-collapse.png)
   skin/classic/browser/devtools/debugger-collapse@2x.png  (../shared/devtools/images/debugger-collapse@2x.png)
   skin/classic/browser/devtools/debugger-expand.png    (../shared/devtools/images/debugger-expand.png)
   skin/classic/browser/devtools/debugger-expand@2x.png    (../shared/devtools/images/debugger-expand@2x.png)
   skin/classic/browser/devtools/debugger-pause.png     (../shared/devtools/images/debugger-pause.png)
   skin/classic/browser/devtools/debugger-pause@2x.png     (../shared/devtools/images/debugger-pause@2x.png)
   skin/classic/browser/devtools/debugger-play.png      (../shared/devtools/images/debugger-play.png)
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -363,17 +363,17 @@ browser.jar:
   skin/classic/browser/devtools/magnifying-glass-light@2x.png (../shared/devtools/images/magnifying-glass-light@2x.png)
   skin/classic/browser/devtools/itemToggle.png              (../shared/devtools/images/itemToggle.png)
   skin/classic/browser/devtools/itemToggle@2x.png           (../shared/devtools/images/itemToggle@2x.png)
   skin/classic/browser/devtools/itemArrow-dark-rtl.svg      (../shared/devtools/images/itemArrow-dark-rtl.svg)
   skin/classic/browser/devtools/itemArrow-dark-ltr.svg      (../shared/devtools/images/itemArrow-dark-ltr.svg)
   skin/classic/browser/devtools/itemArrow-rtl.svg           (../shared/devtools/images/itemArrow-rtl.svg)
   skin/classic/browser/devtools/itemArrow-ltr.svg           (../shared/devtools/images/itemArrow-ltr.svg)
   skin/classic/browser/devtools/noise.png                   (../shared/devtools/images/noise.png)
-  skin/classic/browser/devtools/dropmarker.png              (../shared/devtools/images/dropmarker.png)
+  skin/classic/browser/devtools/dropmarker.svg              (../shared/devtools/images/dropmarker.svg)
   skin/classic/browser/devtools/layoutview.css              (../shared/devtools/layoutview.css)
   skin/classic/browser/devtools/debugger-collapse.png       (../shared/devtools/images/debugger-collapse.png)
   skin/classic/browser/devtools/debugger-collapse@2x.png    (../shared/devtools/images/debugger-collapse@2x.png)
   skin/classic/browser/devtools/debugger-expand.png         (../shared/devtools/images/debugger-expand.png)
   skin/classic/browser/devtools/debugger-expand@2x.png      (../shared/devtools/images/debugger-expand@2x.png)
   skin/classic/browser/devtools/debugger-pause.png          (../shared/devtools/images/debugger-pause.png)
   skin/classic/browser/devtools/debugger-pause@2x.png       (../shared/devtools/images/debugger-pause@2x.png)
   skin/classic/browser/devtools/debugger-play.png           (../shared/devtools/images/debugger-play.png)
--- a/browser/themes/shared/devtools/canvasdebugger.inc.css
+++ b/browser/themes/shared/devtools/canvasdebugger.inc.css
@@ -148,16 +148,21 @@
 
 #snapshots-list .selected label {
   /* Text inside a selected item should not be custom colored. */
   color: inherit !important;
 }
 
 /* Debugging pane controls */
 
+#debugging-controls .devtools-toolbarbutton > .toolbarbutton-icon {
+  width: 16px;
+  height: 16px;
+}
+
 #resume {
   list-style-image: url(debugger-play.png);
   -moz-image-region: rect(0px,32px,16px,16px);
 }
 
 #step-over {
   list-style-image: url(debugger-step-over.png);
 }
@@ -165,16 +170,35 @@
 #step-in {
   list-style-image: url(debugger-step-in.png);
 }
 
 #step-out {
   list-style-image: url(debugger-step-out.png);
 }
 
+@media (min-resolution: 2dppx) {
+  #resume {
+    list-style-image: url(debugger-play@2x.png);
+    -moz-image-region: rect(0px,64px,32px,32px);
+  }
+
+  #step-over {
+    list-style-image: url(debugger-step-over@2x.png);
+  }
+
+  #step-in {
+    list-style-image: url(debugger-step-in@2x.png);
+  }
+
+  #step-out {
+    list-style-image: url(debugger-step-out@2x.png);
+  }
+}
+
 #debugging-controls > toolbarbutton {
   transition: opacity 0.15s ease-in-out;
 }
 
 #debugging-controls > toolbarbutton[disabled=true] {
   opacity: 0.5;
 }
 
@@ -251,16 +275,22 @@
 
 .selected .call-item-gutter {
   background-image: url("editor-debug-location.png");
   background-repeat: no-repeat;
   background-position: 6px center;
   background-size: 12px;
 }
 
+@media (min-resolution: 2dppx) {
+  .selected .call-item-gutter {
+    background-image: url("editor-debug-location@2x.png");
+  }
+}
+
 .theme-dark .call-item-gutter {
   background-color: #181d20;
   color: #5f7387;
   border-color: #000;
 }
 
 .theme-light .call-item-gutter {
   background-color: #f7f7f7;
deleted file mode 100644
index 7e91860fb5bccc230d94b2ebc5b549b9e2637087..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/devtools/images/dropmarker.svg
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="8" height="4" viewBox="0 0 8 4" enable-background="new 0 0 8 4">
+  <polygon points="0,0 4,4 8,0" fill="#B6BABF" />
+</svg>
\ No newline at end of file
--- a/browser/themes/shared/devtools/responsivedesign.inc.css
+++ b/browser/themes/shared/devtools/responsivedesign.inc.css
@@ -91,17 +91,17 @@
 .devtools-responsiveui-menulist > .menulist-label-box {
   text-align: center;
 }
 
 .devtools-responsiveui-menulist > .menulist-dropmarker {
   -moz-appearance: none;
   display: -moz-box;
   background-color: transparent;
-  list-style-image: url("chrome://browser/skin/devtools/dropmarker.png");
+  list-style-image: url("chrome://browser/skin/devtools/dropmarker.svg");
   -moz-box-align: center;
   border-width: 0;
   min-width: 16px;
 }
 
 .devtools-responsiveui-toolbarbutton[type=menu-button] > .toolbarbutton-menubutton-button {
   -moz-appearance: none;
   color: inherit;
@@ -117,17 +117,17 @@
 .devtools-responsiveui-toolbarbutton[type=menu-button] {
   padding: 0 1px;
   -moz-box-align: stretch;
 }
 
 .devtools-responsiveui-toolbarbutton[type=menu] > .toolbarbutton-menu-dropmarker,
 .devtools-responsiveui-toolbarbutton[type=menu-button] > .toolbarbutton-menubutton-dropmarker {
   -moz-appearance: none !important;
-  list-style-image: url("chrome://browser/skin/devtools/dropmarker.png");
+  list-style-image: url("chrome://browser/skin/devtools/dropmarker.svg");
   -moz-box-align: center;
   padding: 0 3px;
 }
 
 .devtools-responsiveui-toolbar:-moz-locale-dir(ltr) > *:first-child,
 .devtools-responsiveui-toolbar:-moz-locale-dir(rtl) > *:last-child {
   margin-left: 0;
 }
--- a/browser/themes/shared/devtools/toolbars.inc.css
+++ b/browser/themes/shared/devtools/toolbars.inc.css
@@ -131,17 +131,17 @@
 
 .devtools-menulist > .menulist-label-box {
   text-align: center;
 }
 
 .devtools-menulist > .menulist-dropmarker {
   -moz-appearance: none;
   display: -moz-box;
-  list-style-image: url("chrome://browser/skin/devtools/dropmarker.png");
+  list-style-image: url("chrome://browser/skin/devtools/dropmarker.svg");
   -moz-box-align: center;
   min-width: 16px;
 }
 
 .devtools-toolbarbutton[type=menu-button] > .toolbarbutton-menubutton-button {
   -moz-appearance: none;
   color: inherit;
   border-width: 0;
@@ -163,17 +163,17 @@
 .devtools-toolbarbutton[type=menu-button] {
   padding: 0 1px;
   -moz-box-align: stretch;
 }
 
 .devtools-toolbarbutton[type=menu] > .toolbarbutton-menu-dropmarker,
 .devtools-toolbarbutton[type=menu-button] > .toolbarbutton-menubutton-dropmarker {
   -moz-appearance: none !important;
-  list-style-image: url("chrome://browser/skin/devtools/dropmarker.png");
+  list-style-image: url("chrome://browser/skin/devtools/dropmarker.svg");
   -moz-box-align: center;
   padding: 0 3px;
 }
 
 /* Toolbar button groups */
 .theme-light .devtools-toolbarbutton-group > .devtools-toolbarbutton,
 .theme-dark .devtools-toolbarbutton-group > .devtools-toolbarbutton {
   margin: 0;
--- a/browser/themes/shared/devtools/webaudioeditor.inc.css
+++ b/browser/themes/shared/devtools/webaudioeditor.inc.css
@@ -120,15 +120,35 @@ text {
 #inspector-pane-toggle {
   background: none;
   box-shadow: none;
   border: none;
   list-style-image: url(debugger-collapse.png);
   -moz-image-region: rect(0px,16px,16px,0px);
 }
 
+#inspector-pane-toggle > .toolbarbutton-icon {
+  width: 16px;
+  height: 16px;
+}
+
 #inspector-pane-toggle[pane-collapsed] {
   list-style-image: url(debugger-expand.png);
 }
 
 #inspector-pane-toggle:active {
   -moz-image-region: rect(0px,32px,16px,16px);
 }
+
+@media (min-resolution: 2dppx) {
+  #inspector-pane-toggle {
+    list-style-image: url(debugger-collapse@2x.png);
+    -moz-image-region: rect(0px,32px,32px,0px);
+  }
+
+  #inspector-pane-toggle[pane-collapsed] {
+    list-style-image: url(debugger-expand@2x.png);
+  }
+
+  #inspector-pane-toggle:active {
+    -moz-image-region: rect(0px,64px,32px,32px);
+  }
+}
\ No newline at end of file
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -283,17 +283,17 @@ browser.jar:
         skin/classic/browser/devtools/magnifying-glass-light@2x.png  (../shared/devtools/images/magnifying-glass-light@2x.png)
         skin/classic/browser/devtools/itemToggle.png                (../shared/devtools/images/itemToggle.png)
         skin/classic/browser/devtools/itemToggle@2x.png             (../shared/devtools/images/itemToggle@2x.png)
         skin/classic/browser/devtools/itemArrow-dark-rtl.svg        (../shared/devtools/images/itemArrow-dark-rtl.svg)
         skin/classic/browser/devtools/itemArrow-dark-ltr.svg        (../shared/devtools/images/itemArrow-dark-ltr.svg)
         skin/classic/browser/devtools/itemArrow-rtl.svg             (../shared/devtools/images/itemArrow-rtl.svg)
         skin/classic/browser/devtools/itemArrow-ltr.svg             (../shared/devtools/images/itemArrow-ltr.svg)
         skin/classic/browser/devtools/noise.png                     (../shared/devtools/images/noise.png)
-        skin/classic/browser/devtools/dropmarker.png                (../shared/devtools/images/dropmarker.png)
+        skin/classic/browser/devtools/dropmarker.svg                (../shared/devtools/images/dropmarker.svg)
         skin/classic/browser/devtools/layoutview.css                (../shared/devtools/layoutview.css)
         skin/classic/browser/devtools/debugger-collapse.png         (../shared/devtools/images/debugger-collapse.png)
         skin/classic/browser/devtools/debugger-collapse@2x.png      (../shared/devtools/images/debugger-collapse@2x.png)
         skin/classic/browser/devtools/debugger-expand.png           (../shared/devtools/images/debugger-expand.png)
         skin/classic/browser/devtools/debugger-expand@2x.png        (../shared/devtools/images/debugger-expand@2x.png)
         skin/classic/browser/devtools/debugger-pause.png            (../shared/devtools/images/debugger-pause.png)
         skin/classic/browser/devtools/debugger-pause@2x.png         (../shared/devtools/images/debugger-pause@2x.png)
         skin/classic/browser/devtools/debugger-play.png             (../shared/devtools/images/debugger-play.png)
@@ -683,17 +683,17 @@ browser.jar:
         skin/classic/aero/browser/devtools/magnifying-glass-light@2x.png  (../shared/devtools/images/magnifying-glass-light@2x.png)
         skin/classic/aero/browser/devtools/itemToggle.png            (../shared/devtools/images/itemToggle.png)
         skin/classic/aero/browser/devtools/itemToggle@2x.png         (../shared/devtools/images/itemToggle@2x.png)
         skin/classic/aero/browser/devtools/itemArrow-dark-rtl.svg    (../shared/devtools/images/itemArrow-dark-rtl.svg)
         skin/classic/aero/browser/devtools/itemArrow-dark-ltr.svg    (../shared/devtools/images/itemArrow-dark-ltr.svg)
         skin/classic/aero/browser/devtools/itemArrow-rtl.svg         (../shared/devtools/images/itemArrow-rtl.svg)
         skin/classic/aero/browser/devtools/itemArrow-ltr.svg         (../shared/devtools/images/itemArrow-ltr.svg)
         skin/classic/aero/browser/devtools/noise.png                 (../shared/devtools/images/noise.png)
-        skin/classic/aero/browser/devtools/dropmarker.png            (../shared/devtools/images/dropmarker.png)
+        skin/classic/aero/browser/devtools/dropmarker.svg            (../shared/devtools/images/dropmarker.svg)
         skin/classic/aero/browser/devtools/layoutview.css            (../shared/devtools/layoutview.css)
         skin/classic/aero/browser/devtools/debugger-collapse.png     (../shared/devtools/images/debugger-collapse.png)
         skin/classic/aero/browser/devtools/debugger-collapse@2x.png  (../shared/devtools/images/debugger-collapse@2x.png)
         skin/classic/aero/browser/devtools/debugger-expand.png       (../shared/devtools/images/debugger-expand.png)
         skin/classic/aero/browser/devtools/debugger-expand@2x.png    (../shared/devtools/images/debugger-expand@2x.png)
         skin/classic/aero/browser/devtools/debugger-pause.png        (../shared/devtools/images/debugger-pause.png)
         skin/classic/aero/browser/devtools/debugger-pause@2x.png     (../shared/devtools/images/debugger-pause@2x.png)
         skin/classic/aero/browser/devtools/debugger-play.png         (../shared/devtools/images/debugger-play.png)
--- a/build/automation.py.in
+++ b/build/automation.py.in
@@ -809,17 +809,17 @@ class Automation(object):
   def checkForCrashes(self, minidumpDir, symbolsPath):
     return mozcrash.check_for_crashes(minidumpDir, symbolsPath, test_name=self.lastTestSeen)
 
   def runApp(self, testURL, env, app, profileDir, extraArgs,
              runSSLTunnel = False, utilityPath = None,
              xrePath = None, certPath = None,
              debuggerInfo = None, symbolsPath = None,
              timeout = -1, maxTime = None, onLaunch = None,
-             webapprtChrome = False, screenshotOnFail=False):
+             webapprtChrome = False, screenshotOnFail=False, testPath=None):
     """
     Run the app, log the duration it took to execute, return the status code.
     Kills the app if it runs for longer than |maxTime| seconds, or outputs nothing for |timeout| seconds.
     """
 
     if utilityPath == None:
       utilityPath = self.DIST_BIN
     if xrePath == None:
--- a/configure.in
+++ b/configure.in
@@ -9182,16 +9182,17 @@ if test -z "$direct_nspr_config"; then
     CC="$_SUBDIR_CC"
     CXX="$_SUBDIR_CXX"
     CFLAGS="$_SUBDIR_CFLAGS"
     CPPFLAGS="$_SUBDIR_CPPFLAGS"
     CXXFLAGS="$_SUBDIR_CXXFLAGS"
     LDFLAGS="$_SUBDIR_LDFLAGS"
     HOST_CC="$_SUBDIR_HOST_CC"
     HOST_CFLAGS="$_SUBDIR_HOST_CFLAGS"
+    HOST_CXXFLAGS="$_SUBDIR_HOST_CXXFLAGS"
     HOST_LDFLAGS="$_SUBDIR_HOST_LDFLAGS"
     RC=
 fi
 
 unset MAKEFILES
 unset CONFIG_FILES
 
 # Run all configure scripts specified by a subconfigure
@@ -9284,16 +9285,17 @@ dnl ====================================
 CC="$_SUBDIR_CC"
 CXX="$_SUBDIR_CXX"
 CFLAGS="$_SUBDIR_CFLAGS"
 CPPFLAGS="$_SUBDIR_CPPFLAGS"
 CXXFLAGS="$_SUBDIR_CXXFLAGS"
 LDFLAGS="$_SUBDIR_LDFLAGS"
 HOST_CC="$_SUBDIR_HOST_CC"
 HOST_CFLAGS="$_SUBDIR_HOST_CFLAGS"
+HOST_CXXFLAGS="$_SUBDIR_HOST_CXXFLAGS"
 HOST_LDFLAGS="$_SUBDIR_HOST_LDFLAGS"
 RC=
 
 if test -n "$ENABLE_CLANG_PLUGIN"; then
     ac_configure_args="$_SUBDIR_CONFIG_ARGS"
     AC_OUTPUT_SUBDIRS(build/clang-plugin)
 fi
 
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -133,17 +133,18 @@ typedef CallbackObjectHolder<NodeFilter,
 #define NS_IDOCUMENT_IID \
 { 0x0300e2e0, 0x24c9, 0x4ecf, \
   { 0x81, 0xec, 0x64, 0x26, 0x9a, 0x4b, 0xef, 0x18 } }
 
 // Enum for requesting a particular type of document when creating a doc
 enum DocumentFlavor {
   DocumentFlavorLegacyGuess, // compat with old code until made HTML5-compliant
   DocumentFlavorHTML, // HTMLDocument with HTMLness bit set to true
-  DocumentFlavorSVG // SVGDocument
+  DocumentFlavorSVG, // SVGDocument
+  DocumentFlavorPlain, // Just a Document
 };
 
 // Document states
 
 // RTL locale: specific to the XUL localedir attribute
 #define NS_DOCUMENT_STATE_RTL_LOCALE              NS_DEFINE_EVENT_STATE_MACRO(0)
 // Window activation status
 #define NS_DOCUMENT_STATE_WINDOW_INACTIVE         NS_DEFINE_EVENT_STATE_MACRO(1)
@@ -2680,17 +2681,18 @@ private:
   uint32_t                mMicroTaskLevel;
 };
 
 // XXX These belong somewhere else
 nsresult
 NS_NewHTMLDocument(nsIDocument** aInstancePtrResult, bool aLoadedAsData = false);
 
 nsresult
-NS_NewXMLDocument(nsIDocument** aInstancePtrResult, bool aLoadedAsData = false);
+NS_NewXMLDocument(nsIDocument** aInstancePtrResult, bool aLoadedAsData = false,
+                  bool aIsPlainDocument = false);
 
 nsresult
 NS_NewSVGDocument(nsIDocument** aInstancePtrResult);
 
 nsresult
 NS_NewImageDocument(nsIDocument** aInstancePtrResult);
 
 nsresult
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -12062,17 +12062,17 @@ nsIDocument::Constructor(const GlobalObj
                       NullString(),
                       EmptyString(),
                       nullptr,
                       uri,
                       uri,
                       prin->GetPrincipal(),
                       true,
                       global,
-                      DocumentFlavorLegacyGuess);
+                      DocumentFlavorPlain);
   if (NS_FAILED(res)) {
     rv.Throw(res);
     return nullptr;
   }
 
   nsCOMPtr<nsIDocument> doc = do_QueryInterface(document);
   doc->SetReadyStateInternal(nsIDocument::READYSTATE_COMPLETE);
 
--- a/content/base/test/mochitest.ini
+++ b/content/base/test/mochitest.ini
@@ -561,16 +561,17 @@ run-if = os == 'linux'
 [test_copyimage.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') || toolkit != 'gtk2' && toolkit != 'gtk3' && toolkit != 'windows' || e10s #b2g-desktop(Bug 931116, b2g desktop specific, initial triage)
 [test_copypaste.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s #bug 904183 # b2g(clipboard undefined) b2g-debug(clipboard undefined) b2g-desktop(clipboard undefined)
 [test_copypaste.xhtml]
 skip-if = buildapp == 'b2g' || toolkit == 'android' #bug 904183 # b2g(bug 904183) b2g-debug(bug 904183) b2g-desktop(bug 904183)
 [test_createHTMLDocument.html]
 [test_declare_stylesheet_obsolete.html]
+[test_document_constructor.html]
 [test_domparser_null_char.html]
 [test_domparsing.html]
 [test_elementTraversal.html]
 [test_encodeToStringWithMaxLength.html]
 [test_fileapi.html]
 skip-if = e10s
 [test_fileapi_slice.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s #bug 775227
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_document_constructor.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1017932
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1017932</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript">
+
+  /** Test for Bug 1017932 **/
+   var doc = new Document;
+   ok(doc instanceof Document, "Should have a document");
+   ok(!(doc instanceof XMLDocument), "Should not be an XMLDocument");
+   ok(!("load" in doc), "Should not have a load() method");
+   is(Object.getPrototypeOf(doc), Document.prototype,
+      "Should have the right proto");
+  </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1017932">Mozilla Bug 1017932</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
--- a/content/xml/document/src/XMLDocument.cpp
+++ b/content/xml/document/src/XMLDocument.cpp
@@ -45,16 +45,17 @@
 #include "nsNodeUtils.h"
 #include "nsIConsoleService.h"
 #include "nsIScriptError.h"
 #include "nsIHTMLDocument.h"
 #include "mozilla/BasicEvents.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/XMLDocumentBinding.h"
+#include "mozilla/dom/DocumentBinding.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 // ==================================================================
 // =
 // ==================================================================
 
@@ -82,17 +83,20 @@ NS_NewDOMDocument(nsIDOMDocument** aInst
   nsCOMPtr<nsIDocument> d;
   bool isHTML = false;
   bool isXHTML = false;
   if (aFlavor == DocumentFlavorSVG) {
     rv = NS_NewSVGDocument(getter_AddRefs(d));
   } else if (aFlavor == DocumentFlavorHTML) {
     rv = NS_NewHTMLDocument(getter_AddRefs(d));
     isHTML = true;
+  } else if (aFlavor == DocumentFlavorPlain) {
+    rv = NS_NewXMLDocument(getter_AddRefs(d), aLoadedAsData, true);
   } else if (aDoctype) {
+    MOZ_ASSERT(aFlavor == DocumentFlavorLegacyGuess);
     nsAutoString publicId, name;
     aDoctype->GetPublicId(publicId);
     if (publicId.IsEmpty()) {
       aDoctype->GetName(name);
     }
     if (name.EqualsLiteral("html") ||
         publicId.EqualsLiteral("-//W3C//DTD HTML 4.01//EN") ||
         publicId.EqualsLiteral("-//W3C//DTD HTML 4.01 Frameset//EN") ||
@@ -112,16 +116,17 @@ NS_NewDOMDocument(nsIDOMDocument** aInst
     else if (publicId.EqualsLiteral("-//W3C//DTD SVG 1.1//EN")) {
       rv = NS_NewSVGDocument(getter_AddRefs(d));
     }
     // XXX Add support for XUL documents.
     else {
       rv = NS_NewXMLDocument(getter_AddRefs(d));
     }
   } else {
+    MOZ_ASSERT(aFlavor == DocumentFlavorLegacyGuess);
     rv = NS_NewXMLDocument(getter_AddRefs(d));
   }
 
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   if (nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aEventObject)) {
@@ -167,28 +172,30 @@ NS_NewDOMDocument(nsIDOMDocument** aInst
 
   *aInstancePtrResult = doc;
   NS_ADDREF(*aInstancePtrResult);
 
   return NS_OK;
 }
 
 nsresult
-NS_NewXMLDocument(nsIDocument** aInstancePtrResult, bool aLoadedAsData)
+NS_NewXMLDocument(nsIDocument** aInstancePtrResult, bool aLoadedAsData,
+                  bool aIsPlainDocument)
 {
   nsRefPtr<XMLDocument> doc = new XMLDocument();
 
   nsresult rv = doc->Init();
 
   if (NS_FAILED(rv)) {
     *aInstancePtrResult = nullptr;
     return rv;
   }
 
   doc->SetLoadedAsData(aLoadedAsData);
+  doc->mIsPlainDocument = aIsPlainDocument;
   doc.forget(aInstancePtrResult);
 
   return NS_OK;
 }
 
 nsresult
 NS_NewXBLDocument(nsIDOMDocument** aInstancePtrResult,
                   nsIURI* aDocumentURI,
@@ -592,20 +599,25 @@ XMLDocument::Clone(nsINodeInfo *aNodeInf
                "Can't import this document into another document!");
 
   nsRefPtr<XMLDocument> clone = new XMLDocument();
   nsresult rv = CloneDocHelper(clone);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // State from XMLDocument
   clone->mAsync = mAsync;
+  clone->mIsPlainDocument = mIsPlainDocument;
 
   return CallQueryInterface(clone.get(), aResult);
 }
 
 JSObject*
 XMLDocument::WrapNode(JSContext *aCx)
 {
+  if (mIsPlainDocument) {
+    return DocumentBinding::Wrap(aCx, this);
+  }
+
   return XMLDocumentBinding::Wrap(aCx, this);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/content/xml/document/src/XMLDocument.h
+++ b/content/xml/document/src/XMLDocument.h
@@ -65,23 +65,29 @@ public:
   using nsIDocument::GetLocation;
   // But then we need to also pull in the nsDocument XPCOM version
   // because nsXULDocument tries to forward to it.
   using nsDocument::GetLocation;
 
 protected:
   virtual JSObject* WrapNode(JSContext *aCx) MOZ_OVERRIDE;
 
+  friend nsresult (::NS_NewXMLDocument)(nsIDocument**, bool, bool);
+
+
   // mChannelIsPending indicates whether we're currently asynchronously loading
   // data from mChannel (via document.load() or normal load).  It's set to true
   // when we first find out about the channel (StartDocumentLoad) and set to
   // false in EndLoad or if ResetToURI() is called.  In the latter case our
   // mChannel is also cancelled.  Note that if this member is true, mChannel
   // cannot be null.
   bool mChannelIsPending;
   bool mAsync;
   bool mLoopingForSyncLoad;
+
+  // If true. we're really a Document, not an XMLDocument
+  bool mIsPlainDocument;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_XMLDocument_h
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -77,17 +77,17 @@
 #include "AudioChannelManager.h"
 #endif
 
 #ifdef MOZ_B2G_FM
 #include "mozilla/dom/FMRadio.h"
 #endif
 
 #include "nsIDOMGlobalPropertyInitializer.h"
-#include "nsIDataStoreService.h"
+#include "mozilla/dom/DataStoreService.h"
 #include "nsJSUtils.h"
 
 #include "nsScriptNameSpaceManager.h"
 
 #include "mozilla/dom/NavigatorBinding.h"
 #include "mozilla/dom/Promise.h"
 
 #include "nsIUploadChannel2.h"
@@ -1461,18 +1461,17 @@ Navigator::GetDataStores(nsPIDOMWindow* 
                          const nsAString& aName,
                          ErrorResult& aRv)
 {
   if (!aWindow || !aWindow->GetDocShell()) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
-  nsCOMPtr<nsIDataStoreService> service =
-    do_GetService("@mozilla.org/datastore-service;1");
+  nsRefPtr<DataStoreService> service = DataStoreService::GetOrCreate();
   if (!service) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   nsCOMPtr<nsISupports> promise;
   aRv = service->GetDataStores(aWindow, aName, getter_AddRefs(promise));
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -4334,23 +4334,17 @@ nsGlobalWindow::GetOwnPropertyNames(JSCo
     nameSpaceManager->EnumerateGlobalNames(EnumerateGlobalName, &closure);
   }
 }
 
 /* static */ bool
 nsGlobalWindow::IsChromeWindow(JSContext* aCx, JSObject* aObj)
 {
   // For now, have to deal with XPConnect objects here.
-  nsGlobalWindow* win;
-  nsresult rv = UNWRAP_OBJECT(Window, aObj, win);
-  if (NS_FAILED(rv)) {
-    nsCOMPtr<nsPIDOMWindow> piWin = do_QueryWrapper(aCx, aObj);
-    win = static_cast<nsGlobalWindow*>(piWin.get());
-  }
-  return win->IsChromeWindow();
+  return xpc::WindowOrNull(aObj)->IsChromeWindow();
 }
 
 nsIDOMOfflineResourceList*
 nsGlobalWindow::GetApplicationCache(ErrorResult& aError)
 {
   FORWARD_TO_INNER_OR_THROW(GetApplicationCache, (aError), aError, nullptr);
 
   if (!mApplicationCache) {
@@ -4466,19 +4460,19 @@ nsGlobalWindow::GetControllers(nsIContro
   ErrorResult rv;
   nsCOMPtr<nsIControllers> controllers = GetControllers(rv);
   controllers.forget(aResult);
 
   return rv.ErrorCode();
 }
 
 nsIDOMWindow*
-nsGlobalWindow::GetOpener(ErrorResult& aError)
-{
-  FORWARD_TO_OUTER_OR_THROW(GetOpener, (aError), aError, nullptr);
+nsGlobalWindow::GetOpenerWindow(ErrorResult& aError)
+{
+  FORWARD_TO_OUTER_OR_THROW(GetOpenerWindow, (aError), aError, nullptr);
 
   nsCOMPtr<nsPIDOMWindow> opener = do_QueryReferent(mOpener);
   if (!opener) {
     return nullptr;
   }
 
   // First, check if we were called from a privileged chrome script
   if (nsContentUtils::IsCallerChrome()) {
@@ -4507,81 +4501,115 @@ nsGlobalWindow::GetOpener(ErrorResult& a
         return opener;
       }
     }
   }
 
   return nullptr;
 }
 
+JS::Value
+nsGlobalWindow::GetOpener(JSContext* aCx, ErrorResult& aError)
+{
+  nsCOMPtr<nsIDOMWindow> opener = GetOpenerWindow(aError);
+  if (aError.Failed() || !opener) {
+    return JS::NullValue();
+  }
+
+  JS::Rooted<JS::Value> val(aCx);
+  aError = nsContentUtils::WrapNative(aCx, opener, &val);
+  return val;
+}
+
+NS_IMETHODIMP
+nsGlobalWindow::GetScriptableOpener(JSContext* aCx,
+                                    JS::MutableHandle<JS::Value> aOpener)
+{
+  ErrorResult rv;
+  aOpener.set(GetOpener(aCx, rv));
+
+  return rv.ErrorCode();
+}
+
 NS_IMETHODIMP
 nsGlobalWindow::GetOpener(nsIDOMWindow** aOpener)
 {
   ErrorResult rv;
-  nsCOMPtr<nsIDOMWindow> opener = GetOpener(rv);
+  nsCOMPtr<nsIDOMWindow> opener = GetOpenerWindow(rv);
   opener.forget(aOpener);
-
-  return rv.ErrorCode();
-}
-
-void
-nsGlobalWindow::SetOpener(nsIDOMWindow* aOpener, ErrorResult& aError)
+  return rv.ErrorCode();
+}
+
+void
+nsGlobalWindow::SetOpener(JSContext* aCx, JS::Handle<JS::Value> aOpener,
+                          ErrorResult& aError)
 {
   // Check if we were called from a privileged chrome script.  If not, and if
   // aOpener is not null, just define aOpener on our inner window's JS object,
   // wrapped into the current compartment so that for Xrays we define on the
   // Xray expando object, but don't set it on the outer window, so that it'll
   // get reset on navigation.  This is just like replaceable properties, but
   // we're not quite readonly.
-  if (aOpener && !nsContentUtils::IsCallerChrome()) {
-    // JS_WrapObject will outerize, so we don't care if aOpener is an inner.
-    nsCOMPtr<nsIGlobalObject> glob = do_QueryInterface(aOpener);
-    if (!glob) {
-      aError.Throw(NS_ERROR_UNEXPECTED);
-      return;
-    }
-
-    AutoJSContext cx;
-    JSAutoRequest ar(cx);
-    // Note we explicitly do NOT enter any particular compartment here; we want
-    // the caller compartment in cases when we have a caller, so that we define
-    // expandos on Xrays as needed.
-
-    JS::Rooted<JSObject*> otherObj(cx, glob->GetGlobalJSObject());
-    if (!otherObj) {
-      aError.Throw(NS_ERROR_UNEXPECTED);
-      return;
-    }
-
-    JS::Rooted<JSObject*> thisObj(cx, GetWrapperPreserveColor());
+  if (!aOpener.isNull() && !nsContentUtils::IsCallerChrome()) {
+    JS::Rooted<JSObject*> thisObj(aCx, GetWrapperPreserveColor());
     if (!thisObj) {
       aError.Throw(NS_ERROR_UNEXPECTED);
       return;
     }
 
-    if (!JS_WrapObject(cx, &otherObj) ||
-        !JS_WrapObject(cx, &thisObj) ||
-        !JS_DefineProperty(cx, thisObj, "opener", otherObj, JSPROP_ENUMERATE,
+    if (!JS_WrapObject(aCx, &thisObj) ||
+        !JS_DefineProperty(aCx, thisObj, "opener", aOpener, JSPROP_ENUMERATE,
                            JS_PropertyStub, JS_StrictPropertyStub)) {
       aError.Throw(NS_ERROR_FAILURE);
     }
 
     return;
   }
 
-  SetOpenerWindow(aOpener, false);
+  if (!aOpener.isObjectOrNull()) {
+    // Chrome code trying to set some random value as opener
+    aError.Throw(NS_ERROR_INVALID_ARG);
+    return;
+  }
+
+  nsGlobalWindow* win = nullptr;
+  if (aOpener.isObject()) {
+    JSObject* unwrapped = js::CheckedUnwrap(&aOpener.toObject(),
+                                            /* stopAtOuter = */ false);
+    if (!unwrapped) {
+      aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
+      return;
+    }
+
+    win = xpc::WindowOrNull(unwrapped);
+    if (!win) {
+      // Wasn't a window
+      aError.Throw(NS_ERROR_INVALID_ARG);
+      return;
+    }
+  }
+
+  SetOpenerWindow(win, false);
+}
+
+NS_IMETHODIMP
+nsGlobalWindow::SetScriptableOpener(JSContext* aCx,
+                                    JS::Handle<JS::Value> aOpener)
+{
+  ErrorResult rv;
+  SetOpener(aCx, aOpener, rv);
+
+  return rv.ErrorCode();
 }
 
 NS_IMETHODIMP
 nsGlobalWindow::SetOpener(nsIDOMWindow* aOpener)
 {
-  ErrorResult rv;
-  SetOpener(aOpener, rv);
-
-  return rv.ErrorCode();
+  SetOpenerWindow(aOpener, false);
+  return NS_OK;
 }
 
 void
 nsGlobalWindow::GetStatus(nsAString& aStatus, ErrorResult& aError)
 {
   FORWARD_TO_OUTER_OR_THROW(GetStatus, (aStatus, aError), aError, );
 
   aStatus = mStatus;
@@ -13694,23 +13722,17 @@ nsGlobalModalWindow::SetReturnValue(nsIV
   return NS_OK;
 }
 
 /* static */
 bool
 nsGlobalWindow::IsModalContentWindow(JSContext* aCx, JSObject* aGlobal)
 {
   // For now, have to deal with XPConnect objects here.
-  nsGlobalWindow* win;
-  nsresult rv = UNWRAP_OBJECT(Window, aGlobal, win);
-  if (NS_FAILED(rv)) {
-    nsCOMPtr<nsPIDOMWindow> piWin = do_QueryWrapper(aCx, aGlobal);
-    win = static_cast<nsGlobalWindow*>(piWin.get());
-  }
-  return win->IsModalContentWindow();
+  return xpc::WindowOrNull(aGlobal)->IsModalContentWindow();
 }
 
 NS_IMETHODIMP
 nsGlobalWindow::GetConsole(JSContext* aCx,
                            JS::MutableHandle<JS::Value> aConsole)
 {
   ErrorResult rv;
   nsRefPtr<Console> console = GetConsole(rv);
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -831,18 +831,22 @@ public:
   already_AddRefed<nsIDOMWindow> GetFrames(mozilla::ErrorResult& aError);
   uint32_t Length();
   already_AddRefed<nsIDOMWindow> GetTop(mozilla::ErrorResult& aError)
   {
     nsCOMPtr<nsIDOMWindow> top;
     aError = GetScriptableTop(getter_AddRefs(top));
     return top.forget();
   }
-  nsIDOMWindow* GetOpener(mozilla::ErrorResult& aError);
-  void SetOpener(nsIDOMWindow* aOpener, mozilla::ErrorResult& aError);
+protected:
+  nsIDOMWindow* GetOpenerWindow(mozilla::ErrorResult& aError);
+public:
+  JS::Value GetOpener(JSContext* aCx, mozilla::ErrorResult& aError);
+  void SetOpener(JSContext* aCx, JS::Handle<JS::Value> aOpener,
+                 mozilla::ErrorResult& aError);
   using nsIDOMWindow::GetParent;
   already_AddRefed<nsIDOMWindow> GetParent(mozilla::ErrorResult& aError);
   mozilla::dom::Element* GetFrameElement(mozilla::ErrorResult& aError);
   already_AddRefed<nsIDOMWindow> Open(const nsAString& aUrl,
                                       const nsAString& aName,
                                       const nsAString& aOptions,
                                       mozilla::ErrorResult& aError);
   mozilla::dom::Navigator* GetNavigator(mozilla::ErrorResult& aError);
--- a/dom/base/test/test_setting_opener.html
+++ b/dom/base/test/test_setting_opener.html
@@ -54,16 +54,35 @@ https://bugzilla.mozilla.org/show_bug.cg
 
   function continueOpenerTest(win) {
     is(win.opener, window, "Navigating a window should have reset the opener we stashed on it temporarily");
     is(evalsb("win.opener", sb1), window,
        "Navigating a window should have reset the opener in sb1");
     is(evalsb("win.opener", sb2), window,
        "Navigating a window should have reset the opener in sb2");
 
+    win.opener = 5;
+    evalsb("win.opener = 5", sb1);
+    evalsb("win.opener = 5", sb2);
+    is(win.opener, 5, "Should be able to set an opener to a primitive");
+    is(evalsb("win.opener", sb1), 5,
+       "Should be able to set the opener to a primitive in a sandbox one");
+    is(evalsb("win.opener", sb2), 5,
+       "Should be able to set the opener to a primitive in a sandbox two");
+    win.location = "data:text/html,<script>opener.setTimeout(opener.continueOpenerTest2, 0, this);</" + "script>";
+  }
+
+  function continueOpenerTest2(win) {
+    is(win.opener, window,
+       "Navigating a window again should have reset the opener we stashed on it temporarily");
+    is(evalsb("win.opener", sb1), window,
+       "Navigating a window again should have reset the opener in sb1");
+    is(evalsb("win.opener", sb2), window,
+       "Navigating a window again should have reset the opener in sb2");
+
     win.opener = null;
     is(win.opener, null, "Should be able to set the opener to null");
     is(evalsb("win.opener", sb1), null,
        "Setting the opener to null should be visible in sb1");
     is(evalsb("win.opener", sb2), null,
        "Setting the opener to null should be visible in sb2");
 
     win.location = "data:text/html,Loaded";
--- a/dom/bindings/CallbackObject.cpp
+++ b/dom/bindings/CallbackObject.cpp
@@ -81,18 +81,20 @@ CallbackObject::CallSetup::CallSetup(Cal
   nsIGlobalObject* globalObject = nullptr;
 
   {
     // Bug 955660: we cannot do "proper" rooting here because we need the
     // global to get a context. Everything here is simple getters that cannot
     // GC, so just paper over the necessary dataflow inversion.
     JS::AutoSuppressGCAnalysis nogc;
     if (mIsMainThread) {
-      // Now get the global and JSContext for this callback.
-      nsGlobalWindow* win = xpc::WindowGlobalOrNull(realCallback);
+      // Now get the global and JSContext for this callback.  Note that for the
+      // case of JS-implemented WebIDL we never have a window here.
+      nsGlobalWindow* win =
+        aIsJSImplementedWebIDL ? nullptr : xpc::WindowGlobalOrNull(realCallback);
       if (win) {
         // Make sure that if this is a window it's the current inner, since the
         // nsIScriptContext and hence JSContext are associated with the outer
         // window.  Which means that if someone holds on to a function from a
         // now-unloaded document we'd have the new document as the script entry
         // point...
         MOZ_ASSERT(win->IsInnerWindow());
         nsPIDOMWindow* outer = win->GetOuterWindow();
@@ -146,17 +148,19 @@ CallbackObject::CallSetup::CallSetup(Cal
     // operations _after_ the Push() above, which lets us take advantage of the
     // JSAutoRequest embedded in the pusher.
     //
     // We can do this even though we're not in the right compartment yet, because
     // Rooted<> does not care about compartments.
     mRootedCallable.construct(cx, aCallback->Callback());
   }
 
-  if (mIsMainThread) {
+  // JS-implemented WebIDL is always OK to run, since it runs with Chrome
+  // privileges anyway.
+  if (mIsMainThread && !aIsJSImplementedWebIDL) {
     // Check that it's ok to run this callback at all.
     // Make sure to use realCallback to get the global of the callback object,
     // not the wrapper.
     bool allowed = nsContentUtils::GetSecurityManager()->
       ScriptAllowed(js::GetGlobalForObjectCrossCompartment(realCallback));
 
     if (!allowed) {
       return;
@@ -215,34 +219,34 @@ CallbackObject::CallSetup::~CallSetup()
   // so we end up reporting them while in the compartment of our entry point,
   // not whatever cross-compartment wrappper mCallback might be.
   // Be careful: the JSAutoCompartment might not have been constructed at all!
   mAc.destroyIfConstructed();
 
   // Now, if we have a JSContext, report any pending errors on it, unless we
   // were told to re-throw them.
   if (mCx) {
-    bool dealtWithPendingException = false;
+    bool needToDealWithException = JS_IsExceptionPending(mCx);
     if ((mCompartment && mExceptionHandling == eRethrowContentExceptions) ||
         mExceptionHandling == eRethrowExceptions) {
       // Restore the old context options
       JS::ContextOptionsRef(mCx) = mSavedJSContextOptions;
       mErrorResult.MightThrowJSException();
-      if (JS_IsExceptionPending(mCx)) {
+      if (needToDealWithException) {
         JS::Rooted<JS::Value> exn(mCx);
         if (JS_GetPendingException(mCx, &exn) &&
             ShouldRethrowException(exn)) {
           mErrorResult.ThrowJSException(mCx, exn);
           JS_ClearPendingException(mCx);
-          dealtWithPendingException = true;
+          needToDealWithException = false;
         }
       }
     }
 
-    if (!dealtWithPendingException) {
+    if (needToDealWithException) {
       // Either we're supposed to report our exceptions, or we're supposed to
       // re-throw them but we failed to JS_GetPendingException.  Either way,
       // just report the pending exception, if any.
       //
       // We don't use nsJSUtils::ReportPendingException here because all it
       // does at this point is JS_SaveFrameChain and enter a compartment around
       // a JS_ReportPendingException call.  But our mAutoEntryScript should
       // already do a JS_SaveFrameChain and we are already in the compartment
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -7478,53 +7478,56 @@ class CGMemberJITInfo(CGThing):
     def __init__(self, descriptor, member):
         self.member = member
         self.descriptor = descriptor
 
     def declare(self):
         return ""
 
     def defineJitInfo(self, infoName, opName, opType, infallible, movable,
-                      aliasSet, hasSlot, slotIndex, returnTypes, args):
+                      aliasSet, alwaysInSlot, lazilyInSlot, slotIndex,
+                      returnTypes, args):
         """
         aliasSet is a JSJitInfo::AliasSet value, without the "JSJitInfo::" bit.
 
         args is None if we don't want to output argTypes for some
         reason (e.g. we have overloads or we're not a method) and
         otherwise an iterable of the arguments for this method.
         """
         assert(not movable or aliasSet != "AliasEverything")  # Can't move write-aliasing things
-        assert(not hasSlot or movable)  # Things with slots had better be movable
+        assert(not alwaysInSlot or movable)  # Things always in slots had better be movable
 
         def jitInfoInitializer(isTypedMethod):
             initializer = fill(
                 """
                 {
                  { ${opName} },
                   prototypes::id::${name},
                   PrototypeTraits<prototypes::id::${name}>::Depth,
                   JSJitInfo::${opType},
                   JSJitInfo::${aliasSet}, /* aliasSet.  Not relevant for setters. */
                   ${returnType},  /* returnType.  Not relevant for setters. */
                   ${isInfallible},  /* isInfallible. False in setters. */
                   ${isMovable},  /* isMovable.  Not relevant for setters. */
-                  ${isInSlot},  /* isInSlot.  Only relevant for getters. */
+                  ${isAlwaysInSlot}, /* isAlwaysInSlot.  Only relevant for getters. */
+                  ${isLazilyCachedInSlot}, /* isLazilyCachedInSlot.  Only relevant for getters. */
                   ${isTypedMethod},  /* isTypedMethod.  Only relevant for methods. */
                   ${slotIndex}   /* Reserved slot index, if we're stored in a slot, else 0. */
                 }
                 """,
                 opName=opName,
                 name=self.descriptor.name,
                 opType=opType,
                 aliasSet=aliasSet,
                 returnType=reduce(CGMemberJITInfo.getSingleReturnType, returnTypes,
                                   ""),
                 isInfallible=toStringBool(infallible),
                 isMovable=toStringBool(movable),
-                isInSlot=toStringBool(hasSlot),
+                isAlwaysInSlot=toStringBool(alwaysInSlot),
+                isLazilyCachedInSlot=toStringBool(lazilyInSlot),
                 isTypedMethod=toStringBool(isTypedMethod),
                 slotIndex=slotIndex)
             return initializer.rstrip()
 
         if args is not None:
             argTypes = "%s_argTypes" % infoName
             args = [CGMemberJITInfo.getJSArgType(arg.type) for arg in args]
             args.append("JSJitInfo::ArgTypeListEnd")
@@ -7563,39 +7566,44 @@ class CGMemberJITInfo(CGThing):
                 aliasSet = "AliasNone"
             elif getterpure:
                 aliasSet = "AliasDOMSets"
             else:
                 aliasSet = "AliasEverything"
             movable = getterpure and getterinfal
 
             getterinfal = getterinfal and infallibleForMember(self.member, self.member.type, self.descriptor)
-            isInSlot = self.member.getExtendedAttribute("StoreInSlot")
-            if isInSlot:
+            isAlwaysInSlot = self.member.getExtendedAttribute("StoreInSlot")
+            if self.member.slotIndex is not None:
+                assert isAlwaysInSlot or self.member.getExtendedAttribute("Cached")
+                isLazilyCachedInSlot = not isAlwaysInSlot
                 slotIndex = memberReservedSlot(self.member)
                 # We'll statically assert that this is not too big in
-                # CGUpdateMemberSlotsMethod
-            else:
+                # CGUpdateMemberSlotsMethod, in the case when
+                # isAlwaysInSlot is true.
+            else:
+                isLazilyCachedInSlot = False
                 slotIndex = "0"
 
             result = self.defineJitInfo(getterinfo, getter, "Getter",
                                         getterinfal, movable, aliasSet,
-                                        isInSlot, slotIndex,
+                                        isAlwaysInSlot, isLazilyCachedInSlot,
+                                        slotIndex,
                                         [self.member.type], None)
             if (not self.member.readonly or
                 self.member.getExtendedAttribute("PutForwards") is not None or
                 self.member.getExtendedAttribute("Replaceable") is not None):
                 setterinfo = ("%s_setterinfo" % self.member.identifier.name)
                 # Actually a JSJitSetterOp, but JSJitGetterOp is first in the
                 # union.
                 setter = ("(JSJitGetterOp)set_%s" % self.member.identifier.name)
                 # Setters are always fallible, since they have to do a typed unwrap.
                 result += self.defineJitInfo(setterinfo, setter, "Setter",
                                              False, False, "AliasEverything",
-                                             False, "0",
+                                             False, False, "0",
                                              [BuiltinTypes[IDLBuiltinType.Types.void]],
                                              None)
             return result
         if self.member.isMethod():
             methodinfo = ("%s_methodinfo" % self.member.identifier.name)
             name = CppKeywords.checkMethodName(self.member.identifier.name)
             if self.member.returnsPromise():
                 name = CGMethodPromiseWrapper.makeName(name)
@@ -7636,17 +7644,18 @@ class CGMemberJITInfo(CGThing):
                 else:
                     args = None
 
             if args is not None:
                 aliasSet = "AliasDOMSets"
             else:
                 aliasSet = "AliasEverything"
             result = self.defineJitInfo(methodinfo, method, "Method",
-                                        methodInfal, movable, aliasSet, False, "0",
+                                        methodInfal, movable, aliasSet,
+                                        False, False, "0",
                                         [s[0] for s in sigs], args)
             return result
         raise TypeError("Illegal member type to CGPropertyJITInfo")
 
     @staticmethod
     def getJSReturnTypeTag(t):
         if t.nullable():
             # Sometimes it might return null, sometimes not
--- a/dom/datastore/DataStore.manifest
+++ b/dom/datastore/DataStore.manifest
@@ -1,2 +1,2 @@
-component {d193d0e2-c677-4a7b-bb0a-19155b470f2e} DataStoreService.js
-contract @mozilla.org/datastore-service;1 {d193d0e2-c677-4a7b-bb0a-19155b470f2e}
+component {db5c9602-030f-4bff-a3de-881a8de370f2} DataStoreImpl.js
+contract @mozilla.org/dom/datastore;1 {db5c9602-030f-4bff-a3de-881a8de370f2}
new file mode 100644
--- /dev/null
+++ b/dom/datastore/DataStoreCallbacks.h
@@ -0,0 +1,48 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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_DataStoreCallbacks_h
+#define mozilla_dom_DataStoreCallbacks_h
+
+#include "nsISupports.h"
+
+namespace mozilla {
+namespace dom {
+
+class DataStoreDB;
+
+class DataStoreDBCallback
+{
+public:
+  NS_IMETHOD_(MozExternalRefCountType) AddRef(void) = 0;
+  NS_IMETHOD_(MozExternalRefCountType) Release(void) = 0;
+
+  virtual void Run(DataStoreDB* aDb, bool aSuccess) = 0;
+
+protected:
+  virtual ~DataStoreDBCallback()
+  {
+  }
+};
+
+class DataStoreRevisionCallback
+{
+public:
+  NS_IMETHOD_(MozExternalRefCountType) AddRef(void) = 0;
+  NS_IMETHOD_(MozExternalRefCountType) Release(void) = 0;
+
+  virtual void Run(const nsAString& aRevisionID) = 0;
+
+protected:
+  virtual ~DataStoreRevisionCallback()
+  {
+  }
+};
+
+} // dom namespace
+} // mozilla namespace
+
+#endif // mozilla_dom_DataStoreCallbacks_h
--- a/dom/datastore/DataStoreChangeNotifier.jsm
+++ b/dom/datastore/DataStoreChangeNotifier.jsm
@@ -7,19 +7,16 @@
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 this.EXPORTED_SYMBOLS = ["DataStoreChangeNotifier"];
 
 function debug(s) {
   //dump('DEBUG DataStoreChangeNotifier: ' + s + '\n');
 }
 
-// DataStoreServiceInternal should not be converted into a lazy getter as it
-// runs code during initialization.
-Cu.import('resource://gre/modules/DataStoreServiceInternal.jsm');
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
                                    "@mozilla.org/parentprocessmessagemanager;1",
                                    "nsIMessageBroadcaster");
 
 this.DataStoreChangeNotifier = {
new file mode 100644
--- /dev/null
+++ b/dom/datastore/DataStoreDB.cpp
@@ -0,0 +1,319 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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 "DataStoreDB.h"
+
+#include "DataStoreCallbacks.h"
+#include "mozilla/dom/IDBDatabaseBinding.h"
+#include "mozilla/dom/IDBFactoryBinding.h"
+#include "mozilla/dom/indexedDB/IDBDatabase.h"
+#include "mozilla/dom/indexedDB/IDBFactory.h"
+#include "mozilla/dom/indexedDB/IDBIndex.h"
+#include "mozilla/dom/indexedDB/IDBObjectStore.h"
+#include "mozilla/dom/indexedDB/IDBRequest.h"
+#include "nsIDOMEvent.h"
+
+#define DATASTOREDB_VERSION        1
+#define DATASTOREDB_NAME           "DataStoreDB"
+#define DATASTOREDB_REVISION_INDEX "revisionIndex"
+
+using namespace mozilla::dom::indexedDB;
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_ISUPPORTS(DataStoreDB, nsIDOMEventListener)
+
+DataStoreDB::DataStoreDB(const nsAString& aManifestURL, const nsAString& aName)
+  : mState(Inactive)
+{
+  mDatabaseName.Assign(aName);
+  mDatabaseName.AppendASCII("|");
+  mDatabaseName.Append(aManifestURL);
+}
+
+DataStoreDB::~DataStoreDB()
+{
+}
+
+nsresult
+DataStoreDB::CreateFactoryIfNeeded()
+{
+  if (!mFactory) {
+    nsresult rv = IDBFactory::Create(nullptr, getter_AddRefs(mFactory));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  return NS_OK;
+}
+
+nsresult
+DataStoreDB::Open(IDBTransactionMode aMode, const Sequence<nsString>& aDbs,
+                  DataStoreDBCallback* aCallback)
+{
+  MOZ_ASSERT(mState == Inactive);
+
+  nsresult rv = CreateFactoryIfNeeded();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  ErrorResult error;
+  mRequest = mFactory->Open(mDatabaseName, DATASTOREDB_VERSION, error);
+  if (NS_WARN_IF(error.Failed())) {
+    return error.ErrorCode();
+  }
+
+  rv = AddEventListeners();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  mState = Active;
+  mTransactionMode = aMode;
+  mObjectStores = aDbs;
+  mCallback = aCallback;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DataStoreDB::HandleEvent(nsIDOMEvent* aEvent)
+{
+  nsString type;
+  nsresult rv = aEvent->GetType(type);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (type.EqualsASCII("success")) {
+    RemoveEventListeners();
+    mState = Inactive;
+
+    rv = DatabaseOpened();
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      mCallback->Run(this, false);
+    } else {
+      mCallback->Run(this, true);
+    }
+
+    mRequest = nullptr;
+    return NS_OK;
+  }
+
+  if (type.EqualsASCII("upgradeneeded")) {
+    return UpgradeSchema();
+  }
+
+  if (type.EqualsASCII("error") || type.EqualsASCII("blocked")) {
+    RemoveEventListeners();
+    mState = Inactive;
+    mCallback->Run(this, false);
+    mRequest = nullptr;
+    return NS_OK;
+  }
+
+  MOZ_ASSUME_UNREACHABLE("This should not happen");
+  return NS_OK;
+}
+
+nsresult
+DataStoreDB::UpgradeSchema()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  AutoSafeJSContext cx;
+
+  ErrorResult error;
+  JS::Rooted<JS::Value> result(cx, mRequest->GetResult(error));
+  if (NS_WARN_IF(error.Failed())) {
+    return error.ErrorCode();
+  }
+
+  MOZ_ASSERT(result.isObject());
+
+  IDBDatabase* database = nullptr;
+  nsresult rv = UNWRAP_OBJECT(IDBDatabase, &result.toObject(), database);
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Didn't get the object we expected!");
+    return rv;
+  }
+
+  {
+    RootedDictionary<IDBObjectStoreParameters> params(cx);
+    params.Init(NS_LITERAL_STRING("{ \"autoIncrement\": true }"));
+    nsRefPtr<IDBObjectStore> store =
+      database->CreateObjectStore(cx, NS_LITERAL_STRING(DATASTOREDB_NAME),
+                                  params, error);
+    if (NS_WARN_IF(error.Failed())) {
+      return error.ErrorCode();
+    }
+  }
+
+  nsRefPtr<IDBObjectStore> store;
+
+  {
+    RootedDictionary<IDBObjectStoreParameters> params(cx);
+    params.Init(NS_LITERAL_STRING("{ \"autoIncrement\": true, \"keyPath\": \"internalRevisionId\" }"));
+
+    store =
+      database->CreateObjectStore(cx, NS_LITERAL_STRING(DATASTOREDB_REVISION),
+                                  params, error);
+    if (NS_WARN_IF(error.Failed())) {
+      return error.ErrorCode();
+    }
+  }
+
+  {
+    RootedDictionary<IDBIndexParameters> params(cx);
+    params.Init(NS_LITERAL_STRING("{ \"unique\": true }"));
+    nsRefPtr<IDBIndex> index =
+      store->CreateIndex(cx, NS_LITERAL_STRING(DATASTOREDB_REVISION_INDEX),
+                         NS_LITERAL_STRING("revisionId"), params, error);
+    if (NS_WARN_IF(error.Failed())) {
+      return error.ErrorCode();
+    }
+  }
+
+  return NS_OK;
+}
+
+nsresult
+DataStoreDB::DatabaseOpened()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  AutoSafeJSContext cx;
+
+  ErrorResult error;
+  JS::Rooted<JS::Value> result(cx, mRequest->GetResult(error));
+  if (NS_WARN_IF(error.Failed())) {
+    return error.ErrorCode();
+  }
+
+  MOZ_ASSERT(result.isObject());
+
+  nsresult rv = UNWRAP_OBJECT(IDBDatabase, &result.toObject(), mDatabase);
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Didn't get the object we expected!");
+    return rv;
+  }
+
+  nsRefPtr<IDBTransaction> txn = mDatabase->Transaction(mObjectStores,
+                                                        mTransactionMode,
+                                                        error);
+  if (NS_WARN_IF(error.Failed())) {
+    return error.ErrorCode();
+  }
+
+  mTransaction = txn.forget();
+  return NS_OK;
+}
+
+nsresult
+DataStoreDB::Delete()
+{
+  MOZ_ASSERT(mState == Inactive);
+
+  nsresult rv = CreateFactoryIfNeeded();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  mTransaction = nullptr;
+
+  if (mDatabase) {
+    rv = mDatabase->Close();
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    mDatabase = nullptr;
+  }
+
+  ErrorResult error;
+  nsRefPtr<IDBOpenDBRequest> request =
+    mFactory->DeleteDatabase(mDatabaseName, IDBOpenDBOptions(), error);
+  if (NS_WARN_IF(error.Failed())) {
+    return error.ErrorCode();
+  }
+
+  return NS_OK;
+}
+
+indexedDB::IDBTransaction*
+DataStoreDB::Transaction() const
+{
+  MOZ_ASSERT(mTransaction);
+  MOZ_ASSERT(mTransaction->IsOpen());
+  return mTransaction;
+}
+
+nsresult
+DataStoreDB::AddEventListeners()
+{
+  nsresult rv;
+  rv = mRequest->EventTarget::AddEventListener(NS_LITERAL_STRING("success"),
+                                               this, false);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = mRequest->EventTarget::AddEventListener(NS_LITERAL_STRING("upgradeneeded"),
+                                               this, false);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = mRequest->EventTarget::AddEventListener(NS_LITERAL_STRING("error"),
+                                               this, false);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = mRequest->EventTarget::AddEventListener(NS_LITERAL_STRING("blocked"),
+                                               this, false);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+DataStoreDB::RemoveEventListeners()
+{
+  nsresult rv;
+  rv = mRequest->RemoveEventListener(NS_LITERAL_STRING("success"),
+                                     this, false);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = mRequest->RemoveEventListener(NS_LITERAL_STRING("upgradeneeded"),
+                                     this, false);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = mRequest->RemoveEventListener(NS_LITERAL_STRING("error"),
+                                     this, false);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = mRequest->RemoveEventListener(NS_LITERAL_STRING("blocked"),
+                                     this, false);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/datastore/DataStoreDB.h
@@ -0,0 +1,82 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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_DataStoreDB_h
+#define mozilla_dom_DataStoreDB_h
+
+#include "mozilla/dom/IDBTransactionBinding.h"
+#include "nsAutoPtr.h"
+#include "nsIDOMEventListener.h"
+#include "nsISupportsImpl.h"
+#include "nsString.h"
+
+#define DATASTOREDB_REVISION       "revision"
+
+namespace mozilla {
+namespace dom {
+
+namespace indexedDB {
+class IDBDatabase;
+class IDBFactory;
+class IDBObjectStore;
+class IDBOpenDBRequest;
+class IDBTransaction;
+}
+
+class DataStoreDBCallback;
+
+class DataStoreDB MOZ_FINAL : public nsIDOMEventListener
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  DataStoreDB(const nsAString& aManifestURL, const nsAString& aName);
+  ~DataStoreDB();
+
+  nsresult Open(IDBTransactionMode aMode, const Sequence<nsString>& aDb,
+                DataStoreDBCallback* aCallback);
+
+  nsresult Delete();
+
+  indexedDB::IDBTransaction* Transaction() const;
+
+  // nsIDOMEventListener
+  NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent);
+
+private:
+  nsresult CreateFactoryIfNeeded();
+
+  nsresult UpgradeSchema();
+
+  nsresult DatabaseOpened();
+
+  nsresult AddEventListeners();
+
+  nsresult RemoveEventListeners();
+
+  nsString mDatabaseName;
+
+  nsRefPtr<indexedDB::IDBFactory> mFactory;
+  nsRefPtr<indexedDB::IDBOpenDBRequest> mRequest;
+  nsRefPtr<indexedDB::IDBDatabase> mDatabase;
+  nsRefPtr<indexedDB::IDBTransaction> mTransaction;
+
+  nsRefPtr<DataStoreDBCallback> mCallback;
+
+  // Internal state to avoid strange use of this class.
+  enum StateType {
+    Inactive,
+    Active
+  } mState;
+
+  IDBTransactionMode mTransactionMode;
+  Sequence<nsString> mObjectStores;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_DataStoreDB_h
rename from dom/datastore/DataStoreImpl.jsm
rename to dom/datastore/DataStoreImpl.js
--- a/dom/datastore/DataStoreImpl.jsm
+++ b/dom/datastore/DataStoreImpl.js
@@ -1,18 +1,16 @@
 /* -*- Mode: js2; js2-basic-offset: 2; indent-tabs-mode: nil; -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* 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'
 
-this.EXPORTED_SYMBOLS = ["DataStore"];
-
 function debug(s) {
   //dump('DEBUG DataStore: ' + s + '\n');
 }
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 const REVISION_ADDED = "added";
 const REVISION_UPDATED = "updated";
@@ -54,27 +52,26 @@ function validateId(aId) {
     return aId.length;
   }
 
   aId = parseInt(aId);
   return (!isNaN(aId) && aId > 0);
 }
 
 /* DataStore object */
-this.DataStore = function(aWindow, aName, aOwner, aReadOnly) {
+function DataStore() {
   debug("DataStore created");
-  this.init(aWindow, aName, aOwner, aReadOnly);
 }
 
-this.DataStore.prototype = {
+DataStore.prototype = {
   classDescription: "DataStore XPCOM Component",
   classID: Components.ID("{db5c9602-030f-4bff-a3de-881a8de370f2}"),
   contractID: "@mozilla.org/dom/datastore-impl;1",
-  QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsISupports,
-                                         Components.interfaces.nsIObserver]),
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIDataStore, Ci.nsISupports,
+                                         Ci.nsIObserver]),
 
   callbacks: [],
 
   _window: null,
   _name: null,
   _owner: null,
   _readOnly: null,
   _revisionId: null,
@@ -531,8 +528,10 @@ this.DataStore.prototype = {
     let cursorImpl = this._window.DataStoreCursorImpl.
                                   _create(this._window, this._cursor);
 
     let exposedCursor = new this._window.DataStoreCursor();
     exposedCursor.setDataStoreCursorImpl(cursorImpl);
     return exposedCursor;
   }
 };
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DataStore]);
new file mode 100644
--- /dev/null
+++ b/dom/datastore/DataStoreRevision.cpp
@@ -0,0 +1,102 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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 "DataStoreRevision.h"
+
+#include "DataStoreCallbacks.h"
+#include "DataStoreService.h"
+#include "mozilla/dom/DataStoreBinding.h"
+#include "mozilla/dom/indexedDB/IDBObjectStore.h"
+#include "nsIDOMEvent.h"
+
+namespace mozilla {
+namespace dom {
+
+using namespace indexedDB;
+
+NS_IMPL_ISUPPORTS(DataStoreRevision, nsIDOMEventListener)
+
+// Note: this code in it must not assume anything about the compartment cx is
+// in.
+nsresult
+DataStoreRevision::AddRevision(JSContext* aCx,
+                               IDBObjectStore* aStore,
+                               uint32_t aObjectId,
+                               RevisionType aRevisionType,
+                               DataStoreRevisionCallback* aCallback)
+{
+  MOZ_ASSERT(aStore);
+  MOZ_ASSERT(aCallback);
+
+  nsRefPtr<DataStoreService> service = DataStoreService::Get();
+  if (!service) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsString id;
+  nsresult rv = service->GenerateUUID(mRevisionID);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  DataStoreRevisionData data;
+  data.mRevisionId = mRevisionID;
+  data.mObjectId = aObjectId;
+
+  switch (aRevisionType) {
+    case RevisionVoid:
+      data.mOperation = NS_LITERAL_STRING("void");
+      break;
+
+    default:
+      MOZ_ASSUME_UNREACHABLE("This should not happen");
+      break;
+  }
+
+  JS::Rooted<JS::Value> value(aCx);
+  if (!data.ToObject(aCx, &value)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  ErrorResult error;
+  mRequest = aStore->Put(aCx, value, JS::UndefinedHandleValue, error);
+  if (NS_WARN_IF(error.Failed())) {
+    return error.ErrorCode();
+  }
+
+  rv = mRequest->EventTarget::AddEventListener(NS_LITERAL_STRING("success"),
+                                               this, false);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  mCallback = aCallback;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DataStoreRevision::HandleEvent(nsIDOMEvent* aEvent)
+{
+  nsString type;
+  nsresult rv = aEvent->GetType(type);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (!type.EqualsASCII("success")) {
+    MOZ_ASSUME_UNREACHABLE("This should not happen");
+    return NS_ERROR_FAILURE;
+  }
+
+  mRequest->RemoveEventListener(NS_LITERAL_STRING("success"), this, false);
+  mRequest = nullptr;
+
+  mCallback->Run(mRevisionID);
+  return NS_OK;
+}
+
+} // dom namespace
+} // mozilla namespace
new file mode 100644
--- /dev/null
+++ b/dom/datastore/DataStoreRevision.h
@@ -0,0 +1,52 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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_DataStoreRevision_h
+#define mozilla_dom_DataStoreRevision_h
+
+#include "jsapi.h"
+#include "nsAutoPtr.h"
+#include "nsIDOMEventListener.h"
+#include "nsString.h"
+
+namespace mozilla {
+namespace dom {
+
+namespace indexedDB {
+class IDBObjectStore;
+class IDBRequest;
+}
+
+class DataStoreRevisionCallback;
+
+class DataStoreRevision MOZ_FINAL : public nsIDOMEventListener
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  enum RevisionType {
+    RevisionVoid
+  };
+
+  nsresult AddRevision(JSContext* aCx,
+                       indexedDB::IDBObjectStore* aStore,
+                       uint32_t aObjectId,
+                       RevisionType aRevisionType,
+                       DataStoreRevisionCallback* aCallback);
+
+  // nsIDOMEventListener
+  NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent);
+
+private:
+  nsRefPtr<DataStoreRevisionCallback> mCallback;
+  nsRefPtr<indexedDB::IDBRequest> mRequest;
+  nsString mRevisionID;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_DataStoreRevision_h
new file mode 100644
--- /dev/null
+++ b/dom/datastore/DataStoreService.cpp
@@ -0,0 +1,1352 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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 "DataStoreService.h"
+
+#include "DataStoreCallbacks.h"
+#include "DataStoreDB.h"
+#include "DataStoreRevision.h"
+#include "mozilla/dom/DataStore.h"
+#include "mozilla/dom/DataStoreBinding.h"
+#include "mozilla/dom/DataStoreImplBinding.h"
+#include "nsIDataStore.h"
+
+#include "mozilla/Preferences.h"
+#include "mozilla/Services.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/DOMError.h"
+#include "mozilla/dom/indexedDB/IDBCursor.h"
+#include "mozilla/dom/indexedDB/IDBObjectStore.h"
+#include "mozilla/dom/PermissionMessageUtils.h"
+#include "mozilla/dom/Promise.h"
+#include "mozilla/unused.h"
+
+#include "mozIApplication.h"
+#include "mozIApplicationClearPrivateDataParams.h"
+#include "nsIAppsService.h"
+#include "nsIDOMEvent.h"
+#include "nsIDocument.h"
+#include "nsIDOMGlobalPropertyInitializer.h"
+#include "nsIIOService.h"
+#include "nsIObserverService.h"
+#include "nsIPermissionManager.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsIUUIDGenerator.h"
+#include "nsPIDOMWindow.h"
+#include "nsIURI.h"
+
+#include "nsContentUtils.h"
+#include "nsNetCID.h"
+#include "nsServiceManagerUtils.h"
+#include "nsThreadUtils.h"
+#include "nsXULAppAPI.h"
+
+#define ASSERT_PARENT_PROCESS()                                             \
+  AssertIsInMainProcess();                                                  \
+  if (NS_WARN_IF(!IsMainProcess())) {                                       \
+    return NS_ERROR_FAILURE;                                                \
+  }
+
+namespace mozilla {
+namespace dom {
+
+using namespace indexedDB;
+
+// This class contains all the information about a DataStore.
+class DataStoreInfo
+{
+public:
+  DataStoreInfo()
+    : mReadOnly(true)
+    , mEnabled(false)
+  {}
+
+  DataStoreInfo(const nsAString& aName,
+                const nsAString& aOriginURL,
+                const nsAString& aManifestURL,
+                bool aReadOnly,
+                bool aEnabled)
+  {
+    Init(aName, aOriginURL, aManifestURL, aReadOnly, aEnabled);
+  }
+
+  void Init(const nsAString& aName,
+            const nsAString& aOriginURL,
+            const nsAString& aManifestURL,
+            bool aReadOnly,
+            bool aEnabled)
+  {
+    mName = aName;
+    mOriginURL = aOriginURL;
+    mManifestURL = aManifestURL;
+    mReadOnly = aReadOnly;
+    mEnabled = aEnabled;
+  }
+
+  void Update(const nsAString& aName,
+              const nsAString& aOriginURL,
+              const nsAString& aManifestURL,
+              bool aReadOnly)
+  {
+    mName = aName;
+    mOriginURL = aOriginURL;
+    mManifestURL = aManifestURL;
+    mReadOnly = aReadOnly;
+  }
+
+  void Enable()
+  {
+    mEnabled = true;
+  }
+
+  nsString mName;
+  nsString mOriginURL;
+  nsString mManifestURL;
+  bool mReadOnly;
+
+  // A DataStore is enabled when it has its first revision.
+  bool mEnabled;
+};
+
+namespace {
+
+// Singleton for DataStoreService.
+StaticRefPtr<DataStoreService> gDataStoreService;
+static uint64_t gCounterID = 0;
+
+typedef nsClassHashtable<nsUint32HashKey, DataStoreInfo> HashApp;
+
+bool
+IsMainProcess()
+{
+  static const bool isMainProcess =
+    XRE_GetProcessType() == GeckoProcessType_Default;
+  return isMainProcess;
+}
+
+void
+AssertIsInMainProcess()
+{
+  MOZ_ASSERT(IsMainProcess());
+}
+
+void
+RejectPromise(nsPIDOMWindow* aWindow, Promise* aPromise, nsresult aRv)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(NS_FAILED(aRv));
+
+  nsRefPtr<DOMError> error;
+  if (aRv == NS_ERROR_DOM_SECURITY_ERR) {
+    error = new DOMError(aWindow, NS_LITERAL_STRING("SecurityError"),
+                         NS_LITERAL_STRING("Access denied"));
+  } else {
+    error = new DOMError(aWindow, NS_LITERAL_STRING("InternalError"),
+                         NS_LITERAL_STRING("An error occurred"));
+  }
+
+  aPromise->MaybeReject(error);
+}
+
+void
+DeleteDatabase(const nsAString& aName,
+               const nsAString& aManifestURL)
+{
+  AssertIsInMainProcess();
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsRefPtr<DataStoreDB> db = new DataStoreDB(aManifestURL, aName);
+  db->Delete();
+}
+
+PLDHashOperator
+DeleteDataStoresAppEnumerator(
+                             const uint32_t& aAppId,
+                             nsAutoPtr<DataStoreInfo>& aInfo,
+                             void* aUserData)
+{
+  AssertIsInMainProcess();
+  MOZ_ASSERT(NS_IsMainThread());
+
+  auto* appId = static_cast<uint32_t*>(aUserData);
+  if (*appId != aAppId) {
+    return PL_DHASH_NEXT;
+  }
+
+  DeleteDatabase(aInfo->mName, aInfo->mManifestURL);
+  return PL_DHASH_REMOVE;
+}
+
+PLDHashOperator
+DeleteDataStoresEnumerator(const nsAString& aName,
+                           nsAutoPtr<HashApp>& aApps,
+                           void* aUserData)
+{
+  AssertIsInMainProcess();
+  MOZ_ASSERT(NS_IsMainThread());
+
+  aApps->Enumerate(DeleteDataStoresAppEnumerator, aUserData);
+  return aApps->Count() ? PL_DHASH_NEXT : PL_DHASH_REMOVE;
+}
+
+void
+GeneratePermissionName(nsAString& aPermission,
+                       const nsAString& aName,
+                       const nsAString& aManifestURL)
+{
+  aPermission.AssignASCII("indexedDB-chrome-");
+  aPermission.Append(aName);
+  aPermission.AppendASCII("|");
+  aPermission.Append(aManifestURL);
+}
+
+nsresult
+ResetPermission(uint32_t aAppId, const nsAString& aOriginURL,
+                const nsAString& aManifestURL,
+                const nsAString& aPermission,
+                bool aReadOnly)
+{
+  AssertIsInMainProcess();
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsresult rv;
+  nsCOMPtr<nsIIOService> ioService(do_GetService(NS_IOSERVICE_CONTRACTID, &rv));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCOMPtr<nsIURI> uri;
+  rv = ioService->NewURI(NS_ConvertUTF16toUTF8(aOriginURL), nullptr, nullptr,
+                         getter_AddRefs(uri));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
+  if (!ssm) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsIPrincipal> principal;
+  rv = ssm->GetAppCodebasePrincipal(uri, aAppId, false,
+                                    getter_AddRefs(principal));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCOMPtr<nsIPermissionManager> pm =
+    do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
+  if (!pm) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCString basePermission;
+  basePermission.Append(NS_ConvertUTF16toUTF8(aPermission));
+
+  // Write permission
+  {
+    nsCString permission;
+    permission.Append(basePermission);
+    permission.AppendASCII("-write");
+
+    uint32_t perm = nsIPermissionManager::UNKNOWN_ACTION;
+    rv = pm->TestExactPermissionFromPrincipal(principal, permission.get(),
+                                              &perm);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    if (aReadOnly && perm == nsIPermissionManager::ALLOW_ACTION) {
+      rv = pm->RemoveFromPrincipal(principal, permission.get());
+    }
+    else if (!aReadOnly && perm != nsIPermissionManager::ALLOW_ACTION) {
+      rv = pm->AddFromPrincipal(principal, permission.get(),
+                                nsIPermissionManager::ALLOW_ACTION,
+                                nsIPermissionManager::EXPIRE_NEVER, 0);
+    }
+
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  // Read permission
+  {
+    nsCString permission;
+    permission.Append(basePermission);
+    permission.AppendASCII("-read");
+
+    uint32_t perm = nsIPermissionManager::UNKNOWN_ACTION;
+    rv = pm->TestExactPermissionFromPrincipal(principal, permission.get(),
+                                              &perm);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    if (perm != nsIPermissionManager::ALLOW_ACTION) {
+      rv = pm->AddFromPrincipal(principal, permission.get(),
+                                nsIPermissionManager::ALLOW_ACTION,
+                                nsIPermissionManager::EXPIRE_NEVER, 0);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+    }
+  }
+
+  // Generic permission
+  uint32_t perm = nsIPermissionManager::UNKNOWN_ACTION;
+  rv = pm->TestExactPermissionFromPrincipal(principal, basePermission.get(),
+                                            &perm);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (perm != nsIPermissionManager::ALLOW_ACTION) {
+    rv = pm->AddFromPrincipal(principal, basePermission.get(),
+                              nsIPermissionManager::ALLOW_ACTION,
+                              nsIPermissionManager::EXPIRE_NEVER, 0);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  return NS_OK;
+}
+
+class MOZ_STACK_CLASS GetDataStoreInfosData
+{
+public:
+  GetDataStoreInfosData(nsClassHashtable<nsStringHashKey, HashApp>& aAccessStores,
+                        const nsAString& aName, uint32_t aAppId,
+                        nsTArray<DataStoreInfo>& aStores)
+    : mAccessStores(aAccessStores)
+    , mName(aName)
+    , mAppId(aAppId)
+    , mStores(aStores)
+  {}
+
+  nsClassHashtable<nsStringHashKey, HashApp>& mAccessStores;
+  nsString mName;
+  uint32_t mAppId;
+  nsTArray<DataStoreInfo>& mStores;
+};
+
+PLDHashOperator
+GetDataStoreInfosEnumerator(const uint32_t& aAppId,
+                            DataStoreInfo* aInfo,
+                            void* aUserData)
+{
+  AssertIsInMainProcess();
+  MOZ_ASSERT(NS_IsMainThread());
+
+  auto* data = static_cast<GetDataStoreInfosData*>(aUserData);
+  if (aAppId == data->mAppId) {
+    return PL_DHASH_NEXT;
+  }
+
+  HashApp* apps;
+  if (!data->mAccessStores.Get(data->mName, &apps)) {
+    return PL_DHASH_NEXT;
+  }
+
+  DataStoreInfo* accessInfo = nullptr;
+  if (!apps->Get(data->mAppId, &accessInfo)) {
+    return PL_DHASH_NEXT;
+  }
+
+  bool readOnly = aInfo->mReadOnly || accessInfo->mReadOnly;
+  DataStoreInfo* accessStore = data->mStores.AppendElement();
+  accessStore->Init(aInfo->mName, aInfo->mOriginURL,
+                    aInfo->mManifestURL, readOnly,
+                    aInfo->mEnabled);
+
+  return PL_DHASH_NEXT;
+}
+
+// This class is useful to enumerate the add permissions for each app.
+class MOZ_STACK_CLASS AddPermissionsData
+{
+public:
+  AddPermissionsData(const nsAString& aPermission, bool aReadOnly)
+    : mPermission(aPermission)
+    , mReadOnly(aReadOnly)
+    , mResult(NS_OK)
+  {}
+
+  nsString mPermission;
+  bool mReadOnly;
+  nsresult mResult;
+};
+
+PLDHashOperator
+AddPermissionsEnumerator(const uint32_t& aAppId,
+                         DataStoreInfo* aInfo,
+                         void* userData)
+{
+  AssertIsInMainProcess();
+  MOZ_ASSERT(NS_IsMainThread());
+
+  auto* data = static_cast<AddPermissionsData*>(userData);
+
+  // ReadOnly is decided by the owner first.
+  bool readOnly = data->mReadOnly || aInfo->mReadOnly;
+
+  data->mResult = ResetPermission(aAppId, aInfo->mOriginURL,
+                                  aInfo->mManifestURL,
+                                  data->mPermission,
+                                  readOnly);
+  return NS_FAILED(data->mResult) ? PL_DHASH_STOP : PL_DHASH_NEXT;
+}
+
+// This class is useful to enumerate the add permissions for each app.
+class MOZ_STACK_CLASS AddAccessPermissionsData
+{
+public:
+  AddAccessPermissionsData(uint32_t aAppId, const nsAString& aName,
+                           const nsAString& aOriginURL, bool aReadOnly)
+    : mAppId(aAppId)
+    , mName(aName)
+    , mOriginURL(aOriginURL)
+    , mReadOnly(aReadOnly)
+    , mResult(NS_OK)
+  {}
+
+  uint32_t mAppId;
+  nsString mName;
+  nsString mOriginURL;
+  bool mReadOnly;
+  nsresult mResult;
+};
+
+PLDHashOperator
+AddAccessPermissionsEnumerator(const uint32_t& aAppId,
+                               DataStoreInfo* aInfo,
+                               void* userData)
+{
+  AssertIsInMainProcess();
+  MOZ_ASSERT(NS_IsMainThread());
+
+  auto* data = static_cast<AddAccessPermissionsData*>(userData);
+
+  nsString permission;
+  GeneratePermissionName(permission, data->mName, aInfo->mManifestURL);
+
+  // ReadOnly is decided by the owner first.
+  bool readOnly = aInfo->mReadOnly || data->mReadOnly;
+
+  data->mResult = ResetPermission(data->mAppId, data->mOriginURL,
+                                  aInfo->mManifestURL,
+                                  permission, readOnly);
+  return NS_FAILED(data->mResult) ? PL_DHASH_STOP : PL_DHASH_NEXT;
+}
+
+} /* anonymous namespace */
+
+// A PendingRequest is created when a content code wants a list of DataStores
+// but some of them are not enabled yet.
+class PendingRequest
+{
+public:
+  void Init(nsPIDOMWindow* aWindow, Promise* aPromise,
+            const nsTArray<DataStoreInfo>& aStores,
+            const nsTArray<nsString>& aPendingDataStores)
+  {
+    mWindow = aWindow;
+    mPromise = aPromise;
+    mStores = aStores;
+    mPendingDataStores = aPendingDataStores;
+  }
+
+  nsCOMPtr<nsPIDOMWindow> mWindow;
+  nsRefPtr<Promise> mPromise;
+  nsTArray<DataStoreInfo> mStores;
+
+  // This array contains the list of manifestURLs of the DataStores that are
+  // not enabled yet.
+  nsTArray<nsString> mPendingDataStores;
+};
+
+// This callback is used to enable a DataStore when its first revisionID is
+// created.
+class RevisionAddedEnableStoreCallback MOZ_FINAL :
+  public DataStoreRevisionCallback
+{
+public:
+  NS_INLINE_DECL_REFCOUNTING(RevisionAddedEnableStoreCallback);
+
+  RevisionAddedEnableStoreCallback(uint32_t aAppId,
+                                   const nsAString& aName,
+                                   const nsAString& aManifestURL)
+    : mAppId(aAppId)
+    , mName(aName)
+    , mManifestURL(aManifestURL)
+  {
+    AssertIsInMainProcess();
+    MOZ_ASSERT(NS_IsMainThread());
+  }
+
+  void
+  Run(const nsAString& aRevisionId)
+  {
+    AssertIsInMainProcess();
+    MOZ_ASSERT(NS_IsMainThread());
+
+    nsRefPtr<DataStoreService> service = DataStoreService::Get();
+    MOZ_ASSERT(service);
+
+    service->EnableDataStore(mAppId, mName, mManifestURL);
+  }
+
+private:
+  uint32_t mAppId;
+  nsString mName;
+  nsString mManifestURL;
+};
+
+// This DataStoreDBCallback is called when DataStoreDB opens the DataStore DB.
+// Then the first revision will be created if it doesn't exist yet.
+class FirstRevisionIdCallback MOZ_FINAL : public DataStoreDBCallback
+                                        , public nsIDOMEventListener
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  FirstRevisionIdCallback(uint32_t aAppId, const nsAString& aName,
+                          const nsAString& aManifestURL)
+    : mAppId(aAppId)
+    , mName(aName)
+    , mManifestURL(aManifestURL)
+  {
+    AssertIsInMainProcess();
+    MOZ_ASSERT(NS_IsMainThread());
+  }
+
+  void
+  Run(DataStoreDB* aDb, bool aSuccess)
+  {
+    AssertIsInMainProcess();
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(aDb);
+
+    if (!aSuccess) {
+      NS_WARNING("Failed to create the first revision.");
+      return;
+    }
+
+    mTxn = aDb->Transaction();
+
+    ErrorResult rv;
+    nsRefPtr<IDBObjectStore> store =
+      mTxn->ObjectStore(NS_LITERAL_STRING(DATASTOREDB_REVISION), rv);
+    if (NS_WARN_IF(rv.Failed())) {
+      return;
+    }
+
+    // a Null JSContext is ok because OpenCursor ignores it if the range is
+    // undefined.
+    mRequest = store->OpenCursor(nullptr, JS::UndefinedHandleValue,
+                                 IDBCursorDirection::Prev, rv);
+    if (NS_WARN_IF(rv.Failed())) {
+      return;
+    }
+
+    nsresult res;
+    res = mRequest->EventTarget::AddEventListener(NS_LITERAL_STRING("success"),
+                                                  this, false);
+    if (NS_WARN_IF(NS_FAILED(res))) {
+      return;
+    }
+  }
+
+  // nsIDOMEventListener
+  NS_IMETHOD
+  HandleEvent(nsIDOMEvent* aEvent)
+  {
+    AssertIsInMainProcess();
+    MOZ_ASSERT(NS_IsMainThread());
+
+    nsString type;
+    nsresult rv = aEvent->GetType(type);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    if (!type.EqualsASCII("success")) {
+      return NS_ERROR_FAILURE;
+    }
+
+    mRequest->RemoveEventListener(NS_LITERAL_STRING("success"), this, false);
+
+    // Note: this cx is only used for rooting and AddRevision, neither of which
+    // actually care which compartment we're in.
+    AutoSafeJSContext cx;
+
+    ErrorResult error;
+    JS::Rooted<JS::Value> result(cx, mRequest->GetResult(error));
+    if (NS_WARN_IF(error.Failed())) {
+      return error.ErrorCode();
+    }
+
+    // This means that the content is a IDBCursor, so the first revision already
+    // exists.
+    if (result.isObject()) {
+      nsRefPtr<DataStoreService> service = DataStoreService::Get();
+      MOZ_ASSERT(service);
+
+      return service->EnableDataStore(mAppId, mName, mManifestURL);
+    }
+
+    MOZ_ASSERT(mTxn);
+    nsRefPtr<IDBObjectStore> store =
+      mTxn->ObjectStore(NS_LITERAL_STRING(DATASTOREDB_REVISION), error);
+    if (NS_WARN_IF(error.Failed())) {
+      return error.ErrorCode();
+    }
+    MOZ_ASSERT(store);
+
+    nsRefPtr<RevisionAddedEnableStoreCallback> callback =
+      new RevisionAddedEnableStoreCallback(mAppId, mName, mManifestURL);
+
+    // If the revision doesn't exist, let's create it.
+    nsRefPtr<DataStoreRevision> mRevision = new DataStoreRevision();
+    return mRevision->AddRevision(cx, store, 0, DataStoreRevision::RevisionVoid,
+                                  callback);
+  }
+
+private:
+  nsRefPtr<IDBRequest> mRequest;
+
+  nsRefPtr<IDBTransaction> mTxn;
+  nsRefPtr<DataStoreRevision> mRevision;
+
+  uint32_t mAppId;
+  nsString mName;
+  nsString mManifestURL;
+};
+
+NS_IMPL_ISUPPORTS(FirstRevisionIdCallback, nsIDOMEventListener)
+
+// This class calls the 'retrieveRevisionId' method of the DataStore object for
+// any DataStore in the 'mResults' array. When all of them are called, the
+// promise is resolved with 'mResults'.
+// The reson why this has to be done is because DataStore are object that can be
+// created in any thread and in any process. The first revision has been
+// created, but they don't know its value yet.
+class RetrieveRevisionsCounter
+{
+public:
+  NS_INLINE_DECL_REFCOUNTING(RetrieveRevisionsCounter);
+
+  RetrieveRevisionsCounter(uint32_t aId, Promise* aPromise, uint32_t aCount)
+    : mPromise(aPromise)
+    , mId(aId)
+    , mCount(aCount)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+  }
+
+  void
+  AppendDataStore(JSContext* aCx, DataStore* aDataStore,
+                  nsIDataStore* aDataStoreIf)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    mResults.AppendElement(aDataStore);
+
+    // DataStore will run this callback when the revisionID is retrieved.
+    JSFunction* func = js::NewFunctionWithReserved(aCx, JSCallback,
+                                                   0 /* nargs */, 0 /* flags */,
+                                                   nullptr, nullptr);
+    if (!func) {
+      return;
+    }
+
+    JS::Rooted<JSObject*> obj(aCx, JS_GetFunctionObject(func));
+    if (!obj) {
+      return;
+    }
+
+    // We use the ID to know which counter is this. The service keeps all of
+    // these counters alive with their own IDs in an hashtable.
+    js::SetFunctionNativeReserved(obj, 0, JS::Int32Value(mId));
+
+    JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*obj));
+    nsresult rv = aDataStoreIf->RetrieveRevisionId(value);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return;
+    }
+  }
+
+private:
+  static bool
+  JSCallback(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    JS::CallArgs args = CallArgsFromVp(aArgc, aVp);
+
+    JS::Rooted<JS::Value> value(aCx,
+                                js::GetFunctionNativeReserved(&args.callee(), 0));
+    uint32_t id = value.toInt32();
+
+    nsRefPtr<DataStoreService> service = DataStoreService::Get();
+    MOZ_ASSERT(service);
+
+    nsRefPtr<RetrieveRevisionsCounter> counter = service->GetCounter(id);
+    MOZ_ASSERT(counter);
+
+    // When all the callbacks are called, we can resolve the promise and remove
+    // the counter from the service.
+    --counter->mCount;
+    if (!counter->mCount) {
+      service->RemoveCounter(id);
+      counter->mPromise->MaybeResolve(counter->mResults);
+    }
+
+    return true;
+  }
+
+  nsRefPtr<Promise> mPromise;
+  nsTArray<nsRefPtr<DataStore>> mResults;
+
+  uint32_t mId;
+  uint32_t mCount;
+};
+
+/* static */ already_AddRefed<DataStoreService>
+DataStoreService::GetOrCreate()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!gDataStoreService) {
+    nsRefPtr<DataStoreService> service = new DataStoreService();
+    if (NS_WARN_IF(NS_FAILED(service->Init()))) {
+      return nullptr;
+    }
+
+    gDataStoreService = service;
+  }
+
+  nsRefPtr<DataStoreService> service = gDataStoreService.get();
+  return service.forget();
+}
+
+/* static */ already_AddRefed<DataStoreService>
+DataStoreService::Get()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsRefPtr<DataStoreService> service = gDataStoreService.get();
+  return service.forget();
+}
+
+/* static */ void
+DataStoreService::Shutdown()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (gDataStoreService) {
+    if (IsMainProcess()) {
+      nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+      if (obs) {
+        obs->RemoveObserver(gDataStoreService, "webapps-clear-data");
+      }
+    }
+
+    gDataStoreService = nullptr;
+  }
+}
+
+NS_INTERFACE_MAP_BEGIN(DataStoreService)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDataStoreService)
+  NS_INTERFACE_MAP_ENTRY(nsIDataStoreService)
+  NS_INTERFACE_MAP_ENTRY(nsIObserver)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_ADDREF(DataStoreService)
+NS_IMPL_RELEASE(DataStoreService)
+
+DataStoreService::DataStoreService()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+}
+
+DataStoreService::~DataStoreService()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+}
+
+nsresult
+DataStoreService::Init()
+{
+  if (!IsMainProcess()) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+  if (!obs) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsresult rv = obs->AddObserver(this, "webapps-clear-data", false);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DataStoreService::InstallDataStore(uint32_t aAppId,
+                                   const nsAString& aName,
+                                   const nsAString& aOriginURL,
+                                   const nsAString& aManifestURL,
+                                   bool aReadOnly)
+{
+  ASSERT_PARENT_PROCESS()
+  MOZ_ASSERT(NS_IsMainThread());
+
+  HashApp* apps = nullptr;
+  if (!mStores.Get(aName, &apps)) {
+    apps = new HashApp();
+    mStores.Put(aName, apps);
+  }
+
+  DataStoreInfo* info = nullptr;
+  if (!apps->Get(aAppId, &info)) {
+    info = new DataStoreInfo(aName, aOriginURL, aManifestURL, aReadOnly, false);
+    apps->Put(aAppId, info);
+  } else {
+    info->Update(aName, aOriginURL, aManifestURL, aReadOnly);
+  }
+
+  nsresult rv = AddPermissions(aAppId, aName, aOriginURL, aManifestURL,
+                               aReadOnly);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // Immediately create the first revision.
+  return CreateFirstRevisionId(aAppId, aName, aManifestURL);
+}
+
+NS_IMETHODIMP
+DataStoreService::InstallAccessDataStore(uint32_t aAppId,
+                                         const nsAString& aName,
+                                         const nsAString& aOriginURL,
+                                         const nsAString& aManifestURL,
+                                         bool aReadOnly)
+{
+  ASSERT_PARENT_PROCESS()
+  MOZ_ASSERT(NS_IsMainThread());
+
+  HashApp* apps = nullptr;
+  if (!mAccessStores.Get(aName, &apps)) {
+    apps = new HashApp();
+    mAccessStores.Put(aName, apps);
+  }
+
+  DataStoreInfo* info = nullptr;
+  if (!apps->Get(aAppId, &info)) {
+    info = new DataStoreInfo(aName, aOriginURL, aManifestURL, aReadOnly, false);
+    apps->Put(aAppId, info);
+  } else {
+    info->Update(aName, aOriginURL, aManifestURL, aReadOnly);
+  }
+
+  return AddAccessPermissions(aAppId, aName, aOriginURL, aManifestURL,
+                              aReadOnly);
+}
+
+NS_IMETHODIMP
+DataStoreService::GetDataStores(nsIDOMWindow* aWindow,
+                                const nsAString& aName,
+                                nsISupports** aDataStores)
+{
+  // FIXME This will be a thread-safe method.
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
+  if (!window) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(window);
+  nsRefPtr<Promise> promise = new Promise(global);
+
+  nsCOMPtr<nsIDocument> document = window->GetDoc();
+  MOZ_ASSERT(document);
+
+  nsCOMPtr<nsIPrincipal> principal = document->NodePrincipal();
+  MOZ_ASSERT(principal);
+
+  nsTArray<DataStoreInfo> stores;
+
+  // If this request comes from the main process, we have access to the
+  // window, so we can skip the ipc communication.
+  if (IsMainProcess()) {
+    uint32_t appId;
+    nsresult rv = principal->GetAppId(&appId);
+    if (NS_FAILED(rv)) {
+      RejectPromise(window, promise, rv);
+      promise.forget(aDataStores);
+      return NS_OK;
+    }
+
+    rv = GetDataStoreInfos(aName, appId, stores);
+    if (NS_FAILED(rv)) {
+      RejectPromise(window, promise, rv);
+      promise.forget(aDataStores);
+      return NS_OK;
+    }
+  }
+
+  else {
+    // This method can be called in the child so we need to send a request
+    // to the parent and create DataStore object here.
+    ContentChild* contentChild = ContentChild::GetSingleton();
+
+    nsTArray<DataStoreSetting> array;
+    if (!contentChild->SendDataStoreGetStores(nsAutoString(aName),
+                                              IPC::Principal(principal),
+                                              &array)) {
+      RejectPromise(window, promise, NS_ERROR_FAILURE);
+      promise.forget(aDataStores);
+      return NS_OK;
+    }
+
+    for (uint32_t i = 0; i < array.Length(); ++i) {
+      DataStoreInfo* info = stores.AppendElement();
+      info->Init(array[i].name(), array[i].originURL(),
+                 array[i].manifestURL(), array[i].readOnly(),
+                 array[i].enabled());
+    }
+  }
+
+  GetDataStoresCreate(window, promise, stores);
+  promise.forget(aDataStores);
+  return NS_OK;
+}
+
+void
+DataStoreService::GetDataStoresCreate(nsPIDOMWindow* aWindow, Promise* aPromise,
+                                      const nsTArray<DataStoreInfo>& aStores)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!aStores.Length()) {
+    GetDataStoresResolve(aWindow, aPromise, aStores);
+    return;
+  }
+
+  nsTArray<nsString> pendingDataStores;
+  for (uint32_t i = 0; i < aStores.Length(); ++i) {
+    if (!aStores[i].mEnabled) {
+      pendingDataStores.AppendElement(aStores[i].mManifestURL);
+    }
+  }
+
+  if (!pendingDataStores.Length()) {
+    GetDataStoresResolve(aWindow, aPromise, aStores);
+    return;
+  }
+
+  PendingRequests* requests;
+  if (!mPendingRequests.Get(aStores[0].mName, &requests)) {
+    requests = new PendingRequests();
+    mPendingRequests.Put(aStores[0].mName, requests);
+  }
+
+  PendingRequest* request = requests->AppendElement();
+  request->Init(aWindow, aPromise, aStores, pendingDataStores);
+}
+
+void
+DataStoreService::GetDataStoresResolve(nsPIDOMWindow* aWindow,
+                                       Promise* aPromise,
+                                       const nsTArray<DataStoreInfo>& aStores)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!aStores.Length()) {
+    nsTArray<nsRefPtr<DataStore>> results;
+    aPromise->MaybeResolve(results);
+    return;
+  }
+
+  AutoSafeJSContext cx;
+
+  // The counter will finish this task once all the DataStores will know their
+  // first revision Ids.
+  nsRefPtr<RetrieveRevisionsCounter> counter =
+    new RetrieveRevisionsCounter(++gCounterID, aPromise, aStores.Length());
+  mPendingCounters.Put(gCounterID, counter);
+
+  for (uint32_t i = 0; i < aStores.Length(); ++i) {
+    nsCOMPtr<nsIDataStore> dataStore =
+      do_CreateInstance("@mozilla.org/dom/datastore;1");
+    if (NS_WARN_IF(!dataStore)) {
+      return;
+    }
+
+    nsresult rv = dataStore->Init(aWindow, aStores[i].mName,
+                                  aStores[i].mManifestURL,
+                                  aStores[i].mReadOnly);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return;
+    }
+
+    nsCOMPtr<nsIXPConnectWrappedJS> xpcwrappedjs = do_QueryInterface(dataStore);
+    if (NS_WARN_IF(!xpcwrappedjs)) {
+      return;
+    }
+
+    JS::Rooted<JSObject*> dataStoreJS(cx, xpcwrappedjs->GetJSObject());
+    if (NS_WARN_IF(!dataStoreJS)) {
+      return;
+    }
+
+    JSAutoCompartment ac(cx, dataStoreJS);
+    nsRefPtr<DataStoreImpl> dataStoreObj = new DataStoreImpl(dataStoreJS,
+                                                             aWindow);
+
+    nsRefPtr<DataStore> exposedStore = new DataStore(aWindow);
+
+    ErrorResult error;
+    exposedStore->SetDataStoreImpl(*dataStoreObj, error);
+    if (error.Failed()) {
+      return;
+    }
+
+    JS::Rooted<JSObject*> obj(cx, exposedStore->WrapObject(cx));
+    MOZ_ASSERT(obj);
+
+    JS::Rooted<JS::Value> exposedObject(cx, JS::ObjectValue(*obj));
+    dataStore->SetExposedObject(exposedObject);
+
+    counter->AppendDataStore(cx, exposedStore, dataStore);
+  }
+}
+
+// Thie method populates 'aStores' with the list of DataStores with 'aName' as
+// name and available for this 'aAppId'.
+nsresult
+DataStoreService::GetDataStoreInfos(const nsAString& aName,
+                                    uint32_t aAppId,
+                                    nsTArray<DataStoreInfo>& aStores)
+{
+  AssertIsInMainProcess();
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsCOMPtr<nsIAppsService> appsService =
+    do_GetService("@mozilla.org/AppsService;1");
+  if (NS_WARN_IF(!appsService)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<mozIApplication> app;
+  nsresult rv = appsService->GetAppByLocalId(aAppId, getter_AddRefs(app));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (!app) {
+    return NS_ERROR_DOM_SECURITY_ERR;
+  }
+
+  uint16_t status;
+  rv = app->GetAppStatus(&status);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (status != nsIPrincipal::APP_STATUS_CERTIFIED &&
+      !Preferences::GetBool("dom.testing.datastore_enabled_for_hosted_apps",
+                            false)) {
+    return NS_ERROR_DOM_SECURITY_ERR;
+  }
+
+  aStores.Clear();
+
+  HashApp* apps = nullptr;
+  if (!mStores.Get(aName, &apps)) {
+    return NS_OK;
+  }
+
+  DataStoreInfo* info = nullptr;
+  if (apps->Get(aAppId, &info)) {
+    DataStoreInfo* owned = aStores.AppendElement();
+    owned->Init(info->mName, info->mOriginURL, info->mManifestURL, false,
+                info->mEnabled);
+  }
+
+  GetDataStoreInfosData data(mAccessStores, aName, aAppId, aStores);
+  apps->EnumerateRead(GetDataStoreInfosEnumerator, &data);
+  return NS_OK;
+}
+
+// This method is called when an app with DataStores is deleted.
+void
+DataStoreService::DeleteDataStores(uint32_t aAppId)
+{
+  AssertIsInMainProcess();
+  MOZ_ASSERT(NS_IsMainThread());
+
+  mStores.Enumerate(DeleteDataStoresEnumerator, &aAppId);
+  mAccessStores.Enumerate(DeleteDataStoresEnumerator, &aAppId);
+}
+
+NS_IMETHODIMP
+DataStoreService::Observe(nsISupports* aSubject,
+                          const char* aTopic,
+                          const char16_t* aData)
+{
+  AssertIsInMainProcess();
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (strcmp(aTopic, "webapps-clear-data")) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<mozIApplicationClearPrivateDataParams> params =
+    do_QueryInterface(aSubject);
+  MOZ_ASSERT(params);
+
+  // DataStore is explosed to apps, not browser content.
+  bool browserOnly;
+  nsresult rv = params->GetBrowserOnly(&browserOnly);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (browserOnly) {
+    return NS_OK;
+  }
+
+  uint32_t appId;
+  rv = params->GetAppId(&appId);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  DeleteDataStores(appId);
+
+  return NS_OK;
+}
+
+nsresult
+DataStoreService::AddPermissions(uint32_t aAppId,
+                                 const nsAString& aName,
+                                 const nsAString& aOriginURL,
+                                 const nsAString& aManifestURL,
+                                 bool aReadOnly)
+{
+  AssertIsInMainProcess();
+  MOZ_ASSERT(NS_IsMainThread());
+
+  // This is the permission name.
+  nsString permission;
+  GeneratePermissionName(permission, aName, aManifestURL);
+
+  // When a new DataStore is installed, the permissions must be set for the
+  // owner app.
+  nsresult rv = ResetPermission(aAppId, aOriginURL, aManifestURL, permission,
+                                aReadOnly);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // For any app that wants to have access to this DataStore we add the
+  // permissions.
+  HashApp* apps;
+  if (!mAccessStores.Get(aName, &apps)) {
+    return NS_OK;
+  }
+
+  AddPermissionsData data(permission, aReadOnly);
+  apps->EnumerateRead(AddPermissionsEnumerator, &data);
+  return data.mResult;
+}
+
+nsresult
+DataStoreService::AddAccessPermissions(uint32_t aAppId, const nsAString& aName,
+                                       const nsAString& aOriginURL,
+                                       const nsAString& aManifestURL,
+                                       bool aReadOnly)
+{
+  AssertIsInMainProcess();
+  MOZ_ASSERT(NS_IsMainThread());
+
+  // When an app wants to have access to a DataStore, the permissions must be
+  // set.
+  HashApp* apps = nullptr;
+  if (!mStores.Get(aName, &apps)) {
+    return NS_OK;
+  }
+
+  AddAccessPermissionsData data(aAppId, aName, aOriginURL, aReadOnly);
+  apps->EnumerateRead(AddAccessPermissionsEnumerator, &data);
+  return data.mResult;
+}
+
+// This method starts the operation to create the first revision for a DataStore
+// if needed.
+nsresult
+DataStoreService::CreateFirstRevisionId(uint32_t aAppId,
+                                        const nsAString& aName,
+                                        const nsAString& aManifestURL)
+{
+  AssertIsInMainProcess();
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsRefPtr<DataStoreDB> db = new DataStoreDB(aManifestURL, aName);
+
+  nsRefPtr<FirstRevisionIdCallback> callback =
+    new FirstRevisionIdCallback(aAppId, aName, aManifestURL);
+
+  Sequence<nsString> dbs;
+  dbs.AppendElement(NS_LITERAL_STRING(DATASTOREDB_REVISION));
+
+  return db->Open(IDBTransactionMode::Readwrite, dbs, callback);
+}
+
+nsresult
+DataStoreService::EnableDataStore(uint32_t aAppId, const nsAString& aName,
+                                  const nsAString& aManifestURL)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  {
+    HashApp* apps = nullptr;
+    DataStoreInfo* info = nullptr;
+    if (mStores.Get(aName, &apps) && apps->Get(aAppId, &info)) {
+      info->Enable();
+    }
+  }
+
+  // Notify the child processes.
+  if (IsMainProcess()) {
+    nsTArray<ContentParent*> children;
+    ContentParent::GetAll(children);
+    for (uint32_t i = 0; i < children.Length(); i++) {
+      if (children[i]->NeedsDataStoreInfos()) {
+        unused << children[i]->SendDataStoreNotify(aAppId, nsAutoString(aName),
+                                                   nsAutoString(aManifestURL));
+      }
+    }
+  }
+
+  // Maybe we have some pending request waiting for this DataStore.
+  PendingRequests* requests;
+  if (!mPendingRequests.Get(aName, &requests)) {
+    return NS_OK;
+  }
+
+  for (uint32_t i = 0; i < requests->Length();) {
+    PendingRequest& request = requests->ElementAt(i);
+    nsTArray<nsString>::index_type pos =
+      request.mPendingDataStores.IndexOf(aManifestURL);
+    if (pos != request.mPendingDataStores.NoIndex) {
+      request.mPendingDataStores.RemoveElementAt(pos);
+
+      // No other pending dataStores.
+      if (request.mPendingDataStores.IsEmpty()) {
+        GetDataStoresResolve(request.mWindow, request.mPromise,
+                             request.mStores);
+        requests->RemoveElementAt(i);
+        continue;
+      }
+    }
+
+    ++i;
+  }
+
+  // No other pending requests for this name.
+  if (requests->IsEmpty()) {
+    mPendingRequests.Remove(aName);
+  }
+
+  return NS_OK;
+}
+
+already_AddRefed<RetrieveRevisionsCounter>
+DataStoreService::GetCounter(uint32_t aId) const
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsRefPtr<RetrieveRevisionsCounter> counter;
+  return mPendingCounters.Get(aId, getter_AddRefs(counter))
+           ? counter.forget() : nullptr;
+}
+
+void
+DataStoreService::RemoveCounter(uint32_t aId)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  mPendingCounters.Remove(aId);
+}
+
+nsresult
+DataStoreService::GetDataStoresFromIPC(const nsAString& aName,
+                                       nsIPrincipal* aPrincipal,
+                                       nsTArray<DataStoreSetting>* aValue)
+{
+  MOZ_ASSERT(IsMainProcess());
+  MOZ_ASSERT(NS_IsMainThread());
+
+  uint32_t appId;
+  nsresult rv = aPrincipal->GetAppId(&appId);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsTArray<DataStoreInfo> stores;
+  rv = GetDataStoreInfos(aName, appId, stores);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  for (uint32_t i = 0; i < stores.Length(); ++i) {
+    DataStoreSetting* data = aValue->AppendElement();
+    data->name() = stores[i].mName;
+    data->originURL() = stores[i].mOriginURL;
+    data->manifestURL() = stores[i].mManifestURL;
+    data->readOnly() = stores[i].mReadOnly;
+    data->enabled() = stores[i].mEnabled;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+DataStoreService::GenerateUUID(nsAString& aID)
+{
+  nsresult rv;
+
+  if (!mUUIDGenerator) {
+    mUUIDGenerator = do_GetService("@mozilla.org/uuid-generator;1", &rv);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  nsID id;
+  rv = mUUIDGenerator->GenerateUUIDInPlace(&id);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  char chars[NSID_LENGTH];
+  id.ToProvidedString(chars);
+  CopyASCIItoUTF16(chars, aID);
+
+  return NS_OK;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/datastore/DataStoreService.h
@@ -0,0 +1,110 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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_DataStoreService_h
+#define mozilla_dom_DataStoreService_h
+
+#include "mozilla/dom/PContent.h"
+#include "nsClassHashtable.h"
+#include "nsIDataStoreService.h"
+#include "nsIObserver.h"
+#include "nsRefPtrHashtable.h"
+
+class nsIPrincipal;
+class nsIUUIDGenerator;
+class nsPIDOMWindow;
+
+namespace mozilla {
+namespace dom {
+
+class DataStoreInfo;
+class FirstRevisionIdCallback;
+class PendingRequest;
+class Promise;
+class RetrieveRevisionsCounter;
+class RevisionAddedEnableStoreCallback;
+
+class DataStoreService MOZ_FINAL : public nsIDataStoreService
+                                 , public nsIObserver
+{
+  friend class ContentChild;
+  friend class FirstRevisionIdCallback;
+  friend class RetrieveRevisionsCounter;
+  friend class RevisionAddedEnableStoreCallback;
+
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+  NS_DECL_NSIDATASTORESERVICE
+
+  // Returns the DataStoreService singleton. Only to be called from main
+  // thread.
+  static already_AddRefed<DataStoreService> GetOrCreate();
+
+  static already_AddRefed<DataStoreService> Get();
+
+  static void Shutdown();
+
+  nsresult GenerateUUID(nsAString& aID);
+
+  nsresult GetDataStoresFromIPC(const nsAString& aName,
+                                nsIPrincipal* aPrincipal,
+                                nsTArray<DataStoreSetting>* aValue);
+
+private:
+  DataStoreService();
+  ~DataStoreService();
+
+  nsresult Init();
+
+  typedef nsClassHashtable<nsUint32HashKey, DataStoreInfo> HashApp;
+
+  nsresult AddPermissions(uint32_t aAppId, const nsAString& aName,
+                          const nsAString& aOriginURL,
+                          const nsAString& aManifestURL,
+                          bool aReadOnly);
+
+  nsresult AddAccessPermissions(uint32_t aAppId, const nsAString& aName,
+                                const nsAString& aOriginURL,
+                                const nsAString& aManifestURL,
+                                bool aReadOnly);
+
+  nsresult CreateFirstRevisionId(uint32_t aAppId, const nsAString& aName,
+                                 const nsAString& aManifestURL);
+
+  void GetDataStoresCreate(nsPIDOMWindow* aWindow, Promise* aPromise,
+                           const nsTArray<DataStoreInfo>& aStores);
+
+  void GetDataStoresResolve(nsPIDOMWindow* aWindow, Promise* aPromise,
+                            const nsTArray<DataStoreInfo>& aStores);
+
+  nsresult GetDataStoreInfos(const nsAString& aName, uint32_t aAppId,
+                             nsTArray<DataStoreInfo>& aStores);
+
+  void DeleteDataStores(uint32_t aAppId);
+
+  nsresult EnableDataStore(uint32_t aAppId, const nsAString& aName,
+                           const nsAString& aManifestURL);
+
+  already_AddRefed<RetrieveRevisionsCounter> GetCounter(uint32_t aId) const;
+
+  void RemoveCounter(uint32_t aId);
+
+  nsClassHashtable<nsStringHashKey, HashApp> mStores;
+  nsClassHashtable<nsStringHashKey, HashApp> mAccessStores;
+
+  typedef nsTArray<PendingRequest> PendingRequests;
+  nsClassHashtable<nsStringHashKey, PendingRequests> mPendingRequests;
+
+  nsRefPtrHashtable<nsUint32HashKey, RetrieveRevisionsCounter> mPendingCounters;
+
+  nsCOMPtr<nsIUUIDGenerator> mUUIDGenerator;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_DataStoreService_h
deleted file mode 100644
--- a/dom/datastore/DataStoreService.js
+++ /dev/null
@@ -1,522 +0,0 @@
-/* -*- Mode: js2; js2-basic-offset: 2; indent-tabs-mode: nil; -*- */
-/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
-/* 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'
-
-/* static functions */
-
-function debug(s) {
-  //dump('DEBUG DataStoreService: ' + s + '\n');
-}
-
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-
-Cu.import('resource://gre/modules/XPCOMUtils.jsm');
-Cu.import('resource://gre/modules/Services.jsm');
-Cu.import('resource://gre/modules/DataStoreImpl.jsm');
-Cu.import("resource://gre/modules/DataStoreDB.jsm");
-Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
-
-XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
-                                   "@mozilla.org/childprocessmessagemanager;1",
-                                   "nsIMessageSender");
-
-XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
-                                   "@mozilla.org/parentprocessmessagemanager;1",
-                                   "nsIMessageBroadcaster");
-
-XPCOMUtils.defineLazyServiceGetter(this, "permissionManager",
-                                   "@mozilla.org/permissionmanager;1",
-                                   "nsIPermissionManager");
-
-XPCOMUtils.defineLazyServiceGetter(this, "secMan",
-                                   "@mozilla.org/scriptsecuritymanager;1",
-                                   "nsIScriptSecurityManager");
-
-/* DataStoreService */
-
-const DATASTORESERVICE_CID = Components.ID('{d193d0e2-c677-4a7b-bb0a-19155b470f2e}');
-const REVISION_VOID = "void";
-
-function DataStoreService() {
-  debug('DataStoreService Constructor');
-
-  this.inParent = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime)
-                    .processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
-
-  if (this.inParent) {
-    let obs = Services.obs;
-    if (!obs) {
-      debug("DataStore Error: observer-service is null!");
-      return;
-    }
-
-    obs.addObserver(this, 'webapps-clear-data', false);
-  }
-
-  let self = this;
-  cpmm.addMessageListener("datastore-first-revision-created",
-                          function(aMsg) { self.receiveMessage(aMsg); });
-}
-
-DataStoreService.prototype = {
-  inParent: false,
-
-  // Hash of DataStores
-  stores: {},
-  accessStores: {},
-  pendingRequests: {},
-
-  installDataStore: function(aAppId, aName, aOrigin, aOwner, aReadOnly) {
-    debug('installDataStore - appId: ' + aAppId + ', aName: ' +
-          aName + ', aOrigin: ' + aOrigin + ', aOwner:' + aOwner +
-          ', aReadOnly: ' + aReadOnly);
-
-    this.checkIfInParent();
-
-    if (aName in this.stores && aAppId in this.stores[aName]) {
-      debug('This should not happen');
-      return;
-    }
-
-    if (!(aName in this.stores)) {
-      this.stores[aName] = {};
-    }
-
-    // A DataStore is enabled when it has a first valid revision.
-    this.stores[aName][aAppId] = { origin: aOrigin, owner: aOwner,
-                                   readOnly: aReadOnly, enabled: false };
-
-    this.addPermissions(aAppId, aName, aOrigin, aOwner, aReadOnly);
-
-    this.createFirstRevisionId(aAppId, aName, aOwner);
-  },
-
-  installAccessDataStore: function(aAppId, aName, aOrigin, aOwner, aReadOnly) {
-    debug('installAccessDataStore - appId: ' + aAppId + ', aName: ' +
-          aName + ', aOrigin: ' + aOrigin + ', aOwner:' + aOwner +
-          ', aReadOnly: ' + aReadOnly);
-
-    this.checkIfInParent();
-
-    if (aName in this.accessStores && aAppId in this.accessStores[aName]) {
-      debug('This should not happen');
-      return;
-    }
-
-    if (!(aName in this.accessStores)) {
-      this.accessStores[aName] = {};
-    }
-
-    this.accessStores[aName][aAppId] = { origin: aOrigin, owner: aOwner,
-                                         readOnly: aReadOnly };
-    this.addAccessPermissions(aAppId, aName, aOrigin, aOwner, aReadOnly);
-  },
-
-  checkIfInParent: function() {
-    if (!this.inParent) {
-      throw "DataStore can execute this operation just in the parent process";
-    }
-  },
-
-  createFirstRevisionId: function(aAppId, aName, aOwner) {
-    debug("createFirstRevisionId database: " + aName);
-
-    let self = this;
-    let db = new DataStoreDB();
-    db.init(aOwner, aName);
-    db.revisionTxn(
-      'readwrite',
-      function(aTxn, aRevisionStore) {
-        debug("createFirstRevisionId - transaction success");
-
-        let request = aRevisionStore.openCursor(null, 'prev');
-        request.onsuccess = function(aEvent) {
-          let cursor = aEvent.target.result;
-          if (cursor) {
-            debug("First revision already created.");
-            self.enableDataStore(aAppId, aName, aOwner);
-          } else {
-            // If the revision doesn't exist, let's create the first one.
-            db.addRevision(aRevisionStore, 0, REVISION_VOID, function() {
-              debug("First revision created.");
-              self.enableDataStore(aAppId, aName, aOwner);
-            });
-          }
-        };
-      }
-    );
-  },
-
-  enableDataStore: function(aAppId, aName, aOwner) {
-    if (aName in this.stores && aAppId in this.stores[aName]) {
-      this.stores[aName][aAppId].enabled = true;
-      ppmm.broadcastAsyncMessage('datastore-first-revision-created',
-                                 { name: aName, owner: aOwner });
-    }
-  },
-
-  addPermissions: function(aAppId, aName, aOrigin, aOwner, aReadOnly) {
-    // When a new DataStore is installed, the permissions must be set for the
-    // owner app.
-    let permission = "indexedDB-chrome-" + aName + '|' + aOwner;
-    this.resetPermissions(aAppId, aOrigin, aOwner, permission, aReadOnly);
-
-    // For any app that wants to have access to this DataStore we add the
-    // permissions.
-    if (aName in this.accessStores) {
-      for (let appId in this.accessStores[aName]) {
-        // ReadOnly is decided by the owner first.
-        let readOnly = aReadOnly || this.accessStores[aName][appId].readOnly;
-        this.resetPermissions(appId, this.accessStores[aName][appId].origin,
-                              this.accessStores[aName][appId].owner,
-                              permission, readOnly);
-      }
-    }
-  },
-
-  addAccessPermissions: function(aAppId, aName, aOrigin, aOwner, aReadOnly) {
-    // When an app wants to have access to a DataStore, the permissions must be
-    // set.
-    if (!(aName in this.stores)) {
-      return;
-    }
-
-    for (let appId in this.stores[aName]) {
-      let permission = "indexedDB-chrome-" + aName + '|' + this.stores[aName][appId].owner;
-      // The ReadOnly is decied by the owenr first.
-      let readOnly = this.stores[aName][appId].readOnly || aReadOnly;
-      this.resetPermissions(aAppId, aOrigin, aOwner, permission, readOnly);
-    }
-  },
-
-  resetPermissions: function(aAppId, aOrigin, aOwner, aPermission, aReadOnly) {
-    debug("ResetPermissions - appId: " + aAppId + " - origin: " + aOrigin +
-          " - owner: " + aOwner + " - permissions: " + aPermission +
-          " - readOnly: " + aReadOnly);
-
-    let uri = Services.io.newURI(aOrigin, null, null);
-    let principal = secMan.getAppCodebasePrincipal(uri, aAppId, false);
-
-    let result = permissionManager.testExactPermissionFromPrincipal(principal,
-                                                                    aPermission + '-write');
-
-    if (aReadOnly && result == Ci.nsIPermissionManager.ALLOW_ACTION) {
-      debug("Write permission removed");
-      permissionManager.removeFromPrincipal(principal, aPermission + '-write');
-    } else if (!aReadOnly && result != Ci.nsIPermissionManager.ALLOW_ACTION) {
-      debug("Write permission added");
-      permissionManager.addFromPrincipal(principal, aPermission + '-write',
-                                         Ci.nsIPermissionManager.ALLOW_ACTION);
-    }
-
-    result = permissionManager.testExactPermissionFromPrincipal(principal,
-                                                                aPermission + '-read');
-    if (result != Ci.nsIPermissionManager.ALLOW_ACTION) {
-      debug("Read permission added");
-      permissionManager.addFromPrincipal(principal, aPermission + '-read',
-                                         Ci.nsIPermissionManager.ALLOW_ACTION);
-    }
-
-    result = permissionManager.testExactPermissionFromPrincipal(principal, aPermission);
-    if (result != Ci.nsIPermissionManager.ALLOW_ACTION) {
-      debug("Generic permission added");
-      permissionManager.addFromPrincipal(principal, aPermission,
-                                         Ci.nsIPermissionManager.ALLOW_ACTION);
-    }
-  },
-
-  getDataStores: function(aWindow, aName) {
-    debug('getDataStores - aName: ' + aName);
-
-    let self = this;
-    return new aWindow.Promise(function(resolve, reject) {
-      // If this request comes from the main process, we have access to the
-      // window, so we can skip the ipc communication.
-      if (self.inParent) {
-        let stores = self.getDataStoresInfo(aName, aWindow.document.nodePrincipal.appId);
-        if (stores === null) {
-          reject(new aWindow.DOMError("SecurityError", "Access denied"));
-          return;
-        }
-        self.getDataStoreCreate(aWindow, resolve, stores);
-      } else {
-        // This method can be called in the child so we need to send a request
-        // to the parent and create DataStore object here.
-        new DataStoreServiceChild(aWindow, aName, function(aStores) {
-          debug("DataStoreServiceChild success callback!");
-          self.getDataStoreCreate(aWindow, resolve, aStores);
-        }, function() {
-          debug("DataStoreServiceChild error callback!");
-          reject(new aWindow.DOMError("SecurityError", "Access denied"));
-        });
-      }
-    });
-  },
-
-  getDataStoresInfo: function(aName, aAppId) {
-    debug('GetDataStoresInfo');
-
-    let appsService = Cc["@mozilla.org/AppsService;1"]
-                        .getService(Ci.nsIAppsService);
-    let app = appsService.getAppByLocalId(aAppId);
-    if (!app) {
-      return null;
-    }
-
-    let prefName = "dom.testing.datastore_enabled_for_hosted_apps";
-    if (app.appStatus != Ci.nsIPrincipal.APP_STATUS_CERTIFIED &&
-        (Services.prefs.getPrefType(prefName) == Services.prefs.PREF_INVALID ||
-          !Services.prefs.getBoolPref(prefName))) {
-      return null;
-    }
-
-    let results = [];
-
-    if (aName in this.stores) {
-      if (aAppId in this.stores[aName]) {
-        results.push({ name: aName,
-                       owner: this.stores[aName][aAppId].owner,
-                       readOnly: false,
-                       enabled: this.stores[aName][aAppId].enabled });
-      }
-
-      for (var i in this.stores[aName]) {
-        if (i == aAppId) {
-          continue;
-        }
-
-        let access = this.getDataStoreAccess(aName, aAppId);
-        if (!access) {
-          continue;
-        }
-
-        let readOnly = this.stores[aName][i].readOnly || access.readOnly;
-        results.push({ name: aName,
-                       owner: this.stores[aName][i].owner,
-                       readOnly: readOnly,
-                       enabled: this.stores[aName][i].enabled });
-      }
-    }
-
-    return results;
-  },
-
-  getDataStoreCreate: function(aWindow, aResolve, aStores) {
-    debug("GetDataStoreCreate");
-
-    let results = new aWindow.Array();
-
-    if (!aStores.length) {
-      aResolve(results);
-      return;
-    }
-
-    let pendingDataStores = [];
-
-    for (let i = 0; i < aStores.length; ++i) {
-      if (!aStores[i].enabled) {
-        pendingDataStores.push(aStores[i].owner);
-      }
-    }
-
-    if (!pendingDataStores.length) {
-      this.getDataStoreResolve(aWindow, aResolve, aStores);
-      return;
-    }
-
-    if (!(aStores[0].name in this.pendingRequests)) {
-      this.pendingRequests[aStores[0].name] = [];
-    }
-
-    this.pendingRequests[aStores[0].name].push({ window: aWindow,
-                                                 resolve: aResolve,
-                                                 stores: aStores,
-                                                 pendingDataStores: pendingDataStores });
-  },
-
-  getDataStoreResolve: function(aWindow, aResolve, aStores) {
-    debug("GetDataStoreResolve");
-
-    let callbackPending = aStores.length;
-    let results = new aWindow.Array();
-
-    if (!callbackPending) {
-      aResolve(results);
-      return;
-    }
-
-    for (let i = 0; i < aStores.length; ++i) {
-      let obj = new DataStore(aWindow, aStores[i].name,
-                              aStores[i].owner, aStores[i].readOnly);
-
-      let storeImpl = aWindow.DataStoreImpl._create(aWindow, obj);
-
-      let exposedStore = new aWindow.DataStore();
-      exposedStore.setDataStoreImpl(storeImpl);
-
-      obj.exposedObject = exposedStore;
-
-      results.push(exposedStore);
-
-      obj.retrieveRevisionId(
-        function() {
-          --callbackPending;
-          if (!callbackPending) {
-            aResolve(results);
-          }
-        }
-      );
-    }
-  },
-
-  getDataStoreAccess: function(aName, aAppId) {
-    if (!(aName in this.accessStores) ||
-        !(aAppId in this.accessStores[aName])) {
-      return null;
-    }
-
-    return this.accessStores[aName][aAppId];
-  },
-
-  observe: function observe(aSubject, aTopic, aData) {
-    debug('observe - aTopic: ' + aTopic);
-    if (aTopic != 'webapps-clear-data') {
-      return;
-    }
-
-    let params =
-      aSubject.QueryInterface(Ci.mozIApplicationClearPrivateDataParams);
-
-    // DataStore is explosed to apps, not browser content.
-    if (params.browserOnly) {
-      return;
-    }
-
-    function isEmpty(aMap) {
-      for (var key in aMap) {
-        if (aMap.hasOwnProperty(key)) {
-          return false;
-        }
-      }
-      return true;
-    }
-
-    for (let key in this.stores) {
-      if (params.appId in this.stores[key]) {
-        this.deleteDatabase(key, this.stores[key][params.appId].owner);
-        delete this.stores[key][params.appId];
-      }
-
-      if (isEmpty(this.stores[key])) {
-        delete this.stores[key];
-      }
-    }
-
-    for (let key in this.accessStores) {
-      if (params.appId in this.accessStores[key]) {
-        delete this.accessStores[key][params.appId];
-      }
-
-      if (isEmpty(this.accessStores[key])) {
-        delete this.accessStores[key];
-      }
-    }
-  },
-
-  deleteDatabase: function(aName, aOwner) {
-    debug("delete database: " + aName);
-
-    let db = new DataStoreDB();
-    db.init(aOwner, aName);
-    db.delete();
-  },
-
-  receiveMessage: function(aMsg) {
-    debug("receiveMessage");
-    let data = aMsg.json;
-
-    if (!(data.name in this.pendingRequests)) {
-      return;
-    }
-
-    for (let i = 0; i < this.pendingRequests[data.name].length;) {
-      let pos = this.pendingRequests[data.name][i].pendingDataStores.indexOf(data.owner);
-      if (pos != -1) {
-        this.pendingRequests[data.name][i].pendingDataStores.splice(pos, 1);
-        if (!this.pendingRequests[data.name][i].pendingDataStores.length) {
-          this.getDataStoreResolve(this.pendingRequests[data.name][i].window,
-                                   this.pendingRequests[data.name][i].resolve,
-                                   this.pendingRequests[data.name][i].stores);
-          this.pendingRequests[data.name].splice(i, 1);
-          continue;
-        }
-      }
-
-      ++i;
-    }
-
-    if (!this.pendingRequests[data.name].length) {
-      delete this.pendingRequests[data.name];
-    }
-  },
-
-  classID : DATASTORESERVICE_CID,
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIDataStoreService,
-                                         Ci.nsIObserver]),
-  classInfo: XPCOMUtils.generateCI({
-    classID: DATASTORESERVICE_CID,
-    contractID: '@mozilla.org/datastore-service;1',
-    interfaces: [Ci.nsIDataStoreService, Ci.nsIObserver],
-    flags: Ci.nsIClassInfo.SINGLETON
-  })
-};
-
-/* DataStoreServiceChild */
-
-function DataStoreServiceChild(aWindow, aName, aSuccessCb, aErrorCb) {
-  debug("DataStoreServiceChild created");
-  this.init(aWindow, aName, aSuccessCb, aErrorCb);
-}
-
-DataStoreServiceChild.prototype = {
-  __proto__: DOMRequestIpcHelper.prototype,
-
-  init: function(aWindow, aName, aSuccessCb, aErrorCb) {
-    debug("DataStoreServiceChild init");
-    this._successCb = aSuccessCb;
-    this._errorCb = aErrorCb;
-    this._name = aName;
-
-    this.initDOMRequestHelper(aWindow, [ "DataStore:Get:Return:OK",
-                                         "DataStore:Get:Return:KO" ]);
-
-    cpmm.sendAsyncMessage("DataStore:Get",
-                          { name: aName }, null, aWindow.document.nodePrincipal );
-  },
-
-  receiveMessage: function(aMessage) {
-    debug("DataStoreServiceChild receiveMessage");
-
-    if (aMessage.data.name != this._name) {
-      return;
-    }
-
-    switch (aMessage.name) {
-      case 'DataStore:Get:Return:OK':
-        this.destroyDOMRequestHelper();
-        this._successCb(aMessage.data.stores);
-        break;
-
-      case 'DataStore:Get:Return:KO':
-        this.destroyDOMRequestHelper();
-        this._errorCb();
-        break;
-    }
-  }
-}
-
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DataStoreService]);
deleted file mode 100644
--- a/dom/datastore/DataStoreServiceInternal.jsm
+++ /dev/null
@@ -1,68 +0,0 @@
-/* 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"
-
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-
-this.EXPORTED_SYMBOLS = ["DataStoreServiceInternal"];
-
-function debug(s) {
-  //dump('DEBUG DataStoreServiceInternal: ' + s + '\n');
-}
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-
-XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
-                                   "@mozilla.org/parentprocessmessagemanager;1",
-                                   "nsIMessageBroadcaster");
-
-XPCOMUtils.defineLazyServiceGetter(this, "dataStoreService",
-                                   "@mozilla.org/datastore-service;1",
-                                   "nsIDataStoreService");
-
-this.DataStoreServiceInternal = {
-  init: function() {
-    debug("init");
-
-    let inParent = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime)
-                      .processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
-    if (inParent) {
-      ppmm.addMessageListener("DataStore:Get", this);
-    }
-  },
-
-  receiveMessage: function(aMessage) {
-    debug("receiveMessage");
-
-    if (aMessage.name != 'DataStore:Get') {
-      return;
-    }
-
-    let prefName = 'dom.testing.datastore_enabled_for_hosted_apps';
-    if ((Services.prefs.getPrefType(prefName) == Services.prefs.PREF_INVALID ||
-         !Services.prefs.getBoolPref(prefName)) &&
-        !aMessage.target.assertAppHasStatus(Ci.nsIPrincipal.APP_STATUS_CERTIFIED)) {
-      return;
-    }
-
-    let msg = aMessage.data;
-
-    if (!aMessage.principal ||
-        aMessage.principal.appId == Ci.nsIScriptSecurityManager.UNKNOWN_APP_ID) {
-      aMessage.target.sendAsyncMessage("DataStore:Get:Return:KO");
-      return;
-    }
-
-    msg.stores = dataStoreService.getDataStoresInfo(msg.name, aMessage.principal.appId);
-    if (msg.stores === null) {
-      aMessage.target.sendAsyncMessage("DataStore:Get:Return:KO");
-      return;
-    }
-    aMessage.target.sendAsyncMessage("DataStore:Get:Return:OK", msg);
-  }
-}
-
-DataStoreServiceInternal.init();
--- a/dom/datastore/moz.build
+++ b/dom/datastore/moz.build
@@ -1,42 +1,48 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 XPIDL_SOURCES += [
+    'nsIDataStore.idl',
     'nsIDataStoreService.idl',
 ]
 
 XPIDL_MODULE = 'dom_datastore'
 
 EXPORTS.mozilla.dom += [
     'DataStore.h',
     'DataStoreCursor.h',
+    'DataStoreService.h',
 ]
 
 SOURCES += [
     'DataStore.cpp',
     'DataStoreCursor.cpp',
+    'DataStoreDB.cpp',
+    'DataStoreRevision.cpp',
+    'DataStoreService.cpp',
 ]
 
 LOCAL_INCLUDES += [
     '/js/xpconnect/wrappers',
 ]
 
 EXTRA_COMPONENTS += [
     'DataStore.manifest',
-    'DataStoreService.js',
+    'DataStoreImpl.js',
 ]
 
 EXTRA_JS_MODULES += [
     'DataStoreChangeNotifier.jsm',
     'DataStoreCursorImpl.jsm',
     'DataStoreDB.jsm',
-    'DataStoreImpl.jsm',
-    'DataStoreServiceInternal.jsm',
 ]
 
 MOCHITEST_MANIFESTS += ['tests/mochitest.ini']
 
+include('/ipc/chromium/chromium-config.mozbuild')
+
 FINAL_LIBRARY = 'xul'
+FAIL_ON_WARNINGS = True
new file mode 100644
--- /dev/null
+++ b/dom/datastore/nsIDataStore.idl
@@ -0,0 +1,23 @@
+/* -*- 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 "nsISupports.idl"
+
+interface nsIDOMWindow;
+
+// NOTE: This is a temporary interface.
+// It will be removed in the next patches for rewriting DataStore in C++.
+[scriptable, uuid(0b41fef5-14ba-48b0-923c-3d8fb64692ae)]
+interface nsIDataStore : nsISupports
+{
+  void init(in nsIDOMWindow window,
+            in DOMString name,
+            in DOMString manifestURL,
+            in boolean readOnly);
+
+  attribute jsval exposedObject;
+
+  void retrieveRevisionId(in jsval cb);
+};
--- a/dom/datastore/nsIDataStoreService.idl
+++ b/dom/datastore/nsIDataStoreService.idl
@@ -2,34 +2,26 @@
 /* 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 "nsISupports.idl"
 
 interface nsIDOMWindow;
 
-[scriptable, uuid(bd02d09c-41ab-47b7-9319-57aa8e5059b0)]
+[scriptable, uuid(0a050c4f-d292-4a14-8712-09bc1019840a)]
 interface nsIDataStoreService : nsISupports
 {
   void installDataStore(in unsigned long appId,
                         in DOMString name,
                         in DOMString originURL,
                         in DOMString manifestURL,
                         in boolean readOnly);
 
   void installAccessDataStore(in unsigned long appId,
                               in DOMString name,
                               in DOMString originURL,
                               in DOMString manifestURL,
                               in boolean readOnly);
 
   nsISupports getDataStores(in nsIDOMWindow window,
                             in DOMString name);
-
-  // This is an array of objects composed by:
-  // - readOnly: boolean
-  // - name: DOMString
-  // - owner: DOMString
-  // - enabled: true/false - true if this dataStore is ready to be used.
-  jsval getDataStoresInfo(in DOMString name,
-                          in unsigned long appId);
 };
new file mode 100644
--- /dev/null
+++ b/dom/datastore/tests/file_bug957086.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for DataStore - bug 1008044</title>
+</head>
+<body>
+<div id="container"></div>
+  <script type="application/javascript;version=1.7">
+
+function is(a, b, msg) {
+  alert((a == b ? 'OK' : 'KO') + ' ' + msg)
+}
+
+function cbError() {
+  alert('KO error');
+}
+
+function finish() {
+  alert('DONE');
+}
+
+navigator.getDataStores('foo').then(function(stores) {
+  is (stores.length, 1, "Correct number of Stores");
+  is (stores[0].name, 'foo', "Correct name is received!");
+  stores[0].get(42).then(function(something) {
+    is (something, null, "Correct value");
+    finish();
+  }, cbError);
+}, cbError);
+
+  </script>
+</body>
+</html>
--- a/dom/datastore/tests/mochitest.ini
+++ b/dom/datastore/tests/mochitest.ini
@@ -24,16 +24,17 @@ support-files =
   file_bug986056.html
   file_bug986056.template.webapp
   file_event_maker.html
   file_event_receiver.html
   file_transactions.html
   file_basic_common.js
   file_sync_common.js
   file_bug1008044.html
+  file_bug957086.html
 
 [test_app_install.html]
 [test_readonly.html]
 skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug)) # b2g-debug(debug-only failure; time out) b2g-desktop(Bug 931116, b2g desktop specific, initial triage)
 [test_basic.html]
 [test_basic_worker.html]
 [test_changes.html]
 skip-if = (toolkit == 'gonk' && debug) #intermittent failures, bug 961021
@@ -49,8 +50,9 @@ skip-if = (buildapp == 'b2g' && (toolkit
 skip-if = (toolkit == 'gonk' && debug) #debug-only failure; time out
 [test_keys.html]
 [test_duplicate.html]
 [test_bug976311.html]
 [test_bug986056.html]
 [test_oop_events.html]
 [test_transactions.html]
 [test_bug1008044.html]
+[test_bug957086.html]
copy from dom/datastore/tests/test_basic.html
copy to dom/datastore/tests/test_bug957086.html
--- a/dom/datastore/tests/test_basic.html
+++ b/dom/datastore/tests/test_bug957086.html
@@ -1,53 +1,57 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
-  <title>Test for DataStore - basic operation on a readonly db</title>
+  <title>Test for DataStore - bug 957086</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
 <div id="container"></div>
   <script type="application/javascript;version=1.7">
 
-  var gHostedManifestURL = 'http://test/tests/dom/datastore/tests/file_app.sjs?testToken=file_basic.html';
-  var gApp;
+  var gHostedManifestURL1 = 'http://test/tests/dom/datastore/tests/file_app.sjs?testToken=file_bug957086.html&template=file_app2.template.webapp';
+  var gHostedManifestURL2 = 'http://example.com/tests/dom/datastore/tests/file_app.sjs?testToken=file_bug957086.html';
+  var gApps = [];
 
   function cbError() {
     ok(false, "Error callback invoked");
     finish();
   }
 
-  function installApp() {
-    var request = navigator.mozApps.install(gHostedManifestURL);
+  function installApp(manifest) {
+    var request = navigator.mozApps.install(manifest);
     request.onerror = cbError;
     request.onsuccess = function() {
-      gApp = request.result;
+      gApps.push(request.result);
       runTest();
     }
   }
 
   function uninstallApp() {
-    // Uninstall the app.
-    var request = navigator.mozApps.mgmt.uninstall(gApp);
-    request.onerror = cbError;
-    request.onsuccess = function() {
+    if (!gApps.length) {
       // All done.
       info("All done");
       runTest();
+      return;
     }
+
+    // Uninstall the app.
+    var request = navigator.mozApps.mgmt.uninstall(gApps.pop());
+    request.onerror = cbError;
+    request.onsuccess = uninstallApp;
   }
 
   function testApp() {
     var ifr = document.createElement('iframe');
     ifr.setAttribute('mozbrowser', 'true');
-    ifr.setAttribute('mozapp', gApp.manifestURL);
-    ifr.setAttribute('src', gApp.manifest.launch_path);
+    ifr.setAttribute('mozapp', gApps[0].manifestURL);
+    ifr.setAttribute('src', gApps[0].manifest.launch_path);
     var domParent = document.getElementById('container');
 
     // Set us up to listen for messages from the app.
     var listener = function(e) {
       var message = e.detail.message;
       if (/^OK/.exec(message)) {
         ok(true, "Message from app: " + message);
       } else if (/KO/.exec(message)) {
@@ -72,33 +76,37 @@
         [{ "type": "browser", "allow": 1, "context": document },
          { "type": "embed-apps", "allow": 1, "context": document },
          { "type": "webapps-manage", "allow": 1, "context": document }], runTest);
     },
 
     // Preferences
     function() {
       SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true],
+                                         ["dom.ipc.browser_frames.oop_by_default", true],
                                          ["dom.testing.ignore_ipc_principal", true],
                                          ["dom.testing.datastore_enabled_for_hosted_apps", true]]}, runTest);
     },
 
     function() {
       SpecialPowers.setAllAppsLaunchable(true);
       SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true);
       runTest();
     },
 
     // No confirmation needed when an app is installed
     function() {
       SpecialPowers.autoConfirmAppInstall(runTest);
     },
 
-    // Installing the app
-    installApp,
+    // Installing the app - access only
+    function() { installApp(gHostedManifestURL1) },
+
+    // Installing the app - owner only
+    function() { installApp(gHostedManifestURL2) },
 
     // Run tests in app
     testApp,
 
     // Uninstall the app
     uninstallApp
   ];
 
--- a/dom/indexedDB/IDBFactory.cpp
+++ b/dom/indexedDB/IDBFactory.cpp
@@ -230,19 +230,16 @@ IDBFactory::Create(JSContext* aCx,
 // static
 nsresult
 IDBFactory::Create(ContentParent* aContentParent,
                    IDBFactory** aFactory)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
   NS_ASSERTION(nsContentUtils::IsCallerChrome(), "Only for chrome!");
-  NS_ASSERTION(aContentParent, "Null ContentParent!");
-
-  NS_ASSERTION(!nsContentUtils::GetCurrentJSContext(), "Should be called from C++");
 
   // We need to get this information before we push a null principal to avoid
   // IsCallerChrome() assertion in quota manager.
   nsCString group;
   nsCString origin;
   StoragePrivilege privilege;
   PersistenceType defaultPersistenceType;
   QuotaManager::GetInfoForChrome(&group, &origin, &privilege,
--- a/dom/indexedDB/IDBFactory.h
+++ b/dom/indexedDB/IDBFactory.h
@@ -70,17 +70,17 @@ public:
   // Called when using IndexedDB from a JS component or a JSM in the current
   // process.
   static nsresult Create(JSContext* aCx,
                          JS::Handle<JSObject*> aOwningObject,
                          ContentParent* aContentParent,
                          IDBFactory** aFactory);
 
   // Called when using IndexedDB from a JS component or a JSM in a different
-  // process.
+  // process or from a C++ component.
   static nsresult Create(ContentParent* aContentParent,
                          IDBFactory** aFactory);
 
   static already_AddRefed<nsIFileURL>
   GetDatabaseFileURL(nsIFile* aDatabaseFile,
                      PersistenceType aPersistenceType,
                      const nsACString& aGroup,
                      const nsACString& aOrigin);
--- a/dom/indexedDB/IDBRequest.cpp
+++ b/dom/indexedDB/IDBRequest.cpp
@@ -322,17 +322,17 @@ IDBRequest::ReadyState() const
 
 JSObject*
 IDBRequest::WrapObject(JSContext* aCx)
 {
   return IDBRequestBinding::Wrap(aCx, this);
 }
 
 JS::Value
-IDBRequest::GetResult(JSContext* aCx, mozilla::ErrorResult& aRv) const
+IDBRequest::GetResult(mozilla::ErrorResult& aRv) const
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (!mHaveResultOrErrorCode) {
     // XXX Need a real error code here.
     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
   }
 
--- a/dom/indexedDB/IDBRequest.h
+++ b/dom/indexedDB/IDBRequest.h
@@ -127,17 +127,23 @@ public:
   // WebIDL
   nsPIDOMWindow*
   GetParentObject() const
   {
     return GetOwner();
   }
 
   JS::Value
-  GetResult(JSContext* aCx, ErrorResult& aRv) const;
+  GetResult(ErrorResult& aRv) const;
+
+  JS::Value
+  GetResult(JSContext* aCx, ErrorResult& aRv) const
+  {
+    return GetResult(aRv);
+  }
 
   IDBTransaction*
   GetTransaction() const
   {
     NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     return mTransaction;
   }
 
--- a/dom/interfaces/base/nsIDOMWindow.idl
+++ b/dom/interfaces/base/nsIDOMWindow.idl
@@ -19,17 +19,17 @@ interface nsIVariant;
  * The nsIDOMWindow interface is the primary interface for a DOM
  * window object. It represents a single window object that may
  * contain child windows if the document in the window contains a
  * HTML frameset document or if the document contains iframe elements.
  *
  * @see <http://www.whatwg.org/html/#window>
  */
 
-[scriptable, uuid(fbefa573-0ba2-4d15-befb-d60277643a0b)]
+[scriptable, uuid(d4316591-d16e-405c-8093-b441cbef3230)]
 interface nsIDOMWindow : nsISupports
 {
   // the current browsing context
   readonly attribute nsIDOMWindow                       window;
 
   /* [replaceable] self */
   readonly attribute nsIDOMWindow                       self;
 
@@ -146,17 +146,21 @@ interface nsIDOMWindow : nsISupports
 
   %{C++
   inline nsresult GetParent(nsIDOMWindow **aWindow)
   {
     return GetRealParent(aWindow);
   }
   %}
 
-           attribute nsIDOMWindow                       opener;
+  [implicit_jscontext, binaryname(ScriptableOpener)]
+           attribute jsval                              opener;
+
+  [noscript, binaryname(Opener)]
+           attribute nsIDOMWindow                       openerWindow;
 
   /**
    * |frameElement| gets this window's <iframe> or <frame> element, if it has
    * one.
    *
    * When script reads this property (or when C++ calls
    * ScriptableFrameElement), we return |null| if the window is inside an
    * <iframe mozbrowser>.  In contrast, when C++ calls GetFrameElement, we
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -137,16 +137,17 @@
 #include "ProcessUtils.h"
 #include "StructuredCloneUtils.h"
 #include "URIUtils.h"
 #include "nsContentUtils.h"
 #include "nsIPrincipal.h"
 #include "nsDeviceStorage.h"
 #include "AudioChannelService.h"
 #include "JavaScriptChild.h"
+#include "mozilla/dom/DataStoreService.h"
 #include "mozilla/dom/telephony/PTelephonyChild.h"
 #include "mozilla/dom/time/DateCacheCleaner.h"
 #include "mozilla/net/NeckoMessageUtils.h"
 
 using namespace base;
 using namespace mozilla;
 using namespace mozilla::docshell;
 using namespace mozilla::dom::bluetooth;
@@ -764,16 +765,34 @@ ContentChild::RecvAudioChannelNotify()
         AudioChannelService::GetAudioChannelService();
     if (service) {
         service->Notify();
     }
     return true;
 }
 
 bool
+ContentChild::RecvDataStoreNotify(const uint32_t& aAppId,
+                                  const nsString& aName,
+                                  const nsString& aManifestURL)
+{
+  nsRefPtr<DataStoreService> service = DataStoreService::GetOrCreate();
+  if (NS_WARN_IF(!service)) {
+    return false;
+  }
+
+  nsresult rv = service->EnableDataStore(aAppId, aName, aManifestURL);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+
+  return true;
+}
+
+bool
 ContentChild::DeallocPMemoryReportRequestChild(PMemoryReportRequestChild* actor)
 {
     static_cast<MemoryReportRequestChild*>(actor)->Release();
     return true;
 }
 
 PCycleCollectWithLogsChild*
 ContentChild::AllocPCycleCollectWithLogsChild(const bool& aDumpAllTraces,
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -152,16 +152,20 @@ public:
     RecvPCycleCollectWithLogsConstructor(PCycleCollectWithLogsChild* aChild,
                                          const bool& aDumpAllTraces,
                                          const FileDescriptor& aGCLog,
                                          const FileDescriptor& aCCLog) MOZ_OVERRIDE;
 
     virtual bool
     RecvAudioChannelNotify() MOZ_OVERRIDE;
 
+    virtual bool
+    RecvDataStoreNotify(const uint32_t& aAppId, const nsString& aName,
+                        const nsString& aManifestURL) MOZ_OVERRIDE;
+
     virtual PTestShellChild* AllocPTestShellChild() MOZ_OVERRIDE;
     virtual bool DeallocPTestShellChild(PTestShellChild*) MOZ_OVERRIDE;
     virtual bool RecvPTestShellConstructor(PTestShellChild*) MOZ_OVERRIDE;
     jsipc::JavaScriptChild *GetCPOWManager();
 
     virtual PNeckoChild* AllocPNeckoChild() MOZ_OVERRIDE;
     virtual bool DeallocPNeckoChild(PNeckoChild*) MOZ_OVERRIDE;
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -28,16 +28,17 @@
 #include "IHistory.h"
 #include "IDBFactory.h"
 #include "IndexedDBParent.h"
 #include "IndexedDatabaseManager.h"
 #include "mozIApplication.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/dom/asmjscache/AsmJSCache.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/DataStoreService.h"
 #include "mozilla/dom/ExternalHelperAppParent.h"
 #include "mozilla/dom/PFileDescriptorSetParent.h"
 #include "mozilla/dom/PCycleCollectWithLogsParent.h"
 #include "mozilla/dom/PMemoryReportRequestParent.h"
 #include "mozilla/dom/power/PowerManagerService.h"
 #include "mozilla/dom/DOMStorageIPC.h"
 #include "mozilla/dom/bluetooth/PBluetoothParent.h"
 #include "mozilla/dom/PFMRadioParent.h"
@@ -1499,16 +1500,17 @@ ContentParent::InitializeMembers()
 {
     mSubprocess = nullptr;
     mChildID = gContentChildID++;
     mGeolocationWatchID = -1;
     mForceKillTask = nullptr;
     mNumDestroyingTabs = 0;
     mIsAlive = true;
     mSendPermissionUpdates = false;
+    mSendDataStoreInfos = false;
     mCalledClose = false;
     mCalledCloseWithError = false;
     mCalledKillHard = false;
 }
 
 ContentParent::ContentParent(mozIApplication* aApp,
                              bool aIsForBrowser,
                              bool aIsForPreallocated,
@@ -2081,16 +2083,36 @@ ContentParent::RecvAudioChannelChangeDef
     if (service) {
        service->SetDefaultVolumeControlChannelInternal(aChannel,
                                                        aHidden, mChildID);
     }
     return true;
 }
 
 bool
+ContentParent::RecvDataStoreGetStores(
+                                    const nsString& aName,
+                                    const IPC::Principal& aPrincipal,
+                                    InfallibleTArray<DataStoreSetting>* aValue)
+{
+  nsRefPtr<DataStoreService> service = DataStoreService::GetOrCreate();
+  if (NS_WARN_IF(!service)) {
+    return false;
+  }
+
+  nsresult rv = service->GetDataStoresFromIPC(aName, aPrincipal, aValue);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+
+  mSendDataStoreInfos = true;
+  return true;
+}
+
+bool
 ContentParent::RecvBroadcastVolume(const nsString& aVolumeName)
 {
 #ifdef MOZ_WIDGET_GONK
     nsresult rv;
     nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID, &rv);
     if (vs) {
         vs->BroadcastVolume(aVolumeName);
     }
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -157,20 +157,24 @@ public:
 #endif
 
     GeckoChildProcessHost* Process() {
         return mSubprocess;
     }
 
     int32_t Pid();
 
-    bool NeedsPermissionsUpdate() {
+    bool NeedsPermissionsUpdate() const {
         return mSendPermissionUpdates;
     }
 
+    bool NeedsDataStoreInfos() const {
+        return mSendDataStoreInfos;
+    }
+
     BlobParent* GetOrCreateActorForBlob(nsIDOMBlob* aBlob);
 
     /**
      * Kill our subprocess and make sure it dies.  Should only be used
      * in emergency situations since it bypasses the normal shutdown
      * process.
      */
     void KillHard();
@@ -527,16 +531,21 @@ private:
 
     virtual bool RecvAudioChannelChangedNotification() MOZ_OVERRIDE;
 
     virtual bool RecvAudioChannelChangeDefVolChannel(const int32_t& aChannel,
                                                      const bool& aHidden) MOZ_OVERRIDE;
     virtual bool RecvGetSystemMemory(const uint64_t& getterId) MOZ_OVERRIDE;
     virtual bool RecvBroadcastVolume(const nsString& aVolumeName) MOZ_OVERRIDE;
 
+    virtual bool RecvDataStoreGetStores(
+                       const nsString& aName,
+                       const IPC::Principal& aPrincipal,
+                       InfallibleTArray<DataStoreSetting>* aValue) MOZ_OVERRIDE;
+
     virtual bool RecvSpeakerManagerGetSpeakerStatus(bool* aValue) MOZ_OVERRIDE;
 
     virtual bool RecvSpeakerManagerForceSpeaker(const bool& aEnable) MOZ_OVERRIDE;
 
     virtual bool RecvSystemMessageHandled() MOZ_OVERRIDE;
 
     virtual bool RecvNuwaReady() MOZ_OVERRIDE;
 
@@ -601,16 +610,17 @@ private:
     int32_t mNumDestroyingTabs;
     // True only while this is ready to be used to host remote tabs.
     // This must not be used for new purposes after mIsAlive goes to
     // false, but some previously scheduled IPC traffic may still pass
     // through.
     bool mIsAlive;
 
     bool mSendPermissionUpdates;
+    bool mSendDataStoreInfos;
     bool mIsForBrowser;
     bool mIsNuwaProcess;
 
     // These variables track whether we've called Close(), CloseWithError()
     // and KillHard() on our channel.
     bool mCalledClose;
     bool mCalledCloseWithError;
     bool mCalledKillHard;
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -257,16 +257,24 @@ union MaybePrefValue {
 };
 
 struct PrefSetting {
   nsCString name;
   MaybePrefValue defaultValue;
   MaybePrefValue userValue;
 };
 
+struct DataStoreSetting {
+  nsString name;
+  nsString originURL;
+  nsString manifestURL;
+  bool readOnly;
+  bool enabled;
+};
+
 intr protocol PContent
 {
     parent opens PCompositor;
     parent opens PSharedBufferManager;
     parent opens PImageBridge;
     child opens PBackground;
 
     manages PAsmJSCacheEntry;
@@ -333,16 +341,19 @@ child:
 
     /**
      * Notify the AudioChannelService in the child processes.
      */
     async AudioChannelNotify();
 
     async SpeakerManagerNotify();
 
+    async DataStoreNotify(uint32_t aAppId, nsString aName,
+                          nsString aManifestURL);
+
     /**
      * Dump this process's GC and CC logs to the provided files.
      *
      * For documentation on the other args, see dumpGCAndCCLogsToFile in
      * nsIMemoryInfoDumper.idl
      */
     PCycleCollectWithLogs(bool dumpAllTraces,
                           FileDescriptor gcLog,
@@ -545,16 +556,19 @@ parent:
     sync AudioChannelRegisterType(AudioChannel aChannel, bool aWithVideo);
     sync AudioChannelUnregisterType(AudioChannel aChannel,
                                     bool aElementHidden,
                                     bool aWithVideo);
 
     async AudioChannelChangedNotification();
     async AudioChannelChangeDefVolChannel(int32_t aChannel, bool aHidden);
 
+    sync DataStoreGetStores(nsString aName, Principal aPrincipal)
+        returns (DataStoreSetting[] dataStores);
+
     async FilePathUpdateNotify(nsString aType,
                                nsString aStorageName,
                                nsString aFilepath,
                                nsCString aReason);
     // get nsIVolumeService to broadcast volume information
     async BroadcastVolume(nsString volumeName);
 
     // Notify the parent that the child has finished handling a system message.
--- a/dom/webidl/DataStore.webidl
+++ b/dom/webidl/DataStore.webidl
@@ -103,8 +103,15 @@ dictionary DataStoreTask {
   DOMString revisionId;
 
   DataStoreOperation operation;
 
   // When |operation| is "clear" or "done", this must return null.
   DataStoreKey? id;
   any data;
 };
+
+// For internal use.
+dictionary DataStoreRevisionData {
+  DOMString revisionId = "";
+  unsigned long objectId = 0;
+  DOMString operation = "";
+};
--- a/dom/webidl/Window.webidl
+++ b/dom/webidl/Window.webidl
@@ -49,17 +49,17 @@ typedef any Transferable;
   [Throws, CrossOriginCallable] void focus();
   [Throws, CrossOriginCallable] void blur();
 
   // other browsing contexts
   [Replaceable, Throws, CrossOriginReadable] readonly attribute WindowProxy frames;
   [Replaceable, CrossOriginReadable] readonly attribute unsigned long length;
   //[Unforgeable, Throws, CrossOriginReadable] readonly attribute WindowProxy top;
   [Unforgeable, Throws, CrossOriginReadable] readonly attribute WindowProxy? top;
-  [Throws, CrossOriginReadable] attribute WindowProxy? opener;
+  [Throws, CrossOriginReadable] attribute any opener;
   //[Throws] readonly attribute WindowProxy parent;
   [Replaceable, Throws, CrossOriginReadable] readonly attribute WindowProxy? parent;
   [Throws] readonly attribute Element? frameElement;
   //[Throws] WindowProxy open(optional DOMString url = "about:blank", optional DOMString target = "_blank", [TreatNullAs=EmptyString] optional DOMString features = "", optional boolean replace = false);
   [Throws] WindowProxy? open(optional DOMString url = "", optional DOMString target = "", [TreatNullAs=EmptyString] optional DOMString features = "");
   // We think the indexed getter is a bug in the spec, it actually needs to live
   // on the WindowProxy
   //getter WindowProxy (unsigned long index);
--- a/gfx/thebes/gfxUtils.cpp
+++ b/gfx/thebes/gfxUtils.cpp
@@ -137,63 +137,20 @@ gfxUtils::UnpremultiplyDataSurface(DataS
     }
 
     aSurface->Unmap();
     dest->Unmap();
     return dest;
 }
 
 void
-gfxUtils::ConvertBGRAtoRGBA(gfxImageSurface *aSourceSurface,
-                            gfxImageSurface *aDestSurface) {
-    if (!aDestSurface)
-        aDestSurface = aSourceSurface;
-
-    MOZ_ASSERT(aSourceSurface->Format() == aDestSurface->Format() &&
-               aSourceSurface->Width()  == aDestSurface->Width() &&
-               aSourceSurface->Height() == aDestSurface->Height() &&
-               aSourceSurface->Stride() == aDestSurface->Stride(),
-               "Source and destination surfaces don't have identical characteristics");
-
-    MOZ_ASSERT(aSourceSurface->Stride() == aSourceSurface->Width() * 4,
-               "Source surface stride isn't tightly packed");
-
-    MOZ_ASSERT(aSourceSurface->Format() == gfxImageFormat::ARGB32 || aSourceSurface->Format() == gfxImageFormat::RGB24,
-               "Surfaces must be ARGB32 or RGB24");
-
-    uint8_t *src = aSourceSurface->Data();
-    uint8_t *dst = aDestSurface->Data();
-
-    uint32_t dim = aSourceSurface->Width() * aSourceSurface->Height();
-    uint8_t *srcEnd = src + 4*dim;
-
-    if (src == dst) {
-        uint8_t buffer[4];
-        for (; src != srcEnd; src += 4) {
-            buffer[0] = src[2];
-            buffer[1] = src[1];
-            buffer[2] = src[0];
-
-            src[0] = buffer[0];
-            src[1] = buffer[1];
-            src[2] = buffer[2];
-        }
-    } else {
-        for (; src != srcEnd; src += 4, dst += 4) {
-            dst[0] = src[2];
-            dst[1] = src[1];
-            dst[2] = src[0];
-            dst[3] = src[3];
-        }
-    }
-}
-
-void
 gfxUtils::ConvertBGRAtoRGBA(uint8_t* aData, uint32_t aLength)
 {
+    MOZ_ASSERT((aLength % 4) == 0, "Loop below will pass srcEnd!");
+
     uint8_t *src = aData;
     uint8_t *srcEnd = src + aLength;
 
     uint8_t buffer[4];
     for (; src != srcEnd; src += 4) {
         buffer[0] = src[2];
         buffer[1] = src[1];
         buffer[2] = src[0];
--- a/gfx/thebes/gfxUtils.h
+++ b/gfx/thebes/gfxUtils.h
@@ -38,18 +38,16 @@ public:
      * stride as the source.
      *
      * If the source is not gfxImageFormat::ARGB32, no operation is performed.  If
      * aDestSurface is given, the data is copied over.
      */
     static void PremultiplyDataSurface(DataSourceSurface *aSurface);
     static mozilla::TemporaryRef<DataSourceSurface> UnpremultiplyDataSurface(DataSourceSurface* aSurface);
 
-    static void ConvertBGRAtoRGBA(gfxImageSurface *aSourceSurface,
-                                  gfxImageSurface *aDestSurface = nullptr);
     static void ConvertBGRAtoRGBA(uint8_t* aData, uint32_t aLength);
 
     /**
      * Draw something drawable while working around limitations like bad support
      * for EXTEND_PAD, lack of source-clipping, or cairo / pixman bugs with
      * extreme user-space-to-image-space transforms.
      *
      * The input parameters here usually come from the output of our image
--- a/js/public/Value.h
+++ b/js/public/Value.h
@@ -297,16 +297,17 @@ typedef union jsval_layout
     struct {
         JSValueTag tag;
         union {
             int32_t        i32;
             uint32_t       u32;
             uint32_t       boo;     // Don't use |bool| -- it must be four bytes.
             JSString       *str;
             JSObject       *obj;
+            js::gc::Cell   *cell;
             void           *ptr;
             JSWhyMagic     why;
             size_t         word;
             uintptr_t      uintptr;
         } payload;
     } s;
     double asDouble;
     void *asPtr;
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -14,16 +14,21 @@
 # include "gc/Nursery.h"
 #endif
 #include "gc/Statistics.h"
 #ifdef JSGC_GENERATIONAL
 # include "gc/StoreBuffer.h"
 #endif
 #include "gc/Tracer.h"
 
+/* Perform validation of incremental marking in debug builds but not on B2G. */
+#if defined(DEBUG) && !defined(MOZ_B2G)
+#define JS_GC_MARKING_VALIDATION
+#endif
+
 namespace js {
 
 struct ScriptAndCounts
 {
     /* This structure is stored and marked from the JSRuntime. */
     JSScript *script;
     ScriptCounts scriptCounts;
 
@@ -405,17 +410,17 @@ class GCRuntime
     int                   sweepKindIndex;
     bool                  abortSweepAfterCurrentGroup;
 
     /*
      * List head of arenas allocated during the sweep phase.
      */
     js::gc::ArenaHeader   *arenasAllocatedDuringSweep;
 
-#ifdef DEBUG
+#ifdef JS_GC_MARKING_VALIDATION
     js::gc::MarkingValidator *markingValidator;
 #endif
 
     /*
      * Indicates that a GC slice has taken place in the middle of an animation
      * frame, rather than at the beginning. In this case, the next slice will be
      * delayed so that we don't get back-to-back slices.
      */
--- a/js/src/jit-test/tests/ion/dce-with-rinstructions.js
+++ b/js/src/jit-test/tests/ion/dce-with-rinstructions.js
@@ -187,16 +187,35 @@ function rsub_object(i) {
     var o = { valueOf: function () { return t; } };
     var x = o - i; /* computed with t == i, not 1000 */
     t = 1000;
     if (uceFault_sub_object(i) || uceFault_sub_object(i))
         assertEq(x, 0);
     return i;
 }
 
+var uceFault_mod_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_mod_number'));
+function rmod_number(i) {
+    var x = i % 98;
+    if (uceFault_mod_number(i) || uceFault_mod_number(i))
+        assertEq(x, 1); /* 99 % 98 = 1 */
+    return i;
+}
+
+var uceFault_mod_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_mod_object'));
+function rmod_object(i) {
+    var t = i;
+    var o = { valueOf: function() { return t; } };
+    var x = o % 98; /* computed with t == i, not 1000 */
+    t = 1000;
+    if(uceFault_mod_object(i) || uceFault_mod_object(i))
+        assertEq(x, 1); /* 99 % 98 = 1 */
+    return i;
+}
+
 for (i = 0; i < 100; i++) {
     rbitnot_number(i);
     rbitnot_object(i);
     rbitor_number(i);
     rbitor_object(i);
     rbitxor_number(i);
     rbitxor_object(i);
     rlsh_number(i);
@@ -207,16 +226,18 @@ for (i = 0; i < 100; i++) {
     rursh_object(i);
     radd_number(i);
     radd_float(i);
     radd_string(i);
     radd_object(i);
     rsub_number(i);
     rsub_float(i);
     rsub_object(i);
+    rmod_number(i);
+    rmod_object(i);
 }
 
 // Test that we can refer multiple time to the same recover instruction, as well
 // as chaining recover instructions.
 
 function alignedAlloc($size, $alignment) {
     var $1 = $size + 4 | 0;
     var $2 = $alignment - 1 | 0;
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/latin1/dependent.js
@@ -0,0 +1,73 @@
+function testSubstrLatin1() {
+    var s1 = toLatin1("abcdefghijklmnopqrstuvwxyz12345678900000a");
+
+    // Static strings.
+    assertEq(s1.substr(s1.length - 1), "a");
+    assertEq(s1.substr(s1.length - 2), "0a");
+    assertEq(s1.substr(26, 3), "123");
+
+    // (Fat) inline strings.
+    var s2 = s1.substr(3, 5);
+    assertEq(isLatin1(s2), true);
+    assertEq(s2, "defgh");
+    s2 = s1.substr(0, 20);
+    assertEq(isLatin1(s2), true);
+    assertEq(s2, "abcdefghijklmnopqrst");
+
+    // Dependent string.
+    s2 = s1.substr(1, s1.length - 2);
+    assertEq(isLatin1(s2), true);
+    assertEq(s2.length, 39);
+    assertEq(s2, "bcdefghijklmnopqrstuvwxyz12345678900000");
+
+    s2 = s2.substr(2).substr(1);
+    assertEq(isLatin1(s2), true);
+    assertEq(s2, "efghijklmnopqrstuvwxyz12345678900000");
+}
+testSubstrLatin1();
+
+function testSubstrTwoByte() {
+    // Two byte string.
+    var s1 = "abcdefghijklmnopqrstuvwxyz12345678900000a\u1480";
+    assertEq(isLatin1(s1), false);
+
+    // Static string.
+    var s2 = s1.substr(28, 1);
+    assertEq(s2, "3");
+
+    // Inline string.
+    s2 = s1.substr(3, 5);
+    assertEq(s2, "defgh");
+
+    // Dependent string.
+    s2 = s1.substr(2);
+    assertEq(isLatin1(s2), false);
+    assertEq(s2, "cdefghijklmnopqrstuvwxyz12345678900000a\u1480");
+
+    s2 = s2.substr(2).substr(1);
+    assertEq(isLatin1(s2), false);
+    assertEq(s2, "fghijklmnopqrstuvwxyz12345678900000a\u1480");
+}
+testSubstrTwoByte();
+
+function testSubstring() {
+    var s1 = toLatin1("abcdefghijklmnopqrstuvwxyz123456789000ab");
+    var s2 = s1.substring(1, 8);
+    assertEq(isLatin1(s2), true);
+    assertEq(s2, "bcdefgh");
+    s2 = s1.substring(0, s1.length - 1);
+    assertEq(isLatin1(s2), true);
+    assertEq(s2, "abcdefghijklmnopqrstuvwxyz123456789000a");
+}
+testSubstring();
+
+function testSlice() {
+    var s1 = toLatin1("abcdefghijklmnopqrstuvwxyz123456789000ABC");
+    var s2 = s1.slice(1, 8);
+    assertEq(isLatin1(s2), true);
+    assertEq(s2, "bcdefgh");
+    s2 = s1.slice(0, -2);
+    assertEq(isLatin1(s2), true);
+    assertEq(s2, "abcdefghijklmnopqrstuvwxyz123456789000A");
+}
+testSlice();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/latin1/other.js
@@ -0,0 +1,31 @@
+var s1 = toLatin1("abcdefg12345");
+var s2 = toLatin1('foo"bar');
+
+function test() {
+    assertEq(s1.valueOf(), s1);
+
+    assertEq(s1.bold(), "<b>abcdefg12345</b>");
+    assertEq(s1.fontsize("twoByte\u1400"), '<font size="twoByte\u1400">abcdefg12345</font>');
+    assertEq(s1.anchor(s1), '<a name="abcdefg12345">abcdefg12345</a>');
+    assertEq(s1.link(s2), '<a href="foo&quot;bar">abcdefg12345</a>');
+
+    assertEq(s1.concat("abc"), "abcdefg12345abc");
+
+    var s3 = s1.concat(s1, s1);
+    assertEq(isLatin1(s3), true);
+    assertEq(s3, "abcdefg12345abcdefg12345abcdefg12345");
+
+    s3 = s1.concat("twoByte\u1400");
+    assertEq(isLatin1(s3), false);
+    assertEq(s3, "abcdefg12345twoByte\u1400");
+
+    assertEq(s1.codePointAt(3), 100);
+    assertEq(s1.codePointAt(10), 52);
+    assertEq(s1.codePointAt(12), undefined);
+
+    s3 = s1.repeat(5);
+    assertEq(s3, "abcdefg12345abcdefg12345abcdefg12345abcdefg12345abcdefg12345");
+    assertEq(isLatin1(s3), true);
+}
+test();
+test();
--- a/js/src/jit/AsmJS.cpp
+++ b/js/src/jit/AsmJS.cpp
@@ -5985,19 +5985,21 @@ CheckModuleReturn(ModuleCompiler &m)
 
 // All registers except the stack pointer.
 static const RegisterSet AllRegsExceptSP =
     RegisterSet(GeneralRegisterSet(Registers::AllMask &
                                    ~(uint32_t(1) << Registers::StackPointer)),
                 FloatRegisterSet(FloatRegisters::AllMask));
 #if defined(JS_CODEGEN_ARM)
 // The ARM system ABI also includes d15 in the non volatile float registers.
+// Also exclude lr (a.k.a. r14) as we preserve it manually)
 static const RegisterSet NonVolatileRegs =
-    RegisterSet(GeneralRegisterSet(Registers::NonVolatileMask),
-                    FloatRegisterSet(FloatRegisters::NonVolatileMask | (1 << FloatRegisters::d15)));
+    RegisterSet(GeneralRegisterSet(Registers::NonVolatileMask &
+                                   ~(uint32_t(1) << Registers::lr)),
+                FloatRegisterSet(FloatRegisters::NonVolatileMask | (1 << FloatRegisters::d15)));
 #else
 static const RegisterSet NonVolatileRegs =
     RegisterSet(GeneralRegisterSet(Registers::NonVolatileMask),
                 FloatRegisterSet(FloatRegisters::NonVolatileMask));
 #endif
 
 static void
 LoadAsmJSActivationIntoRegister(MacroAssembler &masm, Register reg)
@@ -6012,17 +6014,17 @@ static void
 LoadJSContextFromActivation(MacroAssembler &masm, Register activation, Register dest)
 {
     masm.loadPtr(Address(activation, AsmJSActivation::offsetOfContext()), dest);
 }
 
 static void
 AssertStackAlignment(MacroAssembler &masm)
 {
-    JS_ASSERT((AlignmentAtPrologue + masm.framePushed()) % StackAlignment == 0);
+    JS_ASSERT((AlignmentAtAsmJSPrologue + masm.framePushed()) % StackAlignment == 0);
 #ifdef DEBUG
     Label ok;
     JS_ASSERT(IsPowerOfTwo(StackAlignment));
     masm.branchTestPtr(Assembler::Zero, StackPointer, Imm32(StackAlignment - 1), &ok);
     masm.assumeUnreachable("Stack should be aligned.");
     masm.bind(&ok);
 #endif
 }
@@ -6037,17 +6039,17 @@ StackArgBytes(const VectorT &argTypes)
     return iter.stackBytesConsumedSoFar();
 }
 
 static unsigned
 StackDecrementForCall(MacroAssembler &masm, unsigned bytesToPush)
 {
     // Include extra padding so that, after pushing the bytesToPush,
     // the stack is aligned for a call instruction.
-    unsigned alreadyPushed = AlignmentAtPrologue + masm.framePushed();
+    unsigned alreadyPushed = AlignmentAtAsmJSPrologue + masm.framePushed();
     return AlignBytes(alreadyPushed + bytesToPush, StackAlignment) - alreadyPushed;
 }
 
 template <class VectorT>
 static unsigned
 StackDecrementForCall(MacroAssembler &masm, const VectorT &argTypes, unsigned extraBytes = 0)
 {
     return StackDecrementForCall(masm, StackArgBytes(argTypes) + extraBytes);
@@ -6073,16 +6075,24 @@ GenerateEntry(ModuleCompiler &m, const A
 
     // In constrast to the system ABI, the Ion convention is that all registers
     // are clobbered by calls. Thus, we must save the caller's non-volatile
     // registers.
     //
     // NB: GenerateExits assumes that masm.framePushed() == 0 before
     // PushRegsInMask(NonVolatileRegs).
     masm.setFramePushed(0);
+
+#if defined(JS_CODEGEN_ARM)
+    // Push lr without incrementing masm.framePushed since this push is
+    // accounted for by AlignmentAtAsmJSPrologue. The masm.ret at the end will
+    // pop.
+    masm.push(lr);
+#endif // JS_CODEGEN_ARM
+
     masm.PushRegsInMask(NonVolatileRegs);
     JS_ASSERT(masm.framePushed() == FramePushedAfterSave);
 
     // Remember the stack pointer in the current AsmJSActivation. This will be
     // used by error exit paths to set the stack pointer back to what it was
     // right after the (C++) caller's non-volatile registers were saved so that
     // they can be restored.
     Register activation = ABIArgGenerator::NonArgReturnVolatileReg0;
@@ -6169,17 +6179,17 @@ GenerateEntry(ModuleCompiler &m, const A
     }
 
     // Restore clobbered non-volatile registers of the caller.
     masm.PopRegsInMask(NonVolatileRegs);
 
     JS_ASSERT(masm.framePushed() == 0);
 
     masm.move32(Imm32(true), ReturnReg);
-    masm.abiret();
+    masm.ret();
     return true;
 }
 
 static inline bool
 TryEnablingIon(JSContext *cx, AsmJSModule &module, HandleFunction fun, uint32_t exitIndex,
                int32_t argc, Value *argv)
 {
     if (!fun->hasScript())
@@ -6323,35 +6333,39 @@ FillArgumentArray(ModuleCompiler &m, con
 static void
 GenerateFFIInterpreterExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit,
                            unsigned exitIndex, Label *throwLabel)
 {
     MacroAssembler &masm = m.masm();
     masm.align(CodeAlignment);
     m.setInterpExitOffset(exitIndex);
     masm.setFramePushed(0);
+
 #if defined(JS_CODEGEN_ARM)
-    masm.Push(lr);
+    // Push lr without incrementing masm.framePushed since this push is
+    // accounted for by AlignmentAtAsmJSPrologue. The masm.ret at the end will
+    // pop.
+    masm.push(lr);
 #endif
 
     MIRType typeArray[] = { MIRType_Pointer,   // cx
                             MIRType_Pointer,   // exitDatum
                             MIRType_Int32,     // argc
                             MIRType_Pointer }; // argv
     MIRTypeVector invokeArgTypes(m.cx());
     invokeArgTypes.infallibleAppend(typeArray, ArrayLength(typeArray));
 
     // The stack layout looks like:
     // | return address | stack arguments | array of values |
     unsigned arraySize = Max<size_t>(1, exit.sig().args().length()) * sizeof(Value);
     unsigned stackDec = StackDecrementForCall(masm, invokeArgTypes, arraySize + MaybeRetAddr);
     masm.reserveStack(stackDec);
 
     // Fill the argument array.
-    unsigned offsetToCallerStackArgs = AlignmentAtPrologue + masm.framePushed();
+    unsigned offsetToCallerStackArgs = AlignmentAtAsmJSPrologue + masm.framePushed();
     unsigned offsetToArgv = StackArgBytes(invokeArgTypes) + MaybeRetAddr;
     Register scratch = ABIArgGenerator::NonArgReturnVolatileReg0;
     FillArgumentArray(m, exit.sig().args(), offsetToArgv, offsetToCallerStackArgs, scratch);
 
     // Prepare the arguments for the call to InvokeFromAsmJS_*.
     ABIArgMIRTypeIter i(invokeArgTypes);
     Register activation = ABIArgGenerator::NonArgReturnVolatileReg1;
     LoadAsmJSActivationIntoRegister(masm, activation);
@@ -6494,24 +6508,26 @@ GenerateFFIIonExit(ModuleCompiler &m, co
     MacroAssembler &masm = m.masm();
     masm.align(CodeAlignment);
     m.setIonExitOffset(exitIndex);
     masm.setFramePushed(0);
 
 #if defined(JS_CODEGEN_X64)
     masm.Push(HeapReg);
 #elif defined(JS_CODEGEN_ARM)
-    // The lr register holds the return address and needs to be saved.  The GlobalReg
-    // (r10) and HeapReg (r11) also need to be restored before returning to asm.js code.
+    // Push lr without incrementing masm.framePushed since this push is
+    // accounted for by AlignmentAtAsmJSPrologue. The masm.ret at the end will
+    // pop.
+    masm.push(lr);
+
+    // The GlobalReg (r10) and HeapReg (r11) also need to be restored before
+    // returning to asm.js code.
     // The NANReg also needs to be restored, but is a constant and is reloaded before
     // returning to asm.js code.
-    masm.PushRegsInMask(RegisterSet(GeneralRegisterSet((1<<GlobalReg.code()) |
-                                                       (1<<HeapReg.code()) |
-                                                       (1<<lr.code())),
-                                    FloatRegisterSet(uint32_t(0))));
+    masm.PushRegsInMask(GeneralRegisterSet((1<<GlobalReg.code()) | (1<<HeapReg.code())));
 #endif
 
     // The stack frame is used for the call into Ion and also for calls into C for OOL
     // conversion of the result.  A frame large enough for both is allocated.
     //
     // Arguments to the Ion function are in the following order on the stack:
     // | return address | descriptor | callee | argc | this | arg1 | arg2 | ...
     unsigned argBytes = 3 * sizeof(size_t) + (1 + exit.sig().args().length()) * sizeof(Value);
@@ -6695,28 +6711,24 @@ GenerateFFIIonExit(ModuleCompiler &m, co
         break;
       case RetType::Float:
         MOZ_ASSUME_UNREACHABLE("Float shouldn't be returned from a FFI");
         break;
     }
 
     masm.bind(&done);
     masm.freeStack(stackDec);
+#if defined(JS_CODEGEN_X64)
+    masm.Pop(HeapReg);
+#endif
 #if defined(JS_CODEGEN_ARM)
     masm.ma_vimm(GenericNaN(), NANReg);
-    masm.PopRegsInMask(RegisterSet(GeneralRegisterSet((1<<GlobalReg.code()) |
-                                                      (1<<HeapReg.code()) |
-                                                      (1<<pc.code())),
-                                   FloatRegisterSet(uint32_t(0))));
-#else
-# if defined(JS_CODEGEN_X64)
-    masm.Pop(HeapReg);
-# endif
+    masm.PopRegsInMask(GeneralRegisterSet((1<<GlobalReg.code()) | (1<<HeapReg.code())));
+#endif
     masm.ret();
-#endif
     JS_ASSERT(masm.framePushed() == 0);
 
     // oolConvert
     if (oolConvert.used()) {
         masm.bind(&oolConvert);
         masm.setFramePushed(oolConvertFramePushed);
         GenerateOOLConvert(m, exit.sig().retType(), throwLabel);
         masm.setFramePushed(0);
@@ -6746,20 +6758,16 @@ GenerateFFIExit(ModuleCompiler &m, const
 // all the frames.
 static bool
 GenerateStackOverflowExit(ModuleCompiler &m, Label *throwLabel)
 {
     MacroAssembler &masm = m.masm();
     masm.align(CodeAlignment);
     masm.bind(&m.stackOverflowLabel());
 
-    // The overflow check always occurs before the initial function-specific
-    // stack-size adjustment. See CodeGenerator::generateAsmJSPrologue.
-    masm.setFramePushed(AlignmentMidPrologue - AlignmentAtPrologue);
-
     MIRTypeVector argTypes(m.cx());
     argTypes.infallibleAppend(MIRType_Pointer); // cx
 
     unsigned stackDec = StackDecrementForCall(masm, argTypes, MaybeRetAddr);
     masm.reserveStack(stackDec);
 
     Register activation = ABIArgGenerator::NonArgReturnVolatileReg0;
     LoadAsmJSActivationIntoRegister(masm, activation);
@@ -6919,17 +6927,17 @@ GenerateThrowExit(ModuleCompiler &m, Lab
 
     masm.setFramePushed(FramePushedAfterSave);
     masm.loadPtr(Address(activation, AsmJSActivation::offsetOfErrorRejoinSP()), StackPointer);
 
     masm.PopRegsInMask(NonVolatileRegs);
     JS_ASSERT(masm.framePushed() == 0);
 
     masm.mov(ImmWord(0), ReturnReg);
-    masm.abiret();
+    masm.ret();
 
     return !masm.oom();
 }
 
 static bool
 GenerateStubs(ModuleCompiler &m)
 {
     for (unsigned i = 0; i < m.module().numExportedFunctions(); i++) {
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -8323,16 +8323,37 @@ CodeGenerator::visitCallInstanceOf(LCall
 bool
 CodeGenerator::visitGetDOMProperty(LGetDOMProperty *ins)
 {
     const Register JSContextReg = ToRegister(ins->getJSContextReg());
     const Register ObjectReg = ToRegister(ins->getObjectReg());
     const Register PrivateReg = ToRegister(ins->getPrivReg());
     const Register ValueReg = ToRegister(ins->getValueReg());
 
+    Label haveValue;
+    if (ins->mir()->valueMayBeInSlot()) {
+        size_t slot = ins->mir()->domMemberSlotIndex();
+        // It's a bit annoying to redo these slot calculations, which duplcate
+        // LSlots and a few other things like that, but I'm not sure there's a
+        // way to reuse those here.
+        if (slot < JSObject::MAX_FIXED_SLOTS) {
+            masm.loadValue(Address(ObjectReg, JSObject::getFixedSlotOffset(slot)),
+                           JSReturnOperand);
+        } else {
+            // It's a dynamic slot.
+            slot -= JSObject::MAX_FIXED_SLOTS;
+            // Use PrivateReg as a scratch register for the slots pointer.
+            masm.loadPtr(Address(ObjectReg, JSObject::offsetOfSlots()),
+                         PrivateReg);
+            masm.loadValue(Address(PrivateReg, slot*sizeof(js::Value)),
+                           JSReturnOperand);
+        }
+        masm.branchTestUndefined(Assembler::NotEqual, JSReturnOperand, &haveValue);
+    }
+
     DebugOnly<uint32_t> initialStack = masm.framePushed();
 
     masm.checkStackAlignment();
 
     // Make space for the outparam.  Pre-initialize it to UndefinedValue so we
     // can trace it at GC time.
     masm.Push(UndefinedValue());
     // We pass the pointer to our out param as an instance of
@@ -8372,16 +8393,18 @@ CodeGenerator::visitGetDOMProperty(LGetD
     } else {
         masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
 
         masm.loadValue(Address(StackPointer, IonDOMExitFrameLayout::offsetOfResult()),
                        JSReturnOperand);
     }
     masm.adjustStack(IonDOMExitFrameLayout::Size());
 
+    masm.bind(&haveValue);
+
     JS_ASSERT(masm.framePushed() == initialStack);
     return true;
 }
 
 bool
 CodeGenerator::visitGetDOMMember(LGetDOMMember *ins)
 {
     // It's simple to duplicate visitLoadFixedSlotV here than it is to try to
@@ -8655,17 +8678,17 @@ CodeGenerator::visitAsmJSCall(LAsmJSCall
             }
         }
     }
 #endif
 
     if (mir->spIncrement())
         masm.freeStack(mir->spIncrement());
 
-    JS_ASSERT((AlignmentAtPrologue + masm.framePushed()) % StackAlignment == 0);
+    JS_ASSERT((AlignmentAtAsmJSPrologue + masm.framePushed()) % StackAlignment == 0);
 
 #ifdef DEBUG
     Label ok;
     JS_ASSERT(IsPowerOfTwo(StackAlignment));
     masm.branchTestPtr(Assembler::Zero, StackPointer, Imm32(StackAlignment - 1), &ok);
     masm.assumeUnreachable("Stack should be aligned.");
     masm.bind(&ok);
 #endif
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -8847,19 +8847,19 @@ IonBuilder::getPropTryCommonGetter(bool 
     if (!guard)
         return true;
 
     bool isDOM = objTypes->isDOMClass();
 
     if (isDOM && testShouldDOMCall(objTypes, commonGetter, JSJitInfo::Getter)) {
         const JSJitInfo *jitinfo = commonGetter->jitInfo();
         MInstruction *get;
-        if (jitinfo->isInSlot) {
+        if (jitinfo->isAlwaysInSlot) {
             // We can't use MLoadFixedSlot here because it might not have the
-            // right aliasing behavior; we want to alias DOM setters.
+            // right aliasing behavior; we want to alias DOM setters as needed.
             get = MGetDOMMember::New(alloc(), jitinfo, obj, guard);
         } else {
             get = MGetDOMProperty::New(alloc(), jitinfo, obj, guard);
         }
         current->add(get);
         current->push(get);
 
         if (get->isEffectful() && !resumeAfter(get))
--- a/js/src/jit/IonMacroAssembler.h
+++ b/js/src/jit/IonMacroAssembler.h
@@ -246,17 +246,17 @@ class MacroAssembler : public MacroAssem
                 spsPc_ = pc;
                 spsInstrumentation_.construct(&cx->runtime()->spsProfiler, &spsPc_);
                 sps_ = spsInstrumentation_.addr();
                 sps_->setPushed(script);
             }
         }
     }
 
-    // asm.js compilation handles its own IonContet-pushing
+    // asm.js compilation handles its own IonContext-pushing
     struct AsmJSToken {};
     explicit MacroAssembler(AsmJSToken)
       : enoughMemory_(true),
         embedsNurseryPointers_(false),
         sps_(nullptr)
     {
 #ifdef JS_CODEGEN_ARM
         initWithAllocator();
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -4544,16 +4544,21 @@ class MMod : public MBinaryArithInstruct
     }
     bool canBeDivideByZero() const;
     bool canBePowerOfTwoDivisor() const;
 
     bool isUnsigned() const {
         return unsigned_;
     }
 
+    bool writeRecoverData(CompactBufferWriter &writer) const;
+    bool canRecoverOnBailout() const {
+        return specialization_ < MIRType_Object;
+    }
+
     bool fallible() const;
 
     void computeRange(TempAllocator &alloc);
     bool truncate(TruncateKind kind);
     void collectRangeInfoPreTrunc();
     TruncateKind operandTruncateKind(size_t index) const;
 };
 
@@ -8655,19 +8660,22 @@ class MGetDOMProperty
     }
     bool isDomMovable() const {
         return info_->isMovable;
     }
     JSJitInfo::AliasSet domAliasSet() const {
         return info_->aliasSet();
     }
     size_t domMemberSlotIndex() const {
-        MOZ_ASSERT(info_->isInSlot);
+        MOZ_ASSERT(info_->isAlwaysInSlot || info_->isLazilyCachedInSlot);
         return info_->slotIndex;
     }
+    bool valueMayBeInSlot() const {
+        return info_->isLazilyCachedInSlot;
+    }
     MDefinition *object() {
         return getOperand(0);
     }
 
     TypePolicy *typePolicy() {
         return this;
     }
 
--- a/js/src/jit/Recover.cpp
+++ b/js/src/jit/Recover.cpp
@@ -359,16 +359,43 @@ RSub::recover(JSContext *cx, SnapshotIte
     // rounded to a Float32.
     if (isFloatOperation_ && !RoundFloat32(cx, result, &result))
         return false;
 
     iter.storeInstructionResult(result);
     return true;
 }
 
+
+bool
+MMod::writeRecoverData(CompactBufferWriter &writer) const
+{
+	MOZ_ASSERT(canRecoverOnBailout());
+	writer.writeUnsigned(uint32_t(RInstruction::Recover_Mod));
+	return true;
+}
+
+RMod::RMod(CompactBufferReader &reader)
+{ }
+
+bool
+RMod::recover(JSContext *cx, SnapshotIterator &iter) const
+{
+	RootedValue lhs(cx, iter.read());
+	RootedValue rhs(cx, iter.read());
+	RootedValue result(cx);
+
+	MOZ_ASSERT(!lhs.isObject() && !rhs.isObject());
+	if (!js::ModValues(cx, &lhs, &rhs, &result))
+		return false;
+
+	iter.storeInstructionResult(result);
+	return true;
+}
+
 bool
 MNewObject::writeRecoverData(CompactBufferWriter &writer) const
 {
     MOZ_ASSERT(canRecoverOnBailout());
     writer.writeUnsigned(uint32_t(RInstruction::Recover_NewObject));
     writer.writeByte(templateObjectIsClassPrototype_);
     return true;
 }
--- a/js/src/jit/Recover.h
+++ b/js/src/jit/Recover.h
@@ -21,16 +21,17 @@ namespace jit {
     _(BitNot)                                   \
     _(BitOr)                                    \
     _(BitXor)                                   \
     _(Lsh)                                      \
     _(Rsh)                                      \
     _(Ursh)                                     \
     _(Add)                                      \
     _(Sub)                                      \
+    _(Mod)                                      \
     _(NewObject)                                \
     _(NewDerivedTypedObject)
 
 class RResumePoint;
 class SnapshotIterator;
 
 class RInstruction
 {
@@ -192,16 +193,28 @@ class RSub MOZ_FINAL : public RInstructi
 
     virtual uint32_t numOperands() const {
         return 2;
     }
 
     bool recover(JSContext *cx, SnapshotIterator &iter) const;
 };
 
+class RMod MOZ_FINAL : public RInstruction
+{
+  public:
+    RINSTRUCTION_HEADER_(Mod)
+
+    virtual uint32_t numOperands() const {
+        return 2;
+    }
+
+    bool recover(JSContext *cx, SnapshotIterator &iter) const;
+};
+
 class RNewObject MOZ_FINAL : public RInstruction
 {
   private:
     bool templateObjectIsClassPrototype_;
 
   public:
     RINSTRUCTION_HEADER_(NewObject)
 
--- a/js/src/jit/arm/Assembler-arm.h
+++ b/js/src/jit/arm/Assembler-arm.h
@@ -134,19 +134,17 @@ static MOZ_CONSTEXPR_VAR FloatRegister d
 // ldrd/strd (dual-register load/store) operate in a single cycle
 // when the address they are dealing with is 8 byte aligned.
 // Also, the ARM abi wants the stack to be 8 byte aligned at
 // function boundaries.  I'm trying to make sure this is always true.
 static const uint32_t StackAlignment = 8;
 static const uint32_t CodeAlignment = 8;
 static const bool StackKeptAligned = true;
 static const uint32_t NativeFrameSize = sizeof(void*);
-static const uint32_t AlignmentAtPrologue = 0;
-static const uint32_t AlignmentMidPrologue = 4;
-
+static const uint32_t AlignmentAtAsmJSPrologue = sizeof(void*);
 
 static const Scale ScalePointer = TimesFour;
 
 class Instruction;
 class InstBranchImm;
 uint32_t RM(Register r);
 uint32_t RS(Register r);
 uint32_t RD(Register r);
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -48,17 +48,17 @@ CodeGeneratorARM::generatePrologue()
     return true;
 }
 
 bool
 CodeGeneratorARM::generateAsmJSPrologue(Label *stackOverflowLabel)
 {
     JS_ASSERT(gen->compilingAsmJS());
 
-    masm.Push(lr);
+    masm.push(lr);
 
     // The asm.js over-recursed handler wants to be able to assume that SP
     // points to the return address, so perform the check after pushing lr but
     // before pushing frameDepth.
     if (!omitOverRecursedCheck()) {
         masm.branchPtr(Assembler::AboveOrEqual,
                        AsmJSAbsoluteAddress(AsmJSImm_StackLimit),
                        StackPointer,
@@ -80,28 +80,22 @@ CodeGeneratorARM::generateEpilogue()
     if (!gen->compilingAsmJS() && gen->info().executionMode() == SequentialExecution) {
         if (!emitTracelogStopEvent(TraceLogger::IonMonkey))
             return false;
         if (!emitTracelogScriptStop())
             return false;
     }
 #endif
 
-    if (gen->compilingAsmJS()) {
-        // Pop the stack we allocated at the start of the function.
+    if (gen->compilingAsmJS())
         masm.freeStack(frameDepth_);
-        masm.Pop(pc);
-        JS_ASSERT(masm.framePushed() == 0);
-        //masm.as_bkpt();
-    } else {
-        // Pop the stack we allocated at the start of the function.
+    else
         masm.freeStack(frameSize());
-        JS_ASSERT(masm.framePushed() == 0);
-        masm.ma_pop(pc);
-    }
+    JS_ASSERT(masm.framePushed() == 0);
+    masm.pop(pc);
     masm.dumpPool();
     return true;
 }
 
 void
 CodeGeneratorARM::emitBranch(Assembler::Condition cond, MBasicBlock *mirTrue, MBasicBlock *mirFalse)
 {
     if (isNextBlock(mirFalse->lir())) {
@@ -1042,24 +1036,18 @@ CodeGeneratorARM::visitPowHalfD(LPowHalf
 
 MoveOperand
 CodeGeneratorARM::toMoveOperand(const LAllocation *a) const
 {
     if (a->isGeneralReg())
         return MoveOperand(ToRegister(a));
     if (a->isFloatReg())
         return MoveOperand(ToFloatRegister(a));
-    JS_ASSERT((ToStackOffset(a) & 3) == 0);
     int32_t offset = ToStackOffset(a);
-
-    // The way the stack slots work, we assume that everything from depth == 0 downwards is writable
-    // however, since our frame is included in this, ensure that the frame gets skipped
-    if (gen->compilingAsmJS())
-        offset -= AlignmentMidPrologue;
-
+    JS_ASSERT((offset & 3) == 0);
     return MoveOperand(StackPointer, offset);
 }
 
 class js::jit::OutOfLineTableSwitch : public OutOfLineCodeBase<CodeGeneratorARM>
 {
     MTableSwitch *mir_;
     Vector<CodeLabel, 8, IonAllocPolicy> codeLabels_;
 
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -3862,27 +3862,30 @@ void MacroAssemblerARMCompat::checkStack
 {
 #ifdef DEBUG
     ma_tst(sp, Imm32(StackAlignment - 1));
     breakpoint(NonZero);
 #endif
 }
 
 void
-MacroAssemblerARMCompat::callWithABIPre(uint32_t *stackAdjust)
+MacroAssemblerARMCompat::callWithABIPre(uint32_t *stackAdjust, bool callFromAsmJS)
 {
     JS_ASSERT(inCall_);
 
     *stackAdjust = ((usedIntSlots_ > NumIntArgRegs) ? usedIntSlots_ - NumIntArgRegs : 0) * sizeof(intptr_t);
 #if defined(JS_CODEGEN_ARM_HARDFP) || defined(JS_ARM_SIMULATOR)
     if (useHardFpABI())
         *stackAdjust += 2*((usedFloatSlots_ > NumFloatArgRegs) ? usedFloatSlots_ - NumFloatArgRegs : 0) * sizeof(intptr_t);
 #endif
+    uint32_t alignmentAtPrologue = (callFromAsmJS) ? AlignmentAtAsmJSPrologue : 0;
+
     if (!dynamicAlignment_) {
-        *stackAdjust += ComputeByteAlignment(framePushed_ + *stackAdjust, StackAlignment);
+        *stackAdjust += ComputeByteAlignment(framePushed_ + *stackAdjust + alignmentAtPrologue,
+                                             StackAlignment);
     } else {
         // sizeof(intptr_t) account for the saved stack pointer pushed by setupUnalignedABICall
         *stackAdjust += ComputeByteAlignment(*stackAdjust + sizeof(intptr_t), StackAlignment);
     }
 
     reserveStack(*stackAdjust);
 
     // Position all arguments.
@@ -4016,17 +4019,17 @@ MacroAssemblerARMCompat::callWithABI(voi
     ma_call(ImmPtr(fun));
     callWithABIPost(stackAdjust, result);
 }
 
 void
 MacroAssemblerARMCompat::callWithABI(AsmJSImmPtr imm, MoveOp::Type result)
 {
     uint32_t stackAdjust;
-    callWithABIPre(&stackAdjust);
+    callWithABIPre(&stackAdjust, /* callFromAsmJS = */ true);
     call(imm);
     callWithABIPost(stackAdjust, result);
 }
 
 void
 MacroAssemblerARMCompat::callWithABI(const Address &fun, MoveOp::Type result)
 {
     // Load the callee in r12, no instruction between the ldr and call
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -448,19 +448,19 @@ private:
 
         JS_ASSERT(offset == static_cast<int32_t>(set.size() * sizeof(double)) * sign);
         return offset;
     }
 };
 
 class MacroAssemblerARMCompat : public MacroAssemblerARM
 {
+    bool inCall_;
     // Number of bytes the stack is adjusted inside a call to C. Calls to C may
     // not be nested.
-    bool inCall_;
     uint32_t args_;
     // The actual number of arguments that were passed, used to assert that
     // the initial number of arguments declared was correct.
     uint32_t passedArgs_;
     uint32_t passedArgTypes_;
 
     // ARM treats arguments as a vector in registers/memory, that looks like:
     // { r0, r1, r2, r3, [sp], [sp,+4], [sp,+8] ... }
@@ -571,17 +571,19 @@ class MacroAssemblerARMCompat : public M
         else
             rs = L_LDR;
 
         ma_movPatchable(ImmPtr(c->raw()), ScratchRegister, Always, rs);
         ma_callIonHalfPush(ScratchRegister);
     }
 
     void appendCallSite(const CallSiteDesc &desc) {
-        enoughMemory_ &= append(CallSite(desc, currentOffset(), framePushed_));
+        // Add an extra sizeof(void*) to include the return address that was
+        // pushed by the call instruction (see CallSite::stackDepth).
+        enoughMemory_ &= append(CallSite(desc, currentOffset(), framePushed_ + sizeof(void*)));
     }
 
     void call(const CallSiteDesc &desc, const Register reg) {
         call(reg);
         appendCallSite(desc);
     }
     void call(const CallSiteDesc &desc, Label *label) {
         call(label);
@@ -1525,17 +1527,17 @@ class MacroAssemblerARMCompat : public M
   private:
     void passHardFpABIArg(const MoveOperand &from, MoveOp::Type type);
     void passSoftFpABIArg(const MoveOperand &from, MoveOp::Type type);
 
   protected:
     bool buildOOLFakeExitFrame(void *fakeReturnAddr);
 
   private:
-    void callWithABIPre(uint32_t *stackAdjust);
+    void callWithABIPre(uint32_t *stackAdjust, bool callFromAsmJS = false);
     void callWithABIPost(uint32_t stackAdjust, MoveOp::Type result);
 
   public:
     // Emits a call to a C/C++ function, resolving all argument moves.
     void callWithABI(void *fun, MoveOp::Type result = MoveOp::GENERAL);
     void callWithABI(AsmJSImmPtr imm, MoveOp::Type result = MoveOp::GENERAL);
     void callWithABI(const Address &fun, MoveOp::Type result = MoveOp::GENERAL);
 
--- a/js/src/jit/mips/Assembler-mips.h
+++ b/js/src/jit/mips/Assembler-mips.h
@@ -133,17 +133,17 @@ static MOZ_CONSTEXPR_VAR FloatRegister f
 
 // MIPS CPUs can only load multibyte data that is "naturally"
 // four-byte-aligned, sp register should be eight-byte-aligned.
 static const uint32_t StackAlignment = 8;
 static const uint32_t CodeAlignment = 4;
 static const bool StackKeptAligned = true;
 // NativeFrameSize is the size of return adress on stack in AsmJS functions.
 static const uint32_t NativeFrameSize = sizeof(void*);
-static const uint32_t AlignmentAtPrologue = 0;
+static const uint32_t AlignmentAtAsmJSPrologue = 0;
 static const uint32_t AlignmentMidPrologue = NativeFrameSize;
 
 static const Scale ScalePointer = TimesFour;
 
 // MIPS instruction types
 //                +---------------------------------------------------------------+
 //                |    6      |    5    |    5    |    5    |    5    |    6      |
 //                +---------------------------------------------------------------+
--- a/js/src/jit/shared/CodeGenerator-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-shared.cpp
@@ -69,17 +69,17 @@ CodeGeneratorShared::CodeGeneratorShared
         // relies on the a priori stack adjustment (in the prologue) on platforms
         // (like x64) which require the stack to be aligned.
 #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
         bool forceAlign = true;
 #else
         bool forceAlign = false;
 #endif
         if (gen->needsInitialStackAlignment() || forceAlign) {
-            unsigned alignmentAtCall = AlignmentMidPrologue + frameDepth_;
+            unsigned alignmentAtCall = AlignmentAtAsmJSPrologue + frameDepth_;
             if (unsigned rem = alignmentAtCall % StackAlignment) {
                 frameInitialAdjustment_ = StackAlignment - rem;
                 frameDepth_ += frameInitialAdjustment_;
             }
         }
 
         // FrameSizeClass is only used for bailing, which cannot happen in
         // asm.js code.
--- a/js/src/jit/x64/Assembler-x64.h
+++ b/js/src/jit/x64/Assembler-x64.h
@@ -178,18 +178,17 @@ static MOZ_CONSTEXPR_VAR Register OsrFra
 static MOZ_CONSTEXPR_VAR Register PreBarrierReg = rdx;
 
 // GCC stack is aligned on 16 bytes, but we don't maintain the invariant in
 // jitted code.
 static const uint32_t StackAlignment = 16;
 static const bool StackKeptAligned = false;
 static const uint32_t CodeAlignment = 8;
 static const uint32_t NativeFrameSize = sizeof(void*);
-static const uint32_t AlignmentAtPrologue = sizeof(void*);
-static const uint32_t AlignmentMidPrologue = AlignmentAtPrologue;
+static const uint32_t AlignmentAtAsmJSPrologue = sizeof(void*);
 
 static const Scale ScalePointer = TimesEight;
 
 } // namespace jit
 } // namespace js
 
 #include "jit/shared/Assembler-x86-shared.h"
 
--- a/js/src/jit/x86/Assembler-x86.h
+++ b/js/src/jit/x86/Assembler-x86.h
@@ -106,18 +106,17 @@ static MOZ_CONSTEXPR_VAR Register AsmJSI
 #if defined(__GNUC__)
 static const uint32_t StackAlignment = 16;
 #else
 static const uint32_t StackAlignment = 4;
 #endif
 static const bool StackKeptAligned = false;
 static const uint32_t CodeAlignment = 8;
 static const uint32_t NativeFrameSize = sizeof(void*);
-static const uint32_t AlignmentAtPrologue = sizeof(void*);
-static const uint32_t AlignmentMidPrologue = AlignmentAtPrologue;
+static const uint32_t AlignmentAtAsmJSPrologue = sizeof(void*);
 struct ImmTag : public Imm32
 {
     ImmTag(JSValueTag mask)
       : Imm32(int32_t(mask))
     { }
 };
 
 struct ImmType : public ImmTag
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -1875,22 +1875,28 @@ struct JSJitInfo {
 #undef JITINFO_OP_TYPE_BITS
 
     uint32_t isInfallible : 1; /* Is op fallible? False in setters. */
     uint32_t isMovable : 1;    /* Is op movable?  To be movable the op must
                                   not AliasEverything, but even that might
                                   not be enough (e.g. in cases when it can
                                   throw). */
     // XXXbz should we have a JSValueType for the type of the member?
-    uint32_t isInSlot : 1;     /* True if this is a getter that can get a member
-                                  from a slot of the "this" object directly. */
+    uint32_t isAlwaysInSlot : 1; /* True if this is a getter that can always
+                                    get the value from a slot of the "this"
+                                    object. */
+    uint32_t isLazilyCachedInSlot : 1; /* True if this is a getter that can
+                                          sometimes (if the slot doesn't contain
+                                          UndefinedValue()) get the value from a
+                                          slot of the "this" object. */
     uint32_t isTypedMethod : 1; /* True if this is an instance of
                                    JSTypedMethodJitInfo. */
-    uint32_t slotIndex : 12;   /* If isInSlot is true, the index of the slot to
-                                  get the value from.  Otherwise 0. */
+    uint32_t slotIndex : 11;   /* If isAlwaysInSlot or isSometimesInSlot is
+                                  true, the index of the slot to get the value
+                                  from.  Otherwise 0. */
 };
 
 static_assert(sizeof(JSJitInfo) == (sizeof(void*) + 2 * sizeof(uint32_t)),
               "There are several thousand instances of JSJitInfo stored in "
               "a binary. Please don't increase its space requirements without "
               "verifying that there is no other way forward (better packing, "
               "smaller datatypes for fields, subclassing, etc.).");
 
@@ -1938,17 +1944,17 @@ inline int CheckIsParallelNative(JSParal
  * a datum of the type of the union's first member.)
  *
  * Presumably this has something to do with template instantiation.
  * Initializing with a normal function pointer seems to work fine. Hence
  * the ugliness that you see before you.
  */
 #define JS_JITINFO_NATIVE_PARALLEL(infoName, parallelOp)                \
     const JSJitInfo infoName =                                          \
-        {{JS_CAST_PARALLEL_NATIVE_TO(parallelOp, JSJitGetterOp)},0,0,JSJitInfo::ParallelNative,JSJitInfo::AliasEverything,JSVAL_TYPE_MISSING,false,false,false,false,0}
+        {{JS_CAST_PARALLEL_NATIVE_TO(parallelOp, JSJitGetterOp)},0,0,JSJitInfo::ParallelNative,JSJitInfo::AliasEverything,JSVAL_TYPE_MISSING,false,false,false,false,false,0}
 
 #define JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(infoName, wrapperName, serialOp) \
     bool wrapperName##_ParallelNativeThreadSafeWrapper(js::ForkJoinContext *cx, unsigned argc, \
                                                        JS::Value *vp)   \
     {                                                                   \
         return JSParallelNativeThreadSafeWrapper<serialOp>(cx, argc, vp); \
     }                                                                   \
     JS_JITINFO_NATIVE_PARALLEL(infoName, wrapperName##_ParallelNativeThreadSafeWrapper)
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1081,17 +1081,17 @@ GCRuntime::GCRuntime(JSRuntime *rt) :
     sweepingZones(nullptr),
     zoneGroupIndex(0),
     zoneGroups(nullptr),
     currentZoneGroup(nullptr),
     sweepZone(nullptr),
     sweepKindIndex(0),
     abortSweepAfterCurrentGroup(false),
     arenasAllocatedDuringSweep(nullptr),
-#ifdef DEBUG
+#ifdef JS_GC_MARKING_VALIDATION
     markingValidator(nullptr),
 #endif
     interFrameGC(0),
     sliceBudget(SliceBudget::Unlimited),
     incrementalEnabled(true),
     generationalDisabled(0),
     manipulatingDeadZones(false),
     objectsMarkedInDeadZones(0),
@@ -3154,16 +3154,20 @@ class js::gc::MarkingValidator
   private:
     GCRuntime *gc;
     bool initialized;
 
     typedef HashMap<Chunk *, ChunkBitmap *, GCChunkHasher, SystemAllocPolicy> BitmapMap;
     BitmapMap map;
 };
 
+#endif // DEBUG
+
+#ifdef JS_GC_MARKING_VALIDATION
+
 js::gc::MarkingValidator::MarkingValidator(GCRuntime *gc)
   : gc(gc),
     initialized(false)
 {}
 
 js::gc::MarkingValidator::~MarkingValidator()
 {
     if (!map.initialized())
@@ -3342,52 +3346,52 @@ js::gc::MarkingValidator::validate()
                 JS_ASSERT_IF(!bitmap->isMarked(cell, GRAY), !incBitmap->isMarked(cell, GRAY));
 
                 thing += Arena::thingSize(kind);
             }
         }
     }
 }
 
-#endif
+#endif // JS_GC_MARKING_VALIDATION
 
 void
 GCRuntime::computeNonIncrementalMarkingForValidation()
 {
-#ifdef DEBUG
+#ifdef JS_GC_MARKING_VALIDATION
     JS_ASSERT(!markingValidator);
     if (isIncremental && validate)
         markingValidator = js_new<MarkingValidator>(this);
     if (markingValidator)
         markingValidator->nonIncrementalMark();
 #endif
 }
 
 void
 GCRuntime::validateIncrementalMarking()
 {
-#ifdef DEBUG
+#ifdef JS_GC_MARKING_VALIDATION
     if (markingValidator)
         markingValidator->validate();
 #endif
 }
 
 void
 GCRuntime::finishMarkingValidation()
 {
-#ifdef DEBUG
+#ifdef JS_GC_MARKING_VALIDATION
     js_delete(markingValidator);
     markingValidator = nullptr;
 #endif
 }
 
 static void
 AssertNeedsBarrierFlagsConsistent(JSRuntime *rt)
 {
-#ifdef DEBUG
+#ifdef JS_GC_MARKING_VALIDATION
     bool anyNeedsBarrier = false;
     for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next())
         anyNeedsBarrier |= zone->needsBarrier();
     JS_ASSERT(rt->needsBarrier() == anyNeedsBarrier);
 #endif
 }
 
 static void
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -4077,22 +4077,29 @@ js_NewDependentString(JSContext *cx, JSS
 
     JSLinearString *base = baseArg->ensureLinear(cx);
     if (!base)
         return nullptr;
 
     if (start == 0 && length == base->length())
         return base;
 
-    const jschar *chars = base->chars() + start;
-
-    if (JSLinearString *staticStr = cx->staticStrings().lookup(chars, length))
-        return staticStr;
-
-    return JSDependentString::new_(cx, base, chars, length);
+    if (base->hasTwoByteChars()) {
+        AutoCheckCannotGC nogc;
+        const jschar *chars = base->twoByteChars(nogc) + start;
+        if (JSLinearString *staticStr = cx->staticStrings().lookup(chars, length))
+            return staticStr;
+    } else {
+        AutoCheckCannotGC nogc;
+        const Latin1Char *chars = base->latin1Chars(nogc) + start;
+        if (JSLinearString *staticStr = cx->staticStrings().lookup(chars, length))
+            return staticStr;
+    }
+
+    return JSDependentString::new_(cx, base, start, length);
 }
 
 template <AllowGC allowGC>
 JSFlatString *
 js_NewStringCopyN(ExclusiveContext *cx, const jschar *s, size_t n)
 {
     if (JSFatInlineString::twoByteLengthFits(n))
         return NewFatInlineString<allowGC>(cx, TwoByteChars(s, n));
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -5245,45 +5245,48 @@ static const JSJitInfo dom_x_getterinfo 
     { (JSJitGetterOp)dom_get_x },
     0,        /* protoID */
     0,        /* depth */
     JSJitInfo::AliasNone, /* aliasSet */
     JSJitInfo::Getter,
     JSVAL_TYPE_UNKNOWN, /* returnType */
     true,     /* isInfallible. False in setters. */
     true,     /* isMovable */
-    false,    /* isInSlot */
+    false,    /* isAlwaysInSlot */
+    false,    /* isLazilyCachedInSlot */
     false,    /* isTypedMethod */
     0         /* slotIndex */
 };
 
 static const JSJitInfo dom_x_setterinfo = {
     { (JSJitGetterOp)dom_set_x },
     0,        /* protoID */
     0,        /* depth */
     JSJitInfo::Setter,
     JSJitInfo::AliasEverything, /* aliasSet */
     JSVAL_TYPE_UNKNOWN, /* returnType */
     false,    /* isInfallible. False in setters. */
     false,    /* isMovable. */
-    false,    /* isInSlot */
+    false,    /* isAlwaysInSlot */
+    false,    /* isLazilyCachedInSlot */
     false,    /* isTypedMethod */
     0         /* slotIndex */
 };
 
 static const JSJitInfo doFoo_methodinfo = {
     { (JSJitGetterOp)dom_doFoo },
     0,        /* protoID */
     0,        /* depth */
     JSJitInfo::Method,
     JSJitInfo::AliasEverything, /* aliasSet */
     JSVAL_TYPE_UNKNOWN, /* returnType */
     false,    /* isInfallible. False in setters. */
     false,    /* isMovable */
-    false,    /* isInSlot */
+    false,    /* isAlwaysInSlot */
+    false,    /* isLazilyCachedInSlot */
     false,    /* isTypedMethod */
     0         /* slotIndex */
 };
 
 static const JSPropertySpec dom_props[] = {
     {"x",
      JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_NATIVE_ACCESSORS,
      { { (JSPropertyOp)dom_genericGetter, &dom_x_getterinfo } },
--- a/js/src/vm/String-inl.h
+++ b/js/src/vm/String-inl.h
@@ -14,76 +14,91 @@
 #include "jscntxt.h"
 
 #include "gc/Marking.h"
 
 #include "jsgcinlines.h"
 
 namespace js {
 
+template <AllowGC allowGC, typename CharT>
+static MOZ_ALWAYS_INLINE JSInlineString *
+AllocateFatInlineString(ThreadSafeContext *cx, size_t len, CharT **chars)
+{
+    MOZ_ASSERT(JSFatInlineString::lengthFits<CharT>(len));
+
+    if (JSInlineString::lengthFits<CharT>(len)) {
+        JSInlineString *str = JSInlineString::new_<allowGC>(cx);
+        if (!str)
+            return nullptr;
+        *chars = str->init<CharT>(len);
+        return str;
+    }
+
+    JSFatInlineString *str = JSFatInlineString::new_<allowGC>(cx);
+    if (!str)
+        return nullptr;
+    *chars = str->init<CharT>(len);
+    return str;
+}
+
 template <AllowGC allowGC>
 static MOZ_ALWAYS_INLINE JSInlineString *
 NewFatInlineString(ThreadSafeContext *cx, JS::Latin1Chars chars)
 {
     size_t len = chars.length();
-    JS_ASSERT(JSFatInlineString::twoByteLengthFits(len));
 
-    JSInlineString *str;
     jschar *p;
-    if (JSInlineString::twoByteLengthFits(len)) {
-        str = JSInlineString::new_<allowGC>(cx);
-        if (!str)
-            return nullptr;
-        p = str->initTwoByte(len);
-    } else {
-        JSFatInlineString *fatstr = JSFatInlineString::new_<allowGC>(cx);
-        if (!fatstr)
-            return nullptr;
-        p = fatstr->initTwoByte(len);
-        str = fatstr;
-    }
+    JSInlineString *str = AllocateFatInlineString<allowGC>(cx, len, &p);
+    if (!str)
+        return nullptr;
 
     for (size_t i = 0; i < len; ++i)
         p[i] = static_cast<jschar>(chars[i]);
     p[len] = '\0';
     return str;
 }
 
 template <AllowGC allowGC>
 static MOZ_ALWAYS_INLINE JSInlineString *
 NewFatInlineString(ExclusiveContext *cx, JS::TwoByteChars chars)
 {
-    size_t len = chars.length();
-
     /*
      * Don't bother trying to find a static atom; measurement shows that not
      * many get here (for one, Atomize is catching them).
      */
-    JS_ASSERT(JSFatInlineString::twoByteLengthFits(len));
 
-    JSInlineString *str;
+    size_t len = chars.length();
     jschar *storage;
-    if (JSInlineString::twoByteLengthFits(len)) {
-        str = JSInlineString::new_<allowGC>(cx);
-        if (!str)
-            return nullptr;
-        storage = str->initTwoByte(len);
-    } else {
-        JSFatInlineString *fatstr = JSFatInlineString::new_<allowGC>(cx);
-        if (!fatstr)
-            return nullptr;
-        storage = fatstr->initTwoByte(len);
-        str = fatstr;
-    }
+    JSInlineString *str = AllocateFatInlineString<allowGC>(cx, len, &storage);
+    if (!str)
+        return nullptr;
 
     mozilla::PodCopy(storage, chars.start().get(), len);
     storage[len] = 0;
     return str;
 }
 
+template <typename CharT>
+static MOZ_ALWAYS_INLINE JSInlineString *
+NewFatInlineString(ExclusiveContext *cx, Handle<JSLinearString*> base, size_t start, size_t length)
+{
+    MOZ_ASSERT(JSFatInlineString::lengthFits<CharT>(length));
+
+    CharT *chars;
+    JSInlineString *s = AllocateFatInlineString<CanGC>(cx, length, &chars);
+    if (!s)
+        return nullptr;
+
+    AutoCheckCannotGC nogc;
+    mozilla::PodCopy(chars, base->chars<CharT>(nogc) + start, length);
+    chars[length] = 0;
+    return s;
+}
+
 static inline void
 StringWriteBarrierPost(js::ThreadSafeContext *maybecx, JSString **strp)
 {
 }
 
 static inline void
 StringWriteBarrierPostRemove(js::ThreadSafeContext *maybecx, JSString **strp)
 {
@@ -134,71 +149,74 @@ JSRope::new_(js::ThreadSafeContext *cx,
 inline void
 JSRope::markChildren(JSTracer *trc)
 {
     js::gc::MarkStringUnbarriered(trc, &d.s.u2.left, "left child");
     js::gc::MarkStringUnbarriered(trc, &d.s.u3.right, "right child");
 }
 
 MOZ_ALWAYS_INLINE void
-JSDependentString::init(js::ThreadSafeContext *cx, JSLinearString *base, const jschar *chars,
+JSDependentString::init(js::ThreadSafeContext *cx, JSLinearString *base, size_t start,
                         size_t length)
 {
-    JS_ASSERT(!js::IsPoisonedPtr(base));
+    MOZ_ASSERT(!js::IsPoisonedPtr(base));
+    MOZ_ASSERT(start + length <= base->length());
     d.u1.length = length;
-    d.u1.flags = DEPENDENT_FLAGS;
-    d.s.u2.nonInlineCharsTwoByte = chars;
+    JS::AutoCheckCannotGC nogc;
+    if (base->hasLatin1Chars()) {
+        d.u1.flags = DEPENDENT_FLAGS | LATIN1_CHARS_BIT;
+        d.s.u2.nonInlineCharsLatin1 = base->latin1Chars(nogc) + start;
+    } else {
+        d.u1.flags = DEPENDENT_FLAGS;
+        d.s.u2.nonInlineCharsTwoByte = base->twoByteChars(nogc) + start;
+    }
     d.s.u3.base = base;
     js::StringWriteBarrierPost(cx, reinterpret_cast<JSString **>(&d.s.u3.base));
 }
 
 MOZ_ALWAYS_INLINE JSLinearString *
-JSDependentString::new_(js::ExclusiveContext *cx,
-                        JSLinearString *baseArg, const jschar *chars, size_t length)
+JSDependentString::new_(js::ExclusiveContext *cx, JSLinearString *baseArg, size_t start,
+                        size_t length)
 {
     /* Try to avoid long chains of dependent strings. */
-    while (baseArg->isDependent())
+    while (baseArg->isDependent()) {
+        start += baseArg->asDependent().baseOffset();
         baseArg = baseArg->asDependent().base();
-
-    JS_ASSERT(baseArg->isFlat());
+    }
 
-    /*
-     * The chars we are pointing into must be owned by something in the chain
-     * of dependent or undepended strings kept alive by our base pointer.
-     */
-#ifdef DEBUG
-    for (JSLinearString *b = baseArg; ; b = b->base()) {
-        if (chars >= b->chars() && chars < b->chars() + b->length() &&
-            length <= b->length() - (chars - b->chars()))
-        {
-            break;
-        }
-    }
-#endif
+    MOZ_ASSERT(start + length <= baseArg->length());
+    MOZ_ASSERT(baseArg->isFlat());
 
     /*
      * Do not create a string dependent on inline chars from another string,
      * both to avoid the awkward moving-GC hazard this introduces and because it
      * is more efficient to immediately undepend here.
      */
-    if (JSFatInlineString::twoByteLengthFits(length))
-        return js::NewFatInlineString<js::CanGC>(cx, JS::TwoByteChars(chars, length));
+    bool useFatInline = baseArg->hasTwoByteChars()
+                        ? JSFatInlineString::twoByteLengthFits(length)
+                        : JSFatInlineString::latin1LengthFits(length);
+    if (useFatInline) {
+        JS::Rooted<JSLinearString*> base(cx, baseArg);
+        if (baseArg->hasLatin1Chars())
+            return js::NewFatInlineString<JS::Latin1Char>(cx, base, start, length);
+        return js::NewFatInlineString<jschar>(cx, base, start, length);
+    }
 
     JSDependentString *str = (JSDependentString *)js_NewGCString<js::NoGC>(cx);
     if (str) {
-        str->init(cx, baseArg, chars, length);
+        str->init(cx, baseArg, start, length);
         return str;
     }
 
     JS::Rooted<JSLinearString*> base(cx, baseArg);
 
     str = (JSDependentString *)js_NewGCString<js::CanGC>(cx);
     if (!str)
         return nullptr;
-    str->init(cx, base, chars, length);
+    str->init(cx, base, start, length);
     return str;
 }
 
 inline void
 JSString::markBase(JSTracer *trc)
 {
     JS_ASSERT(hasBase());
     js::gc::MarkStringUnbarriered(trc, &d.s.u3.base, "base");
@@ -280,16 +298,44 @@ MOZ_ALWAYS_INLINE JS::Latin1Char *
 JSFatInlineString::initLatin1(size_t length)
 {
     JS_ASSERT(latin1LengthFits(length));
     d.u1.length = length;
     d.u1.flags = INIT_FAT_INLINE_FLAGS | LATIN1_CHARS_BIT;
     return d.inlineStorageLatin1;
 }
 
+template<>
+MOZ_ALWAYS_INLINE JS::Latin1Char *
+JSInlineString::init<JS::Latin1Char>(size_t length)
+{
+    return initLatin1(length);
+}
+
+template<>
+MOZ_ALWAYS_INLINE jschar *
+JSInlineString::init<jschar>(size_t length)
+{
+    return initTwoByte(length);
+}
+
+template<>
+MOZ_ALWAYS_INLINE JS::Latin1Char *
+JSFatInlineString::init<JS::Latin1Char>(size_t length)
+{
+    return initLatin1(length);
+}
+
+template<>
+MOZ_ALWAYS_INLINE jschar *
+JSFatInlineString::init<jschar>(size_t length)
+{
+    return initTwoByte(length);
+}
+
 template <js::AllowGC allowGC>
 MOZ_ALWAYS_INLINE JSFatInlineString *
 JSFatInlineString::new_(js::ThreadSafeContext *cx)
 {
     return js_NewGCFatInlineString<allowGC>(cx);
 }
 
 MOZ_ALWAYS_INLINE void
--- a/js/src/vm/String.h
+++ b/js/src/vm/String.h
@@ -622,16 +622,20 @@ class JSLinearString : public JSString
         JS_ASSERT(!isInline());
         JS_ASSERT(hasTwoByteChars());
         return d.s.u2.nonInlineCharsTwoByte;
     }
 
     MOZ_ALWAYS_INLINE
     const jschar *chars() const;
 
+    template<typename CharT>
+    MOZ_ALWAYS_INLINE
+    const CharT *chars(const JS::AutoCheckCannotGC &nogc) const;
+
     MOZ_ALWAYS_INLINE
     const JS::Latin1Char *latin1Chars(const JS::AutoCheckCannotGC &nogc) const;
 
     MOZ_ALWAYS_INLINE
     const jschar *twoByteChars(const JS::AutoCheckCannotGC &nogc) const;
 
     JS::TwoByteChars range() const {
         JS_ASSERT(JSString::isLinear());
@@ -654,29 +658,42 @@ JS_STATIC_ASSERT(sizeof(JSLinearString) 
 
 class JSDependentString : public JSLinearString
 {
     bool copyNonPureCharsZ(js::ThreadSafeContext *cx, js::ScopedJSFreePtr<jschar> &out) const;
 
     friend class JSString;
     JSFlatString *undepend(js::ExclusiveContext *cx);
 
-    void init(js::ThreadSafeContext *cx, JSLinearString *base, const jschar *chars,
+    void init(js::ThreadSafeContext *cx, JSLinearString *base, size_t start,
               size_t length);
 
     /* Vacuous and therefore unimplemented. */
     bool isDependent() const MOZ_DELETE;
     JSDependentString &asDependent() const MOZ_DELETE;
 
     /* Hide chars(), nonInlineChars() is more efficient. */
     const jschar *chars() const MOZ_DELETE;
 
+    /* The offset of this string's chars in base->chars(). */
+    size_t baseOffset() const {
+        MOZ_ASSERT(JSString::isDependent());
+        JS::AutoCheckCannotGC nogc;
+        size_t offset;
+        if (hasTwoByteChars())
+            offset = twoByteChars(nogc) - base()->twoByteChars(nogc);
+        else
+            offset = latin1Chars(nogc) - base()->latin1Chars(nogc);
+        MOZ_ASSERT(offset < base()->length());
+        return offset;
+    }
+
   public:
     static inline JSLinearString *new_(js::ExclusiveContext *cx, JSLinearString *base,
-                                       const jschar *chars, size_t length);
+                                       size_t start, size_t length);
 };
 
 JS_STATIC_ASSERT(sizeof(JSDependentString) == sizeof(JSString));
 
 class JSFlatString : public JSLinearString
 {
     /* Vacuous and therefore unimplemented. */
     JSFlatString *ensureFlat(JSContext *cx) MOZ_DELETE;
@@ -765,16 +782,19 @@ class JSInlineString : public JSFlatStri
 
   public:
     template <js::AllowGC allowGC>
     static inline JSInlineString *new_(js::ThreadSafeContext *cx);
 
     inline jschar *initTwoByte(size_t length);
     inline JS::Latin1Char *initLatin1(size_t length);
 
+    template <typename CharT>
+    inline CharT *init(size_t length);
+
     inline void resetLength(size_t length);
 
     MOZ_ALWAYS_INLINE
     const jschar *chars() const {
         JS_ASSERT(JSString::isInline());
         JS_ASSERT(hasTwoByteChars());
         return d.inlineStorageTwoByte;
     }
@@ -795,16 +815,19 @@ class JSInlineString : public JSFlatStri
 
     static bool latin1LengthFits(size_t length) {
         return length <= MAX_LENGTH_LATIN1;
     }
     static bool twoByteLengthFits(size_t length) {
         return length <= MAX_LENGTH_TWO_BYTE;
     }
 
+    template<typename CharT>
+    static bool lengthFits(size_t length);
+
     static size_t offsetOfInlineStorage() {
         return offsetof(JSInlineString, d.inlineStorageTwoByte);
     }
 };
 
 JS_STATIC_ASSERT(sizeof(JSInlineString) == sizeof(JSString));
 
 /*
@@ -854,23 +877,29 @@ class JSFatInlineString : public JSInlin
 
     static const size_t MAX_LENGTH_TWO_BYTE = JSString::NUM_INLINE_CHARS_TWO_BYTE +
                                               INLINE_EXTENSION_CHARS_TWO_BYTE
                                               -1 /* null terminator */;
 
     inline jschar *initTwoByte(size_t length);
     inline JS::Latin1Char *initLatin1(size_t length);
 
+    template <typename CharT>
+    inline CharT *init(size_t length);
+
     static bool latin1LengthFits(size_t length) {
         return length <= MAX_LENGTH_LATIN1;
     }
     static bool twoByteLengthFits(size_t length) {
         return length <= MAX_LENGTH_TWO_BYTE;
     }
 
+    template<typename CharT>
+    static bool lengthFits(size_t length);
+
     /* Only called by the GC for strings with the FINALIZE_FAT_INLINE_STRING kind. */
 
     MOZ_ALWAYS_INLINE void finalize(js::FreeOp *fop);
 };
 
 JS_STATIC_ASSERT(sizeof(JSFatInlineString) % js::gc::CellSize == 0);
 
 class JSExternalString : public JSFlatString
@@ -1040,17 +1069,18 @@ class StaticStrings
     }
 
     /* May not return atom, returns null on (reported) failure. */
     inline JSLinearString *getUnitStringForElement(JSContext *cx, JSString *str, size_t index);
 
     static bool isStatic(JSAtom *atom);
 
     /* Return null if no static atom exists for the given (chars, length). */
-    JSAtom *lookup(const jschar *chars, size_t length) {
+    template <typename CharT>
+    JSAtom *lookup(const CharT *chars, size_t length) {
         switch (length) {
           case 1:
             if (chars[0] < UNIT_STATIC_LIMIT)
                 return getUnit(chars[0]);
             return nullptr;
           case 2:
             if (fitsInSmallChar(chars[0]) && fitsInSmallChar(chars[1]))
                 return getLength2(chars[0], chars[1]);
@@ -1260,16 +1290,58 @@ JSLinearString::nonInlineChars(const JS:
 template<>
 MOZ_ALWAYS_INLINE const JS::Latin1Char *
 JSLinearString::nonInlineChars(const JS::AutoCheckCannotGC &nogc) const
 {
     return nonInlineLatin1Chars(nogc);
 }
 
 template<>
+MOZ_ALWAYS_INLINE const jschar *
+JSLinearString::chars(const JS::AutoCheckCannotGC &nogc) const
+{
+    return twoByteChars(nogc);
+}
+
+template<>
+MOZ_ALWAYS_INLINE const JS::Latin1Char *
+JSLinearString::chars(const JS::AutoCheckCannotGC &nogc) const
+{
+    return latin1Chars(nogc);
+}
+
+template<>
+MOZ_ALWAYS_INLINE bool
+JSInlineString::lengthFits<JS::Latin1Char>(size_t length)
+{
+    return latin1LengthFits(length);
+}
+
+template<>
+MOZ_ALWAYS_INLINE bool
+JSInlineString::lengthFits<jschar>(size_t length)
+{
+    return twoByteLengthFits(length);
+}
+
+template<>
+MOZ_ALWAYS_INLINE bool
+JSFatInlineString::lengthFits<JS::Latin1Char>(size_t length)
+{
+    return latin1LengthFits(length);
+}
+
+template<>
+MOZ_ALWAYS_INLINE bool
+JSFatInlineString::lengthFits<jschar>(size_t length)
+{
+    return twoByteLengthFits(length);
+}
+
+template<>
 MOZ_ALWAYS_INLINE void
 JSString::setNonInlineChars(const jschar *chars)
 {
     d.s.u2.nonInlineCharsTwoByte = chars;
 }
 
 template<>
 MOZ_ALWAYS_INLINE void
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -1669,16 +1669,20 @@ ContainerState::FindOpaqueBackgroundColo
         // aligned, so no need to round out and in here.
         if (!bounds.Intersects(appUnitRect))
           continue;
 
         if (!bounds.Contains(appUnitRect))
           break;
       }
 
+      if (item->IsInvisibleInRect(appUnitRect)) {
+        continue;
+      }
+
       nscolor color;
       if (item->IsUniform(mBuilder, &color) && NS_GET_A(color) == 255)
         return color;
 
       break;
     }
     break;
   }
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -1705,16 +1705,25 @@ nsDisplayItem::ZIndex() const
   if (position->mZIndex.GetUnit() == eStyleUnit_Integer)
     return position->mZIndex.GetIntValue();
 
   // sort the auto and 0 elements together
   return 0;
 }
 
 bool
+nsDisplayItem::ComputeVisibility(nsDisplayListBuilder* aBuilder,
+                                 nsRegion* aVisibleRegion,
+                                 const nsRect& aAllowVisibleRegionExpansion)
+{
+  return !mVisibleRect.IsEmpty() &&
+    !IsInvisibleInRect(aVisibleRegion->GetBounds());
+}
+
+bool
 nsDisplayItem::RecomputeVisibility(nsDisplayListBuilder* aBuilder,
                                    nsRegion* aVisibleRegion) {
   nsRect bounds = GetClippedBounds(aBuilder);
 
   nsRegion itemVisible;
   itemVisible.And(*aVisibleRegion, bounds);
   mVisibleRect = itemVisible.GetBounds();
 
@@ -2641,36 +2650,30 @@ nsDisplayOutline::Paint(nsDisplayListBui
   nsPoint offset = ToReferenceFrame();
   nsCSSRendering::PaintOutline(mFrame->PresContext(), *aCtx, mFrame,
                                mVisibleRect,
                                nsRect(offset, mFrame->GetSize()),
                                mFrame->StyleContext());
 }
 
 bool
-nsDisplayOutline::ComputeVisibility(nsDisplayListBuilder* aBuilder,
-                                    nsRegion* aVisibleRegion,
-                                    const nsRect& aAllowVisibleRegionExpansion) {
-  if (!nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion,
-                                        aAllowVisibleRegionExpansion)) {
-    return false;
-  }
-
+nsDisplayOutline::IsInvisibleInRect(const nsRect& aRect)
+{
   const nsStyleOutline* outline = mFrame->StyleOutline();
   nsRect borderBox(ToReferenceFrame(), mFrame->GetSize());
-  if (borderBox.Contains(aVisibleRegion->GetBounds()) &&
+  if (borderBox.Contains(aRect) &&
       !nsLayoutUtils::HasNonZeroCorner(outline->mOutlineRadius)) {
     if (outline->mOutlineOffset >= 0) {
-      // the visible region is entirely inside the border-rect, and the outline
-      // isn't rendered inside the border-rect, so the outline is not visible
-      return false;
+      // aRect is entirely inside the border-rect, and the outline isn't
+      // rendered inside the border-rect, so the outline is not visible.
+      return true;
     }
   }
 
-  return true;
+  return false;
 }
 
 void
 nsDisplayEventReceiver::HitTest(nsDisplayListBuilder* aBuilder,
                                 const nsRect& aRect,
                                 HitTestState* aState,
                                 nsTArray<nsIFrame*> *aOutFrames)
 {
@@ -2720,40 +2723,34 @@ void
 nsDisplayCaret::Paint(nsDisplayListBuilder* aBuilder,
                       nsRenderingContext* aCtx) {
   // Note: Because we exist, we know that the caret is visible, so we don't
   // need to check for the caret's visibility.
   mCaret->PaintCaret(aBuilder, aCtx, mFrame, ToReferenceFrame());
 }
 
 bool
-nsDisplayBorder::ComputeVisibility(nsDisplayListBuilder* aBuilder,
-                                   nsRegion* aVisibleRegion,
-                                   const nsRect& aAllowVisibleRegionExpansion) {
-  if (!nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion,
-                                        aAllowVisibleRegionExpansion)) {
-    return false;
-  }
-
+nsDisplayBorder::IsInvisibleInRect(const nsRect& aRect)
+{
   nsRect paddingRect = mFrame->GetPaddingRect() - mFrame->GetPosition() +
     ToReferenceFrame();
   const nsStyleBorder *styleBorder;
-  if (paddingRect.Contains(aVisibleRegion->GetBounds()) &&
+  if (paddingRect.Contains(aRect) &&
       !(styleBorder = mFrame->StyleBorder())->IsBorderImageLoaded() &&
       !nsLayoutUtils::HasNonZeroCorner(styleBorder->mBorderRadius)) {
-    // the visible region is entirely inside the content rect, and no part
+    // aRect is entirely inside the content rect, and no part
     // of the border is rendered inside the content rect, so we are not
     // visible
     // Skip this if there's a border-image (which draws a background
     // too) or if there is a border-radius (which makes the border draw
     // further in).
-    return false;
-  }
-
-  return true;
+    return true;
+  }
+
+  return false;
 }
   
 nsDisplayItemGeometry* 
 nsDisplayBorder::AllocateGeometry(nsDisplayListBuilder* aBuilder)
 {
   return new nsDisplayBorderGeometry(this, aBuilder);
 }
 
@@ -2875,41 +2872,45 @@ nsDisplayBoxShadowOuter::GetBounds(nsDis
 
 nsRect
 nsDisplayBoxShadowOuter::GetBoundsInternal() {
   return nsLayoutUtils::GetBoxShadowRectForFrame(mFrame, mFrame->GetSize()) +
          ToReferenceFrame();
 }
 
 bool
+nsDisplayBoxShadowOuter::IsInvisibleInRect(const nsRect& aRect)
+{
+  nsPoint origin = ToReferenceFrame();
+  nsRect frameRect(origin, mFrame->GetSize());
+  if (!frameRect.Contains(aRect))
+    return false;
+
+  // the visible region is entirely inside the border-rect, and box shadows
+  // never render within the border-rect (unless there's a border radius).
+  nscoord twipsRadii[8];
+  bool hasBorderRadii = mFrame->GetBorderRadii(twipsRadii);
+  if (!hasBorderRadii)
+    return true;
+
+  return RoundedRectContainsRect(frameRect, twipsRadii, aRect);
+}
+
+bool
 nsDisplayBoxShadowOuter::ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                            nsRegion* aVisibleRegion,
                                            const nsRect& aAllowVisibleRegionExpansion) {
   if (!nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion,
                                         aAllowVisibleRegionExpansion)) {
     return false;
   }
 
   // Store the actual visible region
   mVisibleRegion.And(*aVisibleRegion, mVisibleRect);
-
-  nsPoint origin = ToReferenceFrame();
-  nsRect visibleBounds = aVisibleRegion->GetBounds();
-  nsRect frameRect(origin, mFrame->GetSize());
-  if (!frameRect.Contains(visibleBounds))
-    return true;
-
-  // the visible region is entirely inside the border-rect, and box shadows
-  // never render within the border-rect (unless there's a border radius).
-  nscoord twipsRadii[8];
-  bool hasBorderRadii = mFrame->GetBorderRadii(twipsRadii);
-  if (!hasBorderRadii)
-    return false;
-
-  return !RoundedRectContainsRect(frameRect, twipsRadii, visibleBounds);
+  return true;
 }
 
 void
 nsDisplayBoxShadowOuter::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                                    const nsDisplayItemGeometry* aGeometry,
                                                    nsRegion* aInvalidRegion)
 {
   const nsDisplayItemGenericGeometry* geometry =
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -929,16 +929,24 @@ public:
    * contains the area drawn by this display item
    */
   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap)
   {
     *aSnap = false;
     return nsRect(ToReferenceFrame(), Frame()->GetSize());
   }
   /**
+   * Returns true if nothing will be rendered inside aRect, false if uncertain.
+   * aRect is assumed to be contained in this item's bounds.
+   */
+  virtual bool IsInvisibleInRect(const nsRect& aRect)
+  {
+    return false;
+  }
+  /**
    * Returns the result of GetBounds intersected with the item's clip.
    * The intersection is approximate since rounded corners are not taking into
    * account.
    */
   nsRect GetClippedBounds(nsDisplayListBuilder* aBuilder);
   nsRect GetBorderRect() {
     return nsRect(ToReferenceFrame(), Frame()->GetSize());
   }
@@ -1186,18 +1194,17 @@ public:
    * background behind a plugin is visible.
    * This method needs to be idempotent.
    *
    * @return true if the item is visible, false if no part of the item
    * is visible.
    */
   virtual bool ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                    nsRegion* aVisibleRegion,
-                                   const nsRect& aAllowVisibleRegionExpansion)
-  { return !mVisibleRect.IsEmpty(); }
+                                   const nsRect& aAllowVisibleRegionExpansion);
 
   /**
    * Try to merge with the other item (which is below us in the display
    * list). This gets used by nsDisplayClip to coalesce clipping operations
    * (optimization), by nsDisplayOpacity to merge rendering for the same
    * content element into a single opacity group (correctness), and will be
    * used by nsDisplayOutline to merge multiple outlines for the same element
    * (also for correctness).
@@ -2017,21 +2024,19 @@ public:
     MOZ_COUNT_CTOR(nsDisplayBorder);
   }
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplayBorder() {
     MOZ_COUNT_DTOR(nsDisplayBorder);
   }
 #endif
 
+  virtual bool IsInvisibleInRect(const nsRect& aRect) MOZ_OVERRIDE;
   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) MOZ_OVERRIDE;
   virtual void Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) MOZ_OVERRIDE;
-  virtual bool ComputeVisibility(nsDisplayListBuilder* aBuilder,
-                                   nsRegion* aVisibleRegion,
-                                   const nsRect& aAllowVisibleRegionExpansion) MOZ_OVERRIDE;
   NS_DISPLAY_DECL_NAME("Border", TYPE_BORDER)
   
   virtual nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) MOZ_OVERRIDE;
 
   virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                          const nsDisplayItemGeometry* aGeometry,
                                          nsRegion* aInvalidRegion) MOZ_OVERRIDE;
 
@@ -2339,16 +2344,17 @@ public:
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplayBoxShadowOuter() {
     MOZ_COUNT_DTOR(nsDisplayBoxShadowOuter);
   }
 #endif
 
   virtual void Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) MOZ_OVERRIDE;
   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) MOZ_OVERRIDE;
+  virtual bool IsInvisibleInRect(const nsRect& aRect) MOZ_OVERRIDE;
   virtual bool ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                    nsRegion* aVisibleRegion,
                                    const nsRect& aAllowVisibleRegionExpansion) MOZ_OVERRIDE;
   NS_DISPLAY_DECL_NAME("BoxShadowOuter", TYPE_BOX_SHADOW_OUTER)
   
   virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                          const nsDisplayItemGeometry* aGeometry,
                                          nsRegion* aInvalidRegion) MOZ_OVERRIDE;
@@ -2425,21 +2431,19 @@ public:
     MOZ_COUNT_CTOR(nsDisplayOutline);
   }
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplayOutline() {
     MOZ_COUNT_DTOR(nsDisplayOutline);
   }
 #endif
 
+  virtual bool IsInvisibleInRect(const nsRect& aRect) MOZ_OVERRIDE;
   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) MOZ_OVERRIDE;
   virtual void Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) MOZ_OVERRIDE;
-  virtual bool ComputeVisibility(nsDisplayListBuilder* aBuilder,
-                                   nsRegion* aVisibleRegion,
-                                   const nsRect& aAllowVisibleRegionExpansion) MOZ_OVERRIDE;
   NS_DISPLAY_DECL_NAME("Outline", TYPE_OUTLINE)
 };
 
 /**
  * A class that lets you receive events within the frame bounds but never paints.
  */
 class nsDisplayEventReceiver : public nsDisplayItem {
 public:
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -225,16 +225,18 @@ static void Shutdown();
 #include "mozilla/dom/mobilemessage/SmsServicesFactory.h"
 #include "nsIPowerManagerService.h"
 #include "nsIAlarmHalService.h"
 #include "nsIMediaManager.h"
 #include "nsMixedContentBlocker.h"
 
 #include "AudioChannelService.h"
 
+#include "mozilla/dom/DataStoreService.h"
+
 #include "mozilla/dom/power/PowerManagerService.h"
 #include "mozilla/dom/alarm/AlarmHalService.h"
 #include "mozilla/dom/time/TimeService.h"
 #include "StreamingProtocolService.h"
 
 #include "mozilla/dom/telephony/TelephonyFactory.h"
 #include "nsITelephonyService.h"
 
@@ -584,20 +586,25 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsDOMScri
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(Geolocation, Init)
 
 #define NS_GEOLOCATION_SERVICE_CID \
   { 0x404d02a, 0x1CA, 0xAAAB, { 0x47, 0x62, 0x94, 0x4b, 0x1b, 0xf2, 0xf7, 0xb5 } }
 
 #define NS_AUDIOCHANNEL_SERVICE_CID \
   { 0xf712e983, 0x048a, 0x443f, { 0x88, 0x02, 0xfc, 0xc3, 0xd9, 0x27, 0xce, 0xac }}
 
+#define NS_DATASTORE_SERVICE_CID \
+  { 0x0d4285fe, 0xf1b3, 0x49fa, { 0xbc, 0x51, 0xa4, 0xa8, 0x3f, 0x0a, 0xaf, 0x85 }}
+
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsGeolocationService, nsGeolocationService::GetGeolocationService)
 
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(AudioChannelService, AudioChannelService::GetAudioChannelService)
 
+NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(DataStoreService, DataStoreService::GetOrCreate)
+
 #ifdef MOZ_WEBSPEECH
 NS_GENERIC_FACTORY_CONSTRUCTOR(FakeSpeechRecognitionService)
 #endif
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsCSPContext)
 NS_GENERIC_FACTORY_CONSTRUCTOR(CSPService)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsMixedContentBlocker)
 
@@ -737,16 +744,17 @@ NS_DEFINE_NAMED_CID(NS_HTMLEDITOR_CID);
 NS_DEFINE_NAMED_CID(NS_EDITORCONTROLLER_CID);
 NS_DEFINE_NAMED_CID(NS_EDITINGCONTROLLER_CID);
 NS_DEFINE_NAMED_CID(NS_EDITORCOMMANDTABLE_CID);
 NS_DEFINE_NAMED_CID(NS_EDITINGCOMMANDTABLE_CID);
 NS_DEFINE_NAMED_CID(NS_TEXTSERVICESDOCUMENT_CID);
 NS_DEFINE_NAMED_CID(NS_GEOLOCATION_SERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_GEOLOCATION_CID);
 NS_DEFINE_NAMED_CID(NS_AUDIOCHANNEL_SERVICE_CID);
+NS_DEFINE_NAMED_CID(NS_DATASTORE_SERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_FOCUSMANAGER_CID);
 NS_DEFINE_NAMED_CID(CSPSERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_CSPCONTEXT_CID);
 NS_DEFINE_NAMED_CID(NS_MIXEDCONTENTBLOCKER_CID);
 NS_DEFINE_NAMED_CID(NS_EVENTLISTENERSERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_GLOBALMESSAGEMANAGER_CID);
 NS_DEFINE_NAMED_CID(NS_PARENTPROCESSMESSAGEMANAGER_CID);
 NS_DEFINE_NAMED_CID(NS_CHILDPROCESSMESSAGEMANAGER_CID);
@@ -1025,16 +1033,17 @@ static const mozilla::Module::CIDEntry k
   { &kNS_EDITORCONTROLLER_CID, false, nullptr, nsEditorControllerConstructor },
   { &kNS_EDITINGCONTROLLER_CID, false, nullptr, nsEditingControllerConstructor },
   { &kNS_EDITORCOMMANDTABLE_CID, false, nullptr, nsEditorCommandTableConstructor },
   { &kNS_EDITINGCOMMANDTABLE_CID, false, nullptr, nsEditingCommandTableConstructor },
   { &kNS_TEXTSERVICESDOCUMENT_CID, false, nullptr, nsTextServicesDocumentConstructor },
   { &kNS_GEOLOCATION_SERVICE_CID, false, nullptr, nsGeolocationServiceConstructor },
   { &kNS_GEOLOCATION_CID, false, nullptr, GeolocationConstructor },
   { &kNS_AUDIOCHANNEL_SERVICE_CID, false, nullptr, AudioChannelServiceConstructor },
+  { &kNS_DATASTORE_SERVICE_CID, false, nullptr, DataStoreServiceConstructor },
   { &kNS_FOCUSMANAGER_CID, false, nullptr, CreateFocusManager },
 #ifdef MOZ_WEBSPEECH
   { &kNS_FAKE_SPEECH_RECOGNITION_SERVICE_CID, false, nullptr, FakeSpeechRecognitionServiceConstructor },
   { &kNS_SYNTHVOICEREGISTRY_CID, true, nullptr, nsSynthVoiceRegistryConstructor },
 #endif
   { &kCSPSERVICE_CID, false, nullptr, CSPServiceConstructor },
   { &kNS_CSPCONTEXT_CID, false, nullptr, nsCSPContextConstructor },
   { &kNS_MIXEDCONTENTBLOCKER_CID, false, nullptr, nsMixedContentBlockerConstructor },
@@ -1180,16 +1189,17 @@ static const mozilla::Module::ContractID
   { NS_AUDIOCHANNELAGENT_CONTRACTID, &kNS_AUDIOCHANNELAGENT_CID },
   { "@mozilla.org/editor/htmleditor;1", &kNS_HTMLEDITOR_CID },
   { "@mozilla.org/editor/editorcontroller;1", &kNS_EDITORCONTROLLER_CID },
   { "@mozilla.org/editor/editingcontroller;1", &kNS_EDITINGCONTROLLER_CID },
   { "@mozilla.org/textservices/textservicesdocument;1", &kNS_TEXTSERVICESDOCUMENT_CID },
   { "@mozilla.org/geolocation/service;1", &kNS_GEOLOCATION_SERVICE_CID },
   { "@mozilla.org/geolocation;1", &kNS_GEOLOCATION_CID },
   { "@mozilla.org/audiochannel/service;1", &kNS_AUDIOCHANNEL_SERVICE_CID },
+  { "@mozilla.org/datastore-service;1", &kNS_DATASTORE_SERVICE_CID },
   { "@mozilla.org/focus-manager;1", &kNS_FOCUSMANAGER_CID },
 #ifdef MOZ_WEBSPEECH
   { NS_SPEECH_RECOGNITION_SERVICE_CONTRACTID_PREFIX "fake", &kNS_FAKE_SPEECH_RECOGNITION_SERVICE_CID },
   { NS_SYNTHVOICEREGISTRY_CONTRACTID, &kNS_SYNTHVOICEREGISTRY_CID },
 #endif
   { CSPSERVICE_CONTRACTID, &kCSPSERVICE_CID },
   { NS_CSPCONTEXT_CONTRACTID, &kNS_CSPCONTEXT_CID },
   { NS_MIXEDCONTENTBLOCKER_CONTRACTID, &kNS_MIXEDCONTENTBLOCKER_CID },
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -60,16 +60,17 @@
 #include "nsMathMLOperators.h"
 #include "Navigator.h"
 #include "DOMStorageObserver.h"
 #include "CacheObserver.h"
 #include "DisplayItemClip.h"
 #include "ActiveLayerTracker.h"
 
 #include "AudioChannelService.h"
+#include "mozilla/dom/DataStoreService.h"
 
 #ifdef MOZ_XUL
 #include "nsXULPopupManager.h"
 #include "nsXULContentUtils.h"
 #include "nsXULPrototypeCache.h"
 #include "nsXULTooltipListener.h"
 
 #include "inDOMView.h"
@@ -406,16 +407,18 @@ nsLayoutStatics::Shutdown()
 
   nsLayoutUtils::Shutdown();
 
   nsHyphenationManager::Shutdown();
   nsDOMMutationObserver::Shutdown();
 
   AudioChannelService::Shutdown();
 
+  DataStoreService::Shutdown();
+
   ContentParent::ShutDown();
 
   nsRefreshDriver::Shutdown();
 
   DisplayItemClip::Shutdown();
 
   nsDocument::XPCOMShutdown();
 
--- a/layout/generic/nsColumnSetFrame.cpp
+++ b/layout/generic/nsColumnSetFrame.cpp
@@ -953,16 +953,34 @@ nsColumnSetFrame::Reflow(nsPresContext* 
     NS_ASSERTION(aReflowState.ComputedHeight() != NS_INTRINSICSIZE,
                  "Unexpected computed height");
     AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT);
   }
   else {
     RemoveStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT);
   }
 
+#ifdef DEBUG
+  nsFrameList::Enumerator oc(GetChildList(kOverflowContainersList));
+  for (; !oc.AtEnd(); oc.Next()) {
+    MOZ_ASSERT(!IS_TRUE_OVERFLOW_CONTAINER(oc.get()));
+  }
+  nsFrameList::Enumerator eoc(GetChildList(kExcessOverflowContainersList));
+  for (; !eoc.AtEnd(); eoc.Next()) {
+    MOZ_ASSERT(!IS_TRUE_OVERFLOW_CONTAINER(eoc.get()));
+  }
+#endif
+
+  nsOverflowAreas ocBounds;
+  nsReflowStatus ocStatus = NS_FRAME_COMPLETE;
+  if (GetPrevInFlow()) {
+    ReflowOverflowContainerChildren(aPresContext, aReflowState, ocBounds, 0,
+                                    ocStatus);
+  }
+
   //------------ Handle Incremental Reflow -----------------
 
   ReflowConfig config = ChooseColumnStrategy(aReflowState);
   
   // If balancing, then we allow the last column to grow to unbounded
   // height during the first reflow. This gives us a way to estimate
   // what the average column height should be, because we can measure
   // the heights of all the columns and sum them up. But don't do this
@@ -989,25 +1007,29 @@ nsColumnSetFrame::Reflow(nsPresContext* 
 
   if (aPresContext->HasPendingInterrupt() &&
       aReflowState.AvailableHeight() == NS_UNCONSTRAINEDSIZE) {
     // In this situation, we might be lying about our reflow status, because
     // our last kid (the one that got interrupted) was incomplete.  Fix that.
     aStatus = NS_FRAME_COMPLETE;
   }
 
+  NS_ASSERTION(NS_FRAME_IS_FULLY_COMPLETE(aStatus) ||
+               aReflowState.AvailableHeight() != NS_UNCONSTRAINEDSIZE,
+               "Column set should be complete if the available height is unconstrained");
+
+  // Merge overflow container bounds and status.
+  aDesiredSize.mOverflowAreas.UnionWith(ocBounds);
+  NS_MergeReflowStatusInto(&aStatus, ocStatus);
+
   FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowState, aStatus, false);
 
   aDesiredSize.mCarriedOutBottomMargin = carriedOutBottomMargin;
 
   NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
-
-  NS_ASSERTION(NS_FRAME_IS_FULLY_COMPLETE(aStatus) ||
-               aReflowState.AvailableHeight() != NS_UNCONSTRAINEDSIZE,
-               "Column set should be complete if the available height is unconstrained");
 }
 
 void
 nsColumnSetFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                    const nsRect&           aDirtyRect,
                                    const nsDisplayListSet& aLists) {
   DisplayBorderBackgroundOutline(aBuilder, aLists);
 
--- a/layout/generic/nsColumnSetFrame.h
+++ b/layout/generic/nsColumnSetFrame.h
@@ -2,16 +2,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/. */
 
 /* rendering object for css3 multi-column layout */
 
 #include "mozilla/Attributes.h"
 #include "nsContainerFrame.h"
+#include "nsIFrameInlines.h" // for methods used by IS_TRUE_OVERFLOW_CONTAINER
 
 class nsColumnSetFrame : public nsContainerFrame {
 public:
   NS_DECL_FRAMEARENA_HELPERS
 
   nsColumnSetFrame(nsStyleContext* aContext);
 
   virtual void Reflow(nsPresContext* aPresContext,
@@ -46,18 +47,21 @@ public:
     // if no children return nullptr
     if (!frame)
       return nullptr;
 
     return frame->GetContentInsertionFrame();
   }
 
   virtual nsresult StealFrame(nsIFrame* aChild, bool aForceNormal) MOZ_OVERRIDE
-  { // nsColumnSetFrame keeps overflow containers in main child list
-    return nsContainerFrame::StealFrame(aChild, true);
+  {
+    // nsColumnSetFrame keeps true overflow containers in the normal flow
+    // child lists (i.e. the principal and overflow lists).
+    return nsContainerFrame::StealFrame(aChild,
+                                        IS_TRUE_OVERFLOW_CONTAINER(aChild));
   }
 
   virtual bool IsFrameOfType(uint32_t aFlags) const MOZ_OVERRIDE
    {
      return nsContainerFrame::IsFrameOfType(aFlags &
               ~(nsIFrame::eCanContainOverflowContainers));
    }
 
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -1793,32 +1793,19 @@ static void AdjustViews(nsIFrame* aFrame
     nsFrameList::Enumerator childFrames(lists.CurrentList());
     for (; !childFrames.AtEnd(); childFrames.Next()) {
       AdjustViews(childFrames.get());
     }
   }
 }
 
 static bool
-CanScrollWithBlitting(nsIFrame* aFrame)
+NeedToInvalidateOnScroll(nsIFrame* aFrame)
 {
-  if (aFrame->GetStateBits() & NS_SCROLLFRAME_INVALIDATE_CONTENTS_ON_SCROLL)
-    return false;
-
-  for (nsIFrame* f = aFrame; f;
-       f = nsLayoutUtils::GetCrossDocParentFrame(f)) {
-    if (nsSVGIntegrationUtils::UsingEffectsForFrame(f) ||
-        f->IsFrameOfType(nsIFrame::eSVG) ||
-        f->GetStateBits() & NS_FRAME_NO_COMPONENT_ALPHA) {
-      return false;
-    }
-    if (nsLayoutUtils::IsPopup(f))
-      break;
-  }
-  return true;
+  return (aFrame->GetStateBits() & NS_SCROLLFRAME_INVALIDATE_CONTENTS_ON_SCROLL) != 0;
 }
 
 bool ScrollFrameHelper::IsIgnoringViewportClipping() const
 {
   if (!mIsRoot)
     return false;
   nsSubDocumentFrame* subdocFrame = static_cast<nsSubDocumentFrame*>
     (nsLayoutUtils::GetCrossDocParentFrame(mOuter->PresContext()->PresShell()->GetRootFrame()));
@@ -1895,24 +1882,22 @@ void ScrollFrameHelper::ScrollVisual(nsP
   // scroll frame of a content document, then IsAlwaysActive()
   // will return true from now on and MarkInactive() won't
   // have any effect.
   mHasBeenScrolled = true;
 
   AdjustViews(mScrolledFrame);
   // We need to call this after fixing up the view positions
   // to be consistent with the frame hierarchy.
-  bool canScrollWithBlitting = CanScrollWithBlitting(mOuter);
+  bool needToInvalidateOnScroll = NeedToInvalidateOnScroll(mOuter);
   mOuter->RemoveStateBits(NS_SCROLLFRAME_INVALIDATE_CONTENTS_ON_SCROLL);
-  if (IsScrollingActive()) {
-    if (!canScrollWithBlitting) {
-      MarkInactive();
-    }
+  if (IsScrollingActive() && needToInvalidateOnScroll) {
+    MarkInactive();
   }
-  if (canScrollWithBlitting) {
+  if (!needToInvalidateOnScroll) {
     MarkActive();
   }
 
   mOuter->SchedulePaint();
 }
 
 /**
  * Clamp desired scroll position aDesired and range [aDestLower, aDestUpper]
@@ -2191,19 +2176,18 @@ ScrollFrameHelper::AppendScrollPartsTo(n
   for (nsIFrame* kid = mOuter->GetFirstPrincipalChild(); kid; kid = kid->GetNextSibling()) {
     if (kid == mScrolledFrame ||
         (kid->IsPositioned() || overlayScrollbars) != aPositioned)
       continue;
 
     scrollParts.AppendElement(kid);
   }
 
-  mozilla::layers::FrameMetrics::ViewID scrollTargetId = aCreateLayer
-    ? nsLayoutUtils::FindOrCreateIDFor(mScrolledFrame->GetContent())
-    : mozilla::layers::FrameMetrics::NULL_SCROLL_ID;
+  mozilla::layers::FrameMetrics::ViewID scrollTargetId =
+    nsLayoutUtils::FindOrCreateIDFor(mScrolledFrame->GetContent());
 
   scrollParts.Sort(HoveredStateComparator());
 
   for (uint32_t i = 0; i < scrollParts.Length(); ++i) {
     nsDisplayListCollection partList;
     mOuter->BuildDisplayListForChild(
       aBuilder, scrollParts[i], aDirtyRect, partList,
       nsIFrame::DISPLAY_CHILD_FORCE_STACKING_CONTEXT);
@@ -2211,21 +2195,26 @@ ScrollFrameHelper::AppendScrollPartsTo(n
     uint32_t flags = 0;
     if (scrollParts[i] == mVScrollbarBox) {
       flags |= nsDisplayOwnLayer::VERTICAL_SCROLLBAR;
     }
     if (scrollParts[i] == mHScrollbarBox) {
       flags |= nsDisplayOwnLayer::HORIZONTAL_SCROLLBAR;
     }
 
+    // Always create layers for overlay scrollbars so that we don't create a
+    // giant layer covering the whole scrollport if both scrollbars are visible.
+    bool isOverlayScrollbar = (flags != 0) && overlayScrollbars;
+    bool createLayer = aCreateLayer || isOverlayScrollbar;
+
     // DISPLAY_CHILD_FORCE_STACKING_CONTEXT put everything into
     // partList.PositionedDescendants().
     ::AppendToTop(aBuilder, aLists,
                   partList.PositionedDescendants(), scrollParts[i],
-                  aCreateLayer, flags, scrollTargetId, aPositioned);
+                  createLayer, flags, scrollTargetId, aPositioned);
   }
 }
 
 class ScrollLayerWrapper : public nsDisplayWrapper
 {
 public:
   ScrollLayerWrapper(nsIFrame* aScrollFrame, nsIFrame* aScrolledFrame)
     : mCount(0)
@@ -2413,17 +2402,17 @@ ScrollFrameHelper::BuildDisplayList(nsDi
   if (aBuilder->IsForImageVisibility()) {
     mLastUpdateImagesPos = GetScrollPosition();
   }
 
   mOuter->DisplayBorderBackgroundOutline(aBuilder, aLists);
 
   if (aBuilder->IsPaintingToWindow()) {
     mScrollPosAtLastPaint = GetScrollPosition();
-    if (IsScrollingActive() && !CanScrollWithBlitting(mOuter)) {
+    if (IsScrollingActive() && NeedToInvalidateOnScroll(mOuter)) {
       MarkInactive();
     }
     if (IsScrollingActive()) {
       if (mScrollPosForLayerPixelAlignment == nsPoint(-1,-1)) {
         mScrollPosForLayerPixelAlignment = mScrollPosAtLastPaint;
       }
     } else {
       mScrollPosForLayerPixelAlignment = nsPoint(-1,-1);
@@ -2464,17 +2453,18 @@ ScrollFrameHelper::BuildDisplayList(nsDi
     // Don't clip the scrolled child, and don't paint scrollbars/scrollcorner.
     // The scrolled frame shouldn't have its own background/border, so we
     // can just pass aLists directly.
     mOuter->BuildDisplayListForChild(aBuilder, mScrolledFrame,
                                      aDirtyRect, aLists);
 
     if (addScrollBars) {
       // Add overlay scrollbars.
-      AppendScrollPartsTo(aBuilder, aDirtyRect, aLists, true, true);
+      AppendScrollPartsTo(aBuilder, aDirtyRect, aLists,
+                          createLayersForScrollbars, true);
     }
 
     return;
   }
 
   // Now display the scrollbars and scrollcorner. These parts are drawn
   // in the border-background layer, on top of our own background and
   // borders and underneath borders and backgrounds of later elements
@@ -2656,19 +2646,18 @@ ScrollFrameHelper::BuildDisplayList(nsDi
     // In case we are not using displayport or the nsDisplayScrollLayers are
     // flattened during visibility computation, we still need to export the
     // metadata about this scroll box to the compositor process.
     nsDisplayScrollInfoLayer* layerItem = new (aBuilder) nsDisplayScrollInfoLayer(
       aBuilder, mScrolledFrame, mOuter);
     scrolledContent.BorderBackground()->AppendNewToBottom(layerItem);
   }
   // Now display overlay scrollbars and the resizer, if we have one.
-  // Always create layers for these, so that we don't create a giant layer
-  // covering the whole scrollport if both scrollbars are visible.
-  AppendScrollPartsTo(aBuilder, aDirtyRect, scrolledContent, true, true);
+  AppendScrollPartsTo(aBuilder, aDirtyRect, scrolledContent,
+                      createLayersForScrollbars, true);
   scrolledContent.MoveTo(aLists);
 }
 
 bool
 ScrollFrameHelper::IsRectNearlyVisible(const nsRect& aRect) const
 {
   // Use the right rect depending on if a display port is set.
   nsRect displayPort;
--- a/layout/style/StyleRule.cpp
+++ b/layout/style/StyleRule.cpp
@@ -468,31 +468,47 @@ void nsCSSSelector::SetOperator(char16_t
 {
   mOperator = aOperator;
 }
 
 int32_t nsCSSSelector::CalcWeightWithoutNegations() const
 {
   int32_t weight = 0;
 
-  MOZ_ASSERT(!IsPseudoElement() ||
-             mPseudoType >= nsCSSPseudoElements::ePseudo_PseudoElementCount ||
-             (!mIDList && !mClassList && !mAttrList),
-             "if pseudo-elements can have ID, class or attribute selectors "
+#ifdef MOZ_XUL
+  MOZ_ASSERT(!(IsPseudoElement() &&
+               PseudoType() != nsCSSPseudoElements::ePseudo_XULTree &&
+               mClassList),
+             "If non-XUL-tree pseudo-elements can have class selectors "
+             "after them, specificity calculation must be updated");
+#else
+  MOZ_ASSERT(!(IsPseudoElement() && mClassList),
+             "If pseudo-elements can have class selectors "
+             "after them, specificity calculation must be updated");
+#endif
+  MOZ_ASSERT(!(IsPseudoElement() && (mIDList || mAttrList)),
+             "If pseudo-elements can have id or attribute selectors "
              "after them, specificity calculation must be updated");
 
   if (nullptr != mCasedTag) {
     weight += 0x000001;
   }
   nsAtomList* list = mIDList;
   while (nullptr != list) {
     weight += 0x010000;
     list = list->mNext;
   }
   list = mClassList;
+#ifdef MOZ_XUL
+  // XUL tree pseudo-elements abuse mClassList to store some private
+  // data; ignore that.
+  if (PseudoType() == nsCSSPseudoElements::ePseudo_XULTree) {
+    list = nullptr;
+  }
+#endif
   while (nullptr != list) {
     weight += 0x000100;
     list = list->mNext;
   }
   // FIXME (bug 561154):  This is incorrect for :-moz-any(), which isn't
   // really a pseudo-class.  In order to handle :-moz-any() correctly,
   // we need to compute specificity after we match, based on which
   // option we matched with (and thus also need to try the
--- a/media/libstagefright/frameworks/av/media/libstagefright/foundation/AString.cpp
+++ b/media/libstagefright/frameworks/av/media/libstagefright/foundation/AString.cpp
@@ -320,17 +320,17 @@ bool AString::endsWith(const char *suffi
     return !strcmp(mData + mSize - suffixLen, suffix);
 }
 
 AString StringPrintf(const char *format, ...) {
     va_list ap;
     va_start(ap, format);
 
     char *buffer;
-#ifdef _MSC_VER
+#ifdef WIN32
     int n = vsnprintf(NULL, 0, format, ap);
     buffer = new char[n+1];
     vsnprintf(buffer, n+1, format, ap);
 #else
     vasprintf(&buffer, format, ap);
 #endif
 
     va_end(ap);
--- a/media/libstagefright/moz.build
+++ b/media/libstagefright/moz.build
@@ -1,24 +1,27 @@
 # /*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
-DEFINES['_GLIBCXX_OS_DEFINES'] = True
 DEFINES['ANDROID_SMP'] = 0
 DEFINES['LOG_NDEBUG'] = 0
 
-if CONFIG['_MSC_VER']:
-    DEFINES['ssize_t'] = 'intptr_t'
-    DEFINES['off64_t'] = 'int64_t'
+if CONFIG['OS_TARGET'] != 'WINNT':
+   DEFINES['_GLIBCXX_OS_DEFINES'] = True
+
+if CONFIG['OS_TARGET'] == 'WINNT':
+    if CONFIG['_MSC_VER']:
+        DEFINES['ssize_t'] = 'intptr_t'
+        DEFINES['off64_t'] = 'int64_t'
+        DEFINES['strcasecmp'] = 'stricmp'
+        DEFINES['strncasecmp'] = 'strnicmp'
     DEFINES['HAVE_MS_C_RUNTIME'] = True
-    DEFINES['strcasecmp'] = 'stricmp'
-    DEFINES['strncasecmp'] = 'strnicmp'
     DEFINES['__PRETTY_FUNCTION__'] = '__FUNCTION__'
     LOCAL_INCLUDES += [ 'ports/win32/include' ]
 elif CONFIG['OS_TARGET'] == 'Darwin':
     DEFINES['HAVE_SYS_UIO_H'] = True
     DEFINES['off64_t'] = 'off_t'
     LOCAL_INCLUDES += [ 'ports/darwin/include' ]
 elif CONFIG['OS_TARGET'] in ('DragonFly', 'FreeBSD', 'OpenBSD', 'NetBSD'):
     if not CONFIG['OS_TARGET'] == 'NetBSD':
--- a/media/libstagefright/system/core/liblog/logprint.c
+++ b/media/libstagefright/system/core/liblog/logprint.c
@@ -32,35 +32,37 @@
 #ifdef _MSC_VER
 #include <nspr/prprf.h>
 #define snprintf PR_snprintf
 #define inline
 /* We don't want to indent large blocks because it causes unnecessary merge
  * conflicts */
 #define UNINDENTED_BLOCK_START {
 #define UNINDENTED_BLOCK_END }
+#else
+#define UNINDENTED_BLOCK_START
+#define UNINDENTED_BLOCK_END
+#endif
 
+#ifdef WIN32
 static char *
 strsep(char **stringp, const char *delim)
 {
     char* res = *stringp;
     while (**stringp) {
         const char *c;
         for (c = delim; *c; c++) {
             if (**stringp == *c) {
                 **stringp++ = 0;
                 return res;
             }
         }
     }
     return res;
 }
-#else
-#define UNINDENTED_BLOCK_START
-#define UNINDENTED_BLOCK_END
 #endif
 
 typedef struct FilterInfo_t {
     char *mTag;
     android_LogPriority mPri;
     struct FilterInfo_t *p_next;
 } FilterInfo;
 
--- a/mobile/android/base/sqlite/SQLiteBridge.java
+++ b/mobile/android/base/sqlite/SQLiteBridge.java
@@ -222,16 +222,17 @@ public class SQLiteBridge {
     public int getVersion()
                throws SQLiteBridgeException {
         Cursor cursor = internalQuery("PRAGMA user_version", null);
         int ret = -1;
         if (cursor != null) {
             cursor.moveToFirst();
             String version = cursor.getString(0);
             ret = Integer.parseInt(version);
+            cursor.close();
         }
         return ret;
     }
 
     // Do an SQL query, substituting the parameters in the query with the passed
     // parameters. The parameters are substituted in order: named parameters
     // are not supported.
     private Cursor internalQuery(String aQuery, String[] aParams)
--- a/mobile/android/base/tests/robocop.ini
+++ b/mobile/android/base/tests/robocop.ini
@@ -28,18 +28,16 @@ skip-if = android_version == "10"
 # disabled on 2.3; bug 979603
 skip-if = android_version == "10"
 [testBrowserSearchVisibility]
 [testClearPrivateData]
 # disabled on x86 and 2.3; bug 948591
 skip-if = android_version == "10" || processor == "x86"
 [testDistribution]
 [testDoorHanger]
-# disabled on 2.3; bug 986172
-skip-if = android_version == "10"
 [testFilterOpenTab]
 # [testFindInPage] # see bug 975155, bug 1014708
 [testFlingCorrectness]
 # disabled on x86 only; bug 927476
 skip-if = processor == "x86"
 [testFormHistory]
 [testGetUserMedia]
 # disabled on 2.3; bug 979620
--- a/mobile/android/installer/package-manifest.in
+++ b/mobile/android/installer/package-manifest.in
@@ -619,10 +619,10 @@ bin/components/@DLL_PREFIX@nkgnomevfs@DL
 @BINPATH@/components/marionettecomponent.js
 #endif
 
 #ifdef MOZ_ANDROID_SYNTHAPKS
 @BINPATH@/components/WebappsUpdateTimer.js
 #endif
 
 @BINPATH@/components/DataStore.manifest
-@BINPATH@/components/DataStoreService.js
+@BINPATH@/components/DataStoreImpl.js
 @BINPATH@/components/dom_datastore.xpt
--- a/netwerk/cache2/CacheFileIOManager.cpp
+++ b/netwerk/cache2/CacheFileIOManager.cpp
@@ -1337,20 +1337,43 @@ CacheFileIOManager::OnProfile()
 
   nsresult rv;
 
   nsCOMPtr<nsIFile> directory;
 
   CacheObserver::ParentDirOverride(getter_AddRefs(directory));
 
 #if defined(MOZ_WIDGET_ANDROID)
+  nsCOMPtr<nsIFile> profilelessDirectory;
   char* cachePath = getenv("CACHE_DIRECTORY");
   if (!directory && cachePath && *cachePath) {
     rv = NS_NewNativeLocalFile(nsDependentCString(cachePath),
                                true, getter_AddRefs(directory));
+    if (NS_SUCCEEDED(rv)) {
+      // Save this directory as the profileless path.
+      rv = directory->Clone(getter_AddRefs(profilelessDirectory));
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      // Add profile leaf name to the directory name to distinguish
+      // multiple profiles Fennec supports.
+      nsCOMPtr<nsIFile> profD;
+      rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
+                                  getter_AddRefs(profD));
+
+      nsAutoCString leafName;
+      if (NS_SUCCEEDED(rv)) {
+        rv = profD->GetNativeLeafName(leafName);
+      }
+      if (NS_SUCCEEDED(rv)) {
+        rv = directory->AppendNative(leafName);
+      }
+      if (NS_FAILED(rv)) {
+        directory = nullptr;
+      }
+    }
   }
 #endif
 
   if (!directory) {
     rv = NS_GetSpecialDirectory(NS_APP_CACHE_PARENT_DIR,
                                 getter_AddRefs(directory));
   }
 
@@ -1362,16 +1385,25 @@ CacheFileIOManager::OnProfile()
   if (directory) {
     rv = directory->Append(NS_LITERAL_STRING("cache2"));
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // All functions return a clone.
   ioMan->mCacheDirectory.swap(directory);
 
+#if defined(MOZ_WIDGET_ANDROID)
+  if (profilelessDirectory) {
+    rv = profilelessDirectory->Append(NS_LITERAL_STRING("cache2"));
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  ioMan->mCacheProfilelessDirectory.swap(profilelessDirectory);
+#endif
+
   if (ioMan->mCacheDirectory) {
     CacheIndex::Init(ioMan->mCacheDirectory);
   }
 
   return NS_OK;
 }
 
 // static
@@ -2216,20 +2248,36 @@ void CacheFileIOManager::GetCacheDirecto
 {
   *result = nullptr;
 
   nsRefPtr<CacheFileIOManager> ioMan = gInstance;
   if (!ioMan) {
     return;
   }
 
-  nsCOMPtr<nsIFile> file = ioMan->mCacheDirectory;
-  file.forget(result);
+  ioMan->mCacheDirectory->Clone(result);
 }
 
+#if defined(MOZ_WIDGET_ANDROID)
+
+// static
+void CacheFileIOManager::GetProfilelessCacheDirectory(nsIFile** result)
+{
+  *result = nullptr;
+
+  nsRefPtr<CacheFileIOManager> ioMan = gInstance;
+  if (!ioMan) {
+    return;
+  }
+
+  ioMan->mCacheProfilelessDirectory->Clone(result);
+}
+
+#endif
+
 // static
 nsresult
 CacheFileIOManager::GetEntryInfo(const SHA1Sum::Hash *aHash,
                                  CacheStorageService::EntryInfoCallback *aCallback)
 {
   MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
 
   nsresult rv;
--- a/netwerk/cache2/CacheFileIOManager.h
+++ b/netwerk/cache2/CacheFileIOManager.h
@@ -275,16 +275,19 @@ public:
   static nsresult UpdateIndexEntry();
 
   enum EEnumerateMode {
     ENTRIES,
     DOOMED
   };
 
   static void GetCacheDirectory(nsIFile** result);
+#if defined(MOZ_WIDGET_ANDROID)
+  static void GetProfilelessCacheDirectory(nsIFile** result);
+#endif
 
   // Calls synchronously OnEntryInfo for an entry with the given hash.
   // Tries to find an existing entry in the service hashtables first, if not
   // found, loads synchronously from disk file.
   // Callable on the IO thread only.
   static nsresult GetEntryInfo(const SHA1Sum::Hash *aHash,
                                CacheStorageService::EntryInfoCallback *aCallback);
 
@@ -378,16 +381,23 @@ private:
   // Memory reporting (private part)
   size_t SizeOfExcludingThisInternal(mozilla::MallocSizeOf mallocSizeOf) const;
 
   static CacheFileIOManager           *gInstance;
   TimeStamp                            mStartTime;
   bool                                 mShuttingDown;
   nsRefPtr<CacheIOThread>              mIOThread;
   nsCOMPtr<nsIFile>                    mCacheDirectory;
+#if defined(MOZ_WIDGET_ANDROID)
+  // On Android we add the active profile directory name between the path
+  // and the 'cache2' leaf name.  However, to delete any leftover data from
+  // times before we were doing it, we still need to access the directory
+  // w/o the profile name in the path.  Here it is stored.
+  nsCOMPtr<nsIFile>                    mCacheProfilelessDirectory;
+#endif
   bool                                 mTreeCreated;
   CacheFileHandles                     mHandles;
   nsTArray<CacheFileHandle *>          mHandlesByLastUsed;
   nsTArray<CacheFileHandle *>          mSpecialHandles;
   nsTArray<nsRefPtr<CacheFile> >       mScheduledMetadataWrites;
   nsCOMPtr<nsITimer>                   mMetadataWritesTimer;
   bool                                 mOverLimitEvicting;
   bool                                 mRemovingTrashDirs;
--- a/netwerk/cache2/CacheStorageService.cpp
+++ b/netwerk/cache2/CacheStorageService.cpp
@@ -529,57 +529,113 @@ void CacheStorageService::DropPrivateBro
 
   nsTArray<nsCString> keys;
   sGlobalEntryTables->EnumerateRead(&CollectPrivateContexts, &keys);
 
   for (uint32_t i = 0; i < keys.Length(); ++i)
     DoomStorageEntries(keys[i], nullptr, true, nullptr);
 }
 
+namespace { // anon
+
+class CleaupCacheDirectoriesRunnable : public nsRunnable
+{
+public:
+  NS_DECL_NSIRUNNABLE
+  static bool Post(uint32_t aVersion, uint32_t aActive);
+
+private:
+  CleaupCacheDirectoriesRunnable(uint32_t aVersion, uint32_t aActive)
+    : mVersion(aVersion), mActive(aActive)
+  {
+    nsCacheService::GetDiskCacheDirectory(getter_AddRefs(mCache1Dir));
+    CacheFileIOManager::GetCacheDirectory(getter_AddRefs(mCache2Dir));
+#if defined(MOZ_WIDGET_ANDROID)
+    CacheFileIOManager::GetProfilelessCacheDirectory(getter_AddRefs(mCache2Profileless));
+#endif
+  }
+
+  virtual ~CleaupCacheDirectoriesRunnable() {}
+  uint32_t mVersion, mActive;
+  nsCOMPtr<nsIFile> mCache1Dir, mCache2Dir;
+#if defined(MOZ_WIDGET_ANDROID)
+  nsCOMPtr<nsIFile> mCache2Profileless;
+#endif
+};
+
 // static
-void CacheStorageService::CleaupCacheDirectories(uint32_t aVersion, uint32_t aActive)
+bool CleaupCacheDirectoriesRunnable::Post(uint32_t aVersion, uint32_t aActive)
 {
   // CleaupCacheDirectories is called regardless what cache version is set up to use.
   // To obtain the cache1 directory we must unfortunatelly instantiate the old cache
   // service despite it may not be used at all...  This also initialize nsDeleteDir.
   nsCOMPtr<nsICacheService> service = do_GetService(NS_CACHESERVICE_CONTRACTID);
+  if (!service)
+    return false;
 
-  // Schedule delete of both the cache1 and cache2 remaining trashes.
-  nsCOMPtr<nsIFile> cache1Dir, cache2Dir;
-  nsCacheService::GetDiskCacheDirectory(getter_AddRefs(cache1Dir));
-  CacheFileIOManager::GetCacheDirectory(getter_AddRefs(cache2Dir));
+  nsCOMPtr<nsIEventTarget> thread;
+  service->GetCacheIOTarget(getter_AddRefs(thread));
+  if (!thread)
+    return false;
+
+  nsRefPtr<CleaupCacheDirectoriesRunnable> r =
+    new CleaupCacheDirectoriesRunnable(aVersion, aActive);
+  thread->Dispatch(r, NS_DISPATCH_NORMAL);
+  return true;
+}
 
-  // Make sure we schedule just once in case CleaupCacheDirectories gets called
-  // multiple times from some reason.
-  static bool runOnce = (
-    cache1Dir && NS_SUCCEEDED(nsDeleteDir::RemoveOldTrashes(cache1Dir)),
-    cache2Dir && NS_SUCCEEDED(nsDeleteDir::RemoveOldTrashes(cache2Dir))
-  );
+NS_IMETHODIMP CleaupCacheDirectoriesRunnable::Run()
+{
+  MOZ_ASSERT(!NS_IsMainThread());
 
-  if (!runOnce) {
-    NS_WARNING("Could not start deletion of some of the old cache trashes");
+  if (mCache1Dir) {
+    nsDeleteDir::RemoveOldTrashes(mCache1Dir);
+  }
+  if (mCache2Dir) {
+    nsDeleteDir::RemoveOldTrashes(mCache2Dir);
+  }
+#if defined(MOZ_WIDGET_ANDROID)
+  if (mCache2Profileless) {
+    // Always delete the profileless cache on Android
+    nsDeleteDir::DeleteDir(mCache2Profileless, true, 30000);
+  }
+#endif
+
+  // Delete the non-active version cache data right now
+  if (mVersion == mActive) {
+    return NS_OK;
   }
 
-  // Delete the non-active version cache data right now
-  if (aVersion == aActive) {
-    return;
-  }
-
-  switch (aVersion) {
+  switch (mVersion) {
   case 0:
-    if (cache1Dir) {
-      nsDeleteDir::DeleteDir(cache1Dir, true, 30000);
+    if (mCache1Dir) {
+      nsDeleteDir::DeleteDir(mCache1Dir, true, 30000);
     }
     break;
   case 1:
-    if (cache2Dir) {
-      nsDeleteDir::DeleteDir(cache2Dir, true, 30000);
+    if (mCache2Dir) {
+      nsDeleteDir::DeleteDir(mCache2Dir, true, 30000);
     }
     break;
   }
+
+  return NS_OK;
+}
+
+} // anon
+
+// static
+void CacheStorageService::CleaupCacheDirectories(uint32_t aVersion, uint32_t aActive)
+{
+  // Make sure we schedule just once in case CleaupCacheDirectories gets called
+  // multiple times from some reason.
+  static bool runOnce = CleaupCacheDirectoriesRunnable::Post(aVersion, aActive);
+  if (!runOnce) {
+    NS_WARNING("Could not start cache trashes cleanup");
+  }
 }
 
 // Helper methods
 
 // static
 bool CacheStorageService::IsOnManagementThread()
 {
   nsRefPtr<CacheStorageService> service = Self();
--- a/services/healthreport/healthreporter.jsm
+++ b/services/healthreport/healthreporter.jsm
@@ -397,17 +397,28 @@ AbstractHealthReporter.prototype = Objec
 
       // As soon as we have could have storage, we need to register cleanup or
       // else bad things happen on shutdown.
       Services.obs.addObserver(this, "quit-application", false);
 
       // The database needs to be shut down by the end of shutdown
       // phase profileBeforeChange.
       Metrics.Storage.shutdown.addBlocker("FHR: Flushing storage shutdown",
-        this._promiseShutdown);
+        this._promiseShutdown,
+        () => ({
+            shutdownInitiated: this._shutdownInitiated,
+            initialized: this._initialized,
+            shutdownRequested: this._shutdownRequested,
+            initializeHadError: this._initializeHadError,
+            providerManagerInProgress: this._providerManagerInProgress,
+            storageInProgress: this._storageInProgress,
+            hasProviderManager: !!this._providerManager,
+            hasStorage: !!this._storage,
+            shutdownComplete: this.shutdownComplete
+          }));
 
       try {
         this._storageInProgress = true;
         TelemetryStopwatch.start(this._dbOpenHistogram, this);
         let storage = yield Metrics.Storage(this._dbName);
         TelemetryStopwatch.finish(this._dbOpenHistogram, this);
         yield this._onStorageCreated();
 
@@ -566,17 +577,17 @@ AbstractHealthReporter.prototype = Objec
     this._log.info("Request to shut down.");
 
     this._initialized = false;
     this._shutdownRequested = true;
 
     if (this._initializeHadError) {
       this._log.warn("Initialization had error. Shutting down immediately.");
     } else {
-      if (this._providerManagerInProcess) {
+      if (this._providerManagerInProgress) {
         this._log.warn("Provider manager is in progress of initializing. " +
                        "Waiting to finish.");
         return;
       }
 
       // If storage is in the process of initializing, we need to wait for it
       // to finish before continuing. The initialization process will call us
       // again once storage has initialized.
--- a/testing/mochitest/chrome-harness.js
+++ b/testing/mochitest/chrome-harness.js
@@ -158,21 +158,22 @@ function zList(base, zReader, baseJarNam
  *  single test: [json object, path to test]
  *  list of tests: [json object, null] <- directory [heirarchy]
  */
 function getFileListing(basePath, testPath, dir, srvScope)
 {
   var uri = getResolvedURI(basePath);
   var chromeDir = getChromeDir(uri);
   chromeDir.appendRelativePath(dir);
-  basePath += '/' + dir;
+  basePath += '/' + dir.replace(/\\/g, '/');
 
   if (testPath == "false" || testPath == false) {
     testPath = "";
   }
+  testPath = testPath.replace(/\\\\/g, '\\').replace(/\\/g, '/');
 
   var ioSvc = Components.classes["@mozilla.org/network/io-service;1"].
               getService(Components.interfaces.nsIIOService);
   var testsDirURI = ioSvc.newFileURI(chromeDir);
   var testsDir = ioSvc.newURI(testPath, null, testsDirURI)
                   .QueryInterface(Components.interfaces.nsIFileURL).file;
 
   if (testPath != undefined) {
--- a/testing/mochitest/mach_commands.py
+++ b/testing/mochitest/mach_commands.py
@@ -185,17 +185,17 @@ class MochitestRunner(MozbuildObject):
 
     def run_desktop_test(self, context, suite=None, test_paths=None, debugger=None,
         debugger_args=None, slowscript=False, screenshot_on_fail = False, shuffle=False, keep_open=False,
         rerun_failures=False, no_autorun=False, repeat=0, run_until_failure=False,
         slow=False, chunk_by_dir=0, total_chunks=None, this_chunk=None,
         jsdebugger=False, debug_on_failure=False, start_at=None, end_at=None,
         e10s=False, dmd=False, dump_output_directory=None,
         dump_about_memory_after_test=False, dump_dmd_after_test=False,
-        install_extension=None, quiet=False, environment=[], app_override=None,
+        install_extension=None, quiet=False, environment=[], app_override=None, runByDir=False,
         useTestMediaDevices=False, **kwargs):
         """Runs a mochitest.
 
         test_paths are path to tests. They can be a relative path from the
         top source directory, an absolute filename, or a directory containing
         test files.
 
         suite is the type of mochitest to run. It can be one of ('plain',
@@ -311,16 +311,17 @@ class MochitestRunner(MozbuildObject):
         options.startAt = start_at
         options.endAt = end_at
         options.e10s = e10s
         options.dumpAboutMemoryAfterTest = dump_about_memory_after_test
         options.dumpDMDAfterTest = dump_dmd_after_test
         options.dumpOutputDirectory = dump_output_directory
         options.quiet = quiet
         options.environment = environment
+        options.runByDir = runByDir
         options.useTestMediaDevices = useTestMediaDevices
 
         options.failureFile = failure_file_path
         if install_extension != None:
             options.extensionsToInstall = [os.path.join(self.topsrcdir,install_extension)]
 
         for k, v in kwargs.iteritems():
             setattr(options, k, v)
@@ -521,16 +522,22 @@ def MochitestCommand(func):
         help='Do not print test log lines unless a failure occurs.')
     func = quiet(func)
 
     setenv = CommandArgument('--setenv', default=[], action='append',
                              metavar='NAME=VALUE', dest='environment',
                              help="Sets the given variable in the application's environment")
     func = setenv(func)
 
+    runbydir = CommandArgument('--run-by-dir', default=False,
+                                 action='store_true',
+                                 dest='runByDir',
+        help='Run each directory in a single browser instance with a fresh profile.')
+    func = runbydir(func)
+
     test_media = CommandArgument('--use-test-media-devices', default=False,
                                  action='store_true',
                                  dest='useTestMediaDevices',
         help='Use test media device drivers for media testing.')
     func = test_media(func)
 
     app_override = CommandArgument('--app-override', default=None, action='store',
         help="Override the default binary used to run tests with the path you provide, e.g. " \
--- a/testing/mochitest/mochitest_options.py
+++ b/testing/mochitest/mochitest_options.py
@@ -94,16 +94,22 @@ class MochitestOptions(optparse.OptionPa
           "default": None,
         }],
         [["--chunk-by-dir"],
         { "type": "int",
           "dest": "chunkByDir",
           "help": "group tests together in the same chunk that are in the same top chunkByDir directories",
           "default": 0,
         }],
+        [["--run-by-dir"],
+        { "action": "store_true",
+          "dest": "runByDir",
+          "help": "Run each directory in a single browser instance with a fresh profile",
+          "default": False,
+        }],
         [["--shuffle"],
         { "dest": "shuffle",
           "action": "store_true",
           "help": "randomize test order",
           "default": False,
         }],
         [["--console-level"],
         { "action": "store",
--- a/testing/mochitest/runtests.py
+++ b/testing/mochitest/runtests.py
@@ -412,17 +412,17 @@ class MochitestUtilsMixin(object):
 
     pathPieces = filename.split("/")
 
     return (testPattern.match(pathPieces[-1]) and
             not re.search(r'\^headers\^$', filename))
 
   def getTestPath(self, options):
     if options.ipcplugins:
-      return "dom/plugins/test"
+      return "dom/plugins/test/mochitest"
     else:
       return options.testPath
 
   def getTestRoot(self, options):
     if options.browserChrome:
       if options.immersiveMode:
         return 'metro'
       return 'browser'
@@ -446,64 +446,48 @@ class MochitestUtilsMixin(object):
       testURL = "about:blank"
     return testURL
 
   def buildTestPath(self, options):
     """ Build the url path to the specific test harness and test file or directory
         Build a manifest of tests to run and write out a json file for the harness to read
     """
     manifest = None
-
-    testRoot = self.getTestRoot(options)
-    # testdir refers to 'mochitest' here.
-    testdir = SCRIPT_DIR.split(os.getcwd())[-1]
-    testdir = testdir.strip(os.sep)
-    testRootAbs = os.path.abspath(os.path.join(testdir, testRoot))
-    if isinstance(options.manifestFile, TestManifest):
-        manifest = options.manifestFile
-    elif options.manifestFile and os.path.isfile(options.manifestFile):
-      manifestFileAbs = os.path.abspath(options.manifestFile)
-      assert manifestFileAbs.startswith(testRootAbs)
-      manifest = TestManifest([options.manifestFile], strict=False)
-    else:
-      masterName = self.getTestFlavor(options) + '.ini'
-      masterPath = os.path.join(testdir, testRoot, masterName)
-
-      if os.path.exists(masterPath):
-        manifest = TestManifest([masterPath], strict=False)
+    manifest = self.getTestManifest(options)
 
     if manifest:
       # Python 2.6 doesn't allow unicode keys to be used for keyword
       # arguments. This gross hack works around the problem until we
       # rid ourselves of 2.6.
       info = {}
       for k, v in mozinfo.info.items():
         if isinstance(k, unicode):
           k = k.encode('ascii')
         info[k] = v
 
       # Bug 883858 - return all tests including disabled tests
       testPath = self.getTestPath(options)
+      testPath = testPath.replace('\\', '/')
       if testPath.endswith('.html') or \
          testPath.endswith('.xhtml') or \
          testPath.endswith('.xul') or \
          testPath.endswith('.js'):
           # In the case where we have a single file, we don't want to filter based on options such as subsuite.
           tests = manifest.active_tests(disabled=True, options=None, **info)
           for test in tests:
               if 'disabled' in test:
                   del test['disabled']
       else:
           tests = manifest.active_tests(disabled=True, options=options, **info)
       paths = []
 
       for test in tests:
         pathAbs = os.path.abspath(test['path'])
-        assert pathAbs.startswith(testRootAbs)
-        tp = pathAbs[len(testRootAbs):].replace('\\', '/').strip('/')
+        assert pathAbs.startswith(self.testRootAbs)
+        tp = pathAbs[len(self.testRootAbs):].replace('\\', '/').strip('/')
 
         # Filter out tests if we are using --test-path
         if testPath and not tp.startswith(testPath):
           continue
 
         if not self.isTest(options, tp):
           print 'Warning: %s from manifest %s is not a valid test' % (test['name'], test['manifest'])
           continue
@@ -517,17 +501,17 @@ class MochitestUtilsMixin(object):
       def path_sort(ob1, ob2):
         path1 = ob1['path'].split('/')
         path2 = ob2['path'].split('/')
         return cmp(path1, path2)
 
       paths.sort(path_sort)
 
       # Bug 883865 - add this functionality into manifestDestiny
-      with open(os.path.join(testdir, 'tests.json'), 'w') as manifestFile:
+      with open(os.path.join(SCRIPT_DIR, 'tests.json'), 'w') as manifestFile:
         manifestFile.write(json.dumps({'tests': paths}))
       options.manifestFile = 'tests.json'
 
     return self.buildTestURL(options)
 
   def startWebSocketServer(self, options, debuggerInfo):
     """ Launch the websocket server """
     self.wsserver = WebSocketServer(options, SCRIPT_DIR, debuggerInfo)
@@ -1060,19 +1044,20 @@ class Mochitest(MochitestUtilsMixin):
       browserEnv["NSPR_LOG_FILE"] = "%s/nspr.log" % tempfile.gettempdir()
       browserEnv["GECKO_SEPARATE_NSPR_LOGS"] = "1"
 
     if debugger and not options.slowscript:
       browserEnv["JS_DISABLE_SLOW_SCRIPT_SIGNALS"] = "1"
 
     return browserEnv
 
-  def cleanup(self, manifest, options):
+  def cleanup(self, options):
     """ remove temporary files and profile """
-    os.remove(manifest)
+    if self.manifest is not None:
+      os.remove(self.manifest)
     del self.profile
     if options.pidFile != "":
       try:
         os.remove(options.pidFile)
         if os.path.exists(options.pidFile + ".xpcshell.pid"):
           os.remove(options.pidFile + ".xpcshell.pid")
       except:
         log.warn("cleaning up pidfile '%s' was unsuccessful from the test harness", options.pidFile)
@@ -1181,17 +1166,18 @@ class Mochitest(MochitestUtilsMixin):
              profile,
              extraArgs,
              utilityPath,
              debuggerInfo=None,
              symbolsPath=None,
              timeout=-1,
              onLaunch=None,
              webapprtChrome=False,
-             screenshotOnFail=False):
+             screenshotOnFail=False,
+             testPath=None):
     """
     Run the app, log the duration it took to execute, return the status code.
     Kills the app if it runs for longer than |maxTime| seconds, or outputs nothing for |timeout| seconds.
     """
 
     # debugger information
     interactive = False
     debug_args = None
@@ -1245,17 +1231,17 @@ class Mochitest(MochitestUtilsMixin):
                                          symbolsPath=symbolsPath,
                                          dump_screen_on_timeout=not debuggerInfo,
                                          dump_screen_on_fail=screenshotOnFail,
                                          shutdownLeaks=shutdownLeaks,
         )
 
       def timeoutHandler():
         browserProcessId = outputHandler.browserProcessId
-        self.handleTimeout(timeout, proc, utilityPath, debuggerInfo, browserProcessId)
+        self.handleTimeout(timeout, proc, utilityPath, debuggerInfo, browserProcessId, testPath)
       kp_kwargs = {'kill_on_timeout': False,
                    'cwd': SCRIPT_DIR,
                    'onTimeout': [timeoutHandler]}
       kp_kwargs['processOutputLine'] = [outputHandler]
 
       # create mozrunner instance and start the system under test process
       self.lastTestSeen = self.test_name
       startTime = datetime.now()
@@ -1334,16 +1320,68 @@ class Mochitest(MochitestUtilsMixin):
       if os.path.exists(processLog):
         os.remove(processLog)
 
     return status
 
   def runTests(self, options, onLaunch=None):
     """ Prepare, configure, run tests and cleanup """
 
+    # Create variables to count the number of passes, fails, todos.
+    self.countpass = 0
+    self.countfail = 0
+    self.counttodo = 0
+
+    self.testRoot = self.getTestRoot(options)
+    self.testRootAbs = os.path.join(SCRIPT_DIR, self.testRoot)
+
+    if not options.runByDir:
+      return self.doTests(options, onLaunch)
+
+    dirs = self.getDirectories(options)
+    
+    if options.totalChunks > 1:
+      chunkSize = int(len(dirs) / options.totalChunks) + 1
+      start = chunkSize * (options.thisChunk-1)
+      end = chunkSize * (options.thisChunk)
+      dirs = dirs[start:end]
+
+    options.totalChunks = None
+    options.thisChunk = None
+    options.chunkByDir = 0
+    inputTestPath = self.getTestPath(options)
+    for dir in dirs:
+      options.manifestFile = None
+
+      if inputTestPath and not inputTestPath.startswith(dir):
+        continue
+
+      options.testPath = dir
+      print "testpath: %s" % options.testPath
+
+      options.profilePath = tempfile.mkdtemp()
+      self.urlOpts = []
+      self.doTests(options, onLaunch)
+
+    # printing total number of tests
+    if options.browserChrome:
+      print "TEST-INFO | checking window state"
+      print "Browser Chrome Test Summary"
+      print "\tPassed: %s" % self.countpass
+      print "\tFailed: %s" % self.countfail
+      print "\tTodo: %s" % self.counttodo
+      print "*** End BrowserChrome Test Results ***"
+    else:
+      print "0 INFO TEST-START | Shutdown"
+      print "1 INFO Passed:  %s" % self.countpass
+      print "2 INFO Failed:  %s" % self.countfail
+      print "3 INFO Todo:    %s" % self.counttodo
+      print "4 INFO SimpleTest FINISHED"
+
+  def doTests(self, options, onLaunch=None):
     # get debugger info, a dict of:
     # {'path': path to the debugger (string),
     #  'interactive': whether the debugger is interactive or not (bool)
     #  'args': arguments to the debugger (list)
     # TODO: use mozrunner.local.debugger_arguments:
     # https://github.com/mozilla/mozbase/blob/master/mozrunner/mozrunner/local.py#L42
     debuggerInfo = getDebuggerInfo(self.oldcwd,
                                    options.debugger,
@@ -1354,32 +1392,43 @@ class Mochitest(MochitestUtilsMixin):
       devices = findTestMediaDevices()
       if not devices:
         log.error("Could not find test media devices to use")
         return 1
       self.mediaDevices = devices
 
     self.leak_report_file = os.path.join(options.profilePath, "runtests_leaks.log")
 
-    browserEnv = self.buildBrowserEnv(options, debuggerInfo is not None)
-    if browserEnv is None:
+    self.browserEnv = self.buildBrowserEnv(options, debuggerInfo is not None)
+    if self.browserEnv is None:
       return 1
 
     # buildProfile sets self.profile .
     # This relies on sideeffects and isn't very stateful:
     # https://bugzilla.mozilla.org/show_bug.cgi?id=919300
-    manifest = self.buildProfile(options)
-    if manifest is None:
+    self.manifest = self.buildProfile(options)
+    if self.manifest is None:
       return 1
 
     try:
       self.startServers(options, debuggerInfo)
 
       testURL = self.buildTestPath(options)
-      self.buildURLOptions(options, browserEnv)
+
+      # read the number of tests here, if we are not going to run any, terminate early
+      if os.path.exists(os.path.join(SCRIPT_DIR, 'tests.json')):
+        with open(os.path.join(SCRIPT_DIR, 'tests.json')) as fHandle:
+          tests = json.load(fHandle)
+        count = 0
+        for test in tests['tests']:
+          count += 1
+        if count == 0:
+          return 1
+
+      self.buildURLOptions(options, self.browserEnv)
       if self.urlOpts:
         testURL += "?" + "&".join(self.urlOpts)
 
       if options.webapprtContent:
         options.browserArgs.extend(('-test-mode', testURL))
         testURL = None
 
       if options.immersiveMode:
@@ -1403,27 +1452,28 @@ class Mochitest(MochitestUtilsMixin):
         timeout = 330.0 # default JS harness timeout is 300 seconds
 
       if options.vmwareRecording:
         self.startVMwareRecording(options);
 
       log.info("runtests.py | Running tests: start.\n")
       try:
         status = self.runApp(testURL,
-                             browserEnv,
+                             self.browserEnv,
                              options.app,
                              profile=self.profile,
                              extraArgs=options.browserArgs,
                              utilityPath=options.utilityPath,
                              debuggerInfo=debuggerInfo,
                              symbolsPath=options.symbolsPath,
                              timeout=timeout,
                              onLaunch=onLaunch,
                              webapprtChrome=options.webapprtChrome,
-                             screenshotOnFail=options.screenshotOnFail
+                             screenshotOnFail=options.screenshotOnFail,
+                             testPath=options.testPath
         )
       except KeyboardInterrupt:
         log.info("runtests.py | Received keyboard interrupt.\n");
         status = -1
       except:
         traceback.print_exc()
         log.error("Automation Error: Received unexpected exception while running application\n")
         status = 1
@@ -1438,25 +1488,28 @@ class Mochitest(MochitestUtilsMixin):
     if self.nsprLogs:
       with zipfile.ZipFile("%s/nsprlog.zip" % browserEnv["MOZ_UPLOAD_DIR"], "w", zipfile.ZIP_DEFLATED) as logzip:
         for logfile in glob.glob("%s/nspr*.log*" % tempfile.gettempdir()):
           logzip.write(logfile)
           os.remove(logfile)
 
     log.info("runtests.py | Running tests: end.")
 
-    if manifest is not None:
-      self.cleanup(manifest, options)
+    if self.manifest is not None:
+      self.cleanup(options)
 
     return status
 
-  def handleTimeout(self, timeout, proc, utilityPath, debuggerInfo, browserProcessId):
+  def handleTimeout(self, timeout, proc, utilityPath, debuggerInfo, browserProcessId, testPath=None):
     """handle process output timeout"""
     # TODO: bug 913975 : _processOutput should call self.processOutputLine one more time one timeout (I think)
-    log.info("TEST-UNEXPECTED-FAIL | %s | application timed out after %d seconds with no output", self.lastTestSeen, int(timeout))
+    if testPath:
+      log.info("TEST-UNEXPECTED-FAIL | %s | application timed out after %d seconds with no output on %s", self.lastTestSeen, int(timeout), testPath)
+    else:
+      log.info("TEST-UNEXPECTED-FAIL | %s | application timed out after %d seconds with no output", self.lastTestSeen, int(timeout))
     browserProcessId = browserProcessId or proc.pid
     self.killAndGetStack(browserProcessId, utilityPath, debuggerInfo, dump_screen=not debuggerInfo)
 
   ### output processing
 
   class OutputHandler(object):
     """line output handler for mozrunner"""
     def __init__(self, harness, utilityPath, symbolsPath=None, dump_screen_on_timeout=True, dump_screen_on_fail=False, shutdownLeaks=None):
@@ -1493,16 +1546,17 @@ class Mochitest(MochitestUtilsMixin):
       return [self.fix_stack,
               self.format,
               self.record_last_test,
               self.dumpScreenOnTimeout,
               self.dumpScreenOnFail,
               self.metro_subprocess_id,
               self.trackShutdownLeaks,
               self.log,
+              self.countline,
               ]
 
     def stackFixer(self):
       """
       return 2-tuple, (stackFixerFunction, StackFixerProcess),
       if any, to use on the output lines
       """
 
@@ -1545,16 +1599,30 @@ class Mochitest(MochitestUtilsMixin):
           log.info("TEST-UNEXPECTED-FAIL | runtests.py | Stack fixer process exited with code %d during test run", status)
 
       if self.shutdownLeaks:
         self.shutdownLeaks.process()
 
 
     # output line handlers:
     # these take a line and return a line
+    def countline(self, line):
+      val = 0
+      try:
+        val = int(line.split(':')[-1].strip())
+      except ValueError, e:
+        return line
+
+      if "Passed:" in line:
+        self.harness.countpass += val
+      elif "Failed:" in line:
+        self.harness.countfail += val
+      elif "Todo:" in line:
+        self.harness.counttodo += val
+      return line
 
     def fix_stack(self, line):
       if self.stackFixerFunction:
         return self.stackFixerFunction(line)
       return line
 
     def format(self, line):
       """format the line"""
@@ -1594,23 +1662,22 @@ class Mochitest(MochitestUtilsMixin):
       log.info(line)
       return line
 
 
   def makeTestConfig(self, options):
     "Creates a test configuration file for customizing test execution."
     options.logFile = options.logFile.replace("\\", "\\\\")
     options.testPath = options.testPath.replace("\\", "\\\\")
-    testRoot = self.getTestRoot(options)
 
     if "MOZ_HIDE_RESULTS_TABLE" in os.environ and os.environ["MOZ_HIDE_RESULTS_TABLE"] == "1":
       options.hideResultsTable = True
 
     d = dict(options.__dict__)
-    d['testRoot'] = testRoot
+    d['testRoot'] = self.testRoot
     content = json.dumps(d)
 
     with open(os.path.join(options.profilePath, "testConfig.js"), "w") as config:
       config.write(content)
 
   def installExtensionFromPath(self, options, path, extensionID = None):
     """install an extension to options.profilePath"""
 
@@ -1636,16 +1703,61 @@ class Mochitest(MochitestUtilsMixin):
 
     addons.install_from_path(path)
 
   def installExtensionsToProfile(self, options):
     "Install special testing extensions, application distributed extensions, and specified on the command line ones to testing profile."
     for path in self.getExtensionsToInstall(options):
       self.installExtensionFromPath(options, path)
 
+  def getTestManifest(self, options):
+    if isinstance(options.manifestFile, TestManifest):
+        manifest = options.manifestFile
+    elif options.manifestFile and os.path.isfile(options.manifestFile):
+      manifestFileAbs = os.path.abspath(options.manifestFile)
+      assert manifestFileAbs.startswith(SCRIPT_DIR)
+      manifest = TestManifest([options.manifestFile], strict=False)
+    elif options.manifestFile and os.path.isfile(os.path.join(SCRIPT_DIR, options.manifestFile)):
+      manifestFileAbs = os.path.abspath(os.path.join(SCRIPT_DIR, options.manifestFile))
+      assert manifestFileAbs.startswith(SCRIPT_DIR)
+      manifest = TestManifest([manifestFileAbs], strict=False)
+    else:
+      masterName = self.getTestFlavor(options) + '.ini'
+      masterPath = os.path.join(SCRIPT_DIR, self.testRoot, masterName)
+
+      if os.path.exists(masterPath):
+        manifest = TestManifest([masterPath], strict=False)
+
+    return manifest
+
+  def getDirectories(self, options):
+    """
+        Make the list of directories by parsing manifests
+    """
+    info = {}
+    for k, v in mozinfo.info.items():
+      if isinstance(k, unicode):
+        k = k.encode('ascii')
+      info[k] = v
+
+    dirlist = []
+
+    manifest = self.getTestManifest(options)
+    tests = manifest.active_tests(disabled=False, options=options, **info)
+    for test in tests:
+      pathAbs = os.path.abspath(test['path'])
+      assert pathAbs.startswith(self.testRootAbs)
+      tp = pathAbs[len(self.testRootAbs):].replace('\\', '/').strip('/')
+
+      rootdir = '/'.join(tp.split('/')[:-1])
+      if rootdir not in dirlist:
+        dirlist.append(rootdir)
+
+    dirlist.sort()
+    return dirlist
 
 def main():
 
   # parse command line options
   mochitest = Mochitest()
   parser = MochitestOptions()
   options, args = parser.parse_args()
   options = parser.verifyOptions(options, mochitest)
--- a/testing/mochitest/runtestsremote.py
+++ b/testing/mochitest/runtestsremote.py
@@ -234,25 +234,25 @@ class MochiRemote(Mochitest):
         self.environment = self._automation.environment
         self.remoteProfile = options.remoteTestRoot + "/profile"
         self._automation.setRemoteProfile(self.remoteProfile)
         self.remoteLog = options.remoteLogFile
         self.localLog = options.logFile
         self._automation.deleteANRs()
         self.certdbNew = True
 
-    def cleanup(self, manifest, options):
+    def cleanup(self, options):
         if self._dm.fileExists(self.remoteLog):
             self._dm.getFile(self.remoteLog, self.localLog)
             self._dm.removeFile(self.remoteLog)
         else:
             log.warn("Unable to retrieve log file (%s) from remote device",
                 self.remoteLog)
         self._dm.removeDir(self.remoteProfile)
-        Mochitest.cleanup(self, manifest, options)
+        Mochitest.cleanup(self, options)
 
     def findPath(self, paths, filename = None):
         for path in paths:
             p = path
             if filename:
                 p = os.path.join(p, filename)
             if os.path.exists(self.getFullPath(p)):
                 return path
@@ -706,17 +706,17 @@ def main():
                 # Ensure earlier failures aren't overwritten by success on this run
                 if retVal is None or retVal == 0:
                     retVal = result
             except:
                 log.error("Automation Error: Exception caught while running tests")
                 traceback.print_exc()
                 mochitest.stopServers()
                 try:
-                    mochitest.cleanup(None, options)
+                    mochitest.cleanup(options)
                 except devicemanager.DMError:
                     # device error cleaning up... oh well!
                     pass
                 retVal = 1
                 break
             finally:
                 # Clean-up added bookmarks
                 if test['name'] == "testImportFromAndroid":
@@ -741,17 +741,17 @@ def main():
         try:
             dm.recordLogcat()
             retVal = mochitest.runTests(options)
         except:
             log.error("Automation Error: Exception caught while running tests")
             traceback.print_exc()
             mochitest.stopServers()
             try:
-                mochitest.cleanup(None, options)
+                mochitest.cleanup(options)
             except devicemanager.DMError:
                 # device error cleaning up... oh well!
                 pass
             retVal = 1
 
     mochitest.printDeviceInfo(printLogcat=True)
 
     sys.exit(retVal)
deleted file mode 100755
--- a/testing/tps/INSTALL.sh
+++ /dev/null
@@ -1,99 +0,0 @@
-#!/bin/bash
-# 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/.
-
-
-# This scripts sets up a virutalenv and installs TPS into it.
-# It's probably best to specify a path NOT inside the repo, otherwise
-# all the virtualenv files will show up in e.g. hg status.
-
-# get target directory
-if [ ! -z "$1" ]
-then
-  TARGET=$1
-else
-  echo "Usage: INSTALL.sh /path/to/create/virtualenv [/path/to/python2.6]"
-  exit 1
-fi
-
-# decide which python to use
-if [ ! -z "$2" ]
-then
-    PYTHON=$2
-else
-    PYTHON=`which python`
-fi
-if [ -z "${PYTHON}" ]
-then
-    echo "No python found"
-    exit 1
-fi
-
-CWD="`pwd`"
-
-# create the destination directory
-mkdir ${TARGET}
-
-if [ "$?" -gt 0 ]
-then
-  exit 1
-fi
-
-if [ "${OS}" = "Windows_NT" ]
-then
-  BIN_NAME=Scripts/activate
-else
-  BIN_NAME=bin/activate
-fi
-
-# Create a virtualenv:
-curl -L https://raw.github.com/pypa/virtualenv/1.9.1/virtualenv.py | ${PYTHON} - ${TARGET}
-cd ${TARGET}
-. $BIN_NAME
-if [ -z "${VIRTUAL_ENV}" ]
-then
-    echo "virtualenv wasn't installed correctly, aborting"
-    exit 1
-fi
-
-# install TPS
-cd ${CWD}
-python setup.py install
-
-# clean up files created by setup.py
-rm -rf build/
-rm -rf dist/
-rm -rf tps.egg-info/
-
-if [ "$?" -gt 0 ]
-then
-  exit 1
-fi
-
-CONFIG="`find ${VIRTUAL_ENV} -name config.json.in`"
-NEWCONFIG=${CONFIG:0:${#CONFIG}-3}
-
-cd "../../services/sync/tests/tps"
-TESTDIR="`pwd`"
-
-cd "../../tps/extensions"
-EXTDIR="`pwd`"
-
-sed 's|__TESTDIR__|'"${TESTDIR}"'|' "${CONFIG}" | sed 's|__EXTENSIONDIR__|'"${EXTDIR}"'|' > "${NEWCONFIG}"
-rm ${CONFIG}
-
-echo
-echo "***********************************************************************"
-echo
-echo "To run TPS, activate the virtualenv using:"
-echo "  source ${TARGET}/${BIN_NAME}"
-echo "then execute tps using:"
-echo "  runtps --binary=/path/to/firefox"
-echo
-echo "See runtps --help for all options"
-echo
-echo "To change your TPS config, please edit the file: "
-echo "${NEWCONFIG}"
-echo
-echo "***********************************************************************"
new file mode 100755
--- /dev/null
+++ b/testing/tps/create_venv.py
@@ -0,0 +1,116 @@
+#!/usr/bin/env python
+# 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/.
+
+"""
+This scripts sets up a virtualenv and installs TPS into it.
+It's probably best to specify a path NOT inside the repo, otherwise
+all the virtualenv files will show up in e.g. hg status.
+"""
+
+import optparse
+import os
+import shutil
+import subprocess
+import sys
+import urllib
+
+here = os.path.dirname(os.path.abspath(__file__))
+usage_message = """
+***********************************************************************
+
+To run TPS, activate the virtualenv using:
+    source {TARGET}/{BIN_NAME}
+
+To change your TPS config, please edit the file:
+    {TARGET}/config.json
+
+To execute tps use:
+    runtps --binary=/path/to/firefox
+
+See runtps --help for all options
+
+***********************************************************************
+"""
+virtualenv_url = 'https://raw.github.com/pypa/virtualenv/1.9.1/virtualenv.py'
+
+if sys.platform == 'win32':
+    bin_name = os.path.join('Scripts', 'activate.bat')
+    activate_env = os.path.join('Scripts', 'activate_this.py')
+else:
+    bin_name = os.path.join('bin', 'activate')
+    activate_env = os.path.join('bin', 'activate_this.py')
+
+def main():
+    parser = optparse.OptionParser('Usage: %prog [options] path_to_venv')
+    parser.add_option('-p', '--python',
+                      type='string',
+                      dest='python',
+                      metavar='PYTHON_BIN',
+                      default=None,
+                      help='The Python interpreter to use.')
+    (options, args) = parser.parse_args(args=None, values=None)
+
+    if len(args) != 1:
+         parser.error('Path to the environment has to be specified')
+    target = args[0]
+    assert(target)
+
+    # Create a virtual environment
+    urllib.urlretrieve(virtualenv_url, 'virtualenv.py')
+    cmd_args = [sys.executable, 'virtualenv.py', target]
+    if options.python:
+        cmd_args.extend(['-p', options.python])
+    subprocess.check_call(cmd_args)
+
+    # Activate tps environment
+
+    tps_env = os.path.join(target, activate_env)
+    execfile(tps_env, dict(__file__=tps_env))
+
+    # Install TPS in environment
+    subprocess.check_call(['python', os.path.join(here, 'setup.py'),
+                           'install'])
+
+    # Get the path to tests and extensions directory by checking check where
+    # the tests and extensions directories are located
+    sync_dir = os.path.abspath(os.path.join(here, '..', '..', 'services',
+                                            'sync'))
+    if os.path.exists(sync_dir):
+        testdir = os.path.join(sync_dir, 'tests', 'tps')
+        extdir = os.path.join(sync_dir, 'tps', 'extensions')
+    else:
+        testdir = os.path.join(here, 'tests')
+        extdir = os.path.join(here, 'extensions')
+
+    assert(os.path.exists(testdir))
+    assert(os.path.exists(extdir))
+
+    # Update config file
+    config_in_path = os.path.join(here, 'config', 'config.json.in')
+    replacements = {'__TESTDIR__': testdir, '__EXTENSIONDIR__': extdir}
+    lines = []
+    with open(config_in_path) as config:
+        for line in config:
+            for source_string, target_string in replacements.iteritems():
+                line = line.replace(source_string, target_string)
+            lines.append(line)
+
+    with open(os.path.join(target, 'config.json'), 'w') as config:
+        for line in lines:
+            config.write(line)
+
+    # Clean up files created by setup.py
+    shutil.rmtree(os.path.join(here, 'build'))
+    shutil.rmtree(os.path.join(here, 'dist'))
+    shutil.rmtree(os.path.join(here, 'tps.egg-info'))
+    os.remove(os.path.join(here, 'virtualenv.py'))
+    os.remove(os.path.join(here, 'virtualenv.pyc'))
+
+    # Print the user instructions
+    print usage_message.format(TARGET=target,
+                               BIN_NAME=bin_name)
+
+if __name__ == "__main__":
+    main()
--- a/testing/tps/tps/cli.py
+++ b/testing/tps/tps/cli.py
@@ -62,33 +62,35 @@ def main():
                       type='string',
                       dest='resultfile',
                       default='tps_result.json',
                       help='path to the result file [default: %default]')
     parser.add_option('--testfile',
                       action='store',
                       type='string',
                       dest='testfile',
-                      default='../../services/sync/tests/tps/all_tests.json',
+                      default='all_tests.json',
                       help='path to the test file to run [default: %default]')
     (options, args) = parser.parse_args()
 
     configfile = options.configfile
     if configfile is None:
-        if os.environ.get('VIRTUAL_ENV'):
-            configfile = os.path.join(os.path.dirname(__file__), 'config.json')
+        virtual_env = os.environ.get('VIRTUAL_ENV')
+        if virtual_env:
+            configfile = os.path.join(virtual_env, 'config.json')
         if configfile is None or not os.access(configfile, os.F_OK):
             raise Exception('Unable to find config.json in a VIRTUAL_ENV; you must '
                             'specify a config file using the --configfile option')
 
     # load the config file
     f = open(configfile, 'r')
     configcontent = f.read()
     f.close()
     config = json.loads(configcontent)
+    testfile = os.path.join(config.get('testdir', ''), options.testfile)
 
     rlock = RLock()
 
     print 'using result file', options.resultfile
 
     extensionDir = config.get('extensiondir')
     if not extensionDir or extensionDir == '__EXTENSIONDIR__':
         extensionDir = os.path.join(os.getcwd(), '..', '..',
@@ -105,14 +107,14 @@ def main():
                         binary=options.binary,
                         config=config,
                         debug=options.debug,
                         ignore_unused_engines=options.ignore_unused_engines,
                         logfile=options.logfile,
                         mobile=options.mobile,
                         resultfile=options.resultfile,
                         rlock=rlock,
-                        testfile=options.testfile,
+                        testfile=testfile,
                       )
     TPS.run_tests()
 
 if __name__ == '__main__':
     main()
--- a/toolkit/content/tests/browser/browser.ini
+++ b/toolkit/content/tests/browser/browser.ini
@@ -5,16 +5,17 @@ skip-if = e10s # Bug ?????? - test touch
 [browser_browserDrop.js]
 [browser_bug295977_autoscroll_overflow.js]
 skip-if = e10s # Bug 921935 - focusmanager issues with e10s
 [browser_bug594509.js]
 skip-if = e10s # Bug ?????? - intermittent crash of child process reported when run under e10s
 [browser_bug982298.js]
 [browser_default_image_filename.js]
 skip-if = e10s # Bug 933103 - mochitest's EventUtils.synthesizeMouse functions not e10s friendly
+[browser_f7_caret_browsing.js]
 [browser_findbar.js]
 skip-if = e10s # Disabled for e10s: Bug ?????? - seems to be a timing issue with RemoteFinder.jsm messages coming later than the tests expect.
 [browser_input_file_tooltips.js]
 skip-if = e10s # Bug ?????? - test directly manipulates content (TypeError: doc.createElement is not a function)
 [browser_keyevents_during_autoscrolling.js]
 skip-if = e10s # Bug 921935 - focusmanager issues with e10s
 [browser_save_resend_postdata.js]
 support-files =
new file mode 100644
--- /dev/null
+++ b/toolkit/content/tests/browser/browser_f7_caret_browsing.js
@@ -0,0 +1,279 @@
+XPCOMUtils.defineLazyModuleGetter(this, "Promise",
+  "resource://gre/modules/Promise.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Task",
+  "resource://gre/modules/Task.jsm");
+
+let gTab = null;
+let gListener = null;
+const kURL = "data:text/html;charset=utf-8,Caret browsing is fun.<input id='in'>";
+
+const kPrefShortcutEnabled = "accessibility.browsewithcaret_shortcut.enabled";
+const kPrefWarnOnEnable    = "accessibility.warn_on_browsewithcaret";
+const kPrefCaretBrowsingOn = "accessibility.browsewithcaret";
+
+let oldPrefs = {};
+for (let pref of [kPrefShortcutEnabled, kPrefWarnOnEnable, kPrefCaretBrowsingOn]) {
+  oldPrefs[pref] = Services.prefs.getBoolPref(pref);
+}
+
+Services.prefs.setBoolPref(kPrefShortcutEnabled, true);
+Services.prefs.setBoolPref(kPrefWarnOnEnable, true);
+Services.prefs.setBoolPref(kPrefCaretBrowsingOn, false);
+
+registerCleanupFunction(function() {
+  if (gTab)
+    gBrowser.removeTab(gTab);
+  if (gListener)
+    Services.wm.removeListener(gListener);
+
+  for (let pref of [kPrefShortcutEnabled, kPrefWarnOnEnable, kPrefCaretBrowsingOn]) {
+    Services.prefs.setBoolPref(pref, oldPrefs[pref]);
+  }
+});
+
+function waitForCondition(aConditionFn, aMaxTries=50, aCheckInterval=100) {
+  function tryNow() {
+    tries++;
+    if (aConditionFn()) {
+      deferred.resolve();
+    } else if (tries < aMaxTries) {
+      tryAgain();
+    } else {
+      deferred.reject("Condition timed out: " + aConditionFn.toSource());
+    }
+  }
+  function tryAgain() {
+    setTimeout(tryNow, aCheckInterval);
+  }
+  let deferred = Promise.defer();
+  let tries = 0;
+  tryAgain();
+  return deferred.promise;
+}
+
+function promiseWaitForDialogUnload(dialog) {
+  let deferred = Promise.defer();
+  dialog.addEventListener("unload", function listener() {
+    dialog.removeEventListener("unload", listener, false);
+    deferred.resolve();
+  }, false);
+  return deferred.promise;
+}
+
+function promiseWaitForFocusEvent(el) {
+  if (el.ownerDocument.activeElement == el) {
+    return true;
+  }
+  let deferred = Promise.defer();
+  el.addEventListener("focus", function listener() {
+    el.removeEventListener("focus", listener, false);
+    deferred.resolve();
+  }, false);
+  return deferred.promise;
+}
+
+function promiseTestPageLoad() {
+  let deferred = Promise.defer();
+  info("Waiting for test page to load.");
+
+  gTab = gBrowser.selectedTab = gBrowser.addTab(kURL);
+  let browser = gBrowser.selectedBrowser;
+  browser.addEventListener("load", function listener() {
+    if (browser.currentURI.spec == "about:blank")
+      return;
+    info("Page loaded: " + browser.currentURI.spec);
+    browser.removeEventListener("load", listener, true);
+
+    deferred.resolve();
+  }, true);
+
+  return deferred.promise;
+}
+
+function promiseCaretPromptOpened() {
+  let deferred = Promise.defer();
+  if (gListener) {
+    console.trace();
+    ok(false, "Should not be waiting for another prompt right now.");
+    return false;
+  }
+  info("Waiting for caret prompt to open");
+  gListener = {
+    onOpenWindow: function(win) {
+      let window = win.QueryInterface(Ci.nsIInterfaceRequestor)
+                      .getInterface(Ci.nsIDOMWindow);
+      window.addEventListener("load", function listener() {
+        window.removeEventListener("load", listener);
+        if (window.location.href == "chrome://global/content/commonDialog.xul") {
+          info("Caret prompt opened, removing listener and focusing");
+          Services.wm.removeListener(gListener);
+          gListener = null;
+          deferred.resolve(window);
+        }
+      });
+    },
+    onCloseWindow: function() {},
+  };
+  Services.wm.addListener(gListener);
+  return deferred.promise;
+}
+
+function hitF7(async = true) {
+  let f7 = () => EventUtils.sendKey("F7", window.content);
+  // Need to not stop execution inside this task:
+  if (async) {
+    executeSoon(f7);
+  } else {
+    f7();
+  }
+}
+
+function syncToggleCaretNoDialog(expected) {
+  let openedDialog = false;
+  promiseCaretPromptOpened().then(function(win) {
+    openedDialog = true;
+    win.close(); // This will eventually return focus here and allow the test to continue...
+  });
+  // Cause the dialog to appear sync, if it still does.
+  hitF7(false);
+  if (gListener) {
+    Services.wm.removeListener(gListener);
+    gListener = null;
+  }
+  let expectedStr = expected ? "on." : "off.";
+  ok(!openedDialog, "Shouldn't open a dialog to turn caret browsing " + expectedStr);
+  let prefVal = Services.prefs.getBoolPref(kPrefCaretBrowsingOn);
+  is(prefVal, expected, "Caret browsing should now be " + expectedStr);
+}
+
+add_task(function* checkTogglingCaretBrowsing() {
+  yield promiseTestPageLoad();
+  let textEl = window.content.document.getElementById("in");
+  textEl.focus();
+
+  let promiseGotKey = promiseCaretPromptOpened();
+  hitF7();
+  let prompt = yield promiseGotKey;
+  let doc = prompt.document;
+  is(doc.documentElement.defaultButton, "cancel", "No button should be the default");
+  ok(!doc.getElementById("checkbox").checked, "Checkbox shouldn't be checked by default.");
+  let promiseDialogUnloaded = promiseWaitForDialogUnload(prompt);
+  doc.documentElement.cancelDialog();
+  yield promiseDialogUnloaded;
+  yield waitForCondition(() => textEl.ownerDocument.activeElement == textEl);
+  ok(!Services.prefs.getBoolPref(kPrefCaretBrowsingOn), "Caret browsing should still be off after cancelling the dialog.");
+
+  promiseGotKey = promiseCaretPromptOpened();
+  hitF7();
+  prompt = yield promiseGotKey;
+
+  doc = prompt.document;
+  is(doc.documentElement.defaultButton, "cancel", "No button should be the default");
+  ok(!doc.getElementById("checkbox").checked, "Checkbox shouldn't be checked by default.");
+  promiseDialogUnloaded = promiseWaitForDialogUnload(prompt);
+  doc.documentElement.acceptDialog();
+  yield promiseDialogUnloaded;
+  yield waitForCondition(() => textEl.ownerDocument.activeElement == textEl);
+  ok(Services.prefs.getBoolPref(kPrefCaretBrowsingOn), "Caret browsing should be on after accepting the dialog.");
+
+  syncToggleCaretNoDialog(false);
+
+  promiseGotKey = promiseCaretPromptOpened();
+  hitF7();
+  prompt = yield promiseGotKey;
+  doc = prompt.document;
+
+  is(doc.documentElement.defaultButton, "cancel", "No button should be the default");
+  ok(!doc.getElementById("checkbox").checked, "Checkbox shouldn't be checked by default.");
+
+  promiseDialogUnloaded = promiseWaitForDialogUnload(prompt);
+  doc.documentElement.cancelDialog();
+  yield promiseDialogUnloaded;
+  yield waitForCondition(() => textEl.ownerDocument.activeElement == textEl);
+
+  ok(!Services.prefs.getBoolPref(kPrefCaretBrowsingOn), "Caret browsing should still be off after cancelling the dialog.");
+
+  Services.prefs.setBoolPref(kPrefShortcutEnabled, true);
+  Services.prefs.setBoolPref(kPrefWarnOnEnable, true);
+  Services.prefs.setBoolPref(kPrefCaretBrowsingOn, false);
+
+  gBrowser.removeTab(gTab);
+  gTab = null;
+});
+
+add_task(function* toggleCheckboxNoCaretBrowsing() {
+  yield promiseTestPageLoad();
+  let textEl = window.content.document.getElementById("in");
+  textEl.focus();
+
+  let promiseGotKey = promiseCaretPromptOpened();
+  hitF7();
+  let prompt = yield promiseGotKey;
+  let doc = prompt.document;
+  is(doc.documentElement.defaultButton, "cancel", "No button should be the default");
+  let checkbox = doc.getElementById("checkbox");
+  ok(!checkbox.checked, "Checkbox shouldn't be checked by default.");
+
+  // Check the box:
+  checkbox.click();
+  let promiseDialogUnloaded = promiseWaitForDialogUnload(prompt);
+  // Say no:
+  doc.documentElement.getButton("cancel").click();
+  yield promiseDialogUnloaded;
+  yield waitForCondition(() => textEl.ownerDocument.activeElement == textEl);
+  ok(!Services.prefs.getBoolPref(kPrefCaretBrowsingOn), "Caret browsing should still be off.");
+
+  ok(!Services.prefs.getBoolPref(kPrefShortcutEnabled), "Shortcut should now be disabled.");
+
+  syncToggleCaretNoDialog(false);
+  ok(!Services.prefs.getBoolPref(kPrefShortcutEnabled), "Shortcut should still be disabled.");
+
+  Services.prefs.setBoolPref(kPrefShortcutEnabled, true);
+  Services.prefs.setBoolPref(kPrefWarnOnEnable, true);
+  Services.prefs.setBoolPref(kPrefCaretBrowsingOn, false);
+
+  gBrowser.removeTab(gTab);
+  gTab = null;
+});
+
+
+add_task(function* toggleCheckboxWantCaretBrowsing() {
+  yield promiseTestPageLoad();
+  let textEl = window.content.document.getElementById("in");
+  textEl.focus();
+
+  let promiseGotKey = promiseCaretPromptOpened();
+  hitF7();
+  let prompt = yield promiseGotKey;
+  let doc = prompt.document;
+  is(doc.documentElement.defaultButton, "cancel", "No button should be the default");
+  let checkbox = doc.getElementById("checkbox");
+  ok(!checkbox.checked, "Checkbox shouldn't be checked by default.");
+
+  // Check the box:
+  checkbox.click();
+  let promiseDialogUnloaded = promiseWaitForDialogUnload(prompt);
+  // Say yes:
+  doc.documentElement.acceptDialog();
+  yield promiseDialogUnloaded;
+  yield waitForCondition(() => textEl.ownerDocument.activeElement == textEl);
+  ok(Services.prefs.getBoolPref(kPrefCaretBrowsingOn), "Caret browsing should now be on.");
+  ok(Services.prefs.getBoolPref(kPrefShortcutEnabled), "Shortcut should still be enabled.");
+  ok(!Services.prefs.getBoolPref(kPrefWarnOnEnable), "Should no longer warn when enabling.");
+
+
+  syncToggleCaretNoDialog(false);
+  syncToggleCaretNoDialog(true);
+  syncToggleCaretNoDialog(false);
+  
+  Services.prefs.setBoolPref(kPrefShortcutEnabled, true);
+  Services.prefs.setBoolPref(kPrefWarnOnEnable, true);
+  Services.prefs.setBoolPref(kPrefCaretBrowsingOn, false);
+
+  gBrowser.removeTab(gTab);
+  gTab = null;
+});
+
+
+
+
--- a/toolkit/content/widgets/browser.xml
+++ b/toolkit/content/widgets/browser.xml
@@ -1095,58 +1095,70 @@
     </implementation>
 
     <handlers>
       <handler event="keypress" keycode="VK_F7" group="system">
         <![CDATA[
           if (event.defaultPrevented || !event.isTrusted)
             return;
 
-          var isEnabled = this.mPrefs.getBoolPref("accessibility.browsewithcaret_shortcut.enabled");
+          const kPrefShortcutEnabled = "accessibility.browsewithcaret_shortcut.enabled";
+          const kPrefWarnOnEnable    = "accessibility.warn_on_browsewithcaret";
+          const kPrefCaretBrowsingOn = "accessibility.browsewithcaret";
+
+          var isEnabled = this.mPrefs.getBoolPref(kPrefShortcutEnabled);
           if (!isEnabled)
             return;
 
           // Toggle browse with caret mode
           var browseWithCaretOn = false;
           var warn = true;
 
           try {
-            warn = this.mPrefs.getBoolPref("accessibility.warn_on_browsewithcaret");
+            warn = this.mPrefs.getBoolPref(kPrefWarnOnEnable);
           } catch (ex) {
           }
 
           try {
-            browseWithCaretOn = this.mPrefs.getBoolPref("accessibility.browsewithcaret");
+            browseWithCaretOn = this.mPrefs.getBoolPref(kPrefCaretBrowsingOn);
           } catch (ex) {
           }
           if (warn && !browseWithCaretOn) {
             var checkValue = {value:false};
             var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
                                           .getService(Components.interfaces.nsIPromptService);
 
             var buttonPressed = promptService.confirmEx(window,
               this.mStrBundle.GetStringFromName('browsewithcaret.checkWindowTitle'),
               this.mStrBundle.GetStringFromName('browsewithcaret.checkLabel'),
-              promptService.STD_YES_NO_BUTTONS,
+              // Make "No" the default:
+              promptService.STD_YES_NO_BUTTONS | promptService.BUTTON_POS_1_DEFAULT,
               null, null, null, this.mStrBundle.GetStringFromName('browsewithcaret.checkMsg'),
               checkValue);
-            if (buttonPressed != 0)
+            if (buttonPressed != 0) {
+              if (checkValue.value) {
+                try {
+                  this.mPrefs.setBoolPref(kPrefShortcutEnabled, false);
+                } catch (ex) {
+                }
+              }
               return;
+            }
             if (checkValue.value) {
               try {
-                this.mPrefs.setBoolPref("accessibility.warn_on_browsewithcaret", false);
+                this.mPrefs.setBoolPref(kPrefWarnOnEnable, false);
               }
               catch (ex) {
               }
             }
           }
 
           // Toggle the pref
           try {
-            this.mPrefs.setBoolPref("accessibility.browsewithcaret",!browseWithCaretOn);
+            this.mPrefs.setBoolPref(kPrefCaretBrowsingOn, !browseWithCaretOn);
           } catch (ex) {
           }
         ]]>
       </handler>
       <handler event="dragover" group="system">
       <![CDATA[
         if (!this.droppedLinkHandler || event.defaultPrevented)
           return;
--- a/toolkit/content/widgets/tree.xml
+++ b/toolkit/content/widgets/tree.xml
@@ -425,16 +425,18 @@
       </method>
 
       <method name="_moveByOffset">
         <parameter name="offset"/>
         <parameter name="edge"/>
         <parameter name="event"/>
         <body>
           <![CDATA[
+            event.preventDefault();
+
             if (this.view.rowCount == 0)
               return;
 
             if (this._isAccelPressed(event) && this.view.selection.single) {
               this.treeBoxObject.scrollByLines(offset);
               return;
             }
    
@@ -467,16 +469,18 @@
       </method>
 
       <method name="_moveByOffsetShift">
         <parameter name="offset"/>
         <parameter name="edge"/>
         <parameter name="event"/>
         <body>
           <![CDATA[
+            event.preventDefault();
+
             if (this.view.rowCount == 0)
               return;
 
             if (this.view.selection.single) {
               this.treeBoxObject.scrollByLines(offset);
               return;
             }
 
@@ -504,16 +508,18 @@
       </method>
 
       <method name="_moveByPage">
         <parameter name="offset"/>
         <parameter name="edge"/>
         <parameter name="event"/>
         <body>
           <![CDATA[
+            event.preventDefault();
+
             if (this.view.rowCount == 0)
               return;
 
             if (this.pageUpOrDownMovesSelection == this._isAccelPressed(event)) {
                this.treeBoxObject.scrollByPages(offset);
                return;
             }
 
@@ -553,16 +559,18 @@
       </method>
 
       <method name="_moveByPageShift">
         <parameter name="offset"/>
         <parameter name="edge"/>
         <parameter name="event"/>
         <body>
           <![CDATA[
+            event.preventDefault();
+
             if (this.view.rowCount == 0)
               return;
 
             if (this.view.rowCount == 1 && !this.view.selection.isSelected(0) &&
                 !(this.pageUpOrDownMovesSelection == this._isAccelPressed(event))) {
               this.view.selection.timedSelect(0, this._selectDelay);
               return;
             }
@@ -603,16 +611,18 @@
         </body>
       </method>
 
       <method name="_moveToEdge">
         <parameter name="edge"/>
         <parameter name="event"/>
         <body>
           <![CDATA[
+            event.preventDefault();
+
             if (this.view.rowCount == 0)
               return;
 
             if (this.view.selection.isSelected(edge) && this.view.selection.count == 1) {
               this.currentIndex = edge;
               return;
             }
 
@@ -629,16 +639,18 @@
         </body>
       </method>
 
       <method name="_moveToEdgeShift">
         <parameter name="edge"/>
         <parameter name="event"/>
         <body>
           <![CDATA[
+            event.preventDefault();
+
             if (this.view.rowCount == 0)
               return;
 
             if (this.view.rowCount == 1 && !this.view.selection.isSelected(0)) {
               this.view.selection.timedSelect(0, this._selectDelay);
               return;
             }
 
@@ -863,105 +875,93 @@
           }
         ]]>
       </handler>
       <handler event="keydown" keycode="VK_UP" modifiers="accel any">
         <![CDATA[
           if (this._editingColumn)
             return;
           _moveByOffset(-1, 0, event);
-          event.preventDefault();
         ]]>
       </handler>
       <handler event="keydown" keycode="VK_DOWN" modifiers="accel any">
         <![CDATA[
           if (this._editingColumn)
             return;
           _moveByOffset(1, this.view.rowCount - 1, event);
-          event.preventDefault();
         ]]>
       </handler>
       <handler event="keydown" keycode="VK_UP" modifiers="accel any, shift">
         <![CDATA[
           if (this._editingColumn)
             return;
           _moveByOffsetShift(-1, 0, event);
-          event.preventDefault();
         ]]>
       </handler>
       <handler event="keydown" keycode="VK_DOWN" modifiers="accel any, shift">
         <![CDATA[
           if (this._editingColumn)
             return;
           _moveByOffsetShift(1, this.view.rowCount - 1, event);
-          event.preventDefault();
         ]]>
       </handler>
       <handler event="keydown" keycode="VK_PAGE_UP" modifiers="accel any">
         <![CDATA[
           if (this._editingColumn)
             return;
           _moveByPage(-1, 0, event);
-          event.preventDefault();
         ]]>
       </handler>
       <handler event="keydown" keycode="VK_PAGE_DOWN" modifiers="accel any">
         <![CDATA[
           if (this._editingColumn)
             return;
           _moveByPage(1, this.view.rowCount - 1, event);
-          event.preventDefault();
         ]]>
       </handler>
       <handler event="keydown" keycode="VK_PAGE_UP" modifiers="accel any, shift">
         <![CDATA[
           if (this._editingColumn)
             return;
           _moveByPageShift(-1, 0, event);
-          event.preventDefault();
         ]]>
       </handler>
       <handler event="keydown" keycode="VK_PAGE_DOWN" modifiers="accel any, shift">
         <![CDATA[
           if (this._editingColumn)
             return;
           _moveByPageShift(1, this.view.rowCount - 1, event);
-          event.preventDefault();
         ]]>
       </handler>
       <handler event="keydown" keycode="VK_HOME" modifiers="accel any">
         <![CDATA[
           if (this._editingColumn)
             return;
           _moveToEdge(0, event);
-          event.preventDefault();
         ]]>
       </handler>
       <handler event="keydown" keycode="VK_END" modifiers="accel any">
         <![CDATA[
           if (this._editingColumn)
             return;
           _moveToEdge(this.view.rowCount - 1, event);
-          event.preventDefault();
         ]]>
       </handler>
       <handler event="keydown" keycode="VK_HOME" modifiers="accel any, shift">
         <![CDATA[
           if (this._editingColumn)
             return;
           _moveToEdgeShift(0, event);
-          event.preventDefault();
         ]]>
       </handler>
       <handler event="keydown" keycode="VK_END" modifiers="accel any, shift">
         <![CDATA[
           if (this._editingColumn)
             return;
           _moveToEdgeShift(this.view.rowCount - 1, event);
-          event.preventDefault();
         ]]>
       </handler>
       <handler event="keypress">
         <![CDATA[
          if (this._editingColumn)
            return;
 
          if (event.charCode == ' '.charCodeAt(0)) {
--- a/toolkit/crashreporter/test/unit/test_crash_AsyncShutdown.js
+++ b/toolkit/crashreporter/test/unit/test_crash_AsyncShutdown.js
@@ -15,25 +15,28 @@ function setup_crash() {
   let phase = AsyncShutdown._getPhase(TOPIC);
   phase.addBlocker("A blocker that is never satisfied", function() {
     dump("Installing blocker\n");
     let deferred = Promise.defer();
     return deferred.promise;
   });
 
   Services.obs.notifyObservers(null, TOPIC, null);
+  dump(new Error().stack + "\n");
   dump("Waiting for crash\n");
 }
 
 function after_crash(mdump, extra) {
   do_print("after crash: " + extra.AsyncShutdownTimeout);
   let info = JSON.parse(extra.AsyncShutdownTimeout);
-  do_check_eq(info.phase, "testing-async-shutdown-crash");
-  do_print("Condition: " + JSON.stringify(info.conditions));
-  do_check_true(JSON.stringify(info.conditions).indexOf("A blocker that is never satisfied") != -1);
+  Assert.equal(info.phase, "testing-async-shutdown-crash");
+  Assert.equal(info.conditions[0].name, "A blocker that is never satisfied");
+  // This test spawns subprocesses by using argument "-e" of xpcshell, so
+  // this is the filename known to xpcshell.
+  Assert.equal(info.conditions[0].filename, "-e");
 }
 
 // Test that AsyncShutdown + OS.File reports errors correctly, in a case in which
 // the latest operation succeeded
 
 function setup_osfile_crash_noerror() {
   Components.utils.import("resource://gre/modules/Services.jsm", this);
   Components.utils.import("resource://gre/modules/osfile.jsm", this);
--- a/toolkit/modules/AsyncShutdown.jsm
+++ b/toolkit/modules/AsyncShutdown.jsm
@@ -423,22 +423,32 @@ function Barrier(name) {
       if (fetchState && typeof fetchState != "function") {
         throw new TypeError("Expected nothing or a function as third argument");
       }
       if (!this._conditions) {
 	throw new Error("Phase " + this._name +
 			" has already begun, it is too late to register" +
 			" completion condition '" + name + "'.");
       }
+
+      // Determine the filename and line number of the caller.
+      let leaf = Components.stack;
+      let frame;
+      for (frame = leaf; frame != null && frame.filename == leaf.filename; frame = frame.caller) {
+        // Climb up the stack
+      }
       let set = this._conditions.get(condition);
       if (!set) {
         set = [];
         this._conditions.set(condition, set);
       }
-      set.push({name: name, fetchState: fetchState});
+      set.push({name: name,
+                fetchState: fetchState,
+                filename: frame ? frame.filename : "?",
+                lineNumber: frame ? frame.lineNumber : -1});
     }.bind(this),
 
     /**
      * Remove the blocker for a condition.
      *
      * If several blockers have been registered for the same
      * condition, remove all these blockers. If no blocker has been
      * registered for this condition, this is a noop.
@@ -475,19 +485,22 @@ Barrier.prototype = Object.freeze({
   get state() {
     if (this._conditions) {
       return "Not started";
     }
     if (!this._monitors) {
       return "Complete";
     }
     let frozen = [];
-    for (let {name, isComplete, fetchState} of this._monitors) {
+    for (let {name, isComplete, fetchState, filename, lineNumber} of this._monitors) {
       if (!isComplete) {
-        frozen.push({name: name, state: safeGetState(fetchState)});
+        frozen.push({name: name,
+                     state: safeGetState(fetchState),
+                     filename: filename,
+                     lineNumber: lineNumber});
       }
     }
     return frozen;
   },
 
   /**
    * Wait until all currently registered blockers are complete.
    *
@@ -531,17 +544,17 @@ Barrier.prototype = Object.freeze({
 
     // Information to determine and report to the user which conditions
     // are not satisfied yet.
     this._monitors = [];
 
     for (let _condition of conditions.keys()) {
       for (let current of conditions.get(_condition)) {
         let condition = _condition; // Avoid capturing the wrong variable
-        let {name, fetchState} = current;
+        let {name, fetchState, filename, lineNumber} = current;
 
         // An indirection on top of condition, used to let clients
         // cancel a blocker through removeBlocker.
         let indirection = Promise.defer();
         this._indirections.set(condition, indirection);
 
         // Gather all completion conditions
 
@@ -560,17 +573,19 @@ Barrier.prototype = Object.freeze({
           // function returned |undefined| or failed), that new promise
           // isn't going to be terribly interesting, but it will behave
           // as a promise.
           condition = Promise.resolve(condition);
 
           let monitor = {
             isComplete: false,
             name: name,
-            fetchState: fetchState
+            fetchState: fetchState,
+            filename: filename,
+            lineNumber: lineNumber
           };
 
 	  condition = condition.then(null, function onError(error) {
             let msg = "A completion condition encountered an error" +
               " while we were spinning the event loop." +
 	      " Condition: " + name +
               " Phase: " + topic +
               " State: " + safeGetState(fetchState);
@@ -664,45 +679,63 @@ Barrier.prototype = Object.freeze({
       // can do to avoid leaving the user's computer in an unstable (and
       // battery-sucking) situation is report the issue and crash.
       timeToCrash = looseTimer(crashAfterMS);
       timeToCrash.promise.then(
         function onTimeout() {
 	  // Report the problem as best as we can, then crash.
 	  let state = this.state;
 
-	  let msg = "At least one completion condition failed to complete" +
+          // If you change the following message, please make sure
+          // that any information on the topic and state appears
+          // within the first 200 characters of the message. This
+          // helps automatically sort oranges.
+          let msg = "AsyncShutdown timeout in " + topic +
+            " Conditions: " + JSON.stringify(state) +
+            " At least one completion condition failed to complete" +
 	    " within a reasonable amount of time. Causing a crash to" +
 	    " ensure that we do not leave the user with an unresponsive" +
 	    " process draining resources." +
-	    " Conditions: " + JSON.stringify(state) +
-	    " Barrier: " + topic;
 	  err(msg);
 	  if (gCrashReporter && gCrashReporter.enabled) {
             let data = {
               phase: topic,
               conditions: state
 	    };
             gCrashReporter.annotateCrashReport("AsyncShutdownTimeout",
-            JSON.stringify(data));
+              JSON.stringify(data));
 	  } else {
             warn("No crash reporter available");
 	  }
 
-	  let error = new Error();
-	  gDebug.abort(error.fileName, error.lineNumber + 1);
+          // To help sorting out bugs, we want to make sure that the
+          // call to nsIDebug.abort points to a guilty client, rather
+          // than to AsyncShutdown itself. We search through all the
+          // clients until we find one that is guilty and use its
+          // filename/lineNumber, which have been determined during
+          // the call to `addBlocker`.
+          let filename = "?";
+          let lineNumber = -1;
+          for (let monitor of this._monitors) {
+            if (monitor.isComplete) {
+              continue;
+            }
+            filename = monitor.filename;
+            lineNumber = monitor.lineNumber;
+          }
+	  gDebug.abort(filename, lineNumber);
         }.bind(this),
 	  function onSatisfied() {
             // The promise has been rejected, which means that we have satisfied
             // all completion conditions.
           });
 
       promise = promise.then(function() {
         timeToCrash.reject();
-      }.bind(this)/* No error is possible here*/);
+      }/* No error is possible here*/);
     }
 
     return promise;
   },
 });
 
 
 
--- a/toolkit/modules/tests/xpcshell/test_AsyncShutdown.js
+++ b/toolkit/modules/tests/xpcshell/test_AsyncShutdown.js
@@ -265,12 +265,40 @@ add_task(function* test_phase_removeBloc
 
     do_print("Attempt to remove non-registered blocker after wait()");
     do_check_false(lock.removeBlocker("foo"));
     do_check_false(lock.removeBlocker(null));
   }
 
 });
 
-add_task(function() {
+add_task(function* test_state() {
+  do_print("Testing information contained in `state`");
+
+  let BLOCKER_NAME = "test_state blocker " + Math.random();
+
+  // Set up the barrier. Note that we cannot test `barrier.state`
+  // immediately, as it initially contains "Not started"
+  let barrier = new AsyncShutdown.Barrier("test_filename");
+  let deferred = Promise.defer();
+  let {filename, lineNumber} = Components.stack;
+  barrier.client.addBlocker(BLOCKER_NAME,
+    function() {
+      return deferred.promise;
+    });
+
+  let promiseDone = barrier.wait();
+
+  // Now that we have called `wait()`, the state contains interesting things
+  let state = barrier.state[0];
+  do_print("State: " + JSON.stringify(barrier.state, null, "\t"));
+  Assert.equal(state.filename, filename);
+  Assert.equal(state.lineNumber, lineNumber + 2);
+  Assert.equal(state.name, BLOCKER_NAME);
+  
+  deferred.resolve();
+  yield promiseDone;
+});
+
+add_task(function*() {
   Services.prefs.clearUserPref("toolkit.asyncshutdown.testing");
 });
 
--- a/toolkit/mozapps/installer/package-name.mk
+++ b/toolkit/mozapps/installer/package-name.mk
@@ -75,19 +75,18 @@ PKG_SRCPACK_PATH =
 
 else # "pretty" release package names
 
 ifndef MOZ_PKG_APPNAME
 MOZ_PKG_APPNAME = $(MOZ_APP_DISPLAYNAME)
 endif
 MOZ_PKG_APPNAME_LC = $(shell echo $(MOZ_PKG_APPNAME) | tr '[A-Z]' '[a-z]')
 
-
 ifndef MOZ_PKG_LONGVERSION
-MOZ_PKG_LONGVERSION = $(shell echo $(MOZ_PKG_VERSION))
+MOZ_PKG_LONGVERSION = $(MOZ_PKG_VERSION)
 endif
 
 ifeq (,$(filter-out Darwin, $(OS_ARCH))) # Mac
 PKG_BASENAME = $(MOZ_PKG_APPNAME) $(MOZ_PKG_LONGVERSION)
 PKG_INST_BASENAME = $(MOZ_PKG_APPNAME) Setup $(MOZ_PKG_LONGVERSION)
 else
 ifeq (,$(filter-out WINNT, $(OS_ARCH))) # Windows
 PKG_BASENAME = $(MOZ_PKG_APPNAME_LC)-$(MOZ_PKG_VERSION)
--- a/widget/android/AndroidBridge.cpp
+++ b/widget/android/AndroidBridge.cpp
@@ -1827,48 +1827,40 @@ nsresult AndroidBridge::CaptureThumbnail
     nsRect r(nsPresContext::CSSPixelsToAppUnits(srcX / scale),
              nsPresContext::CSSPixelsToAppUnits(srcY / scale),
              nsPresContext::CSSPixelsToAppUnits(srcW / scale),
              nsPresContext::CSSPixelsToAppUnits(srcH / scale));
 
     bool is24bit = (GetScreenDepth() == 24);
     uint32_t stride = bufW * (is24bit ? 4 : 2);
 
-    void* data = env->GetDirectBufferAddress(buffer);
+    uint8_t* data = static_cast<uint8_t*>(env->GetDirectBufferAddress(buffer));
     if (!data)
         return NS_ERROR_FAILURE;
 
-    nsRefPtr<gfxImageSurface> surf =
-        new gfxImageSurface(static_cast<unsigned char*>(data), nsIntSize(bufW, bufH), stride,
-                            is24bit ? gfxImageFormat::RGB24 :
-                                      gfxImageFormat::RGB16_565);
-    if (surf->CairoStatus() != 0) {
-        ALOG_BRIDGE("Error creating gfxImageSurface");
+    MOZ_ASSERT(gfxPlatform::GetPlatform()->SupportsAzureContentForType(BackendType::CAIRO),
+               "Need BackendType::CAIRO support");
+    RefPtr<DrawTarget> dt =
+        Factory::CreateDrawTargetForData(BackendType::CAIRO,
+                                         data,
+                                         IntSize(bufW, bufH),
+                                         stride,
+                                         is24bit ? SurfaceFormat::B8G8R8X8 :
+                                                   SurfaceFormat::R5G6B5);
+    if (!dt) {
+        ALOG_BRIDGE("Error creating DrawTarget");
         return NS_ERROR_FAILURE;
     }
-
-    nsRefPtr<gfxContext> context;
-    if (gfxPlatform::GetPlatform()->SupportsAzureContentForType(BackendType::CAIRO)) {
-        RefPtr<DrawTarget> dt =
-            gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(surf, IntSize(bufW, bufH));
-
-        if (!dt) {
-            ALOG_BRIDGE("Error creating DrawTarget");
-            return NS_ERROR_FAILURE;
-        }
-        context = new gfxContext(dt);
-    } else {
-        context = new gfxContext(surf);
-    }
+    nsRefPtr<gfxContext> context = new gfxContext(dt);
     gfxPoint pt(0, 0);
     context->Translate(pt);
     context->Scale(scale * bufW / srcW, scale * bufH / srcH);
     rv = presShell->RenderDocument(r, renderDocFlags, bgColor, context);
     if (is24bit) {
-        gfxUtils::ConvertBGRAtoRGBA(surf);
+        gfxUtils::ConvertBGRAtoRGBA(data, stride * bufH);
     }
     NS_ENSURE_SUCCESS(rv, rv);
     return NS_OK;
 }
 
 void
 AndroidBridge::GetDisplayPort(bool aPageSizeUpdate, bool aIsBrowserContentDisplayed, int32_t tabId, nsIAndroidViewport* metrics, nsIAndroidDisplayport** displayPort)
 {
--- a/widget/windows/JumpListBuilder.cpp
+++ b/widget/windows/JumpListBuilder.cpp
@@ -16,16 +16,17 @@
 #include "WinTaskbar.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsISimpleEnumerator.h"
 #include "mozilla/Preferences.h"
 #include "nsStringStream.h"
 #include "nsNetUtil.h"
 #include "nsThreadUtils.h"
 #include "mozilla/LazyIdleThread.h"
+#include "nsIObserverService.h"
 
 #include "WinUtils.h"
 
 // The amount of time, in milliseconds, that our IO thread will stay alive after the last event it processes.
 #define DEFAULT_THREAD_TIMEOUT_MS 30000
 
 namespace mozilla {
 namespace widget {
@@ -36,36 +37,42 @@ static NS_DEFINE_CID(kJumpListShortcutCI
 
 // defined in WinTaskbar.cpp
 extern const wchar_t *gMozillaJumpListIDGeneric;
 
 bool JumpListBuilder::sBuildingList = false;
 const char kPrefTaskbarEnabled[] = "browser.taskbar.lists.enabled";
 
 NS_IMPL_ISUPPORTS(JumpListBuilder, nsIJumpListBuilder, nsIObserver)
+#define TOPIC_PROFILE_BEFORE_CHANGE "profile-before-change"
 
 JumpListBuilder::JumpListBuilder() :
   mMaxItems(0),
   mHasCommit(false)
 {
   ::CoInitialize(nullptr);
   
   CoCreateInstance(CLSID_DestinationList, nullptr, CLSCTX_INPROC_SERVER,
                    IID_ICustomDestinationList, getter_AddRefs(mJumpListMgr));
 
   // Make a lazy thread for any IO
   mIOThread = new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS,
                                  NS_LITERAL_CSTRING("Jump List"),
                                  LazyIdleThread::ManualShutdown);
   Preferences::AddStrongObserver(this, kPrefTaskbarEnabled);
+
+  nsCOMPtr<nsIObserverService> observerService =
+    do_GetService("@mozilla.org/observer-service;1");
+  if (observerService) {
+    observerService->AddObserver(this, TOPIC_PROFILE_BEFORE_CHANGE, false);
+  }
 }
 
 JumpListBuilder::~JumpListBuilder()
 {
-  mIOThread->Shutdown();
   Preferences::RemoveObserver(this, kPrefTaskbarEnabled);
   mJumpListMgr = nullptr;
   ::CoUninitialize();
 }
 
 /* readonly attribute short available; */
 NS_IMETHODIMP JumpListBuilder::GetAvailable(int16_t *aAvailable)
 {
@@ -121,16 +128,19 @@ NS_IMETHODIMP JumpListBuilder::InitListB
   if (!mJumpListMgr)
     return NS_ERROR_NOT_AVAILABLE;
 
   if(sBuildingList)
     AbortListBuild();
 
   IObjectArray *objArray;
 
+  // The returned objArray of removed items are for manually removed items.
+  // This does not return items which are removed because they were previously
+  // part of the jump list but are no longer part of the jump list.
   if (SUCCEEDED(mJumpListMgr->BeginList(&mMaxItems, IID_PPV_ARGS(&objArray)))) {
     if (objArray) {
       TransferIObjectArrayToIMutableArray(objArray, removedItems);
       objArray->Release();
     }
 
     RemoveIconCacheForItems(removedItems);
 
@@ -374,16 +384,22 @@ NS_IMETHODIMP JumpListBuilder::AddListTo
       hr = collection->QueryInterface(IID_IObjectArray, (LPVOID*)&pArray);
       if (FAILED(hr))
         return NS_ERROR_UNEXPECTED;
 
       // Add the tasks
       hr = mJumpListMgr->AppendCategory(reinterpret_cast<const wchar_t*>(catName.BeginReading()), pArray);
       if (SUCCEEDED(hr))
         *_retval = true;
+
+      // Get rid of the old icons
+      nsCOMPtr<nsIRunnable> event =
+        new mozilla::widget::AsyncDeleteAllFaviconsFromDisk(true);
+      mIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
+
       return NS_OK;
     }
     break;
   }
   return NS_OK;
 }
 
 /* void abortListBuild(); */
@@ -496,20 +512,29 @@ nsresult JumpListBuilder::TransferIObjec
     if (NS_SUCCEEDED(rv)) {
       removedItems->AppendElement(item, false);
     }
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP JumpListBuilder::Observe(nsISupports* aSubject,
-                                        const char* aTopic,
-                                        const char16_t* aData)
+                                       const char* aTopic,
+                                       const char16_t* aData)
 {
-  if (nsDependentString(aData).EqualsASCII(kPrefTaskbarEnabled)) {
+  NS_ENSURE_ARG_POINTER(aTopic);
+  if (strcmp(aTopic, TOPIC_PROFILE_BEFORE_CHANGE) == 0) {
+    nsCOMPtr<nsIObserverService> observerService =
+      do_GetService("@mozilla.org/observer-service;1");
+    if (observerService) {
+      observerService->RemoveObserver(this, TOPIC_PROFILE_BEFORE_CHANGE);
+    }
+    mIOThread->Shutdown();
+  } else if (strcmp(aTopic, "nsPref:changed") == 0 &&
+             nsDependentString(aData).EqualsASCII(kPrefTaskbarEnabled)) {
     bool enabled = Preferences::GetBool(kPrefTaskbarEnabled, true);
     if (!enabled) {
       
       nsCOMPtr<nsIRunnable> event = 
         new mozilla::widget::AsyncDeleteAllFaviconsFromDisk();
       mIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
     }
   }
--- a/widget/windows/WinUtils.cpp
+++ b/widget/windows/WinUtils.cpp
@@ -953,17 +953,19 @@ NS_IMETHODIMP AsyncDeleteIconFromDisk::R
 
   return NS_OK;
 }
 
 AsyncDeleteIconFromDisk::~AsyncDeleteIconFromDisk()
 {
 }
 
-AsyncDeleteAllFaviconsFromDisk::AsyncDeleteAllFaviconsFromDisk()
+AsyncDeleteAllFaviconsFromDisk::
+  AsyncDeleteAllFaviconsFromDisk(bool aIgnoreRecent)
+  : mIgnoreRecent(aIgnoreRecent)
 {
 }
 
 NS_IMETHODIMP AsyncDeleteAllFaviconsFromDisk::Run()
 {
   // Construct the path of our jump list cache
   nsCOMPtr<nsIFile> jumpListCacheDir;
   nsresult rv = NS_GetSpecialDirectory("ProfLDS", 
@@ -992,16 +994,34 @@ NS_IMETHODIMP AsyncDeleteAllFaviconsFrom
       continue;
 
     if (StringTail(path, 4).LowerCaseEqualsASCII(".ico")) {
       // Check if the cached ICO file exists
       bool exists;
       if (NS_FAILED(currFile->Exists(&exists)) || !exists)
         continue;
 
+      if (mIgnoreRecent) {
+        // Check to make sure the icon wasn't just recently created.
+        // If it was created recently, don't delete it yet.
+        int64_t fileModTime = 0;
+        rv = currFile->GetLastModifiedTime(&fileModTime);
+        fileModTime /= PR_MSEC_PER_SEC;
+        // If the icon is older than the regeneration time (+ 10 min to be
+        // safe), then it's old and we can get rid of it.
+        // This code is only hit directly after a regeneration.
+        int32_t icoNoDeleteSeconds =
+          FaviconHelper::GetICOCacheSecondsTimeout() + 600;
+        int64_t nowTime = PR_Now() / int64_t(PR_USEC_PER_SEC);
+        if (NS_FAILED(rv) ||
+          (nowTime - fileModTime) < icoNoDeleteSeconds) {
+          continue;
+        }
+      }
+
       // We found an ICO file that exists, so we should remove it
       currFile->Remove(false);
     }
   } while(true);
 
   return NS_OK;
 }
 
--- a/widget/windows/WinUtils.h
+++ b/widget/windows/WinUtils.h
@@ -406,18 +406,20 @@ private:
 };
 
 class AsyncDeleteAllFaviconsFromDisk : public nsIRunnable
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIRUNNABLE
 
-  AsyncDeleteAllFaviconsFromDisk();
+  AsyncDeleteAllFaviconsFromDisk(bool aIgnoreRecent = false);
   virtual ~AsyncDeleteAllFaviconsFromDisk();
+private:
+  bool mIgnoreRecent;
 };
 
 class FaviconHelper
 {
 public:
   static const char kJumpListCacheDir[];
   static const char kShortcutCacheDir[];
   static nsresult ObtainCachedIconFile(nsCOMPtr<nsIURI> aFaviconPageURI,
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -1941,25 +1941,25 @@ NS_METHOD nsWindow::GetRestoredBounds(ns
 {
   if (SizeMode() == nsSizeMode_Normal) {
     return GetScreenBounds(aRect);
   }
   if (!mWnd) {
     return NS_ERROR_FAILURE;
   }
 
-  WINDOWPLACEMENT pl = { sizeof WINDOWPLACEMENT, };
+  WINDOWPLACEMENT pl = { sizeof(WINDOWPLACEMENT) };
   VERIFY(::GetWindowPlacement(mWnd, &pl));
   const RECT& r = pl.rcNormalPosition;
 
   HMONITOR monitor = ::MonitorFromWindow(mWnd, MONITOR_DEFAULTTONULL);
   if (!monitor) {
     return NS_ERROR_FAILURE;
   }
-  MONITORINFO mi = { sizeof MONITORINFO, };
+  MONITORINFO mi = { sizeof(MONITORINFO) };
   VERIFY(::GetMonitorInfo(monitor, &mi));
 
   aRect.SetRect(r.left, r.top, r.right - r.left, r.bottom - r.top);
   aRect.MoveBy(mi.rcWork.left - mi.rcMonitor.left,
                mi.rcWork.top - mi.rcMonitor.top);
   return NS_OK;
 }
 
--- a/xpcom/reflect/xptinfo/src/ShimInterfaceInfo.cpp
+++ b/xpcom/reflect/xptinfo/src/ShimInterfaceInfo.cpp
@@ -356,16 +356,50 @@ struct ComponentsInterfaceShimEntry {
 };
 
 #define DEFINE_SHIM_WITH_CUSTOM_INTERFACE(geckoName, domName) \
   { #geckoName, NS_GET_IID(geckoName), \
      mozilla::dom::domName ## Binding::sNativePropertyHooks }
 #define DEFINE_SHIM(name) \
   DEFINE_SHIM_WITH_CUSTOM_INTERFACE(nsIDOM ## name, name)
 
+/**
+ * These shim entries allow us to make old XPIDL interfaces implementing DOM
+ * APIs as non-scriptable in order to save some runtime memory on Firefox OS,
+ * without breaking the entries under Components.interfaces which might both
+ * be used by our code and add-ons.  Specifically, the shim entries provide
+ * the following:
+ *
+ * * Components.interfaces.nsIFoo entries.  These entries basically work
+ *   almost exactly as the usual ones that you would get through the
+ *   XPIDL machinery.  Specifically, they have the right name, they reflect
+ *   the right IID, and they will work properly when passed to QueryInterface.
+ *
+ * * Components.interfaces.nsIFoo.CONSTANT values.  These entries will have
+ *   the right name and the right value for most integer types.  Note that
+ *   support for non-numerical constants is untested and will probably not
+ *   work out of the box.
+ *
+ * FAQ:
+ * * When should I add an entry to the list here?
+ *   Only if you're making an XPIDL interfaces which has a corresponding
+ *   WebIDL interface non-scriptable.
+ * * When should I remove an entry from this list?
+ *   If you are completely removing an XPIDL interface from the code base.  If
+ *   you forget to do so, the compiler will remind you.
+ * * How should I add an entry to the list here?
+ *   First, make sure that the XPIDL interface in question is non-scriptable
+ *   and also has a corresponding WebIDL interface.  Then, add two include
+ *   entries above, one for the XPIDL interface and one for the WebIDL
+ *   interface, and add a shim entry below.  If the name of the XPIDL
+ *   interface only has an "nsIDOM" prefix prepended to the WebIDL name, you
+ *   can use the DEFINE_SHIM macro and pass in the name of the WebIDL
+ *   interface.  Otherwise, use DEFINE_SHIM_WITH_CUSTOM_INTERFACE.
+ */
+
 const ComponentsInterfaceShimEntry kComponentsInterfaceShimMap[] =
 {
   DEFINE_SHIM(AnimationEvent),
   DEFINE_SHIM(Attr),
   DEFINE_SHIM(BeforeUnloadEvent),
   DEFINE_SHIM(CanvasRenderingContext2D),
   DEFINE_SHIM(CDATASection),
   DEFINE_SHIM(CharacterData),
--- a/xulrunner/app/nsXULRunnerApp.cpp
+++ b/xulrunner/app/nsXULRunnerApp.cpp
@@ -44,17 +44,17 @@ using namespace mozilla;
  *        printf-style format string followed by arguments.
  */
 static void Output(bool isError, const char *fmt, ... )
 {
   va_list ap;
   va_start(ap, fmt);
 
 #if (defined(XP_WIN) && !MOZ_WINCONSOLE)
-  char16_t msg[2048];
+  wchar_t msg[2048];
   _vsnwprintf(msg, sizeof(msg)/sizeof(msg[0]), NS_ConvertUTF8toUTF16(fmt).get(), ap);
 
   UINT flags = MB_OK;
   if (isError)
     flags |= MB_ICONERROR;
   else
     flags |= MB_ICONINFORMATION;