Merge inbound to m-c.
authorRyan VanderMeulen <ryanvm@gmail.com>
Wed, 11 Sep 2013 20:24:15 -0400
changeset 146605 162f05aec17a2b535e5f68aa752781ae84aecbe4
parent 146521 5b9ad615d2e1b47011dd28a1fbdad4a8eced5e01 (current diff)
parent 146604 c5fd139db7816f91eaa83b35dc0eb227d12d90cb (diff)
child 146655 2f11fad2f3070b0d1ea93d8585dbe1261d713d2c
push id25266
push userryanvm@gmail.com
push dateThu, 12 Sep 2013 00:24:40 +0000
treeherdermozilla-central@162f05aec17a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone26.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 inbound to m-c.
b2g/app/b2g.js
caps/tests/mochitest/test_principal_extendedorigin_appid_appstatus.html
content/canvas/src/WebGLMemoryMultiReporterWrapper.h
content/html/content/crashtests/832011.html
dom/base/crashtests/844559.html
dom/promise/PromiseResolver.cpp
dom/promise/PromiseResolver.h
dom/quota/UsageRunnable.h
js/src/vm/RegExpStatics-inl.h
js/xpconnect/crashtests/776328.html
js/xpconnect/crashtests/776333.html
js/xpconnect/crashtests/791845.html
js/xpconnect/tests/chrome/test_bug517163.xul
js/xpconnect/tests/mochitest/test_bug785096.html
js/xpconnect/tests/mochitest/test_lookupMethod.html
--- a/CLOBBER
+++ b/CLOBBER
@@ -13,9 +13,9 @@
 #          |               |
 #          O <-- Clobber   O  <-- Clobber
 #
 # Note: The description below will be part of the error message shown to users.
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
-Bug 912832 - Inverse tiers and subtiers for build traversal
+Bug 785884 - Implement support for temporary storage (aka shared pool) 
--- a/accessible/src/generic/Accessible.h
+++ b/accessible/src/generic/Accessible.h
@@ -476,16 +476,17 @@ public:
   bool IsCombobox() const { return HasGenericType(eCombobox); }
 
   bool IsDoc() const { return HasGenericType(eDocument); }
   DocAccessible* AsDoc();
 
   bool IsHyperText() const { return HasGenericType(eHyperText); }
   HyperTextAccessible* AsHyperText();
 
+  bool IsHTMLBr() const { return mType == eHTMLBRType; }
   bool IsHTMLFileInput() const { return mType == eHTMLFileInputType; }
 
   bool IsHTMLListItem() const { return mType == eHTMLLiType; }
   HTMLLIAccessible* AsHTMLListItem();
 
   bool IsHTMLOptGroup() const { return mType == eHTMLOptGroupType; }
 
   bool IsHTMLTable() const { return mType == eHTMLTableType; }
--- a/accessible/src/generic/HyperTextAccessible.cpp
+++ b/accessible/src/generic/HyperTextAccessible.cpp
@@ -8,16 +8,17 @@
 
 #include "Accessible-inl.h"
 #include "nsAccessibilityService.h"
 #include "nsAccUtils.h"
 #include "DocAccessible.h"
 #include "Role.h"
 #include "States.h"
 #include "TextAttrs.h"
+#include "TreeWalker.h"
 
 #include "nsIClipboard.h"
 #include "nsContentUtils.h"
 #include "nsFocusManager.h"
 #include "nsIDOMRange.h"
 #include "nsIEditingSession.h"
 #include "nsIEditor.h"
 #include "nsIFrame.h"
@@ -2057,16 +2058,41 @@ HyperTextAccessible::RemoveChild(Accessi
   int32_t childIndex = aAccessible->IndexInParent();
   int32_t count = mOffsets.Length() - childIndex;
   if (count > 0)
     mOffsets.RemoveElementsAt(childIndex, count);
 
   return Accessible::RemoveChild(aAccessible);
 }
 
+void
+HyperTextAccessible::CacheChildren()
+{
+  // Trailing HTML br element don't play any difference. We don't need to expose
+  // it to AT (see bug https://bugzilla.mozilla.org/show_bug.cgi?id=899433#c16
+  // for details).
+
+  TreeWalker walker(this, mContent);
+  Accessible* child = nullptr;
+  Accessible* lastChild = nullptr;
+  while ((child = walker.NextChild())) {
+    if (lastChild)
+      AppendChild(lastChild);
+
+    lastChild = child;
+  }
+
+  if (lastChild) {
+    if (lastChild->IsHTMLBr())
+      Document()->UnbindFromDocument(lastChild);
+    else
+      AppendChild(lastChild);
+  }
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // HyperTextAccessible public static
 
 nsresult
 HyperTextAccessible::ContentToRenderedOffset(nsIFrame* aFrame, int32_t aContentOffset,
                                              uint32_t* aRenderedOffset)
 {
   if (!aFrame) {
--- a/accessible/src/generic/HyperTextAccessible.h
+++ b/accessible/src/generic/HyperTextAccessible.h
@@ -268,16 +268,17 @@ public:
   /**
    * Return the editor associated with the accessible.
    */
   virtual already_AddRefed<nsIEditor> GetEditor() const;
 
 protected:
   // Accessible
   virtual ENameValueFlag NativeName(nsString& aName) MOZ_OVERRIDE;
+  virtual void CacheChildren() MOZ_OVERRIDE;
 
   // HyperTextAccessible
 
   /**
    * Transform magic offset into text offset.
    */
   int32_t ConvertMagicOffset(int32_t aOffset)
   {
--- a/accessible/src/html/HTMLElementAccessibles.h
+++ b/accessible/src/html/HTMLElementAccessibles.h
@@ -28,17 +28,20 @@ public:
 
 /**
  * Used for HTML br element.
  */
 class HTMLBRAccessible : public LeafAccessible
 {
 public:
   HTMLBRAccessible(nsIContent* aContent, DocAccessible* aDoc) :
-    LeafAccessible(aContent, aDoc) {}
+    LeafAccessible(aContent, aDoc)
+  {
+    mType = eHTMLBRType;
+  }
 
   // Accessible
   virtual a11y::role NativeRole();
   virtual uint64_t NativeState();
 
 protected:
   // Accessible
   virtual ENameValueFlag NativeName(nsString& aName) MOZ_OVERRIDE;
--- a/accessible/src/html/HTMLFormControlAccessible.cpp
+++ b/accessible/src/html/HTMLFormControlAccessible.cpp
@@ -459,32 +459,16 @@ HTMLTextFieldAccessible::GetEditor() con
   pusher.PushNull();
 
   nsCOMPtr<nsIEditor> editor;
   editableElt->GetEditor(getter_AddRefs(editor));
 
   return editor.forget();
 }
 
-void
-HTMLTextFieldAccessible::CacheChildren()
-{
-  // XXX: textarea shouldn't contain anything but text leafs. Currently it may
-  // contain a trailing fake HTML br element added for layout needs. We don't
-  // need to expose it since it'd be confusing for AT.
-  TreeWalker walker(this, mContent);
-  Accessible* child = nullptr;
-  while ((child = walker.NextChild())) {
-    if (child->IsTextLeaf())
-      AppendChild(child);
-    else
-      Document()->UnbindFromDocument(child);
-  }
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLTextFieldAccessible: Widgets
 
 bool
 HTMLTextFieldAccessible::IsWidget() const
 {
   return true;
 }
--- a/accessible/src/html/HTMLFormControlAccessible.h
+++ b/accessible/src/html/HTMLFormControlAccessible.h
@@ -137,18 +137,16 @@ public:
 
   // Widgets
   virtual bool IsWidget() const;
   virtual Accessible* ContainerWidget() const;
 
 protected:
   // Accessible
   virtual ENameValueFlag NativeName(nsString& aName) MOZ_OVERRIDE;
-
-  virtual void CacheChildren() MOZ_OVERRIDE;
 };
 
 
 /**
  * Accessible for input@type="file" element.
  */
 class HTMLFileInputAccessible : public HyperTextAccessibleWrap
 {
--- a/accessible/tests/mochitest/editabletext/test_1.html
+++ b/accessible/tests/mochitest/editabletext/test_1.html
@@ -65,17 +65,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 
     //gA11yEventDumpToConsole = true; // debug stuff
 
     function runTest()
     {
       var testRun = new editableTextTestRun();
 
       addTestEditable("input", testRun);
-      addTestEditable("div", testRun, '\n');
+      addTestEditable("div", testRun);
       addTestEditable(getNode("frame").contentDocument, testRun, '\n');
 
       testRun.run(); // Will call SimpleTest.finish();
     }
 
     function doTest()
     {
       // Prepare tested elements.
--- a/accessible/tests/mochitest/elm/test_HTMLSpec.html
+++ b/accessible/tests/mochitest/elm/test_HTMLSpec.html
@@ -1293,17 +1293,18 @@
   <p id="b_container">normal<b>bold</b></p>
   <p id="bdi_container">User <bdi>إيان</bdi>: 90 points</p>
   <p id="bdo_container"><bdo dir="rtl">This text will go right to left.</bdo></p>
 
   <blockquote id="blockquote" cite="http://developer.mozilla.org">
     <p>This is a quotation taken from the Mozilla Developer Center.</p>
   </blockquote>
 
-  <p id="br_container"><br></p>
+  <!--  two BRs, one will be eaten -->
+  <p id="br_container"><br><br></p>
 
   <button id="button">button</button>
   <form>
     <button id="button_default" type="submit">button</button>
   </form>
 
   <canvas id="canvas"></canvas>
 
--- a/accessible/tests/mochitest/text/test_gettext.html
+++ b/accessible/tests/mochitest/text/test_gettext.html
@@ -91,22 +91,22 @@
   <div id="d2">Brave Sir  Robin   ran</div>
   <div id="e2" contenteditable="true">Brave Sir  Robin   ran</div>
 
   <pre>
   <div id="d3">oneword
 
 two words
 </div>
-  <div id="dbr3">oneword<br/><br/>two words<br/></div>
+  <div id="dbr3">oneword<br/><br/>two words<br/><br/></div>
   <div id="e3" contenteditable="true">oneword
 
 two words
 </div>
-  <div id="ebr3" contenteditable="true">oneword<br/><br/>two words<br/></div>
+  <div id="ebr3" contenteditable="true">oneword<br/><br/>two words<br/><br/></div>
   <textarea id="t3" cols="300">oneword
 
 two words
 </textarea>
   </pre>
 
 </body>
 </html>
--- a/accessible/tests/mochitest/text/test_hypertext.html
+++ b/accessible/tests/mochitest/text/test_hypertext.html
@@ -69,17 +69,17 @@
       IDs = [ "list" ];
       testCharacterCount(IDs, 1);
       testText(IDs, 0, 1, kEmbedChar);
 
       IDs = [ "listitem" ];
       testCharacterCount(IDs, 5);
       testText(IDs, 0, 5, "1.foo");
 
-      testText(["testbr"], 0, 4, "foo\n");
+      testText(["testbr"], 0, 3, "foo");
 
       testTextAtOffset(2, nsIAccessibleText.BOUNDARY_CHAR, "o", 2, 3, "testbr",
                        kOk, kOk, kOk);
       testTextAtOffset(2, nsIAccessibleText.BOUNDARY_WORD_START, "foo\n", 0, 4,
                        "testbr", kTodo, kOk, kTodo);
       testTextBeforeOffset(2, nsIAccessibleText.BOUNDARY_LINE_START, "foo\n",
                            0, 4, "testbr", kTodo, kOk, kTodo);
 
--- a/accessible/tests/mochitest/text/test_lineboundary.html
+++ b/accessible/tests/mochitest/text/test_lineboundary.html
@@ -45,16 +45,17 @@
 
       IDs = [ "ml_div", "ml_divbr", "ml_editable", "ml_editablebr", "ml_textarea"];
 
       testTextBeforeOffset(IDs, BOUNDARY_LINE_START,
                            [ [ 0, 7, "", 0, 0 ],
                              [ 8, 8, "oneword\n", 0, 8 ],
                              [ 9, 18, "\n", 8, 9 ],
                              [ 19, 19, "two words\n", 9, 19 ]]);
+
       testTextBeforeOffset(IDs, BOUNDARY_LINE_END,
                           [ [ 0, 7, "", 0, 0 ],
                             [ 8, 8, "oneword", 0, 7 ],
                             [ 9, 18, "\n", 7, 8 ],
                             [ 19, 19, "\ntwo words", 8, 18 ]]);
 
       testTextAtOffset(IDs, BOUNDARY_LINE_START,
                        [ [ 0, 7, "oneword\n", 0, 8 ],
@@ -91,17 +92,27 @@
       testTextAtOffset([ getAccessible("ht_1").firstChild ], BOUNDARY_LINE_END,
                        [ [ 0, 5, "a " + kEmbedChar + " c", 0, 5 ] ]);
 
       testTextAfterOffset([ getAccessible("ht_1").firstChild ], BOUNDARY_LINE_START,
                           [ [ 0, 5, "", 5, 5 ] ]);
 
       testTextAfterOffset([ getAccessible("ht_1").firstChild ], BOUNDARY_LINE_END,
                           [ [ 0, 5, "", 5, 5 ] ]);
-                           
+
+      //////////////////////////////////////////////////////////////////////////
+      // foo<br> and foo<br><br>
+
+      testTextAtOffset([ getAccessible("ht_2").firstChild.firstChild ],
+                       BOUNDARY_LINE_START,
+                       [ [ 0, 3, "foo", 0, 3 ] ]);
+      testTextAtOffset([ getAccessible("ht_3").firstChild.firstChild ],
+                       BOUNDARY_LINE_START,
+                       [ [ 0, 3, "foo\n", 0, 4 ], [ 4, 4, "", 4, 4 ] ]);
+
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
 <body>
@@ -130,27 +141,30 @@
   <input id="input" value="hello my friend"/>
   <div id="div">hello my friend</div>
   <div id="editable" contenteditable="true">hello my friend</div>
   <textarea id="textarea">hello my friend</textarea>
   <iframe id="ta_cntr"
           src="data:text/html,<html><body><textarea id='ta'>hello my friend</textarea></body></html>"></iframe>
 
   <pre>
-    <div id="ml_div">oneword
+    <div id="ml_div" style="border-style:outset;">oneword
 
 two words
 </div>
-  <div id="ml_divbr">oneword<br/><br/>two words<br/></div>
-  <div id="ml_editable" contenteditable="true">oneword
+  <div id="ml_divbr" style="border-style:outset;">oneword<br/><br/>two words<br/><br/></div>
+  <div id="ml_editable" style="border-style:outset;" contenteditable="true">oneword
 
 two words
 </div>
-  <div id="ml_editablebr" contenteditable="true">oneword<br/><br/>two words<br/></div>
+  <div id="ml_editablebr" contenteditable="true" style="border-style:outset;">oneword<br/><br/>two words<br/><br/></div>
   <textarea id="ml_textarea" cols="300">oneword
 
 two words
 </textarea>
   </pre>
 
   <iframe id="ht_1" src="data:text/html,<html><body>a <a href=''>b</a> c</body></html>"></iframe>
+
+  <iframe id="ht_2" src="data:text/html,<div contentEditable='true'>foo<br/></div>"></iframe>
+  <iframe id="ht_3" src="data:text/html,<div contentEditable='true'>foo<br/><br/></div>"></iframe>
 </body>
 </html>
--- a/accessible/tests/mochitest/text/test_wordboundary.html
+++ b/accessible/tests/mochitest/text/test_wordboundary.html
@@ -263,22 +263,22 @@
   </pre>
   <textarea id="t8" cols="300">Brave Sir  Robin   ran</textarea>
 
   <pre>
 <div id="ml_div1">oneword
 
 two words
 </div>
-<div id="ml_divbr1">oneword<br/><br/>two words<br/></div>
+<div id="ml_divbr1">oneword<br/><br/>two words<br/><br/></div>
 <div id="ml_ediv1" contenteditable="true">oneword
 
 two words
 </div>
-<div id="ml_edivbr1" contenteditable="true">oneword<br/><br/>two words<br/></div>
+<div id="ml_edivbr1" contenteditable="true">oneword<br/><br/>two words<br/><br/></div>
 <textarea id="ml_t1" cols="300">oneword
 
 two words
 </textarea>
   </pre>
 
 </body>
 </html>
--- a/addon-sdk/source/lib/sdk/test/harness.js
+++ b/addon-sdk/source/lib/sdk/test/harness.js
@@ -143,16 +143,19 @@ function dictDiff(last, curr) {
 }
 
 function reportMemoryUsage() {
   memory.gc();
 
   var mgr = Cc["@mozilla.org/memory-reporter-manager;1"]
             .getService(Ci.nsIMemoryReporterManager);
 
+  // XXX: this code is *so* bogus -- nsIMemoryReporter changed its |memoryUsed|
+  // field to |amount| *years* ago, and even bigger changes have happened
+  // since -- that it must just never be run.
   var reporters = mgr.enumerateReporters();
   if (reporters.hasMoreElements())
     print("\n");
 
   while (reporters.hasMoreElements()) {
     var reporter = reporters.getNext();
     reporter.QueryInterface(Ci.nsIMemoryReporter);
     print(reporter.description + ": " + reporter.memoryUsed + "\n");
@@ -371,24 +374,17 @@ function getPotentialLeaks() {
     }
   }
 
   let mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
             getService(Ci.nsIMemoryReporterManager);
 
   let enm = mgr.enumerateReporters();
   while (enm.hasMoreElements()) {
-    let reporter = enm.getNext().QueryInterface(Ci.nsIMemoryReporter);
-    logReporter(reporter.process, reporter.path, reporter.kind, reporter.units,
-                reporter.amount, reporter.description);
-  }
-
-  let enm = mgr.enumerateMultiReporters();
-  while (enm.hasMoreElements()) {
-    let mr = enm.getNext().QueryInterface(Ci.nsIMemoryMultiReporter);
+    let mr = enm.getNext().QueryInterface(Ci.nsIMemoryReporter);
     mr.collectReports(logReporter, null);
   }
 
   return { compartments: compartments, windows: windows };
 }
 
 function nextIteration(tests) {
   if (tests) {
--- a/b2g/components/MozKeyboard.js
+++ b/b2g/components/MozKeyboard.js
@@ -540,18 +540,18 @@ MozInputContext.prototype = {
   },
 
   get lang() {
     return this._context.lang;
   },
 
   getText: function ic_getText(offset, length) {
     let self = this;
-    return this.createPromise(function(resolver) {
-      let resolverId = self.getPromiseResolverId(resolver);
+    return this.createPromise(function(resolve, reject) {
+      let resolverId = self.getPromiseResolverId({ resolve: resolve, reject: reject });
       cpmm.sendAsyncMessage('Keyboard:GetText', {
         contextId: self._contextId,
         requestId: resolverId,
         offset: offset,
         length: length
       });
     });
   },
@@ -569,18 +569,18 @@ MozInputContext.prototype = {
   },
 
   get textAfterCursor() {
     return this._context.textAfterCursor;
   },
 
   setSelectionRange: function ic_setSelectionRange(start, length) {
     let self = this;
-    return this.createPromise(function(resolver) {
-      let resolverId = self.getPromiseResolverId(resolver);
+    return this.createPromise(function(resolve, reject) {
+      let resolverId = self.getPromiseResolverId({ resolve: resolve, reject: reject });
       cpmm.sendAsyncMessage("Keyboard:SetSelectionRange", {
         contextId: self._contextId,
         requestId: resolverId,
         selectionStart: start,
         selectionEnd: start + length
       });
     });
   },
@@ -598,64 +598,64 @@ MozInputContext.prototype = {
   },
 
   set onselectionchange(handler) {
     this.__DOM_IMPL__.setEventHandler("onselectionchange");
   },
 
   replaceSurroundingText: function ic_replaceSurrText(text, offset, length) {
     let self = this;
-    return this.createPromise(function(resolver) {
-      let resolverId = self.getPromiseResolverId(resolver);
+    return this.createPromise(function(resolve, reject) {
+      let resolverId = self.getPromiseResolverId({ resolve: resolve, reject: reject });
       cpmm.sendAsyncMessage('Keyboard:ReplaceSurroundingText', {
         contextId: self._contextId,
         requestId: resolverId,
         text: text,
         beforeLength: offset || 0,
         afterLength: length || 0
       });
     });
   },
 
   deleteSurroundingText: function ic_deleteSurrText(offset, length) {
     return this.replaceSurroundingText(null, offset, length);
   },
 
   sendKey: function ic_sendKey(keyCode, charCode, modifiers) {
     let self = this;
-    return this.createPromise(function(resolver) {
-      let resolverId = self.getPromiseResolverId(resolver);
+    return this.createPromise(function(resolve, reject) {
+      let resolverId = self.getPromiseResolverId({ resolve: resolve, reject: reject });
       cpmm.sendAsyncMessage('Keyboard:SendKey', {
         contextId: self._contextId,
         requestId: resolverId,
         keyCode: keyCode,
         charCode: charCode,
         modifiers: modifiers
       });
     });
   },
 
   setComposition: function ic_setComposition(text, cursor, clauses) {
     let self = this;
-    return this.createPromise(function(resolver) {
-      let resolverId = self.getPromiseResolverId(resolver);
+    return this.createPromise(function(resolve, reject) {
+      let resolverId = self.getPromiseResolverId({ resolve: resolve, reject: reject });
       cpmm.sendAsyncMessage('Keyboard:SetComposition', {
         contextId: self._contextId,
         requestId: resolverId,
         text: text,
         cursor: cursor || text.length,
         clauses: clauses || null
       });
     });
   },
 
   endComposition: function ic_endComposition(text) {
     let self = this;
-    return this.createPromise(function(resolver) {
-      let resolverId = self.getPromiseResolverId(resolver);
+    return this.createPromise(function(resolve, reject) {
+      let resolverId = self.getPromiseResolverId({ resolve: resolve, reject: reject });
       cpmm.sendAsyncMessage('Keyboard:EndComposition', {
         contextId: self._contextId,
         requestId: resolverId,
         text: text || ''
       });
     });
   }
 };
--- a/browser/base/content/browser-plugins.js
+++ b/browser/base/content/browser-plugins.js
@@ -7,19 +7,19 @@ var gPluginHandler = {
   PLUGIN_SCRIPTED_STATE_NONE: 0,
   PLUGIN_SCRIPTED_STATE_FIRED: 1,
   PLUGIN_SCRIPTED_STATE_DONE: 2,
 
   PREF_NOTIFY_MISSING_FLASH: "plugins.notifyMissingFlash",
   PREF_SESSION_PERSIST_MINUTES: "plugin.sessionPermissionNow.intervalInMinutes",
   PREF_PERSISTENT_DAYS: "plugin.persistentPermissionAlways.intervalInDays",
 
-  getPluginUI: function (plugin, className) {
+  getPluginUI: function (plugin, anonid) {
     return plugin.ownerDocument.
-           getAnonymousElementByAttribute(plugin, "class", className);
+           getAnonymousElementByAttribute(plugin, "anonid", anonid);
   },
 
 #ifdef MOZ_CRASHREPORTER
   get CrashSubmit() {
     delete this.CrashSubmit;
     Cu.import("resource://gre/modules/CrashSubmit.jsm", this);
     return this.CrashSubmit;
   },
@@ -231,17 +231,17 @@ var gPluginHandler = {
       if (!(plugin instanceof Ci.nsIObjectLoadingContent))
         return;
     }
 
     if (eventType == "PluginBindingAttached") {
       // The plugin binding fires this event when it is created.
       // As an untrusted event, ensure that this object actually has a binding
       // and make sure we don't handle it twice
-      let overlay = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox");
+      let overlay = this.getPluginUI(plugin, "main");
       if (!overlay || overlay._bindingHandled) {
         return;
       }
       overlay._bindingHandled = true;
 
       // Lookup the handler for this binding
       eventType = this._getBindingType(plugin);
       if (!eventType) {
@@ -259,72 +259,72 @@ var gPluginHandler = {
         break;
 
       case "PluginNotFound":
         let installable = this.showInstallNotification(plugin, eventType);
         // For non-object plugin tags, register a click handler to install the
         // plugin. Object tags can, and often do, deal with that themselves,
         // so don't stomp on the page developers toes.
         if (installable && !(plugin instanceof HTMLObjectElement)) {
-          let installStatus = doc.getAnonymousElementByAttribute(plugin, "class", "installStatus");
+          let installStatus = this.getPluginUI(plugin, "installStatus");
           installStatus.setAttribute("installable", "true");
-          let iconStatus = doc.getAnonymousElementByAttribute(plugin, "class", "icon");
+          let iconStatus = this.getPluginUI(plugin, "icon");
           iconStatus.setAttribute("installable", "true");
 
-          let installLink = doc.getAnonymousElementByAttribute(plugin, "anonid", "installPluginLink");
+          let installLink = this.getPluginUI(plugin, "installPluginLink");
           this.addLinkClickCallback(installLink, "installSinglePlugin", plugin);
         }
         break;
 
       case "PluginBlocklisted":
       case "PluginOutdated":
         shouldShowNotification = true;
         break;
 
       case "PluginVulnerableUpdatable":
-        let updateLink = doc.getAnonymousElementByAttribute(plugin, "anonid", "checkForUpdatesLink");
+        let updateLink = this.getPluginUI(plugin, "checkForUpdatesLink");
         this.addLinkClickCallback(updateLink, "openPluginUpdatePage");
         /* FALLTHRU */
 
       case "PluginVulnerableNoUpdate":
       case "PluginClickToPlay":
         this._handleClickToPlayEvent(plugin);
-        let overlay = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox");
+        let overlay = this.getPluginUI(plugin, "main");
         let pluginName = this._getPluginInfo(plugin).pluginName;
         let messageString = gNavigatorBundle.getFormattedString("PluginClickToActivate", [pluginName]);
-        let overlayText = doc.getAnonymousElementByAttribute(plugin, "class", "msg msgClickToPlay");
+        let overlayText = this.getPluginUI(plugin, "clickToPlay");
         overlayText.textContent = messageString;
         if (eventType == "PluginVulnerableUpdatable" ||
             eventType == "PluginVulnerableNoUpdate") {
           let vulnerabilityString = gNavigatorBundle.getString(eventType);
-          let vulnerabilityText = doc.getAnonymousElementByAttribute(plugin, "anonid", "vulnerabilityStatus");
+          let vulnerabilityText = this.getPluginUI(plugin, "vulnerabilityStatus");
           vulnerabilityText.textContent = vulnerabilityString;
         }
         shouldShowNotification = true;
         break;
 
       case "PluginPlayPreview":
         this._handlePlayPreviewEvent(plugin);
         break;
 
       case "PluginDisabled":
-        let manageLink = doc.getAnonymousElementByAttribute(plugin, "anonid", "managePluginsLink");
+        let manageLink = this.getPluginUI(plugin, "managePluginsLink");
         this.addLinkClickCallback(manageLink, "managePlugins");
         shouldShowNotification = true;
         break;
 
       case "PluginInstantiated":
       case "PluginRemoved":
         shouldShowNotification = true;
         break;
     }
 
     // Hide the in-content UI if it's too big. The crashed plugin handler already did this.
     if (eventType != "PluginCrashed" && eventType != "PluginRemoved") {
-      let overlay = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox");
+      let overlay = this.getPluginUI(plugin, "main");
       if (overlay != null && this.isTooSmall(plugin, overlay))
         overlay.style.visibility = "hidden";
     }
 
     // Only show the notification after we've done the isTooSmall check, so
     // that the notification can decide whether to show the "alert" icon
     if (shouldShowNotification) {
       this._showClickToPlayNotification(browser);
@@ -359,17 +359,17 @@ var gPluginHandler = {
     }
 
     return !objLoadingContent.activated &&
            pluginPermission != Ci.nsIPermissionManager.DENY_ACTION &&
            isFallbackTypeValid;
   },
 
   hideClickToPlayOverlay: function(aPlugin) {
-    let overlay = aPlugin.ownerDocument.getAnonymousElementByAttribute(aPlugin, "class", "mainBox");
+    let overlay = this.getPluginUI(aPlugin, "main");
     if (overlay)
       overlay.style.visibility = "hidden";
   },
 
   stopPlayPreview: function PH_stopPlayPreview(aPlugin, aPlayPlugin) {
     let objLoadingContent = aPlugin.QueryInterface(Ci.nsIObjectLoadingContent);
     if (objLoadingContent.activated)
       return;
@@ -509,27 +509,27 @@ var gPluginHandler = {
     // guard against giving pluginHost.getPermissionStringForType a type
     // not associated with any known plugin
     if (!gPluginHandler.isKnownPlugin(objLoadingContent))
       return;
     let permissionString = pluginHost.getPermissionStringForType(objLoadingContent.actualType);
     let principal = doc.defaultView.top.document.nodePrincipal;
     let pluginPermission = Services.perms.testPermissionFromPrincipal(principal, permissionString);
 
-    let overlay = doc.getAnonymousElementByAttribute(aPlugin, "class", "mainBox");
+    let overlay = this.getPluginUI(aPlugin, "main");
 
     if (pluginPermission == Ci.nsIPermissionManager.DENY_ACTION) {
       if (overlay)
         overlay.style.visibility = "hidden";
       return;
     }
 
     if (overlay) {
       overlay.addEventListener("click", gPluginHandler._overlayClickListener, true);
-      let closeIcon = doc.getAnonymousElementByAttribute(aPlugin, "anonid", "closeIcon");
+      let closeIcon = this.getPluginUI(aPlugin, "closeIcon");
       closeIcon.addEventListener("click", function(aEvent) {
         if (aEvent.button == 0 && aEvent.isTrusted)
           gPluginHandler.hideClickToPlayOverlay(aPlugin);
       }, true);
     }
   },
 
   _overlayClickListener: {
@@ -562,17 +562,17 @@ var gPluginHandler = {
 
   _handlePlayPreviewEvent: function PH_handlePlayPreviewEvent(aPlugin) {
     let doc = aPlugin.ownerDocument;
     let browser = gBrowser.getBrowserForDocument(doc.defaultView.top.document);
     let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
     let pluginInfo = this._getPluginInfo(aPlugin);
     let playPreviewInfo = pluginHost.getPlayPreviewInfo(pluginInfo.mimetype);
 
-    let previewContent = doc.getAnonymousElementByAttribute(aPlugin, "class", "previewPluginContent");
+    let previewContent = this.getPluginUI(aPlugin, "previewPluginContent");
     let iframe = previewContent.getElementsByClassName("previewPluginContentFrame")[0];
     if (!iframe) {
       // lazy initialization of the iframe
       iframe = doc.createElementNS("http://www.w3.org/1999/xhtml", "iframe");
       iframe.className = "previewPluginContentFrame";
       previewContent.appendChild(iframe);
 
       // Force a style flush, so that we ensure our binding is attached.
@@ -602,20 +602,19 @@ var gPluginHandler = {
     }
   },
 
   reshowClickToPlayNotification: function PH_reshowClickToPlayNotification() {
     let browser = gBrowser.selectedBrowser;
     let contentWindow = browser.contentWindow;
     let cwu = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                            .getInterface(Ci.nsIDOMWindowUtils);
-    let doc = contentWindow.document;
     let plugins = cwu.plugins;
     for (let plugin of plugins) {
-      let overlay = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox");
+      let overlay = this.getPluginUI(plugin, "main");
       if (overlay)
         overlay.removeEventListener("click", gPluginHandler._overlayClickListener, true);
       let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
       if (gPluginHandler.canActivatePlugin(objLoadingContent))
         gPluginHandler._handleClickToPlayEvent(plugin);
     }
     gPluginHandler._showClickToPlayNotification(browser);
   },
@@ -809,17 +808,17 @@ var gPluginHandler = {
       let fallbackType = plugin.pluginFallbackType;
       if (fallbackType == plugin.PLUGIN_VULNERABLE_UPDATABLE ||
           fallbackType == plugin.PLUGIN_VULNERABLE_NO_UPDATE ||
           fallbackType == plugin.PLUGIN_BLOCKLISTED) {
         icon = 'blocked-plugins-notification-icon';
         break;
       }
       if (fallbackType == plugin.PLUGIN_CLICK_TO_PLAY) {
-        let overlay = contentDoc.getAnonymousElementByAttribute(plugin, "class", "mainBox");
+        let overlay = this.getPluginUI(plugin, "main");
         if (!overlay || overlay.style.visibility == 'hidden') {
           icon = 'alert-plugins-notification-icon';
         }
       }
     }
 
     let dismissed = notification ? notification.dismissed : true;
     if (aPrimaryPlugin)
@@ -883,19 +882,19 @@ var gPluginHandler = {
     let messageString = gNavigatorBundle.getFormattedString("crashedpluginsMessage.title", [pluginName]);
 
     //
     // Configure the crashed-plugin placeholder.
     //
 
     // Force a layout flush so the binding is attached.
     plugin.clientTop;
+    let overlay = this.getPluginUI(plugin, "main");
+    let statusDiv = this.getPluginUI(plugin, "submitStatus");
     let doc = plugin.ownerDocument;
-    let overlay = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox");
-    let statusDiv = doc.getAnonymousElementByAttribute(plugin, "class", "submitStatus");
 #ifdef MOZ_CRASHREPORTER
     let status;
 
     // Determine which message to show regarding crash reports.
     if (submittedReport) { // submitReports && !doPrompt, handled in observer
       status = "submitted";
     }
     else if (!submitReports && !doPrompt) {
@@ -918,17 +917,17 @@ var gPluginHandler = {
     // If we don't have a minidumpID, we can't (or didn't) submit anything.
     // This can happen if the plugin is killed from the task manager.
     if (!pluginDumpID) {
         status = "noReport";
     }
 
     statusDiv.setAttribute("status", status);
 
-    let helpIcon = doc.getAnonymousElementByAttribute(plugin, "class", "helpIcon");
+    let helpIcon = this.getPluginUI(plugin, "helpIcon");
     this.addLinkClickCallback(helpIcon, "openHelpPage");
 
     // If we're showing the link to manually trigger report submission, we'll
     // want to be able to update all the instances of the UI for this crash to
     // show an updated message when a report is submitted.
     if (doPrompt) {
       let observer = {
         QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
@@ -954,22 +953,22 @@ var gPluginHandler = {
       // it from being GC. But I don't want to manually manage the reference's
       // lifetime (which should be no greater than the page).
       // Clever solution? Use a closue with an event listener on the document.
       // When the doc goes away, so do the listener references and the closure.
       doc.addEventListener("mozCleverClosureHack", observer, false);
     }
 #endif
 
-    let crashText = doc.getAnonymousElementByAttribute(plugin, "class", "msgCrashedText");
+    let crashText = this.getPluginUI(plugin, "crashedText");
     crashText.textContent = messageString;
 
     let browser = gBrowser.getBrowserForDocument(doc.defaultView.top.document);
 
-    let link = doc.getAnonymousElementByAttribute(plugin, "class", "reloadLink");
+    let link = this.getPluginUI(plugin, "reloadLink");
     this.addLinkClickCallback(link, "reloadPage", browser);
 
     let notificationBox = gBrowser.getNotificationBox(browser);
 
     let isShowing = true;
 
     // Is the <object>'s size too small to hold what we want to show?
     if (this.isTooSmall(plugin, overlay)) {
--- a/browser/base/content/test/browser_pluginCrashCommentAndURL.js
+++ b/browser/base/content/test/browser_pluginCrashCommentAndURL.js
@@ -84,17 +84,17 @@ function doNextRun() {
   }
 }
 
 function onCrash() {
   try {
     let plugin = gBrowser.contentDocument.getElementById("plugin");
     let elt = gPluginHandler.getPluginUI.bind(gPluginHandler, plugin);
     let style =
-      gBrowser.contentWindow.getComputedStyle(elt("msg msgPleaseSubmit"));
+      gBrowser.contentWindow.getComputedStyle(elt("pleaseSubmit"));
     is(style.display,
        currentRun.shouldSubmissionUIBeVisible ? "block" : "none",
        "Submission UI visibility should be correct");
     if (!currentRun.shouldSubmissionUIBeVisible) {
       // Done with this run.
       doNextRun();
       return;
     }
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_popupblocker.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_popupblocker.js
@@ -64,19 +64,19 @@ function test() {
 
   registerCleanupFunction(function() {
     windowsToClose.forEach(function(win) {
       win.close();
     });
   });
 
   testOnWindow({}, function(win) {
-    testPopupBlockerMenuItem(false, win,
+    testPopupBlockerMenuItem(false, win, function() {
       testOnWindow({private: true}, function(win) {
-        testPopupBlockerMenuItem(true, win,
+        testPopupBlockerMenuItem(true, win, function() {
           testOnWindow({}, function(win) {
             testPopupBlockerMenuItem(false, win, finishTest);
           })
-        );
+        });
       })
-    );
+    });
   });
 }
--- a/browser/components/sessionstore/src/SessionStorage.jsm
+++ b/browser/components/sessionstore/src/SessionStorage.jsm
@@ -56,17 +56,17 @@ let DomStorage = {
     for (let i = 0; i < shistory.count; i++) {
       let principal = History.getPrincipalForEntry(shistory, i, aDocShell);
       if (!principal)
         continue;
 
       // Check if we're allowed to store sessionStorage data.
       let isHTTPS = principal.URI && principal.URI.schemeIs("https");
       if (aFullData || SessionStore.checkPrivacyLevel(isHTTPS, isPinned)) {
-        let origin = principal.extendedOrigin;
+        let origin = principal.jarPrefix + principal.origin;
 
         // Don't read a host twice.
         if (!(origin in data)) {
           let originData = this._readEntry(principal, aDocShell);
           if (Object.keys(originData).length) {
             data[origin] = originData;
           }
         }
--- a/browser/components/sessionstore/test/browser_461743_sample.html
+++ b/browser/components/sessionstore/test/browser_461743_sample.html
@@ -30,18 +30,19 @@
     var loadExploit = { handleEvent: href };
 
     function delay() {
       var xhr = new XMLHttpRequest();
       xhr.open("GET", location.href, false);
       xhr.send(null);
     }
     function done() {
-      var event = document.createEvent("MessageEvent");
-      event.initMessageEvent("461743", true, false, "done", location.href, "", window);
+      var event = new MessageEvent('461743', { bubbles: true, cancelable: false,
+                                               data: "done", origin: location.href,
+                                               source: window });
       document.dispatchEvent(event);
       frames[0].document.removeEventListener("DOMNodeInserted", loadChrome, true);
       frames[0].document.removeEventListener("DOMNodeInserted", delay, true);
       frames[0].document.removeEventListener("DOMNodeInserted", loadExploit, true);
       frames[0].document.removeEventListener("DOMNodeInserted", done, true);
     }
 
     frames[0].document.addEventListener("DOMNodeInserted", loadChrome, true);
--- a/browser/components/sessionstore/test/browser_464620_a.html
+++ b/browser/components/sessionstore/test/browser_464620_a.html
@@ -39,15 +39,16 @@
     for (var c = 0; !documentInjected && c < 20; c++) {
       var r = new XMLHttpRequest();
       r.open("GET", location.href, false);
       r.overrideMimeType("text/plain");
       r.send(null);
     }
     document.getElementById("state").textContent = "done";
 
-    var event = document.createEvent("MessageEvent");
-    event.initMessageEvent("464620_a", true, false, "done", location.href, "", window);
+    var event = new MessageEvent('464620_a', { bubbles: true, cancelable: false,
+                                               data: "done", origin: location.href,
+                                               source: window });
     document.dispatchEvent(event);
   }
 </script>
 
 <p id="state">pending</p>
--- a/browser/components/sessionstore/test/browser_464620_b.html
+++ b/browser/components/sessionstore/test/browser_464620_b.html
@@ -43,15 +43,16 @@
     for (var c = 0; !documentInjected && c < 20; c++) {
       var r = new XMLHttpRequest();
       r.open("GET", location.href, false);
       r.overrideMimeType("text/plain");
       r.send(null);
     }
     document.getElementById("state").textContent = "done";
 
-    var event = document.createEvent("MessageEvent");
-    event.initMessageEvent("464620_b", true, false, "done", location.href, "", window);
+    var event = new MessageEvent('464620_b', { bubbles: true, cancelable: false,
+                                               data: "done", origin: location.href,
+                                               source: window });
     document.dispatchEvent(event);
   }
 </script>
 
 <p id="state">pending</p>
--- a/browser/components/tabview/groupitems.js
+++ b/browser/components/tabview/groupitems.js
@@ -2288,16 +2288,20 @@ let GroupItems = {
     UI.updateTabButton();
   },
 
   // ----------
   // Function: groupItem
   // Given some sort of identifier, returns the appropriate groupItem.
   // Currently only supports groupItem ids.
   groupItem: function GroupItems_groupItem(a) {
+    if (!this.groupItems) {
+      // uninit has been called
+      return null;
+    }
     var result = null;
     this.groupItems.forEach(function(candidate) {
       if (candidate.id == a)
         result = candidate;
     });
 
     return result;
   },
--- a/browser/locales/filter.py
+++ b/browser/locales/filter.py
@@ -6,32 +6,37 @@ def test(mod, path, entity = None):
   import re
   # ignore anything but Firefox
   if mod not in ("netwerk", "dom", "toolkit", "security/manager",
                  "browser", "browser/metro", "webapprt",
                  "extensions/reporter", "extensions/spellcheck",
                  "other-licenses/branding/firefox",
                  "browser/branding/official",
                  "services/sync"):
-    return False
-  if mod != "browser" and mod != "extensions/spellcheck":
-    # we only have exceptions for browser and extensions/spellcheck
-    return True
+    return "ignore"
+  if mod not in ("browser", "browser/metro", "extensions/spellcheck"):
+    # we only have exceptions for browser, metro and extensions/spellcheck
+    return "error"
   if not entity:
+    # the only files to ignore are spell checkers and search
     if mod == "extensions/spellcheck":
-      return False
+      return "ignore"
     # browser
-    return not (re.match(r"searchplugins\/.+\.xml", path) or
-                re.match(r"chrome\/help\/images\/[A-Za-z-_]+\.png", path))
+    return "ignore" if re.match(r"searchplugins\/.+\.xml", path) else "error"
   if mod == "extensions/spellcheck":
     # l10n ships en-US dictionary or something, do compare
-    return True
+    return "error"
   if path == "defines.inc":
-    return entity != "MOZ_LANGPACK_CONTRIBUTORS"
+    return "ignore" if entity == "MOZ_LANGPACK_CONTRIBUTORS" else "error"
 
-  if path != "chrome/browser-region/region.properties":
+  if mod == "browser" and path == "chrome/browser-region/region.properties":
     # only region.properties exceptions remain, compare all others
-    return True
-  
-  return not (re.match(r"browser\.search\.order\.[1-9]", entity) or
-              re.match(r"browser\.contentHandlers\.types\.[0-5]", entity) or
-              re.match(r"gecko\.handlerService\.schemes\.", entity) or
-              re.match(r"gecko\.handlerService\.defaultHandlersVersion", entity))
+    return ("ignore"
+            if (re.match(r"browser\.search\.order\.[1-9]", entity) or
+                re.match(r"browser\.contentHandlers\.types\.[0-5]", entity) or
+                re.match(r"gecko\.handlerService\.schemes\.", entity) or
+                re.match(r"gecko\.handlerService\.defaultHandlersVersion", entity))
+            else "error")
+  if mod == "browser/metro" and path == "chrome/region.properties":
+      return ("ignore"
+              if re.match(r"browser\.search\.order\.[1-9]", entity)
+              else "error")
+  return "error"
--- a/browser/metro/base/content/apzc.js
+++ b/browser/metro/base/content/apzc.js
@@ -41,19 +41,27 @@ var APZCObserver = {
   handleEvent: function APZC_handleEvent(aEvent) {
     switch (aEvent.type) {
       case 'pageshow':
       case 'TabSelect':
         const ROOT_ID = 1;
         let windowUtils = Browser.selectedBrowser.contentWindow.
                           QueryInterface(Ci.nsIInterfaceRequestor).
                           getInterface(Ci.nsIDOMWindowUtils);
+        // findElementWithViewId will throw if it can't find it
+        let element;
+        try {
+          element = windowUtils.findElementWithViewId(ROOT_ID);
+        } catch (e) {
+          // Not present; nothing to do here
+          break;
+        }
         windowUtils.setDisplayPortForElement(0, 0, ContentAreaObserver.width,
                                              ContentAreaObserver.height,
-                                             windowUtils.findElementWithViewId(ROOT_ID));
+                                             element);
         break;
       case 'TabOpen': {
         let browser = aEvent.originalTarget.linkedBrowser;
         browser.addEventListener("pageshow", this, true);
         browser.messageManager.addMessageListener("scroll", this);
         break;
       }
       case 'TabClose': {
--- a/build/autoconf/check_debug_ranges.py
+++ b/build/autoconf/check_debug_ranges.py
@@ -14,17 +14,17 @@ def get_range_for(compilation_unit, debu
     '''Returns the range offset for a given compilation unit
        in a given debug_info.'''
     name = ranges = ''
     search_cu = False
     for nfo in debug_info.splitlines():
         if 'DW_TAG_compile_unit' in nfo:
             search_cu = True
         elif 'DW_TAG_' in nfo or not nfo.strip():
-            if name == compilation_unit:
+            if name == compilation_unit and ranges != '':
                 return int(ranges, 16)
             name = ranges = ''
             search_cu = False
         if search_cu:
             if 'DW_AT_name' in nfo:
                 name = nfo.rsplit(None, 1)[1]
             elif 'DW_AT_ranges' in nfo:
                 ranges = nfo.rsplit(None, 1)[1]
--- a/caps/idl/nsIPrincipal.idl
+++ b/caps/idl/nsIPrincipal.idl
@@ -15,17 +15,17 @@ struct JSPrincipals;
 
 interface nsIURI;
 interface nsIContentSecurityPolicy;
 
 [ptr] native JSContext(JSContext);
 [ptr] native JSPrincipals(JSPrincipals);
 [ptr] native PrincipalArray(nsTArray<nsCOMPtr<nsIPrincipal> >);
 
-[scriptable, builtinclass, uuid(dbda8bb0-3023-4aec-ad98-8e9931a29d70)]
+[scriptable, builtinclass, uuid(551bf53d-203c-4ac4-8c0b-40aa7b5f1ad6)]
 interface nsIPrincipal : nsISerializable
 {
     /**
      * Returns whether the other principal is equivalent to this principal.
      * Principals are considered equal if they are the same principal, or
      * they have the same origin.
      */
     boolean equals(in nsIPrincipal other);
@@ -157,29 +157,29 @@ interface nsIPrincipal : nsISerializable
                       in boolean allowIfInheritsPrincipal);
 
     /**
      * A Content Security Policy associated with this principal.
      */
     [noscript] attribute nsIContentSecurityPolicy csp;
 
     /**
-     * Returns the extended origin of the principal.
-     * The extended origin is a string that has more information than the origin
-     * and can be used to isolate data or permissions between different
-     * principals while taking into account parameters like the app id or the
-     * fact that the principal is embedded in a mozbrowser.
-     * Some principals will return the origin for extendedOrigin.
-     * Some principals will assert if you try to access the extendedOrigin.
+     * Returns the jar prefix of the principal.
+     * The jar prefix is a string that can be used to isolate data or
+     * permissions between different principals while taking into account
+     * parameters like the app id or the fact that the principal is embedded in
+     * a mozbrowser.
+     * Some principals will return an empty string.
+     * Some principals will assert if you try to access the jarPrefix.
      *
-     * The extendedOrigin is intended to be an opaque identifier. It is
-     * currently "human-readable" but no callers should assume it will stay
-     * as is and it might be crypto-hashed at some point.
+     * The jarPrefix is intended to be an opaque identifier. It is currently
+     * "human-readable" but no callers should assume it will stay as is and
+     * it might be crypto-hashed at some point.
      */
-    readonly attribute AUTF8String extendedOrigin;
+    readonly attribute AUTF8String jarPrefix;
 
     /**
      * The base domain of the codebase URI to which this principal pertains
      * (generally the document URI), handling null principals and
      * non-hierarchical schemes correctly.
      */
     readonly attribute ACString baseDomain;
 
--- a/caps/idl/nsIScriptSecurityManager.idl
+++ b/caps/idl/nsIScriptSecurityManager.idl
@@ -5,17 +5,17 @@
 
 #include "nsISupports.idl"
 #include "nsIPrincipal.idl"
 #include "nsIXPCSecurityManager.idl"
 interface nsIURI;
 interface nsIChannel;
 interface nsIDocShell;
 
-[scriptable, uuid(ae486501-ec57-4ec8-a565-6880ca4ae6c4)]
+[scriptable, uuid(d6475e53-9ece-4dc0-940c-095ac3d85363)]
 interface nsIScriptSecurityManager : nsIXPCSecurityManager
 {
     ///////////////// Security Checks //////////////////
     /**
      * Checks whether the running script is allowed to access aProperty.
      */
     [noscript] void checkPropertyAccess(in JSContextPtr aJSContext,
                                         in JSObjectPtr aJSObject,
@@ -224,20 +224,19 @@ interface nsIScriptSecurityManager : nsI
      * principal.
      */
     [noscript,notxpcom] nsIPrincipal getCxSubjectPrincipal(in JSContextPtr cx);
 
     const unsigned long NO_APP_ID = 0;
     const unsigned long UNKNOWN_APP_ID = 4294967295; // UINT32_MAX
 
     /**
-     * Returns the extended origin for the uri.
+     * Returns the jar prefix for the app.
      * appId can be NO_APP_ID or a valid app id. appId should not be
      * UNKNOWN_APP_ID.
-     * inMozBrowser has to be true if the uri is inside a mozbrowser iframe.
+     * inMozBrowser has to be true if the app is inside a mozbrowser iframe.
      */
-    AUTF8String getExtendedOrigin(in nsIURI uri, in unsigned long appId,
-                                  in boolean inMozBrowser);
+    AUTF8String getJarPrefix(in unsigned long appId, in boolean inMozBrowser);
 };
 
 %{C++
 #define NS_SCRIPTSECURITYMANAGER_CONTRACTID "@mozilla.org/scriptsecuritymanager;1"
 %}
--- a/caps/include/nsPrincipal.h
+++ b/caps/include/nsPrincipal.h
@@ -60,17 +60,17 @@ public:
   NS_IMETHOD GetHashValue(uint32_t* aHashValue);
   NS_IMETHOD GetURI(nsIURI** aURI);
   NS_IMETHOD GetDomain(nsIURI** aDomain);
   NS_IMETHOD SetDomain(nsIURI* aDomain);
   NS_IMETHOD GetOrigin(char** aOrigin);
   NS_IMETHOD Subsumes(nsIPrincipal* other, bool* _retval);
   NS_IMETHOD SubsumesIgnoringDomain(nsIPrincipal* other, bool* _retval);
   NS_IMETHOD CheckMayLoad(nsIURI* uri, bool report, bool allowIfInheritsPrincipal);
-  NS_IMETHOD GetExtendedOrigin(nsACString& aExtendedOrigin);
+  NS_IMETHOD GetJarPrefix(nsACString& aJarPrefix);
   NS_IMETHOD GetAppStatus(uint16_t* aAppStatus);
   NS_IMETHOD GetAppId(uint32_t* aAppStatus);
   NS_IMETHOD GetIsInBrowserElement(bool* aIsInBrowserElement);
   NS_IMETHOD GetUnknownAppId(bool* aUnknownAppId);
   NS_IMETHOD GetIsNullPrincipal(bool* aIsNullPrincipal);
   NS_IMETHOD GetBaseDomain(nsACString& aBaseDomain);
 #ifdef DEBUG
   virtual void dumpImpl();
@@ -143,17 +143,17 @@ public:
   NS_IMETHOD GetHashValue(uint32_t* aHashValue);
   NS_IMETHOD GetURI(nsIURI** aURI);
   NS_IMETHOD GetDomain(nsIURI** aDomain);
   NS_IMETHOD SetDomain(nsIURI* aDomain);
   NS_IMETHOD GetOrigin(char** aOrigin);
   NS_IMETHOD Subsumes(nsIPrincipal* other, bool* _retval);
   NS_IMETHOD SubsumesIgnoringDomain(nsIPrincipal* other, bool* _retval);
   NS_IMETHOD CheckMayLoad(nsIURI* uri, bool report, bool allowIfInheritsPrincipal);
-  NS_IMETHOD GetExtendedOrigin(nsACString& aExtendedOrigin);
+  NS_IMETHOD GetJarPrefix(nsACString& aJarPrefix);
   NS_IMETHOD GetAppStatus(uint16_t* aAppStatus);
   NS_IMETHOD GetAppId(uint32_t* aAppStatus);
   NS_IMETHOD GetIsInBrowserElement(bool* aIsInBrowserElement);
   NS_IMETHOD GetUnknownAppId(bool* aUnknownAppId);
   NS_IMETHOD GetIsNullPrincipal(bool* aIsNullPrincipal);
   NS_IMETHOD GetBaseDomain(nsACString& aBaseDomain);
 #ifdef DEBUG
   virtual void dumpImpl();
--- a/caps/include/nsScriptSecurityManager.h
+++ b/caps/include/nsScriptSecurityManager.h
@@ -521,15 +521,15 @@ public:
     NS_DECL_ISUPPORTS
 
     NS_IMETHOD InitializeNameSet(nsIScriptContext* aScriptContext);
 };
 
 namespace mozilla {
 
 void
-GetExtendedOrigin(nsIURI* aURI, uint32_t aAppid,
-                  bool aInMozBrowser,
-                  nsACString& aExtendedOrigin);
+GetJarPrefix(uint32_t aAppid,
+             bool aInMozBrowser,
+             nsACString& aJarPrefix);
 
 } // namespace mozilla
 
 #endif // nsScriptSecurityManager_h__
--- a/caps/src/nsNullPrincipal.cpp
+++ b/caps/src/nsNullPrincipal.cpp
@@ -254,19 +254,20 @@ nsNullPrincipal::CheckMayLoad(nsIURI* aU
     nsScriptSecurityManager::ReportError(
       nullptr, NS_LITERAL_STRING("CheckSameOriginError"), mURI, aURI);
   }
 
   return NS_ERROR_DOM_BAD_URI;
 }
 
 NS_IMETHODIMP
-nsNullPrincipal::GetExtendedOrigin(nsACString& aExtendedOrigin)
+nsNullPrincipal::GetJarPrefix(nsACString& aJarPrefix)
 {
-  return GetOrigin(getter_Copies(aExtendedOrigin));
+  aJarPrefix.Truncate();
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNullPrincipal::GetAppStatus(uint16_t* aAppStatus)
 {
   *aAppStatus = nsIPrincipal::APP_STATUS_NOT_INSTALLED;
   return NS_OK;
 }
--- a/caps/src/nsPrincipal.cpp
+++ b/caps/src/nsPrincipal.cpp
@@ -419,21 +419,21 @@ nsPrincipal::SetDomain(nsIURI* aDomain)
   success = js::RecomputeWrappers(cx, js::CompartmentsWithPrincipals(principals),
                                   js::ContentCompartmentsOnly());
   NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsPrincipal::GetExtendedOrigin(nsACString& aExtendedOrigin)
+nsPrincipal::GetJarPrefix(nsACString& aJarPrefix)
 {
   MOZ_ASSERT(mAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID);
 
-  mozilla::GetExtendedOrigin(mCodebase, mAppId, mInMozBrowser, aExtendedOrigin);
+  mozilla::GetJarPrefix(mAppId, mInMozBrowser, aJarPrefix);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsPrincipal::GetAppStatus(uint16_t* aAppStatus)
 {
   *aAppStatus = GetAppStatus();
   return NS_OK;
@@ -768,19 +768,20 @@ nsExpandedPrincipal::GetURI(nsIURI** aUR
 NS_IMETHODIMP
 nsExpandedPrincipal::GetWhiteList(nsTArray<nsCOMPtr<nsIPrincipal> >** aWhiteList)
 {
   *aWhiteList = &mPrincipals;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsExpandedPrincipal::GetExtendedOrigin(nsACString& aExtendedOrigin)
+nsExpandedPrincipal::GetJarPrefix(nsACString& aJarPrefix)
 {
-  return GetOrigin(getter_Copies(aExtendedOrigin));
+  aJarPrefix.Truncate();
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsExpandedPrincipal::GetAppStatus(uint16_t* aAppStatus)
 {
   *aAppStatus = nsIPrincipal::APP_STATUS_NOT_INSTALLED;
   return NS_OK;
 }
--- a/caps/src/nsScriptSecurityManager.cpp
+++ b/caps/src/nsScriptSecurityManager.cpp
@@ -1999,17 +1999,17 @@ nsScriptSecurityManager::old_doGetObject
         if (js::IsCallObject(obj)) {
             obj = js::GetObjectParentMaybeScope(obj);
 
             if (!obj)
                 return nullptr;
         }
     }
 
-    js::Class *jsClass = js::GetObjectClass(obj);
+    const js::Class *jsClass = js::GetObjectClass(obj);
 
     do {
         // Note: jsClass is set before this loop, and also at the
         // *end* of this loop.
 
         if (IS_WN_CLASS(jsClass)) {
             result = nsXPConnect::XPConnect()->GetPrincipal(obj,
                                                             aAllowShortCircuit);
@@ -2773,51 +2773,44 @@ nsScriptSecurityManager::InitPrefs()
     Preferences::AddStrongObservers(this, kObservedPrefs);
 
     return NS_OK;
 }
 
 namespace mozilla {
 
 void
-GetExtendedOrigin(nsIURI* aURI, uint32_t aAppId, bool aInMozBrowser,
-                  nsACString& aExtendedOrigin)
+GetJarPrefix(uint32_t aAppId, bool aInMozBrowser, nsACString& aJarPrefix)
 {
-  MOZ_ASSERT(aURI);
   MOZ_ASSERT(aAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID);
 
   if (aAppId == nsIScriptSecurityManager::UNKNOWN_APP_ID) {
     aAppId = nsIScriptSecurityManager::NO_APP_ID;
   }
 
-  nsAutoCString origin;
-  nsPrincipal::GetOriginForURI(aURI, getter_Copies(origin));
+  aJarPrefix.Truncate();
 
   // Fallback.
   if (aAppId == nsIScriptSecurityManager::NO_APP_ID && !aInMozBrowser) {
-    aExtendedOrigin.Assign(origin);
     return;
   }
 
-  // aExtendedOrigin = appId + "+" + { 't', 'f' } "+" + origin;
-  aExtendedOrigin.Truncate();
-  aExtendedOrigin.AppendInt(aAppId);
-  aExtendedOrigin.Append('+');
-  aExtendedOrigin.Append(aInMozBrowser ? 't' : 'f');
-  aExtendedOrigin.Append('+');
-  aExtendedOrigin.Append(origin);
+  // aJarPrefix = appId + "+" + { 't', 'f' } + "+";
+  aJarPrefix.AppendInt(aAppId);
+  aJarPrefix.Append('+');
+  aJarPrefix.Append(aInMozBrowser ? 't' : 'f');
+  aJarPrefix.Append('+');
 
   return;
 }
 
 } // namespace mozilla
 
 NS_IMETHODIMP
-nsScriptSecurityManager::GetExtendedOrigin(nsIURI* aURI,
-                                           uint32_t aAppId,
-                                           bool aInMozBrowser,
-                                           nsACString& aExtendedOrigin)
+nsScriptSecurityManager::GetJarPrefix(uint32_t aAppId,
+                                      bool aInMozBrowser,
+                                      nsACString& aJarPrefix)
 {
   MOZ_ASSERT(aAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID);
 
-  mozilla::GetExtendedOrigin(aURI, aAppId, aInMozBrowser, aExtendedOrigin);
+  mozilla::GetJarPrefix(aAppId, aInMozBrowser, aJarPrefix);
   return NS_OK;
 }
--- a/caps/src/nsSecurityManagerFactory.cpp
+++ b/caps/src/nsSecurityManagerFactory.cpp
@@ -76,17 +76,17 @@ nsSecurityNameSet::InitializeNameSet(nsI
     JS::Rooted<JSObject*> obj(cx, global);
     JS::Rooted<JSObject*> proto(cx);
     for (;;) {
         MOZ_ALWAYS_TRUE(JS_GetPrototype(cx, obj, &proto));
         if (!proto)
             break;
         obj = proto;
     }
-    JSClass *objectClass = JS_GetClass(obj);
+    const JSClass *objectClass = JS_GetClass(obj);
 
     JS::Rooted<JS::Value> v(cx);
     if (!JS_GetProperty(cx, global, "netscape", &v))
         return NS_ERROR_FAILURE;
 
     JS::Rooted<JSObject*> securityObj(cx);
     if (v.isObject()) {
         /*
--- a/caps/src/nsSystemPrincipal.cpp
+++ b/caps/src/nsSystemPrincipal.cpp
@@ -161,19 +161,20 @@ nsSystemPrincipal::GetSecurityPolicy(voi
 
 NS_IMETHODIMP
 nsSystemPrincipal::SetSecurityPolicy(void* aSecurityPolicy)
 {
     return NS_OK;
 }
 
 NS_IMETHODIMP
-nsSystemPrincipal::GetExtendedOrigin(nsACString& aExtendedOrigin)
+nsSystemPrincipal::GetJarPrefix(nsACString& aJarPrefix)
 {
-  return GetOrigin(getter_Copies(aExtendedOrigin));
+  aJarPrefix.Truncate();
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSystemPrincipal::GetAppStatus(uint16_t* aAppStatus)
 {
   *aAppStatus = nsIPrincipal::APP_STATUS_NOT_INSTALLED;
   return NS_OK;
 }
--- a/caps/tests/mochitest/Makefile.in
+++ b/caps/tests/mochitest/Makefile.in
@@ -6,13 +6,13 @@
 MOCHITEST_FILES = 	test_bug423375.html \
                 test_bug246699.html \
                 test_bug292789.html \
                 test_bug470804.html \
                 test_disallowInheritPrincipal.html \
                 test_app_principal_equality.html \
                 $(NULL)
 
-# extendedOrigin test doesn't work on Windows, see bug 776296.
+# jarPrefix test doesn't work on Windows, see bug 776296.
 ifneq ($(OS_ARCH),WINNT)
-MOCHITEST_CHROME_FILES = test_principal_extendedorigin_appid_appstatus.html \
+MOCHITEST_CHROME_FILES = test_principal_jarprefix_origin_appid_appstatus.html \
                          $(NULL)
 endif
rename from caps/tests/mochitest/test_principal_extendedorigin_appid_appstatus.html
rename to caps/tests/mochitest/test_principal_jarprefix_origin_appid_appstatus.html
--- a/caps/tests/mochitest/test_principal_extendedorigin_appid_appstatus.html
+++ b/caps/tests/mochitest/test_principal_jarprefix_origin_appid_appstatus.html
@@ -1,16 +1,16 @@
 <!DOCTYPE HTML>
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=758258
 -->
 <head>
   <meta charset="utf-8">
-  <title>Test for nsIPrincipal extendedOrigin, appStatus and appId</title>
+  <title>Test for nsIPrincipal jarPrefix, origin, appStatus and appId</title>
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=758258">Mozilla Bug 758258</a>
 <p id="display"></p>
 <div id="content">
 
@@ -289,30 +289,30 @@ function checkIFrame(aFrame, data) {
   } else {
     is(principal.appStatus, Ci.nsIPrincipal.APP_STATUS_NOT_INSTALLED,
        'this should not be an installed app');
     is(principal.appId, Ci.nsIScriptSecurityManager.NO_APP_ID,
        "principals from non-installed app should have NO_APP_ID");
   }
 
   if (!data.isapp && !data.browser) {
-    is(principal.extendedOrigin, principal.origin,
-       'extendedOrigin should return the origin for non-app and non-browsers principals');
+    is(principal.jarPrefix, "",
+       'jarPrefix should return an empty string for non-app and non-browsers principals');
   } else {
-    isnot(principal.extendedOrigin, principal.origin,
-          'extendedOrigin should not return the origin for apps or mozbrowsers');
+    isnot(principal.jarPrefix, "",
+          'jarPrefix should not return an empty string for apps or mozbrowsers');
   }
 
   if (data.test.indexOf("eo-unique") != -1) {
-    is(eoList.indexOf(principal.extendedOrigin), -1,
-       "extendedOrigin should be unique");
+    is(eoList.indexOf(principal.jarPrefix + principal.origin), -1,
+       "extended origin should be unique");
   }
   if (data.test.indexOf("eo-as-last") != -1) {
-    is(principal.extendedOrigin, eoList[eoList.length-1],
-       "extendedOrigin should be the same as the last inserted one");
+    is(principal.jarPrefix + principal.origin, eoList[eoList.length-1],
+       "extended origin should be the same as the last inserted one");
   }
 
   is(principal.isInBrowserElement, !!data.browser,
      "check principal.isInBrowserElement");
 
   if (data.child) {
     let childPrincipal = aFrame.contentWindow.frames[0].document.nodePrincipal;
 
@@ -320,27 +320,29 @@ function checkIFrame(aFrame, data) {
       is(childPrincipal.appStatus, Ci.nsIPrincipal.APP_STATUS_INSTALLED,
          "child should be an installed app");
     }
 
     is(childPrincipal.isInBrowserElement, !!data.child.browser || !!data.child.inBrowser,
        "check childPrincipal.isInBrowserElement");
 
     if (data.test.indexOf("child-has-same-eo") != -1) {
-      is(childPrincipal.extendedOrigin, principal.extendedOrigin,
-         "child should have the same extendedOrigin as parent");
+      is(childPrincipal.jarPrefix + childPrincipal.origin,
+         principal.jarPrefix + principal.origin,
+         "child should have the same extended origin as parent");
       is(childPrincipal.appStatus, principal.appStatus,
-         "child should have the same appStatus if it has the same extendedOrigin");
+         "child should have the same appStatus if it has the same extended origin");
       is(childPrincipal.appId, principal.appId,
-         "child should have the same appId if it has the same extendedOrigin");
+         "child should have the same appId if it has the same extended origin");
     }
 
     if (data.test.indexOf("child-has-different-eo") != -1) {
-      isnot(childPrincipal.extendedOrigin, principal.extendedOrigin,
-            "child should not have the same extendedOrigin as parent");
+      isnot(childPrincipal.jarPrefix + childPrincipal.origin,
+            principal.jarPrefix + principal.origin,
+            "child should not have the same extended origin as parent");
     }
 
     if (data.test.indexOf("child-has-same-appstatus") != -1) {
       is(childPrincipal.appStatus, principal.appStatus,
          "childPrincipal and parent principal should have the same appStatus");
     }
 
     if (data.test.indexOf("child-has-different-appstatus") != -1) {
@@ -354,32 +356,34 @@ function checkIFrame(aFrame, data) {
     }
 
     if (data.test.indexOf("child-has-different-appid") != -1) {
       isnot(childPrincipal.appId, principal.appId,
             "childPrincipal and parent principal should have different appId");
     }
   }
 
-  eoList.push(principal.extendedOrigin);
+  eoList.push(principal.jarPrefix + principal.origin);
 
   checkedCount++;
   if (checkedCount == checksTodo) {
     SpecialPowers.removePermission("browser", "http://example.org");
     SpecialPowers.removePermission("embed-apps", "http://example.org");
     SimpleTest.finish();
   } else {
     gTestRunner.next();
   }
 }
 
 is('appStatus' in document.nodePrincipal, true,
    'appStatus should be present in nsIPrincipal');
-is('extendedOrigin' in document.nodePrincipal, true,
-   'extendedOrigin should be present in nsIPrincipal');
+is('jarPrefix' in document.nodePrincipal, true,
+   'jarPrefix should be present in nsIPrincipal');
+is('origin' in document.nodePrincipal, true,
+   'origin should be present in nsIPrincipal');
 is('appId' in document.nodePrincipal, true,
    'appId should be present in nsIPrincipal');
 
 function runTest() {
   // We want to use a generator. Those only work in a one level stack so we
   // can't use .forEach() here.
   for (var i=0; i<gData.length; ++i) {
     let data = gData[i];
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -252,21 +252,21 @@ bool nsContentUtils::sDOMWindowDumpEnabl
 namespace {
 
 static const char kJSStackContractID[] = "@mozilla.org/js/xpc/ContextStack;1";
 static NS_DEFINE_CID(kParserServiceCID, NS_PARSERSERVICE_CID);
 static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
 
 static PLDHashTable sEventListenerManagersHash;
 
-class DOMEventListenerManagersHashReporter MOZ_FINAL : public MemoryReporterBase
+class DOMEventListenerManagersHashReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
   DOMEventListenerManagersHashReporter()
-    : MemoryReporterBase(
+    : MemoryUniReporter(
         "explicit/dom/event-listener-managers-hash",
         KIND_HEAP,
         UNITS_BYTES,
         "Memory used by the event listener manager's hash table.")
   {}
 
 private:
   int64_t Amount()
--- a/content/base/src/nsDOMBlobBuilder.cpp
+++ b/content/base/src/nsDOMBlobBuilder.cpp
@@ -251,16 +251,32 @@ nsDOMMultipartFile::InitBlob(JSContext* 
     }
 
     mBlobs = blobSet.GetBlobs();
   }
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsDOMMultipartFile::GetMozFullPathInternal(nsAString &aFilename)
+{
+  if (!mIsFromNsiFile || mBlobs.Length() == 0) {
+    return nsDOMFile::GetMozFullPathInternal(aFilename);
+  }
+
+  nsIDOMBlob* blob = mBlobs.ElementAt(0).get();
+  nsDOMFileFile* file = static_cast<nsDOMFileFile*>(blob);
+  if (!file) {
+    return nsDOMFile::GetMozFullPathInternal(aFilename);
+  }
+
+  return file->GetMozFullPathInternal(aFilename);
+}
+
 nsresult
 nsDOMMultipartFile::InitFile(JSContext* aCx,
                              uint32_t aArgc,
                              JS::Value* aArgv)
 {
   nsresult rv;
 
   NS_ASSERTION(!mImmutable, "Something went wrong ...");
@@ -300,16 +316,18 @@ nsDOMMultipartFile::InitFile(JSContext* 
       return NS_ERROR_UNEXPECTED;
     }
 
     blob = do_QueryInterface(supports);
     file = do_QueryInterface(supports);
     if (!blob && !file) {
       return NS_ERROR_UNEXPECTED;
     }
+
+    mIsFromNsiFile = true;
   } else {
     // It's a string
     JSString* str = JS_ValueToString(aCx, aArgv[0]);
     NS_ENSURE_TRUE(str, NS_ERROR_XPC_BAD_CONVERT_JS);
 
     nsDependentJSString xpcomStr;
     if (!xpcomStr.init(aCx, str)) {
       return NS_ERROR_XPC_BAD_CONVERT_JS;
--- a/content/base/src/nsDOMBlobBuilder.h
+++ b/content/base/src/nsDOMBlobBuilder.h
@@ -24,37 +24,41 @@ class nsDOMMultipartFile : public nsDOMF
                            public nsIJSNativeInitializer
 {
 public:
   // Create as a file
   nsDOMMultipartFile(nsTArray<nsCOMPtr<nsIDOMBlob> > aBlobs,
                      const nsAString& aName,
                      const nsAString& aContentType)
     : nsDOMFile(aName, aContentType, UINT64_MAX),
-      mBlobs(aBlobs)
+      mBlobs(aBlobs),
+      mIsFromNsiFile(false)
   {
   }
 
   // Create as a blob
   nsDOMMultipartFile(nsTArray<nsCOMPtr<nsIDOMBlob> >& aBlobs,
                      const nsAString& aContentType)
     : nsDOMFile(aContentType, UINT64_MAX),
-      mBlobs(aBlobs)
+      mBlobs(aBlobs),
+      mIsFromNsiFile(false)
   {
   }
 
   // Create as a file to be later initialized
   nsDOMMultipartFile(const nsAString& aName)
-    : nsDOMFile(aName, EmptyString(), UINT64_MAX)
+    : nsDOMFile(aName, EmptyString(), UINT64_MAX),
+      mIsFromNsiFile(false)
   {
   }
 
   // Create as a blob to be later initialized
   nsDOMMultipartFile()
-    : nsDOMFile(EmptyString(), UINT64_MAX)
+    : nsDOMFile(EmptyString(), UINT64_MAX),
+      mIsFromNsiFile(false)
   {
   }
 
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIJSNativeInitializer
   NS_IMETHOD Initialize(nsISupports* aOwner,
                         JSContext* aCx,
@@ -91,18 +95,21 @@ public:
     // Initialization will set the filename, so we can pass in an empty string
     // for now.
     return NewFile(EmptyString(), aNewObject);
   }
 
   virtual const nsTArray<nsCOMPtr<nsIDOMBlob> >*
   GetSubBlobs() const MOZ_OVERRIDE { return &mBlobs; }
 
+  NS_IMETHOD GetMozFullPathInternal(nsAString& aFullPath) MOZ_OVERRIDE;
+
 protected:
   nsTArray<nsCOMPtr<nsIDOMBlob> > mBlobs;
+  bool mIsFromNsiFile;
 };
 
 class BlobSet {
 public:
   BlobSet()
     : mData(nullptr), mDataLen(0), mDataBufferLen(0)
   {}
 
--- a/content/base/src/nsDOMFile.cpp
+++ b/content/base/src/nsDOMFile.cpp
@@ -636,27 +636,27 @@ nsDOMMemoryFile::DataOwner::sDataOwnerMu
 nsDOMMemoryFile::DataOwner::sDataOwners;
 
 /* static */ bool
 nsDOMMemoryFile::DataOwner::sMemoryReporterRegistered;
 
 NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(DOMMemoryFileDataOwnerMallocSizeOf)
 
 class nsDOMMemoryFileDataOwnerMemoryReporter MOZ_FINAL
-  : public nsIMemoryMultiReporter
+  : public nsIMemoryReporter
 {
   NS_DECL_THREADSAFE_ISUPPORTS
 
   NS_IMETHOD GetName(nsACString& aName)
   {
     aName.AssignASCII("dom-memory-file-data-owner");
     return NS_OK;
   }
 
-  NS_IMETHOD CollectReports(nsIMemoryMultiReporterCallback *aCallback,
+  NS_IMETHOD CollectReports(nsIMemoryReporterCallback *aCallback,
                             nsISupports *aClosure)
   {
     typedef nsDOMMemoryFile::DataOwner DataOwner;
 
     StaticMutexAutoLock lock(DataOwner::sDataOwnerMutex);
 
     if (!DataOwner::sDataOwners) {
       return NS_OK;
@@ -720,29 +720,29 @@ class nsDOMMemoryFileDataOwnerMemoryRepo
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
     return NS_OK;
   }
 };
 
 NS_IMPL_ISUPPORTS1(nsDOMMemoryFileDataOwnerMemoryReporter,
-                   nsIMemoryMultiReporter)
+                   nsIMemoryReporter)
 
 /* static */ void
 nsDOMMemoryFile::DataOwner::EnsureMemoryReporterRegistered()
 {
   sDataOwnerMutex.AssertCurrentThreadOwns();
   if (sMemoryReporterRegistered) {
     return;
   }
 
   nsRefPtr<nsDOMMemoryFileDataOwnerMemoryReporter> reporter = new
     nsDOMMemoryFileDataOwnerMemoryReporter();
-  NS_RegisterMemoryMultiReporter(reporter);
+  NS_RegisterMemoryReporter(reporter);
 
   sMemoryReporterRegistered = true;
 }
 
 ////////////////////////////////////////////////////////////////////////////
 // nsDOMFileList implementation
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(nsDOMFileList)
--- a/content/base/test/chrome/Makefile.in
+++ b/content/base/test/chrome/Makefile.in
@@ -46,16 +46,17 @@ MOCHITEST_CHROME_FILES = \
     test_bug800386.xul \
     test_bug816340.xul \
     file_bug816340.xul \
     test_domparsing.xul \
     test_bug814638.xul \
     host_bug814638.xul \
     test_document_register.xul \
     frame_bug814638.xul \
+    test_bug914381.html \
     $(NULL)
 
 ifneq ($(MOZ_WIDGET_TOOLKIT),cocoa)
 MOCHITEST_CHROME_FILES += \
     test_cpows.xul \
     cpows_parent.xul \
     cpows_child.js \
     $(NULL)
new file mode 100644
--- /dev/null
+++ b/content/base/test/chrome/test_bug914381.html
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=650776
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 914381</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=914381">Mozilla Bug 914381</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+function createFileWithData(fileData) {
+  var dirSvc = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
+  var testFile = dirSvc.get("ProfD", Ci.nsIFile);
+  testFile.append("testBug914381");
+
+  var outStream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream);
+  outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
+                 0666, 0);
+  outStream.write(fileData, fileData.length);
+  outStream.close();
+
+  return testFile;
+}
+
+/** Test for Bug 914381. DOMFile's created in JS using an nsIFile should allow mozGetFullPathInternal calls to succeed **/
+var file = createFileWithData("Test bug 914381");
+var f = File(file);
+is(f.mozFullPathInternal, undefined, "mozFullPathInternal is undefined from js");
+is(f.mozFullPath, file.path, "mozFullPath returns path if created with nsIFile");
+
+f = File(file.path);
+is(f.mozFullPathInternal, undefined, "mozFullPathInternal is undefined from js");
+is(f.mozFullPath, "", "mozFullPath returns blank if created with a string");
+</script>
+</pre>
+</body>
+</html>
--- a/content/canvas/src/CanvasRenderingContext2D.cpp
+++ b/content/canvas/src/CanvasRenderingContext2D.cpp
@@ -128,21 +128,21 @@ namespace dom {
 const Float SIGMA_MAX = 100;
 
 /* Memory reporter stuff */
 static int64_t gCanvasAzureMemoryUsed = 0;
 
 // This is KIND_OTHER because it's not always clear where in memory the pixels
 // of a canvas are stored.  Furthermore, this memory will be tracked by the
 // underlying surface implementations.  See bug 655638 for details.
-class Canvas2dPixelsReporter MOZ_FINAL : public MemoryReporterBase
+class Canvas2dPixelsReporter MOZ_FINAL : public MemoryUniReporter
 {
   public:
     Canvas2dPixelsReporter()
-      : MemoryReporterBase("canvas-2d-pixels", KIND_OTHER, UNITS_BYTES,
+      : MemoryUniReporter("canvas-2d-pixels", KIND_OTHER, UNITS_BYTES,
 "Memory used by 2D canvases. Each canvas requires (width * height * 4) bytes.")
     {}
 private:
     int64_t Amount() MOZ_OVERRIDE { return gCanvasAzureMemoryUsed; }
 };
 
 class CanvasRadialGradient : public CanvasGradient
 {
--- a/content/canvas/src/WebGLContext.cpp
+++ b/content/canvas/src/WebGLContext.cpp
@@ -5,17 +5,17 @@
 
 #include "WebGLContext.h"
 #include "WebGL1Context.h"
 #include "WebGLObjectModel.h"
 #include "WebGLExtensions.h"
 #include "WebGLContextUtils.h"
 #include "WebGLBuffer.h"
 #include "WebGLVertexAttribData.h"
-#include "WebGLMemoryMultiReporterWrapper.h"
+#include "WebGLMemoryReporterWrapper.h"
 #include "WebGLFramebuffer.h"
 #include "WebGLVertexArray.h"
 #include "WebGLQuery.h"
 
 #include "AccessCheck.h"
 #include "nsIConsoleService.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIClassInfoImpl.h"
@@ -174,17 +174,17 @@ WebGLContext::WebGLContext()
     mGLMaxColorAttachments = 1;
     mGLMaxDrawBuffers = 1;
     mGLMaxTransformFeedbackSeparateAttribs = 0;
 
     // See OpenGL ES 2.0.25 spec, 6.2 State Tables, table 6.13
     mPixelStorePackAlignment = 4;
     mPixelStoreUnpackAlignment = 4;
 
-    WebGLMemoryMultiReporterWrapper::AddWebGLContext(this);
+    WebGLMemoryReporterWrapper::AddWebGLContext(this);
 
     mAllowRestore = true;
     mContextLossTimerRunning = false;
     mDrawSinceContextLossTimerSet = false;
     mContextRestorer = do_CreateInstance("@mozilla.org/timer;1");
     mContextStatus = ContextNotLost;
     mContextLostErrorSet = false;
     mLoseContextOnHeapMinimize = false;
@@ -208,17 +208,17 @@ WebGLContext::WebGLContext()
     mDisableFragHighP = false;
 
     mDrawCallsSinceLastFlush = 0;
 }
 
 WebGLContext::~WebGLContext()
 {
     DestroyResourcesAndContext();
-    WebGLMemoryMultiReporterWrapper::RemoveWebGLContext(this);
+    WebGLMemoryReporterWrapper::RemoveWebGLContext(this);
     TerminateContextLossTimer();
     mContextRestorer = nullptr;
 }
 
 void
 WebGLContext::DestroyResourcesAndContext()
 {
     if (mMemoryPressureObserver) {
@@ -648,18 +648,18 @@ void WebGLContext::LoseOldestWebGLContex
 #endif
     MOZ_ASSERT(kMaxWebGLContextsPerPrincipal < kMaxWebGLContexts);
 
     // it's important to update the index on a new context before losing old contexts,
     // otherwise new unused contexts would all have index 0 and we couldn't distinguish older ones
     // when choosing which one to lose first.
     UpdateLastUseIndex();
 
-    WebGLMemoryMultiReporterWrapper::ContextsArrayType &contexts
-      = WebGLMemoryMultiReporterWrapper::Contexts();
+    WebGLMemoryReporterWrapper::ContextsArrayType &contexts
+      = WebGLMemoryReporterWrapper::Contexts();
 
     // quick exit path, should cover a majority of cases
     if (contexts.Length() <= kMaxWebGLContextsPerPrincipal) {
         return;
     }
 
     // note that here by "context" we mean "non-lost context". See the check for
     // IsContextLost() below. Indeed, the point of this function is to maybe lose
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -113,17 +113,17 @@ class WebGLContext :
     public nsIDOMWebGLRenderingContext,
     public nsICanvasRenderingContextInternal,
     public nsSupportsWeakReference,
     public WebGLRectangleObject,
     public nsWrapperCache
 {
     friend class WebGLContextUserData;
     friend class WebGLMemoryPressureObserver;
-    friend class WebGLMemoryMultiReporterWrapper;
+    friend class WebGLMemoryReporterWrapper;
     friend class WebGLExtensionLoseContext;
     friend class WebGLExtensionCompressedTextureS3TC;
     friend class WebGLExtensionCompressedTextureATC;
     friend class WebGLExtensionCompressedTexturePVRTC;
     friend class WebGLExtensionDepthTexture;
     friend class WebGLExtensionDrawBuffers;
     friend class WebGLExtensionVertexArray;
 
--- a/content/canvas/src/WebGLContextReporter.cpp
+++ b/content/canvas/src/WebGLContextReporter.cpp
@@ -1,165 +1,165 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGLContext.h"
-#include "WebGLMemoryMultiReporterWrapper.h"
+#include "WebGLMemoryReporterWrapper.h"
 #include "nsIMemoryReporter.h"
 
 using namespace mozilla;
 
 NS_IMPL_ISUPPORTS1(WebGLMemoryPressureObserver, nsIObserver)
 
-class WebGLMemoryMultiReporter MOZ_FINAL : public nsIMemoryMultiReporter 
+class WebGLMemoryReporter MOZ_FINAL : public nsIMemoryReporter
 {
   public:
     NS_DECL_ISUPPORTS
-    NS_DECL_NSIMEMORYMULTIREPORTER
+    NS_DECL_NSIMEMORYREPORTER
 };
 
-NS_IMPL_ISUPPORTS1(WebGLMemoryMultiReporter, nsIMemoryMultiReporter)
+NS_IMPL_ISUPPORTS1(WebGLMemoryReporter, nsIMemoryReporter)
 
 NS_IMETHODIMP
-WebGLMemoryMultiReporter::GetName(nsACString &aName)
+WebGLMemoryReporter::GetName(nsACString &aName)
 {
   aName.AssignLiteral("webgl");
   return NS_OK;
 }
 
 NS_IMETHODIMP
-WebGLMemoryMultiReporter::CollectReports(nsIMemoryMultiReporterCallback* aCb, 
-                                         nsISupports* aClosure)
+WebGLMemoryReporter::CollectReports(nsIMemoryReporterCallback* aCb,
+                                    nsISupports* aClosure)
 {
 #define REPORT(_path, _kind, _units, _amount, _desc)                          \
     do {                                                                      \
       nsresult rv;                                                            \
       rv = aCb->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path), _kind,    \
                          _units, _amount, NS_LITERAL_CSTRING(_desc),          \
                          aClosure);                                           \
       NS_ENSURE_SUCCESS(rv, rv);                                              \
     } while (0)
 
     REPORT("webgl-texture-memory",
            nsIMemoryReporter::KIND_OTHER, nsIMemoryReporter::UNITS_BYTES,
-           WebGLMemoryMultiReporterWrapper::GetTextureMemoryUsed(),
+           WebGLMemoryReporterWrapper::GetTextureMemoryUsed(),
            "Memory used by WebGL textures.The OpenGL"
            " implementation is free to store these textures in either video"
            " memory or main memory. This measurement is only a lower bound,"
-           " actual memory usage may be higher for example if the storage" 
+           " actual memory usage may be higher for example if the storage"
            " is strided.");
-   
+
     REPORT("webgl-texture-count",
            nsIMemoryReporter::KIND_OTHER, nsIMemoryReporter::UNITS_COUNT,
-           WebGLMemoryMultiReporterWrapper::GetTextureCount(), 
+           WebGLMemoryReporterWrapper::GetTextureCount(),
            "Number of WebGL textures.");
 
     REPORT("webgl-buffer-memory",
            nsIMemoryReporter::KIND_OTHER, nsIMemoryReporter::UNITS_BYTES,
-           WebGLMemoryMultiReporterWrapper::GetBufferMemoryUsed(), 
+           WebGLMemoryReporterWrapper::GetBufferMemoryUsed(),
            "Memory used by WebGL buffers. The OpenGL"
            " implementation is free to store these buffers in either video"
            " memory or main memory. This measurement is only a lower bound,"
            " actual memory usage may be higher for example if the storage"
            " is strided.");
 
     REPORT("explicit/webgl/buffer-cache-memory",
            nsIMemoryReporter::KIND_HEAP, nsIMemoryReporter::UNITS_BYTES,
-           WebGLMemoryMultiReporterWrapper::GetBufferCacheMemoryUsed(),
+           WebGLMemoryReporterWrapper::GetBufferCacheMemoryUsed(),
            "Memory used by WebGL buffer caches. The WebGL"
            " implementation caches the contents of element array buffers"
            " only.This adds up with the webgl-buffer-memory value, but"
            " contrary to it, this one represents bytes on the heap,"
            " not managed by OpenGL.");
 
     REPORT("webgl-buffer-count",
            nsIMemoryReporter::KIND_OTHER, nsIMemoryReporter::UNITS_COUNT,
-           WebGLMemoryMultiReporterWrapper::GetBufferCount(),
-           "Number of WebGL buffers."); 
-   
+           WebGLMemoryReporterWrapper::GetBufferCount(),
+           "Number of WebGL buffers.");
+
     REPORT("webgl-renderbuffer-memory",
            nsIMemoryReporter::KIND_OTHER, nsIMemoryReporter::UNITS_BYTES,
-           WebGLMemoryMultiReporterWrapper::GetRenderbufferMemoryUsed(), 
+           WebGLMemoryReporterWrapper::GetRenderbufferMemoryUsed(),
            "Memory used by WebGL renderbuffers. The OpenGL"
            " implementation is free to store these renderbuffers in either"
            " video memory or main memory. This measurement is only a lower"
            " bound, actual memory usage may be higher for example if the"
            " storage is strided.");
-   
+
     REPORT("webgl-renderbuffer-count",
            nsIMemoryReporter::KIND_OTHER, nsIMemoryReporter::UNITS_COUNT,
-           WebGLMemoryMultiReporterWrapper::GetRenderbufferCount(),
+           WebGLMemoryReporterWrapper::GetRenderbufferCount(),
            "Number of WebGL renderbuffers.");
-  
+
     REPORT("explicit/webgl/shader",
            nsIMemoryReporter::KIND_HEAP, nsIMemoryReporter::UNITS_BYTES,
-           WebGLMemoryMultiReporterWrapper::GetShaderSize(), 
+           WebGLMemoryReporterWrapper::GetShaderSize(),
            "Combined size of WebGL shader ASCII sources and translation"
-           " logs cached on the heap."); 
+           " logs cached on the heap.");
 
     REPORT("webgl-shader-count",
            nsIMemoryReporter::KIND_OTHER, nsIMemoryReporter::UNITS_COUNT,
-           WebGLMemoryMultiReporterWrapper::GetShaderCount(), 
-           "Number of WebGL shaders."); 
+           WebGLMemoryReporterWrapper::GetShaderCount(),
+           "Number of WebGL shaders.");
 
     REPORT("webgl-context-count",
            nsIMemoryReporter::KIND_OTHER, nsIMemoryReporter::UNITS_COUNT,
-           WebGLMemoryMultiReporterWrapper::GetContextCount(), 
-           "Number of WebGL contexts."); 
+           WebGLMemoryReporterWrapper::GetContextCount(),
+           "Number of WebGL contexts.");
 
 #undef REPORT
 
     return NS_OK;
 }
 
-WebGLMemoryMultiReporterWrapper* WebGLMemoryMultiReporterWrapper::sUniqueInstance = nullptr;
+WebGLMemoryReporterWrapper* WebGLMemoryReporterWrapper::sUniqueInstance = nullptr;
 
-WebGLMemoryMultiReporterWrapper* WebGLMemoryMultiReporterWrapper::UniqueInstance()
+WebGLMemoryReporterWrapper* WebGLMemoryReporterWrapper::UniqueInstance()
 {
     if (!sUniqueInstance) {
-        sUniqueInstance = new WebGLMemoryMultiReporterWrapper;
+        sUniqueInstance = new WebGLMemoryReporterWrapper;
     }
-    return sUniqueInstance;     
+    return sUniqueInstance;
 }
 
-WebGLMemoryMultiReporterWrapper::WebGLMemoryMultiReporterWrapper()
-{ 
-    mReporter = new WebGLMemoryMultiReporter;   
-    NS_RegisterMemoryMultiReporter(mReporter);
+WebGLMemoryReporterWrapper::WebGLMemoryReporterWrapper()
+{
+    mReporter = new WebGLMemoryReporter;
+    NS_RegisterMemoryReporter(mReporter);
 }
 
-WebGLMemoryMultiReporterWrapper::~WebGLMemoryMultiReporterWrapper()
+WebGLMemoryReporterWrapper::~WebGLMemoryReporterWrapper()
 {
-    NS_UnregisterMemoryMultiReporter(mReporter);
+    NS_UnregisterMemoryReporter(mReporter);
 }
 
 NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(WebGLBufferMallocSizeOf)
 
-int64_t 
-WebGLMemoryMultiReporterWrapper::GetBufferCacheMemoryUsed() {
+int64_t
+WebGLMemoryReporterWrapper::GetBufferCacheMemoryUsed() {
     const ContextsArrayType & contexts = Contexts();
     int64_t result = 0;
     for(size_t i = 0; i < contexts.Length(); ++i) {
         for (const WebGLBuffer *buffer = contexts[i]->mBuffers.getFirst();
              buffer;
              buffer = buffer->getNext())
         {
             if (buffer->Target() == LOCAL_GL_ELEMENT_ARRAY_BUFFER)
                 result += buffer->SizeOfIncludingThis(WebGLBufferMallocSizeOf);
         }
     }
     return result;
 }
 
 NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(WebGLShaderMallocSizeOf)
 
-int64_t 
-WebGLMemoryMultiReporterWrapper::GetShaderSize() {
+int64_t
+WebGLMemoryReporterWrapper::GetShaderSize() {
     const ContextsArrayType & contexts = Contexts();
     int64_t result = 0;
     for(size_t i = 0; i < contexts.Length(); ++i) {
         for (const WebGLShader *shader = contexts[i]->mShaders.getFirst();
              shader;
              shader = shader->getNext())
         {
             result += shader->SizeOfIncludingThis(WebGLShaderMallocSizeOf);
rename from content/canvas/src/WebGLMemoryMultiReporterWrapper.h
rename to content/canvas/src/WebGLMemoryReporterWrapper.h
--- a/content/canvas/src/WebGLMemoryMultiReporterWrapper.h
+++ b/content/canvas/src/WebGLMemoryReporterWrapper.h
@@ -1,42 +1,42 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef WEBGLMEMORYMULTIREPORTWRAPER_H_
-#define WEBGLMEMORYMULTIREPORTWRAPER_H_
+#ifndef WEBGLMEMORYREPORTERWRAPPER_H_
+#define WEBGLMEMORYREPORTERWRAPPER_H_
 
 #include "WebGLContext.h"
 #include "WebGLBuffer.h"
 #include "WebGLVertexAttribData.h"
 #include "WebGLShader.h"
 #include "WebGLProgram.h"
 #include "WebGLUniformLocation.h"
 #include "WebGLTexture.h"
 #include "WebGLRenderbuffer.h"
 
 namespace mozilla {
 
-class WebGLMemoryMultiReporterWrapper
+class WebGLMemoryReporterWrapper
 {
-    WebGLMemoryMultiReporterWrapper();
-    ~WebGLMemoryMultiReporterWrapper();
-    static WebGLMemoryMultiReporterWrapper* sUniqueInstance;
+    WebGLMemoryReporterWrapper();
+    ~WebGLMemoryReporterWrapper();
+    static WebGLMemoryReporterWrapper* sUniqueInstance;
 
-    // here we store plain pointers, not RefPtrs: we don't want the 
-    // WebGLMemoryMultiReporterWrapper unique instance to keep alive all		
+    // here we store plain pointers, not RefPtrs: we don't want the
+    // WebGLMemoryReporterWrapper unique instance to keep alive all
     // WebGLContexts ever created.
     typedef nsTArray<const WebGLContext*> ContextsArrayType;
     ContextsArrayType mContexts;
 
-    nsCOMPtr<nsIMemoryMultiReporter> mReporter;
+    nsCOMPtr<nsIMemoryReporter> mReporter;
 
-    static WebGLMemoryMultiReporterWrapper* UniqueInstance();
+    static WebGLMemoryReporterWrapper* UniqueInstance();
 
     static ContextsArrayType & Contexts() { return UniqueInstance()->mContexts; }
 
     friend class WebGLContext;
 
   public:
 
     static void AddWebGLContext(const WebGLContext* c) {
--- a/content/events/src/nsDOMMessageEvent.cpp
+++ b/content/events/src/nsDOMMessageEvent.cpp
@@ -1,30 +1,38 @@
 /* -*- 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 "nsDOMMessageEvent.h"
+#include "mozilla/dom/MessagePort.h"
+#include "mozilla/dom/MessagePortBinding.h"
+#include "mozilla/dom/MessagePortList.h"
+#include "mozilla/dom/UnionTypes.h"
 
 #include "mozilla/HoldDropJSObjects.h"
 #include "jsapi.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMMessageEvent)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDOMMessageEvent, nsDOMEvent)
   tmp->mData = JSVAL_VOID;
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSource)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindowSource)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPortSource)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPorts)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDOMMessageEvent, nsDOMEvent)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSource)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindowSource)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPortSource)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPorts)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(nsDOMMessageEvent, nsDOMEvent)
   NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mData)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMMessageEvent)
   NS_INTERFACE_MAP_ENTRY(nsIDOMMessageEvent)
@@ -77,37 +85,115 @@ nsDOMMessageEvent::GetLastEventId(nsAStr
 {
   aLastEventId = mLastEventId;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMMessageEvent::GetSource(nsIDOMWindow** aSource)
 {
-  NS_IF_ADDREF(*aSource = mSource);
+  NS_IF_ADDREF(*aSource = mWindowSource);
   return NS_OK;
 }
 
+void
+nsDOMMessageEvent::GetSource(Nullable<mozilla::dom::WindowProxyOrMessagePortReturnValue>& aValue) const
+{
+  if (mWindowSource) {
+    aValue.SetValue().SetAsWindowProxy() = mWindowSource;
+  } else if (mPortSource) {
+    aValue.SetValue().SetAsMessagePort() = mPortSource;
+  }
+}
+
+/* static */ already_AddRefed<nsDOMMessageEvent>
+nsDOMMessageEvent::Constructor(const mozilla::dom::GlobalObject& aGlobal,
+                               JSContext* aCx, const nsAString& aType,
+                               const mozilla::dom::MessageEventInit& aParam,
+                               mozilla::ErrorResult& aRv)
+{
+  nsCOMPtr<mozilla::dom::EventTarget> t =
+    do_QueryInterface(aGlobal.GetAsSupports());
+  nsRefPtr<nsDOMMessageEvent> event =
+    new nsDOMMessageEvent(t, nullptr, nullptr);
+
+  aRv = event->InitEvent(aType, aParam.mBubbles, aParam.mCancelable);
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+
+  bool trusted = event->Init(t);
+  event->SetTrusted(trusted);
+
+  if (aParam.mData.WasPassed()) {
+    event->mData = aParam.mData.Value();
+  }
+
+  mozilla::HoldJSObjects(event.get());
+
+  if (aParam.mOrigin.WasPassed()) {
+    event->mOrigin = aParam.mOrigin.Value();
+  }
+
+  if (aParam.mLastEventId.WasPassed()) {
+    event->mLastEventId = aParam.mLastEventId.Value();
+  }
+
+  if (aParam.mSource) {
+    nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative;
+    nsContentUtils::XPConnect()->
+      GetWrappedNativeOfJSObject(aCx, aParam.mSource,
+                                 getter_AddRefs(wrappedNative));
+
+    if (wrappedNative) {
+      event->mWindowSource = do_QueryWrappedNative(wrappedNative);
+    }
+
+    if (!event->mWindowSource) {
+      MessagePort* port = nullptr;
+      nsresult rv = UNWRAP_OBJECT(MessagePort, aCx, aParam.mSource, port);
+      if (NS_FAILED(rv)) {
+        aRv.Throw(NS_ERROR_INVALID_ARG);
+        return nullptr;
+      }
+
+      event->mPortSource = port;
+    }
+  }
+
+  if (aParam.mPorts.WasPassed() && !aParam.mPorts.Value().IsNull()) {
+    nsTArray<nsRefPtr<MessagePort> > ports;
+    for (uint32_t i = 0, len = aParam.mPorts.Value().Value().Length(); i < len; ++i) {
+      ports.AppendElement(aParam.mPorts.Value().Value()[i].get());
+    }
+
+    event->mPorts = new MessagePortList(static_cast<nsDOMEventBase*>(event),
+                                        ports);
+  }
+
+  return event.forget();
+}
+
 NS_IMETHODIMP
 nsDOMMessageEvent::InitMessageEvent(const nsAString& aType,
                                     bool aCanBubble,
                                     bool aCancelable,
                                     const JS::Value& aData,
                                     const nsAString& aOrigin,
                                     const nsAString& aLastEventId,
                                     nsIDOMWindow* aSource)
 {
   nsresult rv = nsDOMEvent::InitEvent(aType, aCanBubble, aCancelable);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mData = aData;
   mozilla::HoldJSObjects(this);
   mOrigin = aOrigin;
   mLastEventId = aLastEventId;
-  mSource = aSource;
+  mWindowSource = aSource;
 
   return NS_OK;
 }
 
 nsresult
 NS_NewDOMMessageEvent(nsIDOMEvent** aInstancePtrResult,
                       mozilla::dom::EventTarget* aOwner,
                       nsPresContext* aPresContext,
--- a/content/events/src/nsDOMMessageEvent.h
+++ b/content/events/src/nsDOMMessageEvent.h
@@ -6,16 +6,23 @@
 #ifndef nsDOMMessageEvent_h__
 #define nsDOMMessageEvent_h__
 
 #include "nsIDOMMessageEvent.h"
 #include "nsDOMEvent.h"
 #include "nsCycleCollectionParticipant.h"
 #include "mozilla/dom/MessageEventBinding.h"
 
+namespace mozilla {
+namespace dom {
+class MessagePortList;
+class WindowProxyOrMessagePortReturnValue;
+}
+}
+
 /**
  * Implements the MessageEvent event, used for cross-document messaging and
  * server-sent events.
  *
  * See http://www.whatwg.org/specs/web-apps/current-work/#messageevent for
  * further details.
  */
 class nsDOMMessageEvent : public nsDOMEvent,
@@ -38,36 +45,31 @@ public:
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aScope) MOZ_OVERRIDE
   {
     return mozilla::dom::MessageEventBinding::Wrap(aCx, aScope, this);
   }
 
   JS::Value GetData(JSContext* aCx, mozilla::ErrorResult& aRv);
 
-  already_AddRefed<nsIDOMWindow> GetSource()
+  void GetSource(Nullable<mozilla::dom::WindowProxyOrMessagePortReturnValue>& aValue) const;
+
+  mozilla::dom::MessagePortList* GetPorts()
   {
-    nsCOMPtr<nsIDOMWindow> ret = mSource;
-    return ret.forget();
+    return mPorts;
   }
 
-  void InitMessageEvent(JSContext* aCx,
-                        const nsAString& aType,
-                        bool aCanBubble,
-                        bool aCancelable,
-                        JS::Handle<JS::Value> aData,
-                        const nsAString& aOrigin,
-                        const nsAString& aLastEventId,
-                        nsIDOMWindow* aSource,
-                        mozilla::ErrorResult& aRv)
-  {
-    aRv = InitMessageEvent(aType, aCanBubble, aCancelable, aData,
-                           aOrigin, aLastEventId, aSource);
-  }
+  static already_AddRefed<nsDOMMessageEvent>
+  Constructor(const mozilla::dom::GlobalObject& aGlobal, JSContext* aCx,
+              const nsAString& aType,
+              const mozilla::dom::MessageEventInit& aEventInit,
+              mozilla::ErrorResult& aRv);
 
 private:
   JS::Heap<JS::Value> mData;
   nsString mOrigin;
   nsString mLastEventId;
-  nsCOMPtr<nsIDOMWindow> mSource;
+  nsCOMPtr<nsIDOMWindow> mWindowSource;
+  nsCOMPtr<mozilla::dom::MessagePort> mPortSource;
+  nsRefPtr<mozilla::dom::MessagePortList> mPorts;
 };
 
 #endif // nsDOMMessageEvent_h__
--- a/content/events/test/Makefile.in
+++ b/content/events/test/Makefile.in
@@ -91,16 +91,17 @@ MOCHITEST_FILES = \
 		test_dragstart.html \
 		test_bug812744.html \
 		test_addEventListenerExtraArg.html \
 		test_focus_disabled.html \
 		test_bug847597.html \
 		test_bug855741.html \
 		test_dblclick_explicit_original_target.html \
 		test_all_synthetic_events.html \
+		test_messageEvent.html \
 		$(NULL)
 
 ifeq (,$(filter gonk,$(MOZ_WIDGET_TOOLKIT)))
 # THESE TESTS (BELOW) DO NOT RUN ON B2G
 MOCHITEST_FILES += \
 		test_bug864040.html \
 		$(NULL)
 # THESE TESTS (ABOVE) DO NOT RUN ON B2G
--- a/content/events/test/test_all_synthetic_events.html
+++ b/content/events/test/test_all_synthetic_events.html
@@ -184,20 +184,19 @@ const kEventConstructors = {
                                                          return e;
                                                        },
                                              },
   MediaStreamEvent:                          { create: function (aName, aProps) {
                                                          return new MediaStreamEvent(aName, aProps);
                                                        },
                                              },
   MessageEvent:                              { create: function (aName, aProps) {
-                                                         var e = document.createEvent("messageevent");
-                                                         e.initMessageEvent(aName, aProps.bubbles, aProps.cancelable,
-                                                                            aProps.data, aProps.origin,
-                                                                            aProps.lastEventId, aProps.source);
+                                                         var e = new MessageEvent("messageevent", { bubbles: aProps.bubbles,
+                                                             cancelable: aProps.cancelable, data: aProps.data, origin: aProps.origin,
+                                                             lastEventId: aProps.lastEventId, source: aProps.source });
                                                          return e;
                                                        },
                                              },
   MouseEvent:                                { create: function (aName, aProps) {
                                                          return new MouseEvent(aName, aProps);
                                                        },
                                              },
   MouseScrollEvent:                          { create: function (aName, aProps) {
new file mode 100644
--- /dev/null
+++ b/content/events/test/test_messageEvent.html
@@ -0,0 +1,66 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=848294
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 848294</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/WindowSnapshot.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+  <script type="application/javascript">
+  function runTest() {
+    var channel = new MessageChannel();
+
+    var tests = [
+      {},
+      { data: 42 },
+      { data: {} },
+      { data: true, origin: 'wow' },
+      { data: [], lastEventId: 'wow2'  },
+      { data: null, source: null },
+      { data: window, source: window },
+      { data: window, source: channel.port1 },
+      { data: window, source: channel.port1, ports: [ channel.port1, channel.port2 ] },
+      { data: null, ports: null },
+    ];
+
+    while (tests.length) {
+      var test = tests.shift();
+
+      var e = new MessageEvent('message', test);
+      ok(e, "MessageEvent created");
+      is(e.type, 'message', 'MessageEvent.type is right');
+
+      is(e.data, 'data' in test ? test.data : undefined, 'MessageEvent.data is ok');
+      is(e.origin, 'origin' in test ? test.origin : '', 'MessageEvent.origin is ok');
+      is(e.lastEventId, 'lastEventId' in test ? test.lastEventId : '', 'MessageEvent.lastEventId is ok');
+      is(e.source, 'source' in test ? test.source : undefined, 'MessageEvent.source is ok');
+
+      if (test.ports != undefined) {
+        is(e.ports.length, test.ports.length, 'MessageEvent.ports is ok');
+        is(e.ports, e.ports, 'MessageEvent.ports is ok');
+      } else {
+        ok(!('ports' in test) || test.ports == null, 'MessageEvent.ports is ok');
+      }
+    }
+
+    try {
+      var e = new MessageEvent('foobar', { source: 42 });
+      ok(false, "Source has to be a window or a port");
+    } catch(e) {
+      ok(true, "Source has to be a window or a port");
+    }
+
+    SimpleTest.finish();
+  }
+
+  SimpleTest.waitForExplicitFinish();
+  SpecialPowers.pushPrefEnv({"set": [["dom.messageChannel.enabled", true]]}, runTest);
+  </script>
+</body>
+</html>
deleted file mode 100644
--- a/content/html/content/crashtests/832011.html
+++ /dev/null
@@ -1,16 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<script>
-
-function boom()
-{
-    var canvas = document.createElement('canvas');
-    var m = Components.lookupMethod(canvas, "itemProp")
-    m();
-}
-
-</script>
-</head>
-<body onload="boom();"></body>
-</html>
--- a/content/html/content/crashtests/crashtests.list
+++ b/content/html/content/crashtests/crashtests.list
@@ -42,15 +42,14 @@ load 795221-2.html
 load 795221-3.html
 load 795221-4.html
 load 795221-5.xml
 load 798802-1.html
 load 811226.html
 load 819745.html
 pref(dom.experimental_forms,true) load 828472.html
 load 828180.html
-load 832011.html
 load 837033.html
 pref(dom.experimental_forms_range,true) load 838256-1.html
 load 862084.html
 load 865147.html
 load 877910.html
 load 903106.html
--- a/content/media/MediaDecoder.cpp
+++ b/content/media/MediaDecoder.cpp
@@ -68,17 +68,17 @@ class MediaMemoryTracker
 
   typedef nsTArray<MediaDecoder*> DecodersArray;
   static DecodersArray& Decoders() {
     return UniqueInstance()->mDecoders;
   }
 
   DecodersArray mDecoders;
 
-  nsCOMPtr<nsIMemoryMultiReporter> mReporter;
+  nsCOMPtr<nsIMemoryReporter> mReporter;
 
 public:
   static void AddMediaDecoder(MediaDecoder* aDecoder)
   {
     Decoders().AppendElement(aDecoder);
   }
 
   static void RemoveMediaDecoder(MediaDecoder* aDecoder)
@@ -1723,28 +1723,28 @@ MediaDecoder::IsDASHEnabled()
 #ifdef MOZ_WMF
 bool
 MediaDecoder::IsWMFEnabled()
 {
   return WMFDecoder::IsEnabled();
 }
 #endif
 
-class MediaReporter MOZ_FINAL : public nsIMemoryMultiReporter
+class MediaReporter MOZ_FINAL : public nsIMemoryReporter
 {
 public:
   NS_DECL_ISUPPORTS
 
   NS_IMETHOD GetName(nsACString& aName)
   {
     aName.AssignLiteral("media");
     return NS_OK;
   }
 
-  NS_IMETHOD CollectReports(nsIMemoryMultiReporterCallback* aCb,
+  NS_IMETHOD CollectReports(nsIMemoryReporterCallback* aCb,
                             nsISupports* aClosure)
   {
     int64_t video, audio;
     MediaMemoryTracker::GetAmounts(&video, &audio);
 
   #define REPORT(_path, _amount, _desc)                                       \
     do {                                                                      \
         nsresult rv;                                                          \
@@ -1760,30 +1760,30 @@ public:
 
     REPORT("explicit/media/decoded-audio", audio,
            "Memory used by decoded audio chunks.");
 
     return NS_OK;
   }
 };
 
-NS_IMPL_ISUPPORTS1(MediaReporter, nsIMemoryMultiReporter)
+NS_IMPL_ISUPPORTS1(MediaReporter, nsIMemoryReporter)
 
 MediaDecoderOwner*
 MediaDecoder::GetOwner()
 {
   MOZ_ASSERT(NS_IsMainThread());
   return mOwner;
 }
 
 MediaMemoryTracker::MediaMemoryTracker()
   : mReporter(new MediaReporter())
 {
-  NS_RegisterMemoryMultiReporter(mReporter);
+  NS_RegisterMemoryReporter(mReporter);
 }
 
 MediaMemoryTracker::~MediaMemoryTracker()
 {
-  NS_UnregisterMemoryMultiReporter(mReporter);
+  NS_UnregisterMemoryReporter(mReporter);
 }
 
 } // namespace mozilla
 
--- a/content/xbl/src/nsXBLBinding.cpp
+++ b/content/xbl/src/nsXBLBinding.cpp
@@ -71,17 +71,17 @@ using namespace mozilla::dom;
 //
 static void
 XBLFinalize(JSFreeOp *fop, JSObject *obj)
 {
   nsXBLDocumentInfo* docInfo =
     static_cast<nsXBLDocumentInfo*>(::JS_GetPrivate(obj));
   nsContentUtils::DeferredFinalize(docInfo);
   
-  nsXBLJSClass* c = static_cast<nsXBLJSClass*>(::JS_GetClass(obj));
+  nsXBLJSClass* c = nsXBLJSClass::fromJSClass(::JS_GetClass(obj));
   c->Drop();
 }
 
 static bool
 XBLEnumerate(JSContext *cx, JS::Handle<JSObject*> obj)
 {
   nsXBLPrototypeBinding* protoBinding =
     static_cast<nsXBLPrototypeBinding*>(::JS_GetReservedSlot(obj, 0).toPrivate());
@@ -133,16 +133,29 @@ nsXBLJSClass::Destroy()
     // Put this most-recently-used class on end of the LRU-sorted freelist.
     nsXBLService::gClassLRUList->insertBack(this);
     nsXBLService::gClassLRUListLength++;
   }
 
   return 0;
 }
 
+nsXBLJSClass*
+nsXBLService::getClass(const nsCString& k)
+{
+  nsCStringKey key(k);
+  return getClass(&key);
+}
+
+nsXBLJSClass*
+nsXBLService::getClass(nsCStringKey *k)
+{
+  return static_cast<nsXBLJSClass*>(nsXBLService::gClassTable->Get(k));
+}
+
 // Implementation /////////////////////////////////////////////////////////////////
 
 // Constructors/Destructors
 nsXBLBinding::nsXBLBinding(nsXBLPrototypeBinding* aBinding)
   : mMarkedForDeath(false),
     mPrototypeBinding(aBinding)
 {
   NS_ASSERTION(mPrototypeBinding, "Must have a prototype binding!");
@@ -768,17 +781,17 @@ nsXBLBinding::ChangeDocument(nsIDocument
           for ( ; true; base = proto) { // Will break out on null proto
             if (!JS_GetPrototype(cx, base, &proto)) {
               return;
             }
             if (!proto) {
               break;
             }
 
-            JSClass* clazz = ::JS_GetClass(proto);
+            const JSClass* clazz = ::JS_GetClass(proto);
             if (!clazz ||
                 (~clazz->flags &
                  (JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS)) ||
                 JSCLASS_RESERVED_SLOTS(clazz) != 1 ||
                 clazz->finalize != XBLFinalize) {
               // Clearly not the right class
               continue;
             }
@@ -921,19 +934,18 @@ nsXBLBinding::DoInitJSClass(JSContext *c
       char buf[20];
       if (sizeof(jsid) == 4) {
         PR_snprintf(buf, sizeof(buf), " %lx", parent_proto_id.get());
       } else {
         MOZ_ASSERT(sizeof(jsid) == 8);
         PR_snprintf(buf, sizeof(buf), " %llx", parent_proto_id.get());
       }
       xblKey.Append(buf);
-      nsCStringKey key(xblKey);
 
-      c = static_cast<nsXBLJSClass*>(nsXBLService::gClassTable->Get(&key));
+      c = nsXBLService::getClass(xblKey);
       if (c) {
         className.Assign(c->name);
       } else {
         char buf[20];
         PR_snprintf(buf, sizeof(buf), " %llx", nsXBLJSClass::NewId());
         className.Append(buf);
       }
     }
@@ -949,17 +961,17 @@ nsXBLBinding::DoInitJSClass(JSContext *c
     *aNew = false;
     proto = &val.toObject();
   } else {
     // We need to initialize the class.
     *aNew = true;
 
     nsCStringKey key(xblKey);
     if (!c) {
-      c = static_cast<nsXBLJSClass*>(nsXBLService::gClassTable->Get(&key));
+      c = nsXBLService::getClass(&key);
     }
     if (c) {
       // If c is on the LRU list, remove it now!
       if (c->isInList()) {
         c->remove();
         nsXBLService::gClassLRUListLength--;
       }
     } else {
--- a/content/xbl/src/nsXBLDocumentInfo.cpp
+++ b/content/xbl/src/nsXBLDocumentInfo.cpp
@@ -55,17 +55,17 @@ public:
   void Destroy();
   nsIPrincipal* GetPrincipal();
 
   static bool doCheckAccess(JSContext *cx, JS::Handle<JSObject*> obj,
                             JS::Handle<jsid> id, uint32_t accessType);
 
   void ClearGlobalObjectOwner();
 
-  static JSClass gSharedGlobalClass;
+  static const JSClass gSharedGlobalClass;
 
 protected:
   virtual ~nsXBLDocGlobalObject();
 
   JS::Heap<JSObject*> mJSObject;
   nsXBLDocumentInfo* mGlobalObjectOwner; // weak reference
   bool mDestroyed; // Probably not necessary, but let's be safe.
 };
@@ -146,17 +146,17 @@ nsXBLDocGlobalObject_finalize(JSFreeOp *
 static bool
 nsXBLDocGlobalObject_resolve(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id)
 {
   bool did_resolve = false;
   return JS_ResolveStandardClass(cx, obj, id, &did_resolve);
 }
 
 
-JSClass nsXBLDocGlobalObject::gSharedGlobalClass = {
+const JSClass nsXBLDocGlobalObject::gSharedGlobalClass = {
     "nsXBLPrototypeScript compilation scope",
     JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS |
     JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(0),
     JS_PropertyStub,  JS_DeletePropertyStub,
     nsXBLDocGlobalObject_getProperty, nsXBLDocGlobalObject_setProperty,
     JS_EnumerateStub, nsXBLDocGlobalObject_resolve,
     JS_ConvertStub, nsXBLDocGlobalObject_finalize,
     nsXBLDocGlobalObject_checkAccess, nullptr, nullptr, nullptr,
--- a/content/xbl/src/nsXBLProtoImpl.cpp
+++ b/content/xbl/src/nsXBLProtoImpl.cpp
@@ -56,17 +56,17 @@ nsXBLProtoImpl::InstallImplementation(ns
   nsresult rv = InitTargetObjects(aPrototypeBinding,
                                   aBinding->GetBoundElement(),
                                   getter_AddRefs(holder), &targetClassObject,
                                   &targetObjectIsNew);
   NS_ENSURE_SUCCESS(rv, rv); // kick out if we were unable to properly intialize our target objects
   MOZ_ASSERT(targetClassObject);
 
   // Stash a strong reference to the JSClass in the binding.
-  aBinding->SetJSClass(static_cast<nsXBLJSClass*>(JS_GetClass(targetClassObject)));
+  aBinding->SetJSClass(nsXBLJSClass::fromJSClass(JS_GetClass(targetClassObject)));
 
   // If the prototype already existed, we don't need to install anything. return early.
   if (!targetObjectIsNew)
     return NS_OK;
 
   JS::Rooted<JSObject*> targetScriptObject(cx, holder->GetJSObject());
 
   JSAutoCompartment ac(cx, targetClassObject);
--- a/content/xbl/src/nsXBLProtoImplField.cpp
+++ b/content/xbl/src/nsXBLProtoImplField.cpp
@@ -111,17 +111,17 @@ ValueHasISupportsPrivate(JS::Handle<JS::
     return false;
   }
 
   const DOMClass* domClass = GetDOMClass(&v.toObject());
   if (domClass) {
     return domClass->mDOMObjectIsISupports;
   }
 
-  JSClass* clasp = ::JS_GetClass(&v.toObject());
+  const JSClass* clasp = ::JS_GetClass(&v.toObject());
   const uint32_t HAS_PRIVATE_NSISUPPORTS =
     JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS;
   return (clasp->flags & HAS_PRIVATE_NSISUPPORTS) == HAS_PRIVATE_NSISUPPORTS;
 }
 
 #ifdef DEBUG
 static bool
 ValueHasISupportsPrivate(JSContext* cx, const JS::Value& aVal)
--- a/content/xbl/src/nsXBLService.h
+++ b/content/xbl/src/nsXBLService.h
@@ -10,16 +10,17 @@
 
 #include "mozilla/LinkedList.h"
 #include "nsString.h"
 #include "nsIObserver.h"
 #include "nsWeakReference.h"
 #include "js/Class.h"           // nsXBLJSClass derives from JSClass
 #include "nsTArray.h"
 
+class nsCStringKey;
 class nsXBLBinding;
 class nsXBLDocumentInfo;
 class nsXBLJSClass;
 class nsIContent;
 class nsIDocument;
 class nsString;
 class nsIURI;
 class nsIPrincipal;
@@ -127,16 +128,20 @@ public:
 
   static mozilla::LinkedList<nsXBLJSClass>* gClassLRUList;
                                              // LRU list of cached classes.
   static uint32_t gClassLRUListLength;       // Number of classes on LRU list.
   static uint32_t gClassLRUListQuota;        // Quota on class LRU list.
   static bool     gAllowDataURIs;            // Whether we should allow data
                                              // urls in -moz-binding. Needed for
                                              // testing.
+
+  // Look up the class by key in gClassTable.
+  static nsXBLJSClass *getClass(const nsCString &key);
+  static nsXBLJSClass *getClass(nsCStringKey *key);
 };
 
 class nsXBLJSClass : public mozilla::LinkedListElement<nsXBLJSClass>
                    , public JSClass
 {
 private:
   nsrefcnt mRefCnt;
   nsCString mKey;
@@ -151,11 +156,28 @@ public:
 
   nsCString& Key() { return mKey; }
   void SetKey(const nsCString& aKey) { mKey = aKey; }
 
   nsrefcnt Hold() { return ++mRefCnt; }
   nsrefcnt Drop() { return --mRefCnt ? mRefCnt : Destroy(); }
   nsrefcnt AddRef() { return Hold(); }
   nsrefcnt Release() { return Drop(); }
+
+  // Downcast from a pointer to const JSClass to a pointer to non-const
+  // nsXBLJSClass.
+  //
+  // The const_cast is safe because nsXBLJSClass instances are never actually
+  // const. It's necessary because we pass pointers to nsXBLJSClass to code
+  // which uses pointers to const JSClass, and returns them back to us that
+  // way, and we need to convert them back to pointers to non-const
+  // nsXBLJSClass so that we can modify the reference count and add them to
+  // the gClassLRUList list.
+  static nsXBLJSClass*
+  fromJSClass(const JSClass* c)
+  {
+    nsXBLJSClass* x = const_cast<nsXBLJSClass*>(static_cast<const nsXBLJSClass*>(c));
+    MOZ_ASSERT(nsXBLService::getClass(x->mKey) == x);
+    return x;
+  }
 };
 
 #endif
--- a/content/xul/document/src/nsXULPrototypeDocument.cpp
+++ b/content/xul/document/src/nsXULPrototypeDocument.cpp
@@ -65,17 +65,17 @@ public:
 protected:
     virtual ~nsXULPDGlobalObject();
 
     nsCOMPtr<nsIPrincipal> mCachedPrincipal;
     nsXULPrototypeDocument* mGlobalObjectOwner; // weak reference
     JS::Heap<JSObject*> mJSObject;
     bool mDestroyed; // Probably not necessary, but let's be safe.
 
-    static JSClass gSharedGlobalClass;
+    static const JSClass gSharedGlobalClass;
 };
 
 nsIPrincipal* nsXULPrototypeDocument::gSystemPrincipal;
 nsXULPDGlobalObject* nsXULPrototypeDocument::gSystemGlobal;
 uint32_t nsXULPrototypeDocument::gRefCnt;
 
 
 void
@@ -93,17 +93,17 @@ bool
 nsXULPDGlobalObject_resolve(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id)
 {
     bool did_resolve = false;
 
     return JS_ResolveStandardClass(cx, obj, id, &did_resolve);
 }
 
 
-JSClass nsXULPDGlobalObject::gSharedGlobalClass = {
+const JSClass nsXULPDGlobalObject::gSharedGlobalClass = {
     "nsXULPrototypeScript compilation scope",
     JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS |
     JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(0),
     JS_PropertyStub,  JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
     JS_EnumerateStub, nsXULPDGlobalObject_resolve,  JS_ConvertStub,
     nsXULPDGlobalObject_finalize, nullptr, nullptr, nullptr, nullptr,
     nullptr
 };
--- a/dom/apps/src/PermissionsTable.jsm
+++ b/dom/apps/src/PermissionsTable.jsm
@@ -223,17 +223,18 @@ this.PermissionsTable =  { geolocation: 
                            },
                            "storage": {
                              app: ALLOW_ACTION,
                              privileged: ALLOW_ACTION,
                              certified: ALLOW_ACTION,
                              substitute: [
                                "indexedDB-unlimited",
                                "offline-app",
-                               "pin-app"
+                               "pin-app",
+                               "default-persistent-storage"
                              ]
                            },
                            "background-sensors": {
                              app: DENY_ACTION,
                              privileged: DENY_ACTION,
                              certified: ALLOW_ACTION
                            },
                            cellbroadcast: {
--- a/dom/apps/src/Webapps.jsm
+++ b/dom/apps/src/Webapps.jsm
@@ -454,17 +454,17 @@ this.DOMApplicationRegistry = {
     }
   },
 
 #ifdef MOZ_WIDGET_GONK
   fixIndexedDb: function fixIndexedDb() {
     debug("Fixing indexedDb folder names");
     let idbDir = FileUtils.getDir("indexedDBPDir", ["indexedDB"]);
 
-    if (!idbDir.isDirectory()) {
+    if (!idbDir.exists() || !idbDir.isDirectory()) {
       return;
     }
 
     let re = /^(\d+)\+(.*)\+(f|t)$/;
 
     let entries = idbDir.directoryEntries;
     while (entries.hasMoreElements()) {
       let entry = entries.getNext().QueryInterface(Ci.nsIFile);
--- a/dom/base/DOMRequestHelper.jsm
+++ b/dom/base/DOMRequestHelper.jsm
@@ -216,14 +216,15 @@ DOMRequestIpcHelper.prototype = {
       if (this.getRequest(k) instanceof this._window.DOMRequest) {
         aCallback(k);
       }
     }, this);
   },
 
   forEachPromiseResolver: function(aCallback) {
     Object.keys(this._requests).forEach(function(k) {
-      if (this.getPromiseResolver(k) instanceof this._window.PromiseResolver) {
+      if ("resolve" in this.getPromiseResolver(k) &&
+          "reject" in this.getPromiseResolver(k)) {
         aCallback(k);
       }
     }, this);
   },
 }
new file mode 100644
--- /dev/null
+++ b/dom/base/MessagePortList.cpp
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 "MessagePortList.h"
+#include "mozilla/dom/MessagePortListBinding.h"
+#include "mozilla/dom/MessagePort.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_2(MessagePortList, mOwner, mPorts)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(MessagePortList)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(MessagePortList)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MessagePortList)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+JSObject*
+MessagePortList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
+{
+  return MessagePortListBinding::Wrap(aCx, aScope, this);
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/base/MessagePortList.h
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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_MessagePortList_h
+#define mozilla_dom_MessagePortList_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/ErrorResult.h"
+#include "nsCycleCollectionParticipant.h"
+#include "mozilla/dom/MessagePort.h"
+#include "nsWrapperCache.h"
+#include "nsAutoPtr.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace dom {
+
+class MessagePortList MOZ_FINAL : public nsISupports
+                                , public nsWrapperCache
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MessagePortList)
+
+public:
+  MessagePortList(nsISupports* aOwner, nsTArray<nsRefPtr<MessagePort> >& aPorts)
+    : mOwner(aOwner)
+    , mPorts(aPorts)
+  {
+    SetIsDOMBinding();
+  }
+
+  nsISupports*
+  GetParentObject() const
+  {
+    return mOwner;
+  }
+
+  virtual JSObject*
+  WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
+
+  uint32_t
+  Length() const
+  {
+    return mPorts.Length();
+  }
+
+  MessagePort*
+  Item(uint32_t aIndex)
+  {
+    return mPorts.SafeElementAt(aIndex);
+  }
+
+  MessagePort*
+  IndexedGetter(uint32_t aIndex, bool &aFound)
+  {
+    aFound = aIndex < mPorts.Length();
+    if (!aFound) {
+      return nullptr;
+    }
+    return mPorts[aIndex];
+  }
+
+public:
+  nsCOMPtr<nsISupports> mOwner;
+  nsTArray<nsRefPtr<MessagePort> > mPorts;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_MessagePortList_h
+
deleted file mode 100644
--- a/dom/base/crashtests/844559.html
+++ /dev/null
@@ -1,3 +0,0 @@
-<script>
-Components.lookupMethod(Image, "x")
-</script>
--- a/dom/base/crashtests/crashtests.list
+++ b/dom/base/crashtests/crashtests.list
@@ -34,11 +34,10 @@ load 693894.html
 load 693811-1.html
 load 693811-2.html
 load 693811-3.html
 load 695867.html
 load 697643.html
 load 706283-1.html
 load 708405-1.html
 load 745495.html
-load 844559.html
 load 886213.html
 load 898906.html
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -52,31 +52,33 @@ EXPORTS += [
 EXPORTS.mozilla.dom += [
     'BarProps.h',
     'DOMCursor.h',
     'DOMError.h',
     'DOMException.h',
     'DOMRequest.h',
     'MessageChannel.h',
     'MessagePort.h',
+    'MessagePortList.h',
     'ScreenOrientation.h',
     'StructuredCloneTags.h',
     'URL.h',
 ]
 
 CPP_SOURCES += [
     'BarProps.cpp',
     'Crypto.cpp',
     'DOMCursor.cpp',
     'DOMError.cpp',
     'DOMException.cpp',
     'DOMRequest.cpp',
     'Navigator.cpp',
     'MessageChannel.cpp',
     'MessagePort.cpp',
+    'MessagePortList.cpp',
     'nsContentPermissionHelper.cpp',
     'nsDOMClassInfo.cpp',
     'nsDOMNavigationTiming.cpp',
     'nsDOMScriptObjectFactory.cpp',
     'nsDOMWindowList.cpp',
     'nsDOMWindowUtils.cpp',
     'nsFocusManager.cpp',
     'nsGlobalWindow.cpp',
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -848,17 +848,17 @@ NS_INTERFACE_MAP_BEGIN(nsDOMClassInfo)
                                     static_cast<nsXPCClassInfo*>(this));
   else
   NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable)
   NS_INTERFACE_MAP_ENTRY(nsIClassInfo)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIClassInfo)
 NS_INTERFACE_MAP_END
 
 
-static JSClass sDOMConstructorProtoClass = {
+static const JSClass sDOMConstructorProtoClass = {
   "DOM Constructor.prototype", 0,
   JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
   JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, nullptr
 };
 
 
 static const char *
 CutPrefix(const char *aName) {
@@ -2618,17 +2618,17 @@ nsDOMConstructor::HasInstance(nsIXPConne
   JS::Rooted<JSObject*> dom_obj(cx, v.toObjectOrNull());
   NS_ASSERTION(dom_obj, "nsDOMConstructor::HasInstance couldn't get object");
 
   // This might not be the right object, if there are wrappers. Unwrap if we can.
   JSObject *wrapped_obj = js::CheckedUnwrap(dom_obj, /* stopAtOuter = */ false);
   if (wrapped_obj)
       dom_obj = wrapped_obj;
 
-  JSClass *dom_class = JS_GetClass(dom_obj);
+  const JSClass *dom_class = JS_GetClass(dom_obj);
   if (!dom_class) {
     NS_ERROR("nsDOMConstructor::HasInstance can't get class.");
     return NS_ERROR_UNEXPECTED;
   }
 
   const nsGlobalNameStruct *name_struct;
   nsresult rv = GetNameStruct(NS_ConvertASCIItoUTF16(dom_class->name), &name_struct);
   if (NS_FAILED(rv)) {
@@ -4158,17 +4158,17 @@ nsStringListSH::GetStringAt(nsISupports 
   }
 #endif
   return rv;
 }
 
 
 // HTMLAllCollection
 
-JSClass sHTMLDocumentAllClass = {
+const JSClass sHTMLDocumentAllClass = {
   "HTML document.all class",
   JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS | JSCLASS_NEW_RESOLVE |
   JSCLASS_EMULATES_UNDEFINED | JSCLASS_HAS_RESERVED_SLOTS(1),
   JS_PropertyStub,                                         /* addProperty */
   JS_DeletePropertyStub,                                   /* delProperty */
   nsHTMLDocumentSH::DocumentAllGetProperty,                /* getProperty */
   JS_StrictPropertyStub,                                   /* setProperty */
   JS_EnumerateStub,
--- a/dom/base/nsDOMClassInfo.h
+++ b/dom/base/nsDOMClassInfo.h
@@ -426,17 +426,17 @@ public:
 private:
   // Not implemented, nothing should create an instance of this class.
   static nsIClassInfo *doCreate(nsDOMClassInfoData* aData);
 };
 
 
 // HTMLAllCollection
 
-extern JSClass sHTMLDocumentAllClass;
+extern const JSClass sHTMLDocumentAllClass;
 
 class nsHTMLDocumentSH
 {
 protected:
   static bool GetDocumentAllNodeList(JSContext *cx, JS::Handle<JSObject*> obj,
                                      nsDocument *doc,
                                      nsContentList **nodeList);
 public:
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -54,17 +54,19 @@
 #include <gdk/gdk.h>
 #include <gdk/gdkx.h>
 #endif
 
 #include "Layers.h"
 #include "mozilla/layers/ShadowLayers.h"
 
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/IDBFactoryBinding.h"
 #include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
+#include "mozilla/dom/quota/PersistenceType.h"
 #include "mozilla/dom/quota/QuotaManager.h"
 #include "nsDOMBlobBuilder.h"
 #include "nsIDOMFileHandle.h"
 #include "nsPrintfCString.h"
 #include "nsViewportInfo.h"
 #include "nsIFormControl.h"
 #include "nsIScriptError.h"
 #include "nsIAppShell.h"
@@ -2821,38 +2823,52 @@ nsDOMWindowUtils::GetFileId(const JS::Va
     }
   }
 
   *aResult = -1;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDOMWindowUtils::GetFileReferences(const nsAString& aDatabaseName,
-                                    int64_t aId, int32_t* aRefCnt,
-                                    int32_t* aDBRefCnt, int32_t* aSliceRefCnt,
+nsDOMWindowUtils::GetFileReferences(const nsAString& aDatabaseName, int64_t aId,
+                                    const jsval& aOptions,
+                                    int32_t* aRefCnt, int32_t* aDBRefCnt,
+                                    int32_t* aSliceRefCnt, JSContext* aCx,
                                     bool* aResult)
 {
   if (!nsContentUtils::IsCallerChrome()) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
   NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
 
   nsCString origin;
-  nsresult rv = quota::QuotaManager::GetASCIIOriginFromWindow(window, origin);
+  quota::PersistenceType defaultPersistenceType;
+  nsresult rv =
+    quota::QuotaManager::GetInfoFromWindow(window, nullptr, &origin, nullptr,
+                                           &defaultPersistenceType);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  IDBOpenDBOptions options;
+  JS::Rooted<JS::Value> optionsVal(aCx, aOptions);
+  if (!options.Init(aCx, optionsVal)) {
+    return NS_ERROR_TYPE_ERR;
+  }
+
+  quota::PersistenceType persistenceType =
+    quota::PersistenceTypeFromStorage(options.mStorage, defaultPersistenceType);
+
   nsRefPtr<indexedDB::IndexedDatabaseManager> mgr =
     indexedDB::IndexedDatabaseManager::Get();
 
   if (mgr) {
-    rv = mgr->BlockAndGetFileReferences(origin, aDatabaseName, aId, aRefCnt,
-                                        aDBRefCnt, aSliceRefCnt, aResult);
+    rv = mgr->BlockAndGetFileReferences(persistenceType, origin, aDatabaseName,
+                                        aId, aRefCnt, aDBRefCnt, aSliceRefCnt,
+                                        aResult);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   else {
     *aRefCnt = *aDBRefCnt = *aSliceRefCnt = -1;
     *aResult = false;
   }
 
   return NS_OK;
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -2089,17 +2089,17 @@ CreateNativeGlobalForInner(JSContext* aC
 
   nsGlobalWindow *top = NULL;
   if (aNewInner->GetOuterWindow()) {
     top = aNewInner->GetTop();
   }
   JS::CompartmentOptions options;
   if (top) {
     if (top->GetGlobalJSObject()) {
-      options.zoneSpec = JS::SameZoneAs(top->GetGlobalJSObject());
+      options.setSameZoneAs(top->GetGlobalJSObject());
     }
   }
 
   nsIXPConnect* xpc = nsContentUtils::XPConnect();
 
   // Determine if we need the Components object.
   bool needComponents = nsContentUtils::IsSystemPrincipal(aPrincipal) ||
                         TreatAsRemoteXUL(aPrincipal);
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -1119,17 +1119,17 @@ nsJSContext::GetGlobalObject()
     JSObject *inner = JS_ObjectToInnerObject(cx, global);
 
     // If this assertion hits then it means that we have a window object as
     // our global, but we never called CreateOuterObject.
     NS_ASSERTION(inner == global, "Shouldn't be able to innerize here");
   }
 #endif
 
-  JSClass *c = JS_GetClass(global);
+  const JSClass *c = JS_GetClass(global);
 
   // Whenever we end up with globals that are JSCLASS_IS_DOMJSCLASS
   // and have an nsISupports DOM object, we will need to modify this
   // check here.
   MOZ_ASSERT(!(c->flags & JSCLASS_IS_DOMJSCLASS));
   if ((~c->flags) & (JSCLASS_HAS_PRIVATE |
                      JSCLASS_PRIVATE_IS_NSISUPPORTS)) {
     return nullptr;
--- a/dom/base/nsJSUtils.cpp
+++ b/dom/base/nsJSUtils.cpp
@@ -42,17 +42,17 @@ nsJSUtils::GetCallingLocation(JSContext*
   *aLineno = lineno;
 
   return true;
 }
 
 nsIScriptGlobalObject *
 nsJSUtils::GetStaticScriptGlobal(JSObject* aObj)
 {
-  JSClass* clazz;
+  const JSClass* clazz;
   JSObject* glob = aObj; // starting point for search
 
   if (!glob)
     return nullptr;
 
   glob = js::GetGlobalForObjectCrossCompartment(glob);
   NS_ABORT_IF_FALSE(glob, "Infallible returns null");
 
--- a/dom/base/nsScriptNameSpaceManager.cpp
+++ b/dom/base/nsScriptNameSpaceManager.cpp
@@ -111,21 +111,21 @@ GlobalNameHashInitEntry(PLDHashTable *ta
   new (&e->mKey) nsString(*keyStr);
 
   // This will set e->mGlobalName.mType to
   // nsGlobalNameStruct::eTypeNotInitialized
   memset(&e->mGlobalName, 0, sizeof(nsGlobalNameStruct));
   return true;
 }
 
-class ScriptNameSpaceManagerReporter MOZ_FINAL : public MemoryReporterBase
+class ScriptNameSpaceManagerReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
   ScriptNameSpaceManagerReporter(nsScriptNameSpaceManager* aManager)
-    : MemoryReporterBase(
+    : MemoryUniReporter(
         "explicit/script-namespace-manager",
         KIND_HEAP,
         nsIMemoryReporter::UNITS_BYTES,
         "Memory used for the script namespace manager.")
     , mManager(aManager)
   {}
 
 private:
--- a/dom/base/nsWindowMemoryReporter.cpp
+++ b/dom/base/nsWindowMemoryReporter.cpp
@@ -19,44 +19,44 @@
 
 using namespace mozilla;
 
 nsWindowMemoryReporter::nsWindowMemoryReporter()
   : mCheckForGhostWindowsCallbackPending(false)
 {
 }
 
-NS_IMPL_ISUPPORTS3(nsWindowMemoryReporter, nsIMemoryMultiReporter, nsIObserver,
+NS_IMPL_ISUPPORTS3(nsWindowMemoryReporter, nsIMemoryReporter, nsIObserver,
                    nsSupportsWeakReference)
 
 /* static */
 void
 nsWindowMemoryReporter::Init()
 {
   // The memory reporter manager will own this object.
   nsRefPtr<nsWindowMemoryReporter> windowReporter = new nsWindowMemoryReporter();
-  NS_RegisterMemoryMultiReporter(windowReporter);
+  NS_RegisterMemoryReporter(windowReporter);
 
   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
   if (os) {
     // DOM_WINDOW_DESTROYED_TOPIC announces what we call window "detachment",
     // when a window's docshell is set to NULL.
     os->AddObserver(windowReporter, DOM_WINDOW_DESTROYED_TOPIC,
                     /* weakRef = */ true);
     os->AddObserver(windowReporter, "after-minimize-memory-usage",
                     /* weakRef = */ true);
   }
 
-  nsRefPtr<GhostURLsReporter> ghostMultiReporter =
+  nsRefPtr<GhostURLsReporter> ghostURLsReporter =
     new GhostURLsReporter(windowReporter);
-  NS_RegisterMemoryMultiReporter(ghostMultiReporter);
+  NS_RegisterMemoryReporter(ghostURLsReporter);
 
-  nsRefPtr<NumGhostsReporter> ghostReporter =
+  nsRefPtr<NumGhostsReporter> numGhostsReporter =
     new NumGhostsReporter(windowReporter);
-  NS_RegisterMemoryReporter(ghostReporter);
+  NS_RegisterMemoryReporter(numGhostsReporter);
 }
 
 static already_AddRefed<nsIURI>
 GetWindowURI(nsIDOMWindow *aWindow)
 {
   nsCOMPtr<nsPIDOMWindow> pWindow = do_QueryInterface(aWindow);
   NS_ENSURE_TRUE(pWindow, nullptr);
 
@@ -116,17 +116,17 @@ typedef nsDataHashtable<nsUint64HashKey,
 
 static nsresult
 CollectWindowReports(nsGlobalWindow *aWindow,
                      amIAddonManager *addonManager,
                      nsWindowSizes *aWindowTotalSizes,
                      nsTHashtable<nsUint64HashKey> *aGhostWindowIDs,
                      WindowPaths *aWindowPaths,
                      WindowPaths *aTopWindowPaths,
-                     nsIMemoryMultiReporterCallback *aCb,
+                     nsIMemoryReporterCallback *aCb,
                      nsISupports *aClosure)
 {
   nsAutoCString windowPath("explicit/");
 
   // Avoid calling aWindow->GetTop() if there's no outer window.  It will work
   // just fine, but will spew a lot of warnings.
   nsGlobalWindow *top = NULL;
   nsCOMPtr<nsIURI> location;
@@ -314,17 +314,17 @@ GetWindows(const uint64_t& aId, nsGlobal
 NS_IMETHODIMP
 nsWindowMemoryReporter::GetName(nsACString &aName)
 {
   aName.AssignLiteral("window-objects");
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsWindowMemoryReporter::CollectReports(nsIMemoryMultiReporterCallback* aCb,
+nsWindowMemoryReporter::CollectReports(nsIMemoryReporterCallback* aCb,
                                        nsISupports* aClosure)
 {
   nsGlobalWindow::WindowByIdTable* windowsById =
     nsGlobalWindow::GetWindowsTable();
   NS_ENSURE_TRUE(windowsById, NS_OK);
 
   // Hold on to every window in memory so that window objects can't be
   // destroyed while we're calling the memory reporter callback.
@@ -347,19 +347,19 @@ nsWindowMemoryReporter::CollectReports(n
     nsresult rv = CollectWindowReports(windows[i], addonManager,
                                        &windowTotalSizes, &ghostWindows,
                                        &windowPaths, &topWindowPaths, aCb,
                                        aClosure);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // Report JS memory usage.  We do this from here because the JS memory
-  // multi-reporter needs to be passed |windowPaths|.
-  nsresult rv = xpc::JSMemoryMultiReporter::CollectReports(&windowPaths, &topWindowPaths,
-                                                           aCb, aClosure);
+  // reporter needs to be passed |windowPaths|.
+  nsresult rv = xpc::JSReporter::CollectReports(&windowPaths, &topWindowPaths,
+                                                aCb, aClosure);
   NS_ENSURE_SUCCESS(rv, rv);
 
 #define REPORT(_path, _amount, _desc)                                         \
   do {                                                                        \
     nsresult rv;                                                              \
     rv = aCb->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path),             \
                        nsIMemoryReporter::KIND_OTHER,                         \
                        nsIMemoryReporter::UNITS_BYTES, _amount,               \
@@ -658,36 +658,36 @@ nsWindowMemoryReporter::CheckForGhostWin
   CheckForGhostWindowsEnumeratorData ghostEnumData =
     { &nonDetachedWindowDomains, aOutGhostIDs, tldService,
       GetGhostTimeout(), TimeStamp::Now() };
   mDetachedWindows.Enumerate(CheckForGhostWindowsEnumerator,
                              &ghostEnumData);
 }
 
 NS_IMPL_ISUPPORTS1(nsWindowMemoryReporter::GhostURLsReporter,
-                   nsIMemoryMultiReporter)
+                   nsIMemoryReporter)
 
 nsWindowMemoryReporter::
 GhostURLsReporter::GhostURLsReporter(
   nsWindowMemoryReporter* aWindowReporter)
   : mWindowReporter(aWindowReporter)
 {
 }
 
 NS_IMETHODIMP
 nsWindowMemoryReporter::
 GhostURLsReporter::GetName(nsACString& aName)
 {
-  aName.AssignLiteral("ghost-windows");
+  aName.AssignLiteral("ghost-windows-multi");
   return NS_OK;
 }
 
 struct ReportGhostWindowsEnumeratorData
 {
-  nsIMemoryMultiReporterCallback* callback;
+  nsIMemoryReporterCallback* callback;
   nsISupports* closure;
   nsresult rv;
 };
 
 static PLDHashOperator
 ReportGhostWindowsEnumerator(nsUint64HashKey* aIDHashKey, void* aClosure)
 {
   ReportGhostWindowsEnumeratorData *data =
@@ -724,51 +724,32 @@ ReportGhostWindowsEnumerator(nsUint64Has
   }
 
   return PL_DHASH_NEXT;
 }
 
 NS_IMETHODIMP
 nsWindowMemoryReporter::
 GhostURLsReporter::CollectReports(
-  nsIMemoryMultiReporterCallback* aCb,
+  nsIMemoryReporterCallback* aCb,
   nsISupports* aClosure)
 {
   // Get the IDs of all the ghost windows in existance.
   nsTHashtable<nsUint64HashKey> ghostWindows;
   mWindowReporter->CheckForGhostWindows(&ghostWindows);
 
   ReportGhostWindowsEnumeratorData reportGhostWindowsEnumData =
     { aCb, aClosure, NS_OK };
 
   // Call aCb->Callback() for each ghost window.
   ghostWindows.EnumerateEntries(ReportGhostWindowsEnumerator,
                                 &reportGhostWindowsEnumData);
 
   return reportGhostWindowsEnumData.rv;
 }
 
-NS_IMETHODIMP
-nsWindowMemoryReporter::
-NumGhostsReporter::GetDescription(nsACString& aDesc)
-{
-  nsPrintfCString str(
-"The number of ghost windows present (the number of nodes underneath \
-explicit/window-objects/top(none)/ghost, modulo race conditions).  A ghost \
-window is not shown in any tab, does not share a domain with any non-detached \
-windows, and has met these criteria for at least %ds \
-(memory.ghost_window_timeout_seconds) or has survived a round of about:memory's \
-minimize memory usage button.\n\n\
-Ghost windows can happen legitimately, but they are often indicative of leaks \
-in the browser or add-ons.",
-  mWindowReporter->GetGhostTimeout());
-
-  aDesc.Assign(str);
-  return NS_OK;
-}
-
 int64_t
 nsWindowMemoryReporter::NumGhostsReporter::Amount()
 {
   nsTHashtable<nsUint64HashKey> ghostWindows;
   mWindowReporter->CheckForGhostWindows(&ghostWindows);
   return ghostWindows.Count();
 }
--- a/dom/base/nsWindowMemoryReporter.h
+++ b/dom/base/nsWindowMemoryReporter.h
@@ -103,61 +103,65 @@ public:
  *   the top level chrome window).  Exposing this ensures that each tab gets
  *   its own sub-tree, even if multiple tabs are showing the same URI.
  *
  * - <top-uri> is the URI of the top window.  Excepting special windows (such
  *   as browser.xul or hiddenWindow.html) it's what the address bar shows for
  *   the tab.
  *
  */
-class nsWindowMemoryReporter MOZ_FINAL : public nsIMemoryMultiReporter,
+class nsWindowMemoryReporter MOZ_FINAL : public nsIMemoryReporter,
                                          public nsIObserver,
                                          public nsSupportsWeakReference
 {
 public:
   NS_DECL_ISUPPORTS
-  NS_DECL_NSIMEMORYMULTIREPORTER
+  NS_DECL_NSIMEMORYREPORTER
   NS_DECL_NSIOBSERVER
 
   static void Init();
 
 private:
   /**
-   * GhostURLsReporter generates the "ghost-windows" multi-report, which
-   * includes a list of all ghost windows' URLs.  If you're only interested in
-   * this list, running this report is faster than running
-   * nsWindowMemoryReporter.
+   * GhostURLsReporter generates the list of all ghost windows' URLs.  If
+   * you're only interested in this list, running this report is faster than
+   * running nsWindowMemoryReporter.
    */
-  class GhostURLsReporter MOZ_FINAL : public nsIMemoryMultiReporter
+  class GhostURLsReporter MOZ_FINAL : public nsIMemoryReporter
   {
   public:
     GhostURLsReporter(nsWindowMemoryReporter* aWindowReporter);
 
     NS_DECL_ISUPPORTS
-    NS_DECL_NSIMEMORYMULTIREPORTER
+    NS_DECL_NSIMEMORYREPORTER
 
   private:
     nsRefPtr<nsWindowMemoryReporter> mWindowReporter;
   };
 
   /**
-   * nsGhostWindowReporter generates the "ghost-windows" single-report, which
-   * counts the number of ghost windows present.
+   * nsGhostWindowReporter generates the "ghost-windows" report, which counts
+   * the number of ghost windows present.
    */
-  class NumGhostsReporter MOZ_FINAL : public mozilla::MemoryReporterBase
+  class NumGhostsReporter MOZ_FINAL : public mozilla::MemoryUniReporter
   {
   public:
     NumGhostsReporter(nsWindowMemoryReporter* aWindowReporter)
-        // Description is "???" because we define GetDescription below.
-      : MemoryReporterBase("ghost-windows", KIND_OTHER, UNITS_COUNT, "???")
+      : MemoryUniReporter("ghost-windows", KIND_OTHER, UNITS_COUNT,
+"The number of ghost windows present (the number of nodes underneath "
+"explicit/window-objects/top(none)/ghost, modulo race conditions).  A ghost "
+"window is not shown in any tab, does not share a domain with any non-detached "
+"windows, and has met these criteria for at least "
+"memory.ghost_window_timeout_seconds, or has survived a round of "
+"about:memory's minimize memory usage button.\n\n"
+"Ghost windows can happen legitimately, but they are often indicative of "
+"leaks in the browser or add-ons.")
       , mWindowReporter(aWindowReporter)
     {}
 
-    NS_IMETHOD GetDescription(nsACString& aDesc);
-
   private:
     int64_t Amount() MOZ_OVERRIDE;
 
     nsRefPtr<nsWindowMemoryReporter> mWindowReporter;
   };
 
   // Protect ctor, use Init() instead.
   nsWindowMemoryReporter();
--- a/dom/base/test/test_domrequesthelper.xul
+++ b/dom/base/test/test_domrequesthelper.xul
@@ -25,42 +25,43 @@
       __proto__: DOMRequestIpcHelper.prototype
     };
 
     var dummy = new DummyHelperSubclass();
 
     function createPromise() {
       ok(Promise, "Promise object should exist");
 
-      var promise = dummy.createPromise(function(r) {
-        ok(r, "received PromiseResolver");
-        r.resolve(true);
+      var promise = dummy.createPromise(function(resolve, reject) {
+        resolve(true);
       });
       ok(promise instanceof Promise, "returned a Promise");
       promise.then(runTest);
     }
 
     function getResolver() {
       var id;
       var resolver;
-      var promise = dummy.createPromise(function(r) {
+      var promise = dummy.createPromise(function(resolve, reject) {
+        var r = { resolve: resolve, reject: reject };
         id = dummy.getPromiseResolverId(r);
         resolver = r;
         ok(typeof id === "string", "id should be string");
         r.resolve(true);
       }).then(function(unused) {
         var r = dummy.getPromiseResolver(id);
         ok(resolver === r, "get should succeed");
         runTest();
       });
     }
 
     function removeResolver() {
       var id;
-      var promise = dummy.createPromise(function(r) {
+      var promise = dummy.createPromise(function(resolve, reject) {
+        var r = { resolve: resolve, reject: reject };
         id = dummy.getPromiseResolverId(r);
         ok(typeof id === "string", "id should be string");
 
         var resolver = dummy.getPromiseResolver(id);
         ok(resolver === r, "resolver get should succeed");
 
         r.resolve(true);
       }).then(function(unused) {
@@ -69,17 +70,18 @@
         ok(resolver === undefined, "removeResolver: get should fail");
         runTest();
       });
     }
 
     function takeResolver() {
       var id;
       var resolver;
-      var promise = dummy.createPromise(function(r) {
+      var promise = dummy.createPromise(function(resolve, reject) {
+        var r = { resolve: resolve, reject: reject };
         id = dummy.getPromiseResolverId(r);
         resolver = r;
         ok(typeof id === "string", "id should be string");
 
         var gotR = dummy.getPromiseResolver(id);
         ok(gotR === r, "resolver get should succeed");
 
         r.resolve(true);
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -323,17 +323,17 @@ InterfaceObjectToString(JSContext* cx, u
   if (!obj) {
     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_CONVERT_TO,
                          "null", "object");
     return false;
   }
 
   JS::Value v = js::GetFunctionNativeReserved(callee,
                                               TOSTRING_CLASS_RESERVED_SLOT);
-  JSClass* clasp = static_cast<JSClass*>(JSVAL_TO_PRIVATE(v));
+  const JSClass* clasp = static_cast<const JSClass*>(JSVAL_TO_PRIVATE(v));
 
   v = js::GetFunctionNativeReserved(callee, TOSTRING_NAME_RESERVED_SLOT);
   JSString* jsname = static_cast<JSString*>(JSVAL_TO_STRING(v));
   size_t length;
   const jschar* name = JS_GetInternedStringCharsAndLength(jsname, &length);
 
   if (js::GetObjectJSClass(obj) != clasp) {
     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO,
@@ -397,17 +397,17 @@ DefineConstructor(JSContext* cx, JS::Han
   return alreadyDefined ||
          JS_DefineProperty(cx, global, name, OBJECT_TO_JSVAL(constructor),
                            nullptr, nullptr, 0);
 }
 
 static JSObject*
 CreateInterfaceObject(JSContext* cx, JS::Handle<JSObject*> global,
                       JS::Handle<JSObject*> constructorProto,
-                      JSClass* constructorClass,
+                      const JSClass* constructorClass,
                       const JSNativeHolder* constructorNative,
                       unsigned ctorNargs, const NamedConstructor* namedConstructors,
                       JS::Handle<JSObject*> proto,
                       const NativeProperties* properties,
                       const NativeProperties* chromeOnlyProperties,
                       const char* name, bool defineOnGlobal)
 {
   JS::Rooted<JSObject*> constructor(cx);
@@ -437,17 +437,17 @@ CreateInterfaceObject(JSContext* cx, JS:
     }
 
     JSString *str = ::JS_InternString(cx, name);
     if (!str) {
       return NULL;
     }
     JSObject* toStringObj = JS_GetFunctionObject(toString);
     js::SetFunctionNativeReserved(toStringObj, TOSTRING_CLASS_RESERVED_SLOT,
-                                  PRIVATE_TO_JSVAL(constructorClass));
+                                  PRIVATE_TO_JSVAL(const_cast<JSClass *>(constructorClass)));
 
     js::SetFunctionNativeReserved(toStringObj, TOSTRING_NAME_RESERVED_SLOT,
                                   STRING_TO_JSVAL(str));
 
     if (!JS_DefineProperty(cx, constructor, "length", JS::Int32Value(ctorNargs),
                            nullptr, nullptr, JSPROP_READONLY | JSPROP_PERMANENT)) {
       return NULL;
     }
@@ -538,17 +538,17 @@ DefineWebIDLBindingPropertiesOnXPCProto(
   }
 
   return true;
 }
 
 static JSObject*
 CreateInterfacePrototypeObject(JSContext* cx, JS::Handle<JSObject*> global,
                                JS::Handle<JSObject*> parentProto,
-                               JSClass* protoClass,
+                               const JSClass* protoClass,
                                const NativeProperties* properties,
                                const NativeProperties* chromeOnlyProperties)
 {
   JS::Rooted<JSObject*> ourProto(cx,
     JS_NewObjectWithUniqueType(cx, protoClass, parentProto, global));
   if (!ourProto) {
     return NULL;
   }
@@ -588,19 +588,19 @@ CreateInterfacePrototypeObject(JSContext
   }
 
   return ourProto;
 }
 
 void
 CreateInterfaceObjects(JSContext* cx, JS::Handle<JSObject*> global,
                        JS::Handle<JSObject*> protoProto,
-                       JSClass* protoClass, JS::Heap<JSObject*>* protoCache,
+                       const JSClass* protoClass, JS::Heap<JSObject*>* protoCache,
                        JS::Handle<JSObject*> constructorProto,
-                       JSClass* constructorClass, const JSNativeHolder* constructor,
+                       const JSClass* constructorClass, const JSNativeHolder* constructor,
                        unsigned ctorNargs, const NamedConstructor* namedConstructors,
                        JS::Heap<JSObject*>* constructorCache, const DOMClass* domClass,
                        const NativeProperties* properties,
                        const NativeProperties* chromeOnlyProperties,
                        const char* name, bool defineOnGlobal)
 {
   MOZ_ASSERT(protoClass || constructorClass || constructor,
              "Need at least one class or a constructor!");
@@ -730,17 +730,17 @@ TryPreserveWrapper(JSObject* obj)
 }
 
 // Can only be called with the immediate prototype of the instance object. Can
 // only be called on the prototype of an object known to be a DOM instance.
 bool
 InstanceClassHasProtoAtDepth(JS::Handle<JSObject*> protoObject, uint32_t protoID,
                              uint32_t depth)
 {
-  const DOMClass* domClass = static_cast<DOMClass*>(
+  const DOMClass* domClass = static_cast<const DOMClass*>(
     js::GetReservedSlot(protoObject, DOM_PROTO_INSTANCE_CLASS_SLOT).toPrivate());
   return (uint32_t)domClass->mInterfaceChain[depth] == protoID;
 }
 
 // Only set allowNativeWrapper to false if you really know you need it, if in
 // doubt use true. Setting it to false disables security wrappers.
 bool
 XPCOMObjectToJsval(JSContext* cx, JS::Handle<JSObject*> scope,
@@ -1435,17 +1435,17 @@ AppendNamedPropertyIds(JSContext* cx, JS
   }
 
   return true;
 }
 
 JSObject*
 GetXrayExpandoChain(JSObject* obj)
 {
-  js::Class* clasp = js::GetObjectClass(obj);
+  const js::Class* clasp = js::GetObjectClass(obj);
   JS::Value v;
   if (IsDOMClass(clasp) || IsDOMIfaceAndProtoClass(clasp)) {
     v = js::GetReservedSlot(obj, DOM_XRAY_EXPANDO_SLOT);
   } else if (js::IsProxyClass(clasp)) {
     MOZ_ASSERT(js::GetProxyHandler(obj)->family() == ProxyFamily());
     v = js::GetProxyExtra(obj, JSPROXYSLOT_XRAY_EXPANDO);
   } else {
     MOZ_ASSERT(JS_IsNativeFunction(obj, Constructor));
@@ -1453,17 +1453,17 @@ GetXrayExpandoChain(JSObject* obj)
   }
   return v.isUndefined() ? nullptr : &v.toObject();
 }
 
 void
 SetXrayExpandoChain(JSObject* obj, JSObject* chain)
 {
   JS::Value v = chain ? JS::ObjectValue(*chain) : JSVAL_VOID;
-  js::Class* clasp = js::GetObjectClass(obj);
+  const js::Class* clasp = js::GetObjectClass(obj);
   if (IsDOMClass(clasp) || IsDOMIfaceAndProtoClass(clasp)) {
     js::SetReservedSlot(obj, DOM_XRAY_EXPANDO_SLOT, v);
   } else if (js::IsProxyClass(clasp)) {
     MOZ_ASSERT(js::GetProxyHandler(obj)->family() == ProxyFamily());
     js::SetProxyExtra(obj, JSPROXYSLOT_XRAY_EXPANDO, v);
   } else {
     MOZ_ASSERT(JS_IsNativeFunction(obj, Constructor));
     js::SetFunctionNativeReserved(obj, CONSTRUCTOR_XRAY_EXPANDO_SLOT, v);
@@ -1537,17 +1537,17 @@ NativeToString(JSContext* cx, JS::Handle
         str = toStringResult.toString();
       } else {
         str = nullptr;
       }
     } else {
       if (IsDOMProxy(obj)) {
         str = JS_BasicObjectToString(cx, obj);
       } else {
-        js::Class* clasp = js::GetObjectClass(obj);
+        const js::Class* clasp = js::GetObjectClass(obj);
         if (IsDOMClass(clasp)) {
           str = ConcatJSString(cx, "[object ",
                                JS_NewStringCopyZ(cx, clasp->name), "]");
         } else if (IsDOMIfaceAndProtoClass(clasp)) {
           const DOMIfaceAndProtoJSClass* ifaceAndProtoJSClass =
             DOMIfaceAndProtoJSClass::FromJSClass(clasp);
           str = JS_NewStringCopyZ(cx, ifaceAndProtoJSClass->mToString);
         } else {
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -133,17 +133,17 @@ UnwrapDOMObject(JSObject* obj)
 
   JS::Value val = js::GetReservedSlot(obj, DOM_OBJECT_SLOT);
   return static_cast<T*>(val.toPrivate());
 }
 
 inline const DOMClass*
 GetDOMClass(JSObject* obj)
 {
-  js::Class* clasp = js::GetObjectClass(obj);
+  const js::Class* clasp = js::GetObjectClass(obj);
   if (IsDOMClass(clasp)) {
     return &DOMJSClass::FromJSClass(clasp)->mClass;
   }
 
   if (js::IsProxyClass(clasp)) {
     js::BaseProxyHandler* handler = js::GetProxyHandler(obj);
     if (handler->family() == ProxyFamily()) {
       return &static_cast<DOMProxyHandler*>(handler)->mClass;
@@ -162,17 +162,17 @@ UnwrapDOMObjectToISupports(JSObject* aOb
   }
  
   return UnwrapDOMObject<nsISupports>(aObject);
 }
 
 inline bool
 IsDOMObject(JSObject* obj)
 {
-  js::Class* clasp = js::GetObjectClass(obj);
+  const js::Class* clasp = js::GetObjectClass(obj);
   return IsDOMClass(clasp) || IsDOMProxy(obj, clasp);
 }
 
 #define UNWRAP_OBJECT(Interface, cx, obj, value)                             \
   mozilla::dom::UnwrapObject<mozilla::dom::prototypes::id::Interface,        \
     mozilla::dom::Interface##Binding::NativeType>(cx, obj, value)
 
 // Some callers don't want to set an exception when unwrapping fails
@@ -359,19 +359,19 @@ struct NamedConstructor
  * At least one of protoClass, constructorClass or constructor should be
  * non-null. If constructorClass or constructor are non-null, the resulting
  * interface object will be defined on the given global with property name
  * |name|, which must also be non-null.
  */
 void
 CreateInterfaceObjects(JSContext* cx, JS::Handle<JSObject*> global,
                        JS::Handle<JSObject*> protoProto,
-                       JSClass* protoClass, JS::Heap<JSObject*>* protoCache,
+                       const JSClass* protoClass, JS::Heap<JSObject*>* protoCache,
                        JS::Handle<JSObject*> interfaceProto,
-                       JSClass* constructorClass, const JSNativeHolder* constructor,
+                       const JSClass* constructorClass, const JSNativeHolder* constructor,
                        unsigned ctorNargs, const NamedConstructor* namedConstructors,
                        JS::Heap<JSObject*>* constructorCache, const DOMClass* domClass,
                        const NativeProperties* regularProperties,
                        const NativeProperties* chromeOnlyProperties,
                        const char* name, bool defineOnGlobal);
 
 /*
  * Define the unforgeable attributes on an object.
@@ -478,17 +478,17 @@ SetSystemOnlyWrapperSlot(JSObject* obj, 
   MOZ_ASSERT(IsDOMClass(js::GetObjectJSClass(obj)) &&
              !(js::GetObjectJSClass(obj)->flags & JSCLASS_DOM_GLOBAL));
   js::SetReservedSlot(obj, DOM_OBJECT_SLOT_SOW, v);
 }
 
 inline bool
 GetSameCompartmentWrapperForDOMBinding(JSObject*& obj)
 {
-  js::Class* clasp = js::GetObjectClass(obj);
+  const js::Class* clasp = js::GetObjectClass(obj);
   if (dom::IsDOMClass(clasp)) {
     if (!(clasp->flags & JSCLASS_DOM_GLOBAL)) {
       JS::Value v = GetSystemOnlyWrapperSlot(obj);
       if (v.isObject()) {
         obj = &v.toObject();
       }
     }
     return true;
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -184,17 +184,17 @@ class CGDOMJSClass(CGThing):
         if self.descriptor.interface.getExtendedAttribute("NeedNewResolve"):
             newResolveHook = "(JSResolveOp)" + NEWRESOLVE_HOOK_NAME
             classFlags += " | JSCLASS_NEW_RESOLVE"
             enumerateHook = ENUMERATE_HOOK_NAME
         else:
             newResolveHook = "JS_ResolveStub"
             enumerateHook = "JS_EnumerateStub"
         return """
-static DOMJSClass Class = {
+static const DOMJSClass Class = {
   { "%s",
     %s,
     %s, /* addProperty */
     JS_DeletePropertyStub, /* delProperty */
     JS_PropertyStub,       /* getProperty */
     JS_StrictPropertyStub, /* setProperty */
     %s, /* enumerate */
     %s, /* resolve */
@@ -1006,17 +1006,17 @@ class CGAbstractClassHook(CGAbstractStat
         return self.definition_body_prologue() + self.generate_code()
 
     def generate_code(self):
         # Override me
         assert(False)
 
 class CGGetJSClassMethod(CGAbstractMethod):
     def __init__(self, descriptor):
-        CGAbstractMethod.__init__(self, descriptor, 'GetJSClass', 'JSClass*',
+        CGAbstractMethod.__init__(self, descriptor, 'GetJSClass', 'const JSClass*',
                                   [])
 
     def definition_body(self):
         return "  return Class.ToJSClass();"
 
 class CGAddPropertyHook(CGAbstractClassHook):
     """
     A hook for addProperty, used to preserve our wrapper from GC.
--- a/dom/bindings/DOMJSClass.h
+++ b/dom/bindings/DOMJSClass.h
@@ -163,47 +163,40 @@ struct DOMClass
 };
 
 // Special JSClass for reflected DOM objects.
 struct DOMJSClass
 {
   // It would be nice to just inherit from JSClass, but that precludes pure
   // compile-time initialization of the form |DOMJSClass = {...};|, since C++
   // only allows brace initialization for aggregate/POD types.
-  JSClass mBase;
-
-  DOMClass mClass;
+  const JSClass mBase;
 
-  static DOMJSClass* FromJSClass(JSClass* base) {
-    MOZ_ASSERT(base->flags & JSCLASS_IS_DOMJSCLASS);
-    return reinterpret_cast<DOMJSClass*>(base);
-  }
+  const DOMClass mClass;
+
   static const DOMJSClass* FromJSClass(const JSClass* base) {
     MOZ_ASSERT(base->flags & JSCLASS_IS_DOMJSCLASS);
     return reinterpret_cast<const DOMJSClass*>(base);
   }
 
-  static DOMJSClass* FromJSClass(js::Class* base) {
-    return FromJSClass(Jsvalify(base));
-  }
   static const DOMJSClass* FromJSClass(const js::Class* base) {
     return FromJSClass(Jsvalify(base));
   }
 
-  JSClass* ToJSClass() { return &mBase; }
+  const JSClass* ToJSClass() const { return &mBase; }
 };
 
 // Special JSClass for DOM interface and interface prototype objects.
 struct DOMIfaceAndProtoJSClass
 {
   // It would be nice to just inherit from JSClass, but that precludes pure
   // compile-time initialization of the form
   // |DOMJSInterfaceAndPrototypeClass = {...};|, since C++ only allows brace
   // initialization for aggregate/POD types.
-  JSClass mBase;
+  const JSClass mBase;
 
   // Either eInterface or eInterfacePrototype
   DOMObjectType mType;
 
   const NativePropertyHooks* mNativeHooks;
 
   // The value to return for toString() on this interface or interface prototype
   // object.
@@ -215,17 +208,17 @@ struct DOMIfaceAndProtoJSClass
   static const DOMIfaceAndProtoJSClass* FromJSClass(const JSClass* base) {
     MOZ_ASSERT(base->flags & JSCLASS_IS_DOMIFACEANDPROTOJSCLASS);
     return reinterpret_cast<const DOMIfaceAndProtoJSClass*>(base);
   }
   static const DOMIfaceAndProtoJSClass* FromJSClass(const js::Class* base) {
     return FromJSClass(Jsvalify(base));
   }
 
-  JSClass* ToJSClass() { return &mBase; }
+  const JSClass* ToJSClass() const { return &mBase; }
 };
 
 inline bool
 HasProtoAndIfaceArray(JSObject* global)
 {
   MOZ_ASSERT(js::GetObjectClass(global)->flags & JSCLASS_DOM_GLOBAL);
   // This can be undefined if we GC while creating the global
   return !js::GetReservedSlot(global, DOM_PROTOTYPE_SLOT).isUndefined();
--- a/dom/bindings/Nullable.h
+++ b/dom/bindings/Nullable.h
@@ -59,16 +59,38 @@ public:
     MOZ_ASSERT(!mIsNull);
     return mValue;
   }
 
   bool IsNull() const {
     return mIsNull;
   }
 
+  bool Equals(const Nullable<T>& aOtherNullable) const
+  {
+    return (mIsNull && aOtherNullable.mIsNull) ||
+           (!mIsNull && !aOtherNullable.mIsNull &&
+            mValue == aOtherNullable.mValue);
+  }
+
+  bool operator==(const Nullable<T>& aOtherNullable) const
+  {
+    return Equals(aOtherNullable);
+  }
+
+  bool operator!=(const Nullable<T>& aOtherNullable) const
+  {
+    return !Equals(aOtherNullable);
+  }
+
+  operator bool() const
+  {
+    return !mIsNull;
+  }
+
   // Make it possible to use a const Nullable of an array type with other
   // array types.
   template<typename U>
   operator const Nullable< nsTArray<U> >&() const {
     // Make sure that T is ok to reinterpret to nsTArray<U>
     const nsTArray<U>& arr = mValue;
     (void)arr;
     return *reinterpret_cast<const Nullable< nsTArray<U> >*>(this);
--- a/dom/indexedDB/CheckPermissionsHelper.cpp
+++ b/dom/indexedDB/CheckPermissionsHelper.cpp
@@ -14,17 +14,16 @@
 #include "nsIPrincipal.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIURI.h"
 
 #include "CheckQuotaHelper.h"
 #include "nsContentUtils.h"
 #include "nsNetUtil.h"
 #include "nsThreadUtils.h"
-#include "mozilla/dom/quota/QuotaManager.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 
 #include "IndexedDatabaseManager.h"
 
 #define PERMISSION_INDEXEDDB "indexedDB"
 #define PREF_INDEXEDDB_ENABLED "dom.indexedDB.enabled"
 #define TOPIC_PERMISSIONS_PROMPT "indexedDB-permissions-prompt"
@@ -160,20 +159,17 @@ CheckPermissionsHelper::Run()
       uint32_t quotaPermission =
         CheckQuotaHelper::GetQuotaPermission(windowPrincipal);
 
       if (quotaPermission == nsIPermissionManager::ALLOW_ACTION) {
         helper->SetUnlimitedQuotaAllowed();
       }
     }
 
-    quota::QuotaManager* quotaManager = quota::QuotaManager::Get();
-    NS_ASSERTION(quotaManager, "This should never be null!");
-
-    return helper->Dispatch(quotaManager->IOThread());
+    return helper->DispatchToIOThread();
   }
 
   NS_ASSERTION(permission == PERMISSION_PROMPT ||
                permission == PERMISSION_DENIED,
                "Unknown permission!");
 
   helper->SetError(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
 
--- a/dom/indexedDB/CheckPermissionsHelper.h
+++ b/dom/indexedDB/CheckPermissionsHelper.h
@@ -25,27 +25,28 @@ class CheckPermissionsHelper MOZ_FINAL :
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIRUNNABLE
   NS_DECL_NSIINTERFACEREQUESTOR
   NS_DECL_NSIOBSERVER
 
   CheckPermissionsHelper(OpenDatabaseHelper* aHelper,
-                         nsIDOMWindow* aWindow,
-                         bool aForDeletion)
+                         nsIDOMWindow* aWindow)
   : mHelper(aHelper),
     mWindow(aWindow),
     // If we're trying to delete the database, we should never prompt the user.
     // Anything that would prompt is translated to denied.
-    mPromptAllowed(!aForDeletion),
+    mPromptAllowed(!aHelper->mForDeletion),
     mHasPrompted(false),
     mPromptResult(0)
   {
     NS_ASSERTION(aHelper, "Null pointer!");
+    NS_ASSERTION(aHelper->mPersistenceType == quota::PERSISTENCE_TYPE_PERSISTENT,
+                 "Checking permission for non persistent databases?!");
   }
 
 private:
   nsRefPtr<OpenDatabaseHelper> mHelper;
   nsCOMPtr<nsIDOMWindow> mWindow;
   bool mPromptAllowed;
   bool mHasPrompted;
   uint32_t mPromptResult;
--- a/dom/indexedDB/Client.cpp
+++ b/dom/indexedDB/Client.cpp
@@ -2,17 +2,17 @@
 /* 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 "Client.h"
 
 #include "mozilla/dom/quota/QuotaManager.h"
-#include "mozilla/dom/quota/UsageRunnable.h"
+#include "mozilla/dom/quota/UsageInfo.h"
 #include "mozilla/dom/quota/Utilities.h"
 
 #include "IDBDatabase.h"
 #include "IndexedDatabaseManager.h"
 #include "TransactionThreadPool.h"
 
 USING_INDEXEDDB_NAMESPACE
 using mozilla::dom::quota::AssertIsOnIOThread;
@@ -40,59 +40,59 @@ GetDatabaseBaseFilename(const nsAString&
 
 } // anonymous namespace
 
 // This needs to be fully qualified to not confuse trace refcnt assertions.
 NS_IMPL_ADDREF(mozilla::dom::indexedDB::Client)
 NS_IMPL_RELEASE(mozilla::dom::indexedDB::Client)
 
 nsresult
-Client::InitOrigin(const nsACString& aOrigin, UsageRunnable* aUsageRunnable)
+Client::InitOrigin(PersistenceType aPersistenceType, const nsACString& aGroup,
+                   const nsACString& aOrigin, UsageInfo* aUsageInfo)
 {
   AssertIsOnIOThread();
 
   nsCOMPtr<nsIFile> directory;
-  nsresult rv = GetDirectory(aOrigin, getter_AddRefs(directory));
+  nsresult rv =
+    GetDirectory(aPersistenceType, aOrigin, getter_AddRefs(directory));
   NS_ENSURE_SUCCESS(rv, rv);
 
   // We need to see if there are any files in the directory already. If they
   // are database files then we need to cleanup stored files (if it's needed)
   // and also get the usage.
 
   nsAutoTArray<nsString, 20> subdirsToProcess;
   nsAutoTArray<nsCOMPtr<nsIFile> , 20> unknownFiles;
   nsTHashtable<nsStringHashKey> validSubdirs(20);
 
   nsCOMPtr<nsISimpleEnumerator> entries;
   rv = directory->GetDirectoryEntries(getter_AddRefs(entries));
   NS_ENSURE_SUCCESS(rv, rv);
 
   bool hasMore;
   while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) &&
-         hasMore && (!aUsageRunnable || !aUsageRunnable->Canceled())) {
+         hasMore && (!aUsageInfo || !aUsageInfo->Canceled())) {
     nsCOMPtr<nsISupports> entry;
     rv = entries->GetNext(getter_AddRefs(entry));
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
     NS_ENSURE_TRUE(file, NS_NOINTERFACE);
 
     nsString leafName;
     rv = file->GetLeafName(leafName);
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (StringEndsWith(leafName, NS_LITERAL_STRING(".sqlite-journal"))) {
       continue;
     }
 
-#ifdef XP_MACOSX
     if (leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
       continue;
     }
-#endif
 
     bool isDirectory;
     rv = file->IsDirectory(&isDirectory);
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (isDirectory) {
       if (!validSubdirs.GetEntry(leafName)) {
         subdirsToProcess.AppendElement(leafName);
@@ -108,31 +108,34 @@ Client::InitOrigin(const nsACString& aOr
 
     nsCOMPtr<nsIFile> fmDirectory;
     rv = directory->Clone(getter_AddRefs(fmDirectory));
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = fmDirectory->Append(dbBaseFilename);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    rv = FileManager::InitDirectory(fmDirectory, file, aOrigin);
+    rv = FileManager::InitDirectory(fmDirectory, file, aPersistenceType, aGroup,
+                                    aOrigin);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    if (aUsageRunnable) {
+    if (aUsageInfo) {
       int64_t fileSize;
       rv = file->GetFileSize(&fileSize);
       NS_ENSURE_SUCCESS(rv, rv);
 
-      aUsageRunnable->AppendToDatabaseUsage(uint64_t(fileSize));
+      NS_ASSERTION(fileSize >= 0, "Negative size?!");
+
+      aUsageInfo->AppendToDatabaseUsage(uint64_t(fileSize));
 
       uint64_t usage;
       rv = FileManager::GetUsage(fmDirectory, &usage);
       NS_ENSURE_SUCCESS(rv, rv);
 
-      aUsageRunnable->AppendToFileUsage(usage);
+      aUsageInfo->AppendToFileUsage(usage);
     }
 
     validSubdirs.PutEntry(dbBaseFilename);
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
   for (uint32_t i = 0; i < subdirsToProcess.Length(); i++) {
     const nsString& subdir = subdirsToProcess[i];
@@ -162,40 +165,43 @@ Client::InitOrigin(const nsACString& aOr
       }
     }
   }
 
   return NS_OK;
 }
 
 nsresult
-Client::GetUsageForOrigin(const nsACString& aOrigin,
-                          UsageRunnable* aUsageRunnable)
+Client::GetUsageForOrigin(PersistenceType aPersistenceType,
+                          const nsACString& aGroup, const nsACString& aOrigin,
+                          UsageInfo* aUsageInfo)
 {
   AssertIsOnIOThread();
-  NS_ASSERTION(aUsageRunnable, "Null pointer!");
+  NS_ASSERTION(aUsageInfo, "Null pointer!");
 
   nsCOMPtr<nsIFile> directory;
-  nsresult rv = GetDirectory(aOrigin, getter_AddRefs(directory));
+  nsresult rv =
+    GetDirectory(aPersistenceType, aOrigin, getter_AddRefs(directory));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = GetUsageForDirectoryInternal(directory, aUsageRunnable, true);
+  rv = GetUsageForDirectoryInternal(directory, aUsageInfo, true);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 void
-Client::OnOriginClearCompleted(const nsACString& aPattern)
+Client::OnOriginClearCompleted(PersistenceType aPersistenceType,
+                               const OriginOrPatternString& aOriginOrPattern)
 {
   AssertIsOnIOThread();
 
   IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
   if (mgr) {
-    mgr->InvalidateFileManagersForPattern(aPattern);
+    mgr->InvalidateFileManagers(aPersistenceType, aOriginOrPattern);
   }
 }
 
 void
 Client::ReleaseIOThreadObjects()
 {
   AssertIsOnIOThread();
 
@@ -270,68 +276,69 @@ void
 Client::ShutdownTransactionService()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   TransactionThreadPool::Shutdown();
 }
 
 nsresult
-Client::GetDirectory(const nsACString& aOrigin, nsIFile** aDirectory)
+Client::GetDirectory(PersistenceType aPersistenceType,
+                     const nsACString& aOrigin, nsIFile** aDirectory)
 {
   QuotaManager* quotaManager = QuotaManager::Get();
   NS_ASSERTION(quotaManager, "This should never fail!");
 
   nsCOMPtr<nsIFile> directory;
-  nsresult rv =
-    quotaManager->GetDirectoryForOrigin(aOrigin, getter_AddRefs(directory));
+  nsresult rv = quotaManager->GetDirectoryForOrigin(aPersistenceType, aOrigin,
+                                                    getter_AddRefs(directory));
   NS_ENSURE_SUCCESS(rv, rv);
 
   NS_ASSERTION(directory, "What?");
 
   rv = directory->Append(NS_LITERAL_STRING(IDB_DIRECTORY_NAME));
   NS_ENSURE_SUCCESS(rv, rv);
 
   directory.forget(aDirectory);
   return NS_OK;
 }
 
 nsresult
 Client::GetUsageForDirectoryInternal(nsIFile* aDirectory,
-                                     UsageRunnable* aUsageRunnable,
+                                     UsageInfo* aUsageInfo,
                                      bool aDatabaseFiles)
 {
   NS_ASSERTION(aDirectory, "Null pointer!");
-  NS_ASSERTION(aUsageRunnable, "Null pointer!");
+  NS_ASSERTION(aUsageInfo, "Null pointer!");
 
   nsCOMPtr<nsISimpleEnumerator> entries;
   nsresult rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (!entries) {
     return NS_OK;
   }
 
   bool hasMore;
   while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) &&
-         hasMore && !aUsageRunnable->Canceled()) {
+         hasMore && !aUsageInfo->Canceled()) {
     nsCOMPtr<nsISupports> entry;
     rv = entries->GetNext(getter_AddRefs(entry));
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsIFile> file(do_QueryInterface(entry));
     NS_ASSERTION(file, "Don't know what this is!");
 
     bool isDirectory;
     rv = file->IsDirectory(&isDirectory);
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (isDirectory) {
       if (aDatabaseFiles) {
-        rv = GetUsageForDirectoryInternal(file, aUsageRunnable, false);
+        rv = GetUsageForDirectoryInternal(file, aUsageInfo, false);
         NS_ENSURE_SUCCESS(rv, rv);
       }
       else {
         nsString leafName;
         rv = file->GetLeafName(leafName);
         NS_ENSURE_SUCCESS(rv, rv);
 
         if (!leafName.EqualsLiteral(JOURNAL_DIRECTORY_NAME)) {
@@ -344,18 +351,18 @@ Client::GetUsageForDirectoryInternal(nsI
 
     int64_t fileSize;
     rv = file->GetFileSize(&fileSize);
     NS_ENSURE_SUCCESS(rv, rv);
 
     NS_ASSERTION(fileSize >= 0, "Negative size?!");
 
     if (aDatabaseFiles) {
-      aUsageRunnable->AppendToDatabaseUsage(uint64_t(fileSize));
+      aUsageInfo->AppendToDatabaseUsage(uint64_t(fileSize));
     }
     else {
-      aUsageRunnable->AppendToFileUsage(uint64_t(fileSize));
+      aUsageInfo->AppendToFileUsage(uint64_t(fileSize));
     }
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
--- a/dom/indexedDB/Client.h
+++ b/dom/indexedDB/Client.h
@@ -13,41 +13,49 @@
 
 #define IDB_DIRECTORY_NAME "idb"
 #define JOURNAL_DIRECTORY_NAME "journals"
 
 BEGIN_INDEXEDDB_NAMESPACE
 
 class Client : public mozilla::dom::quota::Client
 {
-  typedef mozilla::dom::quota::UsageRunnable UsageRunnable;
+  typedef mozilla::dom::quota::OriginOrPatternString OriginOrPatternString;
+  typedef mozilla::dom::quota::PersistenceType PersistenceType;
+  typedef mozilla::dom::quota::UsageInfo UsageInfo;
 
 public:
   NS_IMETHOD_(nsrefcnt)
   AddRef() MOZ_OVERRIDE;
 
   NS_IMETHOD_(nsrefcnt)
   Release() MOZ_OVERRIDE;
 
   virtual Type
   GetType() MOZ_OVERRIDE
   {
     return IDB;
   }
 
   virtual nsresult
-  InitOrigin(const nsACString& aOrigin,
-             UsageRunnable* aUsageRunnable) MOZ_OVERRIDE;
+  InitOrigin(PersistenceType aPersistenceType,
+             const nsACString& aGroup,
+             const nsACString& aOrigin,
+             UsageInfo* aUsageInfo) MOZ_OVERRIDE;
 
   virtual nsresult
-  GetUsageForOrigin(const nsACString& aOrigin,
-                    UsageRunnable* aUsageRunnable) MOZ_OVERRIDE;
+  GetUsageForOrigin(PersistenceType aPersistenceType,
+                    const nsACString& aGroup,
+                    const nsACString& aOrigin,
+                    UsageInfo* aUsageInfo) MOZ_OVERRIDE;
 
   virtual void
-  OnOriginClearCompleted(const nsACString& aPattern) MOZ_OVERRIDE;
+  OnOriginClearCompleted(PersistenceType aPersistenceType,
+                         const OriginOrPatternString& aOriginOrPattern)
+                         MOZ_OVERRIDE;
 
   virtual void
   ReleaseIOThreadObjects() MOZ_OVERRIDE;
 
   virtual bool
   IsFileServiceUtilized() MOZ_OVERRIDE
   {
     return true;
@@ -66,21 +74,22 @@ public:
   virtual bool
   HasTransactionsForStorage(nsIOfflineStorage* aStorage) MOZ_OVERRIDE;
 
   virtual void
   ShutdownTransactionService() MOZ_OVERRIDE;
 
 private:
   nsresult
-  GetDirectory(const nsACString& aOrigin, nsIFile** aDirectory);
+  GetDirectory(PersistenceType aPersistenceType, const nsACString& aOrigin,
+               nsIFile** aDirectory);
 
   nsresult
   GetUsageForDirectoryInternal(nsIFile* aDirectory,
-                               UsageRunnable* aUsageRunnable,
+                               UsageInfo* aUsageInfo,
                                bool aDatabaseFiles);
 
   nsAutoRefCnt mRefCnt;
   NS_DECL_OWNINGTHREAD
 };
 
 END_INDEXEDDB_NAMESPACE
 
--- a/dom/indexedDB/DatabaseInfo.cpp
+++ b/dom/indexedDB/DatabaseInfo.cpp
@@ -245,18 +245,20 @@ DatabaseInfo::RemoveObjectStore(const ns
 
 already_AddRefed<DatabaseInfo>
 DatabaseInfo::Clone()
 {
   nsRefPtr<DatabaseInfo> dbInfo(new DatabaseInfo());
 
   dbInfo->cloned = true;
   dbInfo->name = name;
+  dbInfo->group = group;
   dbInfo->origin = origin;
   dbInfo->version = version;
+  dbInfo->persistenceType = persistenceType;
   dbInfo->id = id;
   dbInfo->filePath = filePath;
   dbInfo->nextObjectStoreId = nextObjectStoreId;
   dbInfo->nextIndexId = nextIndexId;
 
   if (objectStoreHash) {
     dbInfo->objectStoreHash = new ObjectStoreInfoHash();
     objectStoreHash->EnumerateRead(CloneObjectStoreInfo,
--- a/dom/indexedDB/DatabaseInfo.h
+++ b/dom/indexedDB/DatabaseInfo.h
@@ -4,50 +4,57 @@
  * 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_indexeddb_databaseinfo_h__
 #define mozilla_dom_indexeddb_databaseinfo_h__
 
 #include "mozilla/dom/indexedDB/IndexedDatabase.h"
 
+#include "mozilla/dom/quota/PersistenceType.h"
+#include "nsRefPtrHashtable.h"
+#include "nsHashKeys.h"
+
 #include "mozilla/dom/indexedDB/Key.h"
 #include "mozilla/dom/indexedDB/KeyPath.h"
 #include "mozilla/dom/indexedDB/IDBObjectStore.h"
 
-#include "nsRefPtrHashtable.h"
-#include "nsHashKeys.h"
-
 BEGIN_INDEXEDDB_NAMESPACE
 
 class IndexedDBDatabaseChild;
 struct ObjectStoreInfo;
 
 typedef nsRefPtrHashtable<nsStringHashKey, ObjectStoreInfo>
         ObjectStoreInfoHash;
 
 struct DatabaseInfoGuts
 {
+  typedef mozilla::dom::quota::PersistenceType PersistenceType;
+
   DatabaseInfoGuts()
   : nextObjectStoreId(1), nextIndexId(1)
   { }
 
   bool operator==(const DatabaseInfoGuts& aOther) const
   {
     return this->name == aOther.name &&
+           this->group == aOther.group &&
            this->origin == aOther.origin &&
            this->version == aOther.version &&
+           this->persistenceType == aOther.persistenceType &&
            this->nextObjectStoreId == aOther.nextObjectStoreId &&
            this->nextIndexId == aOther.nextIndexId;
   };
 
   // Make sure to update ipc/SerializationHelpers.h when changing members here!
   nsString name;
+  nsCString group;
   nsCString origin;
   uint64_t version;
+  PersistenceType persistenceType;
   int64_t nextObjectStoreId;
   int64_t nextIndexId;
 };
 
 struct DatabaseInfo : public DatabaseInfoGuts
 {
   DatabaseInfo()
   : cloned(false)
--- a/dom/indexedDB/FileManager.cpp
+++ b/dom/indexedDB/FileManager.cpp
@@ -261,16 +261,18 @@ FileManager::GetFileForId(nsIFile* aDire
 
   return file.forget();
 }
 
 // static
 nsresult
 FileManager::InitDirectory(nsIFile* aDirectory,
                            nsIFile* aDatabaseFile,
+                           PersistenceType aPersistenceType,
+                           const nsACString& aGroup,
                            const nsACString& aOrigin)
 {
   AssertIsOnIOThread();
   NS_ASSERTION(aDirectory, "Null directory!");
   NS_ASSERTION(aDatabaseFile, "Null database file!");
 
   bool exists;
   nsresult rv = aDirectory->Exists(&exists);
@@ -306,17 +308,18 @@ FileManager::InitDirectory(nsIFile* aDir
 
     bool hasElements;
     rv = entries->HasMoreElements(&hasElements);
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (hasElements) {
       nsCOMPtr<mozIStorageConnection> connection;
       rv = OpenDatabaseHelper::CreateDatabaseConnection(aDatabaseFile,
-        aDirectory, NullString(), aOrigin, getter_AddRefs(connection));
+        aDirectory, NullString(), aPersistenceType, aGroup, aOrigin,
+        getter_AddRefs(connection));
       NS_ENSURE_SUCCESS(rv, rv);
 
       mozStorageTransaction transaction(connection, false);
 
       rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
         "CREATE VIRTUAL TABLE fs USING filesystem;"
       ));
       NS_ENSURE_SUCCESS(rv, rv);
--- a/dom/indexedDB/FileManager.h
+++ b/dom/indexedDB/FileManager.h
@@ -7,43 +7,57 @@
 #ifndef mozilla_dom_indexeddb_filemanager_h__
 #define mozilla_dom_indexeddb_filemanager_h__
 
 #include "IndexedDatabase.h"
 
 #include "nsIDOMFile.h"
 #include "nsIFile.h"
 
+#include "mozilla/dom/quota/PersistenceType.h"
 #include "mozilla/dom/quota/StoragePrivilege.h"
 #include "nsDataHashtable.h"
 
 class mozIStorageConnection;
 
 BEGIN_INDEXEDDB_NAMESPACE
 
 class FileInfo;
 
 class FileManager
 {
   friend class FileInfo;
 
+  typedef mozilla::dom::quota::PersistenceType PersistenceType;
   typedef mozilla::dom::quota::StoragePrivilege StoragePrivilege;
 
 public:
-  FileManager(const nsACString& aOrigin, StoragePrivilege aPrivilege,
+  FileManager(PersistenceType aPersistenceType, const nsACString& aGroup,
+              const nsACString& aOrigin, StoragePrivilege aPrivilege,
               const nsAString& aDatabaseName)
-  : mOrigin(aOrigin), mPrivilege(aPrivilege), mDatabaseName(aDatabaseName),
-    mLastFileId(0), mInvalidated(false)
+  : mPersistenceType(aPersistenceType), mGroup(aGroup), mOrigin(aOrigin),
+    mPrivilege(aPrivilege), mDatabaseName(aDatabaseName), mLastFileId(0),
+    mInvalidated(false)
   { }
 
   ~FileManager()
   { }
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FileManager)
 
+  PersistenceType Type()
+  {
+    return mPersistenceType;
+  }
+
+  const nsACString& Group() const
+  {
+    return mGroup;
+  }
+
   const nsACString& Origin() const
   {
     return mOrigin;
   }
 
   const StoragePrivilege& Privilege() const
   {
     return mPrivilege;
@@ -74,21 +88,25 @@ public:
 
   already_AddRefed<FileInfo> GetNewFileInfo();
 
   static already_AddRefed<nsIFile> GetFileForId(nsIFile* aDirectory,
                                                 int64_t aId);
 
   static nsresult InitDirectory(nsIFile* aDirectory,
                                 nsIFile* aDatabaseFile,
+                                PersistenceType aPersistenceType,
+                                const nsACString& aGroup,
                                 const nsACString& aOrigin);
 
   static nsresult GetUsage(nsIFile* aDirectory, uint64_t* aUsage);
 
 private:
+  PersistenceType mPersistenceType;
+  nsCString mGroup;
   nsCString mOrigin;
   StoragePrivilege mPrivilege;
   nsString mDatabaseName;
 
   nsString mDirectoryPath;
   nsString mJournalDirectoryPath;
 
   int64_t mLastFileId;
--- a/dom/indexedDB/IDBDatabase.cpp
+++ b/dom/indexedDB/IDBDatabase.cpp
@@ -192,16 +192,18 @@ IDBDatabase::Create(IDBWrapperCache* aOw
   nsRefPtr<IDBDatabase> db(new IDBDatabase());
 
   db->BindToOwner(aOwnerCache);
   db->SetScriptOwner(aOwnerCache->GetScriptOwner());
   db->mFactory = aFactory;
   db->mDatabaseId = databaseInfo->id;
   db->mName = databaseInfo->name;
   db->mFilePath = databaseInfo->filePath;
+  db->mPersistenceType = databaseInfo->persistenceType;
+  db->mGroup = databaseInfo->group;
   databaseInfo.swap(db->mDatabaseInfo);
   db->mASCIIOrigin = aASCIIOrigin;
   db->mFileManager = aFileManager;
   db->mContentParent = aContentParent;
 
   QuotaManager* quotaManager = QuotaManager::Get();
   NS_ASSERTION(quotaManager, "This should never be null!");
 
@@ -222,18 +224,17 @@ IDBDatabase::Create(IDBWrapperCache* aOw
 IDBDatabase*
 IDBDatabase::FromStorage(nsIOfflineStorage* aStorage)
 {
   return aStorage->GetClient()->GetType() == Client::IDB ?
          static_cast<IDBDatabase*>(aStorage) : nullptr;
 }
 
 IDBDatabase::IDBDatabase()
-: mDatabaseId(0),
-  mActorChild(nullptr),
+: mActorChild(nullptr),
   mActorParent(nullptr),
   mContentParent(nullptr),
   mInvalidated(false),
   mRegistered(false),
   mClosed(false),
   mRunningVersionChange(false)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
--- a/dom/indexedDB/IDBDatabase.h
+++ b/dom/indexedDB/IDBDatabase.h
@@ -11,16 +11,17 @@
 
 #include "nsIDocument.h"
 #include "nsIFileStorage.h"
 #include "nsIOfflineStorage.h"
 
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/IDBObjectStoreBinding.h"
 #include "mozilla/dom/IDBTransactionBinding.h"
+#include "mozilla/dom/quota/PersistenceType.h"
 #include "nsDOMEventTargetHelper.h"
 
 #include "mozilla/dom/indexedDB/FileManager.h"
 #include "mozilla/dom/indexedDB/IDBRequest.h"
 #include "mozilla/dom/indexedDB/IDBWrapperCache.h"
 
 class nsIScriptContext;
 class nsPIDOMWindow;
@@ -205,16 +206,22 @@ public:
   already_AddRefed<indexedDB::IDBTransaction>
   Transaction(const Sequence<nsString>& aStoreNames, IDBTransactionMode aMode,
               ErrorResult& aRv);
 
   IMPL_EVENT_HANDLER(abort)
   IMPL_EVENT_HANDLER(error)
   IMPL_EVENT_HANDLER(versionchange)
 
+  mozilla::dom::StorageType
+  Storage() const
+  {
+    return PersistenceTypeToStorage(mPersistenceType);
+  }
+
   already_AddRefed<IDBRequest>
   MozCreateFileHandle(const nsAString& aName, const Optional<nsAString>& aType,
                       ErrorResult& aRv);
 
   virtual void LastRelease() MOZ_OVERRIDE;
 private:
   IDBDatabase();
   ~IDBDatabase();
--- a/dom/indexedDB/IDBFactory.cpp
+++ b/dom/indexedDB/IDBFactory.cpp
@@ -4,30 +4,30 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "IDBFactory.h"
 
 #include "nsIFile.h"
 #include "nsIPrincipal.h"
 #include "nsIScriptContext.h"
+#include "nsIScriptSecurityManager.h"
 #include "nsIXPConnect.h"
 #include "nsIXPCScriptable.h"
 
 #include <algorithm>
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/IDBFactoryBinding.h"
 #include "mozilla/dom/PBrowserChild.h"
 #include "mozilla/dom/quota/OriginOrPatternString.h"
 #include "mozilla/dom/quota/QuotaManager.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/storage.h"
 #include "nsComponentManagerUtils.h"
-#include "nsIScriptSecurityManager.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsContentUtils.h"
 #include "nsCxPusher.h"
 #include "nsDOMClassInfoID.h"
 #include "nsGlobalWindow.h"
 #include "nsHashKeys.h"
 #include "nsPIDOMWindow.h"
 #include "nsServiceManagerUtils.h"
@@ -46,16 +46,17 @@
 
 #include "ipc/IndexedDBChild.h"
 
 USING_INDEXEDDB_NAMESPACE
 USING_QUOTA_NAMESPACE
 
 using mozilla::dom::ContentChild;
 using mozilla::dom::ContentParent;
+using mozilla::dom::IDBOpenDBOptions;
 using mozilla::dom::NonNull;
 using mozilla::dom::Optional;
 using mozilla::dom::TabChild;
 using mozilla::ErrorResult;
 
 namespace {
 
 struct ObjectStoreInfoMap
@@ -65,17 +66,18 @@ struct ObjectStoreInfoMap
 
   int64_t id;
   ObjectStoreInfo* info;
 };
 
 } // anonymous namespace
 
 IDBFactory::IDBFactory()
-: mOwningObject(nullptr), mActorChild(nullptr), mActorParent(nullptr),
+: mPrivilege(Content), mDefaultPersistenceType(PERSISTENCE_TYPE_TEMPORARY),
+  mOwningObject(nullptr), mActorChild(nullptr), mActorParent(nullptr),
   mContentParent(nullptr), mRootedOwningObject(false)
 {
   SetIsDOMBinding();
 }
 
 IDBFactory::~IDBFactory()
 {
   NS_ASSERTION(!mActorParent, "Actor parent owns us, how can we be dying?!");
@@ -88,16 +90,17 @@ IDBFactory::~IDBFactory()
     mOwningObject = nullptr;
     mozilla::DropJSObjects(this);
   }
 }
 
 // static
 nsresult
 IDBFactory::Create(nsPIDOMWindow* aWindow,
+                   const nsACString& aGroup,
                    const nsACString& aASCIIOrigin,
                    ContentParent* aContentParent,
                    IDBFactory** aFactory)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(aASCIIOrigin.IsEmpty() || nsContentUtils::IsCallerChrome(),
                "Non-chrome may not supply their own origin!");
 
@@ -111,39 +114,52 @@ IDBFactory::Create(nsPIDOMWindow* aWindo
   // Make sure that the manager is up before we do anything here since lots of
   // decisions depend on which process we're running in.
   indexedDB::IndexedDatabaseManager* mgr =
     indexedDB::IndexedDatabaseManager::GetOrCreate();
   NS_ENSURE_TRUE(mgr, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   nsresult rv;
 
+  nsCString group(aGroup);
   nsCString origin(aASCIIOrigin);
+  StoragePrivilege privilege;
+  PersistenceType defaultPersistenceType;
   if (origin.IsEmpty()) {
-    rv = QuotaManager::GetASCIIOriginFromWindow(aWindow, origin);
-    if (NS_FAILED(rv)) {
-      // Not allowed.
-      *aFactory = nullptr;
-      return NS_OK;
-    }
+    NS_ASSERTION(aGroup.IsEmpty(), "Should be empty too!");
+
+    rv = QuotaManager::GetInfoFromWindow(aWindow, &group, &origin, &privilege,
+                                         &defaultPersistenceType);
+  }
+  else {
+    rv = QuotaManager::GetInfoFromWindow(aWindow, nullptr, nullptr, &privilege,
+                                         &defaultPersistenceType);
+  }
+  if (NS_FAILED(rv)) {
+    // Not allowed.
+    *aFactory = nullptr;
+    return NS_OK;
   }
 
   nsRefPtr<IDBFactory> factory = new IDBFactory();
+  factory->mGroup = group;
   factory->mASCIIOrigin = origin;
+  factory->mPrivilege = privilege;
+  factory->mDefaultPersistenceType = defaultPersistenceType;
   factory->mWindow = aWindow;
   factory->mContentParent = aContentParent;
 
   if (!IndexedDatabaseManager::IsMainProcess()) {
     TabChild* tabChild = GetTabChildFrom(aWindow);
     NS_ENSURE_TRUE(tabChild, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
     IndexedDBChild* actor = new IndexedDBChild(origin);
 
     bool allowed;
-    tabChild->SendPIndexedDBConstructor(actor, origin, &allowed);
+    tabChild->SendPIndexedDBConstructor(actor, group, origin, &allowed);
 
     if (!allowed) {
       actor->Send__delete__(actor);
       *aFactory = nullptr;
       return NS_OK;
     }
 
     actor->SetFactory(factory);
@@ -162,22 +178,30 @@ IDBFactory::Create(JSContext* aCx,
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(aCx, "Null context!");
   NS_ASSERTION(aOwningObject, "Null object!");
   NS_ASSERTION(JS_GetGlobalForObject(aCx, aOwningObject) == aOwningObject,
                "Not a global object!");
   NS_ASSERTION(nsContentUtils::IsCallerChrome(), "Only for chrome!");
 
+  nsCString group;
   nsCString origin;
-  nsresult rv = QuotaManager::GetASCIIOriginFromWindow(nullptr, origin);
+  StoragePrivilege privilege;
+  PersistenceType defaultPersistenceType;
+  nsresult rv =
+    QuotaManager::GetInfoFromWindow(nullptr, &group, &origin, &privilege,
+                                    &defaultPersistenceType);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   nsRefPtr<IDBFactory> factory = new IDBFactory();
+  factory->mGroup = group;
   factory->mASCIIOrigin = origin;
+  factory->mPrivilege = privilege;
+  factory->mDefaultPersistenceType = defaultPersistenceType;
   factory->mOwningObject = aOwningObject;
   factory->mContentParent = aContentParent;
 
   if (!IndexedDatabaseManager::IsMainProcess()) {
     ContentChild* contentChild = ContentChild::GetSingleton();
     NS_ENSURE_TRUE(contentChild, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
     IndexedDBChild* actor = new IndexedDBChild(origin);
@@ -233,34 +257,44 @@ IDBFactory::Create(ContentParent* aConte
   factory->mRootedOwningObject = true;
 
   factory.forget(aFactory);
   return NS_OK;
 }
 
 // static
 already_AddRefed<nsIFileURL>
-IDBFactory::GetDatabaseFileURL(nsIFile* aDatabaseFile, const nsACString& aOrigin)
+IDBFactory::GetDatabaseFileURL(nsIFile* aDatabaseFile,
+                               PersistenceType aPersistenceType,
+                               const nsACString& aGroup,
+                               const nsACString& aOrigin)
 {
   nsCOMPtr<nsIURI> uri;
   nsresult rv = NS_NewFileURI(getter_AddRefs(uri), aDatabaseFile);
   NS_ENSURE_SUCCESS(rv, nullptr);
 
   nsCOMPtr<nsIFileURL> fileUrl = do_QueryInterface(uri);
   NS_ASSERTION(fileUrl, "This should always succeed!");
 
-  rv = fileUrl->SetQuery(NS_LITERAL_CSTRING("origin=") + aOrigin);
+  nsAutoCString type;
+  PersistenceTypeToText(aPersistenceType, type);
+
+  rv = fileUrl->SetQuery(NS_LITERAL_CSTRING("persistenceType=") + type +
+                         NS_LITERAL_CSTRING("&group=") + aGroup +
+                         NS_LITERAL_CSTRING("&origin=") + aOrigin);
   NS_ENSURE_SUCCESS(rv, nullptr);
 
   return fileUrl.forget();
 }
 
 // static
 already_AddRefed<mozIStorageConnection>
 IDBFactory::GetConnection(const nsAString& aDatabaseFilePath,
+                          PersistenceType aPersistenceType,
+                          const nsACString& aGroup,
                           const nsACString& aOrigin)
 {
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
   NS_ASSERTION(StringEndsWith(aDatabaseFilePath, NS_LITERAL_STRING(".sqlite")),
                "Bad file path!");
 
   nsCOMPtr<nsIFile> dbFile(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID));
   NS_ENSURE_TRUE(dbFile, nullptr);
@@ -268,17 +302,18 @@ IDBFactory::GetConnection(const nsAStrin
   nsresult rv = dbFile->InitWithPath(aDatabaseFilePath);
   NS_ENSURE_SUCCESS(rv, nullptr);
 
   bool exists;
   rv = dbFile->Exists(&exists);
   NS_ENSURE_SUCCESS(rv, nullptr);
   NS_ENSURE_TRUE(exists, nullptr);
 
-  nsCOMPtr<nsIFileURL> dbFileUrl = GetDatabaseFileURL(dbFile, aOrigin);
+  nsCOMPtr<nsIFileURL> dbFileUrl =
+    GetDatabaseFileURL(dbFile, aPersistenceType, aGroup, aOrigin);
   NS_ENSURE_TRUE(dbFileUrl, nullptr);
 
   nsCOMPtr<mozIStorageService> ss =
     do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
   NS_ENSURE_TRUE(ss, nullptr);
 
   nsCOMPtr<mozIStorageConnection> connection;
   rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(connection));
@@ -519,81 +554,97 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDB
   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mOwningObject)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 DOMCI_DATA(IDBFactory, IDBFactory)
 
 nsresult
 IDBFactory::OpenInternal(const nsAString& aName,
                          int64_t aVersion,
+                         PersistenceType aPersistenceType,
+                         const nsACString& aGroup,
                          const nsACString& aASCIIOrigin,
+                         StoragePrivilege aPrivilege,
                          bool aDeleting,
                          IDBOpenDBRequest** _retval)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(mWindow || mOwningObject, "Must have one of these!");
 
   AutoJSContext cx;
   nsCOMPtr<nsPIDOMWindow> window;
   JS::Rooted<JSObject*> scriptOwner(cx);
-  StoragePrivilege privilege;
 
   if (mWindow) {
     window = mWindow;
     scriptOwner =
       static_cast<nsGlobalWindow*>(window.get())->FastGetGlobalJSObject();
-    privilege = Content;
   }
   else {
     scriptOwner = mOwningObject;
-    privilege = Chrome;
+  }
+
+  if (aPrivilege == Chrome) {
+    // Chrome privilege, ignore the persistence type parameter.
+    aPersistenceType = PERSISTENCE_TYPE_PERSISTENT;
   }
 
   nsRefPtr<IDBOpenDBRequest> request =
     IDBOpenDBRequest::Create(this, window, scriptOwner);
   NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   nsresult rv;
 
   if (IndexedDatabaseManager::IsMainProcess()) {
     nsRefPtr<OpenDatabaseHelper> openHelper =
-      new OpenDatabaseHelper(request, aName, aASCIIOrigin, aVersion, aDeleting,
-                             mContentParent, privilege);
+      new OpenDatabaseHelper(request, aName, aGroup, aASCIIOrigin, aVersion,
+                             aPersistenceType, aDeleting, mContentParent,
+                             aPrivilege);
 
     rv = openHelper->Init();
     NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
-    nsRefPtr<CheckPermissionsHelper> permissionHelper =
-      new CheckPermissionsHelper(openHelper, window, aDeleting);
+    if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) {
+      nsRefPtr<CheckPermissionsHelper> permissionHelper =
+        new CheckPermissionsHelper(openHelper, window);
+
+      QuotaManager* quotaManager = QuotaManager::Get();
+      NS_ASSERTION(quotaManager, "This should never be null!");
 
-    QuotaManager* quotaManager = QuotaManager::Get();
-    NS_ASSERTION(quotaManager, "This should never be null!");
+      rv = quotaManager->
+        WaitForOpenAllowed(OriginOrPatternString::FromOrigin(aASCIIOrigin),
+                           Nullable<PersistenceType>(aPersistenceType),
+                           openHelper->Id(), permissionHelper);
+    }
+    else {
+      NS_ASSERTION(aPersistenceType == PERSISTENCE_TYPE_TEMPORARY, "Huh?");
 
-    rv = quotaManager->WaitForOpenAllowed(OriginOrPatternString::FromOrigin(
-                                          aASCIIOrigin), openHelper->Id(),
-                                          permissionHelper);
+      rv = openHelper->WaitForOpenAllowed();
+    }
     NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
   }
   else if (aDeleting) {
     nsCOMPtr<nsIAtom> databaseId =
-      QuotaManager::GetStorageId(aASCIIOrigin, aName);
+      QuotaManager::GetStorageId(aPersistenceType, aASCIIOrigin, aName);
     NS_ENSURE_TRUE(databaseId, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
     IndexedDBDeleteDatabaseRequestChild* actor =
       new IndexedDBDeleteDatabaseRequestChild(this, request, databaseId);
 
     mActorChild->SendPIndexedDBDeleteDatabaseRequestConstructor(
-                                                               actor,
-                                                               nsString(aName));
+                                                              actor,
+                                                              nsString(aName),
+                                                              aPersistenceType);
   }
   else {
     IndexedDBDatabaseChild* dbActor =
       static_cast<IndexedDBDatabaseChild*>(
         mActorChild->SendPIndexedDBDatabaseConstructor(nsString(aName),
-                                                       aVersion));
+                                                       aVersion,
+                                                       aPersistenceType));
 
     dbActor->SetRequest(request);
   }
 
 #ifdef IDB_PROFILER_USE_MARKS
   {
     NS_ConvertUTF16toUTF8 profilerName(aName);
     if (aDeleting) {
@@ -615,16 +666,32 @@ IDBFactory::OpenInternal(const nsAString
 }
 
 JSObject*
 IDBFactory::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
 {
   return IDBFactoryBinding::Wrap(aCx, aScope, this);
 }
 
+already_AddRefed<IDBOpenDBRequest>
+IDBFactory::Open(const nsAString& aName, const IDBOpenDBOptions& aOptions,
+                 ErrorResult& aRv)
+{
+  return Open(nullptr, aName, aOptions.mVersion, aOptions.mStorage, false, aRv);
+}
+
+already_AddRefed<IDBOpenDBRequest>
+IDBFactory::DeleteDatabase(const nsAString& aName,
+                           const IDBOpenDBOptions& aOptions,
+                           ErrorResult& aRv)
+{
+  return Open(nullptr, aName, Optional<uint64_t>(), aOptions.mStorage, true,
+              aRv);
+}
+
 int16_t
 IDBFactory::Cmp(JSContext* aCx, JS::Handle<JS::Value> aFirst,
                 JS::Handle<JS::Value> aSecond, ErrorResult& aRv)
 {
   Key first, second;
   nsresult rv = first.SetFromJSVal(aCx, aFirst);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
@@ -641,75 +708,97 @@ IDBFactory::Cmp(JSContext* aCx, JS::Hand
     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
     return 0;
   }
 
   return Key::CompareKeys(first, second);
 }
 
 already_AddRefed<IDBOpenDBRequest>
-IDBFactory::OpenForPrincipal(nsIPrincipal* aPrincipal,
-                             const nsAString& aName,
-                             const Optional<uint64_t>& aVersion,
-                             ErrorResult& aRv)
+IDBFactory::OpenForPrincipal(nsIPrincipal* aPrincipal, const nsAString& aName,
+                             uint64_t aVersion, ErrorResult& aRv)
 {
   // Just to be on the extra-safe side
   if (!nsContentUtils::IsCallerChrome()) {
     MOZ_CRASH();
   }
 
-  return Open(aPrincipal, aName, aVersion, false, aRv);
+  return Open(aPrincipal, aName, Optional<uint64_t>(aVersion),
+              Optional<mozilla::dom::StorageType>(), false, aRv);
 }
 
 already_AddRefed<IDBOpenDBRequest>
-IDBFactory::DeleteForPrincipal(nsIPrincipal* aPrincipal,
-                               const nsAString& aName,
+IDBFactory::OpenForPrincipal(nsIPrincipal* aPrincipal, const nsAString& aName,
+                             const IDBOpenDBOptions& aOptions, ErrorResult& aRv)
+{
+  // Just to be on the extra-safe side
+  if (!nsContentUtils::IsCallerChrome()) {
+    MOZ_CRASH();
+  }
+
+  return Open(aPrincipal, aName, aOptions.mVersion, aOptions.mStorage, false,
+              aRv);
+}
+
+already_AddRefed<IDBOpenDBRequest>
+IDBFactory::DeleteForPrincipal(nsIPrincipal* aPrincipal, const nsAString& aName,
+                               const IDBOpenDBOptions& aOptions,
                                ErrorResult& aRv)
 {
   // Just to be on the extra-safe side
   if (!nsContentUtils::IsCallerChrome()) {
     MOZ_CRASH();
   }
 
-  return Open(aPrincipal, aName, Optional<uint64_t>(), true, aRv);
+  return Open(aPrincipal, aName, Optional<uint64_t>(), aOptions.mStorage, true,
+              aRv);
 }
 
 already_AddRefed<IDBOpenDBRequest>
-IDBFactory::Open(nsIPrincipal* aPrincipal,
-                 const nsAString& aName, const Optional<uint64_t>& aVersion,
+IDBFactory::Open(nsIPrincipal* aPrincipal, const nsAString& aName,
+                 const Optional<uint64_t>& aVersion,
+                 const Optional<mozilla::dom::StorageType>& aStorageType,
                  bool aDelete, ErrorResult& aRv)
 {
   nsresult rv;
 
+  nsCString group;
   nsCString origin;
+  StoragePrivilege privilege;
+  PersistenceType defaultPersistenceType;
   if (aPrincipal) {
-    rv = QuotaManager::GetASCIIOriginFromPrincipal(aPrincipal, origin);
+    rv = QuotaManager::GetInfoFromPrincipal(aPrincipal, &group, &origin,
+                                            &privilege,
+                                            &defaultPersistenceType);
     if (NS_FAILED(rv)) {
-      aRv.Throw(rv);
+      aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
       return nullptr;
     }
   }
   else {
+    group = mGroup;
     origin = mASCIIOrigin;
+    privilege = mPrivilege;
+    defaultPersistenceType = mDefaultPersistenceType;
   }
 
-  uint64_t version;
+  uint64_t version = 0;
   if (!aDelete && aVersion.WasPassed()) {
-    version = aVersion.Value();
-    if (version < 1) {
+    if (aVersion.Value() < 1) {
       aRv.ThrowTypeError(MSG_INVALID_VERSION);
       return nullptr;
     }
-  }
-  else {
-    version = 0;
+    version = aVersion.Value();
   }
 
+  PersistenceType persistenceType =
+    PersistenceTypeFromStorage(aStorageType, defaultPersistenceType);
+
   nsRefPtr<IDBOpenDBRequest> request;
-  rv = OpenInternal(aName, version, origin, aDelete,
-                    getter_AddRefs(request));
+  rv = OpenInternal(aName, version, persistenceType, group, origin, privilege,
+                    aDelete, getter_AddRefs(request));
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
   return request.forget();
 }
--- a/dom/indexedDB/IDBFactory.h
+++ b/dom/indexedDB/IDBFactory.h
@@ -3,16 +3,19 @@
 /* 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_indexeddb_idbfactory_h__
 #define mozilla_dom_indexeddb_idbfactory_h__
 
 #include "mozilla/dom/BindingDeclarations.h" // for Optional
+#include "mozilla/dom/StorageTypeBinding.h"
+#include "mozilla/dom/quota/PersistenceType.h"
+#include "mozilla/dom/quota/StoragePrivilege.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsWrapperCache.h"
 
 class mozIStorageConnection;
 class nsIAtom;
 class nsIFile;
 class nsIFileURL;
@@ -20,68 +23,78 @@ class nsIPrincipal;
 class nsPIDOMWindow;
 template<typename> class nsRefPtr;
 
 namespace mozilla {
 class ErrorResult;
 
 namespace dom {
 class ContentParent;
+class IDBOpenDBOptions;
 
 namespace indexedDB {
 
 struct DatabaseInfo;
 class IDBDatabase;
 class IDBOpenDBRequest;
 class IndexedDBChild;
 class IndexedDBParent;
 
 struct ObjectStoreInfo;
 
 class IDBFactory MOZ_FINAL : public nsISupports,
                              public nsWrapperCache
 {
   typedef mozilla::dom::ContentParent ContentParent;
+  typedef mozilla::dom::quota::PersistenceType PersistenceType;
   typedef nsTArray<nsRefPtr<ObjectStoreInfo> > ObjectStoreInfoArray;
+  typedef mozilla::dom::quota::StoragePrivilege StoragePrivilege;
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IDBFactory)
 
   // Called when using IndexedDB from a window in a different process.
   static nsresult Create(nsPIDOMWindow* aWindow,
+                         const nsACString& aGroup,
                          const nsACString& aASCIIOrigin,
                          ContentParent* aContentParent,
                          IDBFactory** aFactory);
 
   // Called when using IndexedDB from a window in the current process.
   static nsresult Create(nsPIDOMWindow* aWindow,
                          ContentParent* aContentParent,
                          IDBFactory** aFactory)
   {
-    return Create(aWindow, EmptyCString(), aContentParent, aFactory);
+    return Create(aWindow, EmptyCString(), EmptyCString(), aContentParent,
+                  aFactory);
   }
 
   // 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.
   static nsresult Create(ContentParent* aContentParent,
                          IDBFactory** aFactory);
 
   static already_AddRefed<nsIFileURL>
-  GetDatabaseFileURL(nsIFile* aDatabaseFile, const nsACString& aOrigin);
+  GetDatabaseFileURL(nsIFile* aDatabaseFile,
+                     PersistenceType aPersistenceType,
+                     const nsACString& aGroup,
+                     const nsACString& aOrigin);
 
   static already_AddRefed<mozIStorageConnection>
   GetConnection(const nsAString& aDatabaseFilePath,
+                PersistenceType aPersistenceType,
+                const nsACString& aGroup,
                 const nsACString& aOrigin);
 
   static nsresult
   SetDefaultPragmas(mozIStorageConnection* aConnection);
 
   static nsresult
   LoadDatabaseInformation(mozIStorageConnection* aConnection,
                           nsIAtom* aDatabaseId,
@@ -91,27 +104,32 @@ public:
   static nsresult
   SetDatabaseMetadata(DatabaseInfo* aDatabaseInfo,
                       uint64_t aVersion,
                       ObjectStoreInfoArray& aObjectStores);
 
   nsresult
   OpenInternal(const nsAString& aName,
                int64_t aVersion,
+               PersistenceType aPersistenceType,
+               const nsACString& aGroup,
                const nsACString& aASCIIOrigin,
+               StoragePrivilege aStoragePrivilege,
                bool aDeleting,
                IDBOpenDBRequest** _retval);
 
   nsresult
   OpenInternal(const nsAString& aName,
                int64_t aVersion,
+               PersistenceType aPersistenceType,
                bool aDeleting,
                IDBOpenDBRequest** _retval)
   {
-    return OpenInternal(aName, aVersion, mASCIIOrigin, aDeleting, _retval);
+    return OpenInternal(aName, aVersion, aPersistenceType, mGroup, mASCIIOrigin,
+                        mPrivilege, aDeleting, _retval);
   }
 
   void
   SetActor(IndexedDBChild* aActorChild)
   {
     NS_ASSERTION(!aActorChild || !mActorChild, "Shouldn't have more than one!");
     mActorChild = aActorChild;
   }
@@ -124,60 +142,72 @@ public:
   }
 
   const nsCString&
   GetASCIIOrigin() const
   {
     return mASCIIOrigin;
   }
 
-  // WrapperCache
-  nsPIDOMWindow* GetParentObject() const
+  // nsWrapperCache
+  virtual JSObject*
+  WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
+
+  // WebIDL
+  nsPIDOMWindow*
+  GetParentObject() const
   {
     return mWindow;
   }
 
-  virtual JSObject* WrapObject(JSContext* aCx,
-                               JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
-
-  // WebIDL
   already_AddRefed<IDBOpenDBRequest>
-  Open(const nsAString& aName, const Optional<uint64_t>& aVersion,
-       ErrorResult& aRv)
+  Open(const nsAString& aName, uint64_t aVersion, ErrorResult& aRv)
   {
-    return Open(nullptr, aName, aVersion, false, aRv);
+    return Open(nullptr, aName, Optional<uint64_t>(aVersion),
+                Optional<mozilla::dom::StorageType>(), false, aRv);
   }
 
   already_AddRefed<IDBOpenDBRequest>
-  DeleteDatabase(const nsAString& aName, ErrorResult& aRv)
-  {
-    return Open(nullptr, aName, Optional<uint64_t>(), true, aRv);
-  }
+  Open(const nsAString& aName, const IDBOpenDBOptions& aOptions,
+       ErrorResult& aRv);
+
+  already_AddRefed<IDBOpenDBRequest>
+  DeleteDatabase(const nsAString& aName, const IDBOpenDBOptions& aOptions,
+                 ErrorResult& aRv);
 
   int16_t
   Cmp(JSContext* aCx, JS::Handle<JS::Value> aFirst,
       JS::Handle<JS::Value> aSecond, ErrorResult& aRv);
 
   already_AddRefed<IDBOpenDBRequest>
   OpenForPrincipal(nsIPrincipal* aPrincipal, const nsAString& aName,
-                   const Optional<uint64_t>& aVersion, ErrorResult& aRv);
+                   uint64_t aVersion, ErrorResult& aRv);
+
+  already_AddRefed<IDBOpenDBRequest>
+  OpenForPrincipal(nsIPrincipal* aPrincipal, const nsAString& aName,
+                   const IDBOpenDBOptions& aOptions, ErrorResult& aRv);
 
   already_AddRefed<IDBOpenDBRequest>
   DeleteForPrincipal(nsIPrincipal* aPrincipal, const nsAString& aName,
-                     ErrorResult& aRv);
+                     const IDBOpenDBOptions& aOptions, ErrorResult& aRv);
 
 private:
   IDBFactory();
   ~IDBFactory();
 
   already_AddRefed<IDBOpenDBRequest>
   Open(nsIPrincipal* aPrincipal, const nsAString& aName,
-       const Optional<uint64_t>& aVersion, bool aDelete, ErrorResult& aRv);
+       const Optional<uint64_t>& aVersion,
+       const Optional<mozilla::dom::StorageType>& aStorageType, bool aDelete,
+       ErrorResult& aRv);
 
+  nsCString mGroup;
   nsCString mASCIIOrigin;
+  StoragePrivilege mPrivilege;
+  PersistenceType mDefaultPersistenceType;
 
   // If this factory lives on a window then mWindow must be non-null. Otherwise
   // mOwningObject must be non-null.
   nsCOMPtr<nsPIDOMWindow> mWindow;
   JS::Heap<JSObject*> mOwningObject;
 
   IndexedDBChild* mActorChild;
   IndexedDBParent* mActorParent;
--- a/dom/indexedDB/IDBFileHandle.cpp
+++ b/dom/indexedDB/IDBFileHandle.cpp
@@ -72,28 +72,32 @@ IDBFileHandle::Create(IDBDatabase* aData
 }
 
 already_AddRefed<nsISupports>
 IDBFileHandle::CreateStream(nsIFile* aFile, bool aReadOnly)
 {
   nsCOMPtr<nsIOfflineStorage> storage = do_QueryInterface(mFileStorage);
   NS_ASSERTION(storage, "This should always succeed!");
 
+  PersistenceType persistenceType = storage->Type();
+  const nsACString& group = storage->Group();
   const nsACString& origin = storage->Origin();
 
   nsCOMPtr<nsISupports> result;
 
   if (aReadOnly) {
-    nsRefPtr<FileInputStream> stream = FileInputStream::Create(
-      origin, aFile, -1, -1, nsIFileInputStream::DEFER_OPEN);
+    nsRefPtr<FileInputStream> stream =
+      FileInputStream::Create(persistenceType, group, origin, aFile, -1, -1,
+                              nsIFileInputStream::DEFER_OPEN);
     result = NS_ISUPPORTS_CAST(nsIFileInputStream*, stream);
   }
   else {
-    nsRefPtr<FileStream> stream = FileStream::Create(
-      origin, aFile, -1, -1, nsIFileStream::DEFER_OPEN);
+    nsRefPtr<FileStream> stream =
+      FileStream::Create(persistenceType, group, origin, aFile, -1, -1,
+                         nsIFileStream::DEFER_OPEN);
     result = NS_ISUPPORTS_CAST(nsIFileStream*, stream);
   }
   NS_ENSURE_TRUE(result, nullptr);
 
   return result.forget();
 }
 
 already_AddRefed<nsIDOMFile>
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -506,17 +506,17 @@ private:
 };
 
 class ThreadLocalJSRuntime
 {
   JSRuntime* mRuntime;
   JSContext* mContext;
   JSObject* mGlobal;
 
-  static JSClass sGlobalClass;
+  static const JSClass sGlobalClass;
   static const unsigned sRuntimeHeapSize = 768 * 1024;
 
   ThreadLocalJSRuntime()
   : mRuntime(NULL), mContext(NULL), mGlobal(NULL)
   {
       MOZ_COUNT_CTOR(ThreadLocalJSRuntime);
   }
 
@@ -577,17 +577,17 @@ class ThreadLocalJSRuntime
     }
 
     if (mRuntime) {
       JS_DestroyRuntime(mRuntime);
     }
   }
 };
 
-JSClass ThreadLocalJSRuntime::sGlobalClass = {
+const JSClass ThreadLocalJSRuntime::sGlobalClass = {
   "IndexedDBTransactionThreadGlobal",
   JSCLASS_GLOBAL_FLAGS,
   JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
   JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub
 };
 
 inline
 already_AddRefed<IDBRequest>
@@ -847,17 +847,17 @@ public:
     }
 
     return obj;
   }
 };
 
 } // anonymous namespace
 
-JSClass IDBObjectStore::sDummyPropJSClass = {
+const JSClass IDBObjectStore::sDummyPropJSClass = {
   "dummy", 0,
   JS_PropertyStub,  JS_DeletePropertyStub,
   JS_PropertyStub,  JS_StrictPropertyStub,
   JS_EnumerateStub, JS_ResolveStub,
   JS_ConvertStub
 };
 
 // static
@@ -3010,18 +3010,20 @@ AddHelper::DoDatabaseWork(mozIStorageCon
 
         rv = nativeFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
         NS_ENSURE_TRUE(nativeFile, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
         // Now we can copy the blob
         nativeFile = fileManager->GetFileForId(directory, id);
         NS_ENSURE_TRUE(nativeFile, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
-        nsRefPtr<FileOutputStream> outputStream = FileOutputStream::Create(
-          mObjectStore->Transaction()->Database()->Origin(), nativeFile);
+        IDBDatabase* database = mObjectStore->Transaction()->Database();
+        nsRefPtr<FileOutputStream> outputStream =
+          FileOutputStream::Create(database->Type(), database->Group(),
+                                   database->Origin(), nativeFile);
         NS_ENSURE_TRUE(outputStream, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
         rv = CopyData(inputStream, outputStream);
         NS_ENSURE_SUCCESS(rv, rv);
 
         cloneFile.mFile->AddFileInfo(fileInfo);
       }
 
--- a/dom/indexedDB/IDBObjectStore.h
+++ b/dom/indexedDB/IDBObjectStore.h
@@ -239,17 +239,17 @@ public:
                             const Key& aKey,
                             const SerializedStructuredCloneReadInfo& aCloneInfo,
                             nsTArray<StructuredCloneFile>& aBlobs,
                             IDBCursor** _retval);
 
   void
   SetInfo(ObjectStoreInfo* aInfo);
 
-  static JSClass sDummyPropJSClass;
+  static const JSClass sDummyPropJSClass;
 
   // nsWrapperCache
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
 
   // WebIDL
   IDBTransaction*
   GetParentObject() const
--- a/dom/indexedDB/IDBTransaction.cpp
+++ b/dom/indexedDB/IDBTransaction.cpp
@@ -356,18 +356,18 @@ IDBTransaction::GetOrCreateConnection(mo
   PROFILER_LABEL("IndexedDB", "IDBTransaction::GetOrCreateConnection");
 
   if (mDatabase->IsInvalidated()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   if (!mConnection) {
     nsCOMPtr<mozIStorageConnection> connection =
-      IDBFactory::GetConnection(mDatabase->FilePath(),
-                                mDatabase->Origin());
+      IDBFactory::GetConnection(mDatabase->FilePath(), mDatabase->Type(),
+                                mDatabase->Group(), mDatabase->Origin());
     NS_ENSURE_TRUE(connection, NS_ERROR_FAILURE);
 
     nsresult rv;
 
     nsRefPtr<UpdateRefcountFunction> function;
     nsCString beginTransaction;
     if (mMode != IDBTransaction::READ_ONLY) {
       function = new UpdateRefcountFunction(Database()->Manager());
--- a/dom/indexedDB/IndexedDatabaseManager.cpp
+++ b/dom/indexedDB/IndexedDatabaseManager.cpp
@@ -11,16 +11,17 @@
 #include "nsIDOMScriptObjectFactory.h"
 #include "nsIFile.h"
 #include "nsIFileStorage.h"
 #include "nsIObserverService.h"
 #include "nsIScriptError.h"
 
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/CondVar.h"
+#include "mozilla/dom/quota/OriginOrPatternString.h"
 #include "mozilla/dom/quota/QuotaManager.h"
 #include "mozilla/dom/quota/Utilities.h"
 #include "mozilla/dom/TabContext.h"
 #include "mozilla/Services.h"
 #include "mozilla/storage.h"
 #include "nsContentUtils.h"
 #include "nsEventDispatcher.h"
 #include "nsThreadUtils.h"
@@ -36,16 +37,63 @@
 #define LOW_DISK_SPACE_DATA_FREE "free"
 
 USING_INDEXEDDB_NAMESPACE
 using namespace mozilla::dom;
 USING_QUOTA_NAMESPACE
 
 static NS_DEFINE_CID(kDOMSOF_CID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
 
+BEGIN_INDEXEDDB_NAMESPACE
+
+class FileManagerInfo
+{
+public:
+  already_AddRefed<FileManager>
+  GetFileManager(PersistenceType aPersistenceType,
+                 const nsAString& aName) const;
+
+  void
+  AddFileManager(FileManager* aFileManager);
+
+  bool
+  HasFileManagers() const
+  {
+    AssertIsOnIOThread();
+
+    return !mPersistentStorageFileManagers.IsEmpty() ||
+           !mTemporaryStorageFileManagers.IsEmpty();
+  }
+
+  void
+  InvalidateAllFileManagers() const;
+
+  void
+  InvalidateAndRemoveFileManagers(PersistenceType aPersistenceType);
+
+  void
+  InvalidateAndRemoveFileManager(PersistenceType aPersistenceType,
+                                 const nsAString& aName);
+
+private:
+  nsTArray<nsRefPtr<FileManager> >&
+  GetArray(PersistenceType aPersistenceType);
+
+  const nsTArray<nsRefPtr<FileManager> >&
+  GetImmutableArray(PersistenceType aPersistenceType) const
+  {
+    return const_cast<FileManagerInfo*>(this)->GetArray(aPersistenceType);
+  }
+
+  nsTArray<nsRefPtr<FileManager> > mPersistentStorageFileManagers;
+  nsTArray<nsRefPtr<FileManager> > mTemporaryStorageFileManagers;
+};
+
+END_INDEXEDDB_NAMESPACE
+
 namespace {
 
 mozilla::StaticRefPtr<IndexedDatabaseManager> gInstance;
 
 mozilla::Atomic<int32_t> gInitialized(0);
 mozilla::Atomic<int32_t> gClosed(0);
 
 class AsyncDeleteFileRunnable MOZ_FINAL : public nsIRunnable
@@ -62,72 +110,63 @@ private:
 };
 
 class GetFileReferencesHelper MOZ_FINAL : public nsIRunnable
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIRUNNABLE
 
-  GetFileReferencesHelper(const nsACString& aOrigin,
+  GetFileReferencesHelper(PersistenceType aPersistenceType,
+                          const nsACString& aOrigin,
                           const nsAString& aDatabaseName,
                           int64_t aFileId)
-  : mOrigin(aOrigin), mDatabaseName(aDatabaseName), mFileId(aFileId),
+  : mPersistenceType(aPersistenceType),
+    mOrigin(aOrigin),
+    mDatabaseName(aDatabaseName),
+    mFileId(aFileId),
     mMutex(IndexedDatabaseManager::FileMutex()),
     mCondVar(mMutex, "GetFileReferencesHelper::mCondVar"),
     mMemRefCnt(-1),
     mDBRefCnt(-1),
     mSliceRefCnt(-1),
     mResult(false),
     mWaiting(true)
   { }
 
   nsresult
   DispatchAndReturnFileReferences(int32_t* aMemRefCnt,
                                   int32_t* aDBRefCnt,
                                   int32_t* aSliceRefCnt,
                                   bool* aResult);
 
 private:
+  PersistenceType mPersistenceType;
   nsCString mOrigin;
   nsString mDatabaseName;
   int64_t mFileId;
 
   mozilla::Mutex& mMutex;
   mozilla::CondVar mCondVar;
   int32_t mMemRefCnt;
   int32_t mDBRefCnt;
   int32_t mSliceRefCnt;
   bool mResult;
   bool mWaiting;
 };
 
-PLDHashOperator
-InvalidateAndRemoveFileManagers(
-                           const nsACString& aKey,
-                           nsAutoPtr<nsTArray<nsRefPtr<FileManager> > >& aValue,
-                           void* aUserArg)
+struct MOZ_STACK_CLASS InvalidateInfo
 {
-  AssertIsOnIOThread();
-  NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
-  NS_ASSERTION(aValue, "Null pointer!");
+  InvalidateInfo(PersistenceType aPersistenceType, const nsACString& aPattern)
+  : persistenceType(aPersistenceType), pattern(aPattern)
+  { }
 
-  const nsACString* pattern =
-    static_cast<const nsACString*>(aUserArg);
-
-  if (!pattern || PatternMatchesOrigin(*pattern, aKey)) {
-    for (uint32_t i = 0; i < aValue->Length(); i++) {
-      nsRefPtr<FileManager>& fileManager = aValue->ElementAt(i);
-      fileManager->Invalidate();
-    }
-    return PL_DHASH_REMOVE;
-  }
-
-  return PL_DHASH_NEXT;
-}
+  PersistenceType persistenceType;
+  const nsACString& pattern;
+};
 
 } // anonymous namespace
 
 IndexedDatabaseManager::IndexedDatabaseManager()
 : mFileMutex("IndexedDatabaseManager.mFileMutex")
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 }
@@ -376,95 +415,127 @@ IndexedDatabaseManager::InLowDiskSpaceMo
   NS_ASSERTION(gInstance,
                "InLowDiskSpaceMode() called before indexedDB has been "
                "initialized!");
   return !!sLowDiskSpaceMode;
 }
 #endif
 
 already_AddRefed<FileManager>
-IndexedDatabaseManager::GetFileManager(const nsACString& aOrigin,
+IndexedDatabaseManager::GetFileManager(PersistenceType aPersistenceType,
+                                       const nsACString& aOrigin,
                                        const nsAString& aDatabaseName)
 {
   AssertIsOnIOThread();
 
-  nsTArray<nsRefPtr<FileManager> >* array;
-  if (!mFileManagers.Get(aOrigin, &array)) {
+  FileManagerInfo* info;
+  if (!mFileManagerInfos.Get(aOrigin, &info)) {
     return nullptr;
   }
 
-  for (uint32_t i = 0; i < array->Length(); i++) {
-    nsRefPtr<FileManager>& fileManager = array->ElementAt(i);
+  nsRefPtr<FileManager> fileManager =
+    info->GetFileManager(aPersistenceType, aDatabaseName);
 
-    if (fileManager->DatabaseName().Equals(aDatabaseName)) {
-      nsRefPtr<FileManager> result = fileManager;
-      return result.forget();
-    }
-  }
-
-  return nullptr;
+  return fileManager.forget();
 }
 
 void
 IndexedDatabaseManager::AddFileManager(FileManager* aFileManager)
 {
   AssertIsOnIOThread();
   NS_ASSERTION(aFileManager, "Null file manager!");
 
-  nsTArray<nsRefPtr<FileManager> >* array;
-  if (!mFileManagers.Get(aFileManager->Origin(), &array)) {
-    array = new nsTArray<nsRefPtr<FileManager> >();
-    mFileManagers.Put(aFileManager->Origin(), array);
+  FileManagerInfo* info;
+  if (!mFileManagerInfos.Get(aFileManager->Origin(), &info)) {
+    info = new FileManagerInfo();
+    mFileManagerInfos.Put(aFileManager->Origin(), info);
   }
 
-  array->AppendElement(aFileManager);
+  info->AddFileManager(aFileManager);
+}
+
+// static
+PLDHashOperator
+IndexedDatabaseManager::InvalidateAndRemoveFileManagers(
+                                             const nsACString& aKey,
+                                             nsAutoPtr<FileManagerInfo>& aValue,
+                                             void* aUserArg)
+{
+  AssertIsOnIOThread();
+  NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
+  NS_ASSERTION(aValue, "Null pointer!");
+
+  if (!aUserArg) {
+    aValue->InvalidateAllFileManagers();
+    return PL_DHASH_REMOVE;
+  }
+
+  InvalidateInfo* info = static_cast<InvalidateInfo*>(aUserArg);
+
+  if (PatternMatchesOrigin(info->pattern, aKey)) {
+    aValue->InvalidateAndRemoveFileManagers(info->persistenceType);
+
+    if (!aValue->HasFileManagers()) {
+      return PL_DHASH_REMOVE;
+    }
+  }
+
+  return PL_DHASH_NEXT;
 }
 
 void
 IndexedDatabaseManager::InvalidateAllFileManagers()
 {
   AssertIsOnIOThread();
 
-  mFileManagers.Enumerate(InvalidateAndRemoveFileManagers, nullptr);
+  mFileManagerInfos.Enumerate(InvalidateAndRemoveFileManagers, nullptr);
 }
 
 void
-IndexedDatabaseManager::InvalidateFileManagersForPattern(
-                                                     const nsACString& aPattern)
+IndexedDatabaseManager::InvalidateFileManagers(
+                                  PersistenceType aPersistenceType,
+                                  const OriginOrPatternString& aOriginOrPattern)
 {
   AssertIsOnIOThread();
-  NS_ASSERTION(!aPattern.IsEmpty(), "Empty pattern!");
+  NS_ASSERTION(!aOriginOrPattern.IsEmpty(), "Empty pattern!");
+
+  if (aOriginOrPattern.IsOrigin()) {
+    FileManagerInfo* info;
+    if (!mFileManagerInfos.Get(aOriginOrPattern, &info)) {
+      return;
+    }
 
-  mFileManagers.Enumerate(InvalidateAndRemoveFileManagers,
-                          const_cast<nsACString*>(&aPattern));
+    info->InvalidateAndRemoveFileManagers(aPersistenceType);
+
+    if (!info->HasFileManagers()) {
+      mFileManagerInfos.Remove(aOriginOrPattern);
+    }
+  }
+  else {
+    InvalidateInfo info(aPersistenceType, aOriginOrPattern);
+    mFileManagerInfos.Enumerate(InvalidateAndRemoveFileManagers, &info);
+  }
 }
 
 void
-IndexedDatabaseManager::InvalidateFileManager(const nsACString& aOrigin,
+IndexedDatabaseManager::InvalidateFileManager(PersistenceType aPersistenceType,
+                                              const nsACString& aOrigin,
                                               const nsAString& aDatabaseName)
 {
   AssertIsOnIOThread();
 
-  nsTArray<nsRefPtr<FileManager> >* array;
-  if (!mFileManagers.Get(aOrigin, &array)) {
+  FileManagerInfo* info;
+  if (!mFileManagerInfos.Get(aOrigin, &info)) {
     return;
   }
 
-  for (uint32_t i = 0; i < array->Length(); i++) {
-    nsRefPtr<FileManager> fileManager = array->ElementAt(i);
-    if (fileManager->DatabaseName().Equals(aDatabaseName)) {
-      fileManager->Invalidate();
-      array->RemoveElementAt(i);
+  info->InvalidateAndRemoveFileManager(aPersistenceType, aDatabaseName);
 
-      if (array->IsEmpty()) {
-        mFileManagers.Remove(aOrigin);
-      }
-
-      break;
-    }
+  if (!info->HasFileManagers()) {
+    mFileManagerInfos.Remove(aOrigin);
   }
 }
 
 nsresult
 IndexedDatabaseManager::AsyncDeleteFile(FileManager* aFileManager,
                                         int64_t aFileId)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
@@ -487,26 +558,28 @@ IndexedDatabaseManager::AsyncDeleteFile(
     quotaManager->IOThread()->Dispatch(runnable, NS_DISPATCH_NORMAL);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 nsresult
 IndexedDatabaseManager::BlockAndGetFileReferences(
-                                                 const nsACString& aOrigin,
-                                                 const nsAString& aDatabaseName,
-                                                 int64_t aFileId,
-                                                 int32_t* aRefCnt,
-                                                 int32_t* aDBRefCnt,
-                                                 int32_t* aSliceRefCnt,
-                                                 bool* aResult)
+                                               PersistenceType aPersistenceType,
+                                               const nsACString& aOrigin,
+                                               const nsAString& aDatabaseName,
+                                               int64_t aFileId,
+                                               int32_t* aRefCnt,
+                                               int32_t* aDBRefCnt,
+                                               int32_t* aSliceRefCnt,
+                                               bool* aResult)
 {
   nsRefPtr<GetFileReferencesHelper> helper =
-    new GetFileReferencesHelper(aOrigin, aDatabaseName, aFileId);
+    new GetFileReferencesHelper(aPersistenceType, aOrigin, aDatabaseName,
+                                aFileId);
 
   nsresult rv = helper->DispatchAndReturnFileReferences(aRefCnt, aDBRefCnt,
                                                         aSliceRefCnt, aResult);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
@@ -597,16 +670,115 @@ IndexedDatabaseManager::Observe(nsISuppo
 
     return NS_OK;
   }
 
    NS_NOTREACHED("Unknown topic!");
    return NS_ERROR_UNEXPECTED;
  }
 
+already_AddRefed<FileManager>
+FileManagerInfo::GetFileManager(PersistenceType aPersistenceType,
+                                const nsAString& aName) const
+{
+  AssertIsOnIOThread();
+
+  const nsTArray<nsRefPtr<FileManager> >& managers =
+    GetImmutableArray(aPersistenceType);
+
+  for (uint32_t i = 0; i < managers.Length(); i++) {
+    const nsRefPtr<FileManager>& fileManager = managers[i];
+
+    if (fileManager->DatabaseName() == aName) {
+      nsRefPtr<FileManager> result = fileManager;
+      return result.forget();
+    }
+  }
+
+  return nullptr;
+}
+
+void
+FileManagerInfo::AddFileManager(FileManager* aFileManager)
+{
+  AssertIsOnIOThread();
+
+  nsTArray<nsRefPtr<FileManager> >& managers = GetArray(aFileManager->Type());
+
+  NS_ASSERTION(!managers.Contains(aFileManager), "Adding more than once?!");
+
+  managers.AppendElement(aFileManager);
+}
+
+void
+FileManagerInfo::InvalidateAllFileManagers() const
+{
+  AssertIsOnIOThread();
+
+  uint32_t i;
+
+  for (i = 0; i < mPersistentStorageFileManagers.Length(); i++) {
+    mPersistentStorageFileManagers[i]->Invalidate();
+  }
+
+  for (i = 0; i < mTemporaryStorageFileManagers.Length(); i++) {
+    mTemporaryStorageFileManagers[i]->Invalidate();
+  }
+}
+
+void
+FileManagerInfo::InvalidateAndRemoveFileManagers(
+                                               PersistenceType aPersistenceType)
+{
+  AssertIsOnIOThread();
+
+  nsTArray<nsRefPtr<FileManager > >& managers = GetArray(aPersistenceType);
+
+  for (uint32_t i = 0; i < managers.Length(); i++) {
+    managers[i]->Invalidate();
+  }
+
+  managers.Clear();
+}
+
+void
+FileManagerInfo::InvalidateAndRemoveFileManager(
+                                               PersistenceType aPersistenceType,
+                                               const nsAString& aName)
+{
+  AssertIsOnIOThread();
+
+  nsTArray<nsRefPtr<FileManager > >& managers = GetArray(aPersistenceType);
+
+  for (uint32_t i = 0; i < managers.Length(); i++) {
+    nsRefPtr<FileManager>& fileManager = managers[i];
+    if (fileManager->DatabaseName() == aName) {
+      fileManager->Invalidate();
+      managers.RemoveElementAt(i);
+      return;
+    }
+  }
+}
+
+nsTArray<nsRefPtr<FileManager> >&
+FileManagerInfo::GetArray(PersistenceType aPersistenceType)
+{
+  switch (aPersistenceType) {
+    case PERSISTENCE_TYPE_PERSISTENT:
+      return mPersistentStorageFileManagers;
+    case PERSISTENCE_TYPE_TEMPORARY:
+      return mTemporaryStorageFileManagers;
+
+    case PERSISTENCE_TYPE_INVALID:
+    default:
+      MOZ_CRASH("Bad storage type value!");
+      return mPersistentStorageFileManagers;
+  }
+}
+
 AsyncDeleteFileRunnable::AsyncDeleteFileRunnable(FileManager* aFileManager,
                                                  int64_t aFileId)
 : mFileManager(aFileManager), mFileId(aFileId)
 {
 }
 
 NS_IMPL_ISUPPORTS1(AsyncDeleteFileRunnable,
                    nsIRunnable)
@@ -632,17 +804,19 @@ AsyncDeleteFileRunnable::Run()
 
   rv = file->Remove(false);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
 
   if (mFileManager->Privilege() != Chrome) {
     QuotaManager* quotaManager = QuotaManager::Get();
     NS_ASSERTION(quotaManager, "Shouldn't be null!");
 
-    quotaManager->DecreaseUsageForOrigin(mFileManager->Origin(), fileSize);
+    quotaManager->DecreaseUsageForOrigin(mFileManager->Type(),
+                                         mFileManager->Group(),
+                                         mFileManager->Origin(), fileSize);
   }
 
   directory = mFileManager->GetJournalDirectory();
   NS_ENSURE_TRUE(directory, NS_ERROR_FAILURE);
 
   file = mFileManager->GetFileForId(directory, mFileId);
   NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
 
@@ -687,17 +861,17 @@ NS_IMETHODIMP
 GetFileReferencesHelper::Run()
 {
   AssertIsOnIOThread();
 
   IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
   NS_ASSERTION(mgr, "This should never fail!");
 
   nsRefPtr<FileManager> fileManager =
-    mgr->GetFileManager(mOrigin, mDatabaseName);
+    mgr->GetFileManager(mPersistenceType, mOrigin, mDatabaseName);
 
   if (fileManager) {
     nsRefPtr<FileInfo> fileInfo = fileManager->GetFileInfo(mFileId);
 
     if (fileInfo) {
       fileInfo->GetReferences(&mMemRefCnt, &mDBRefCnt, &mSliceRefCnt);
 
       if (mMemRefCnt != -1) {
--- a/dom/indexedDB/IndexedDatabaseManager.h
+++ b/dom/indexedDB/IndexedDatabaseManager.h
@@ -8,39 +8,47 @@
 #define mozilla_dom_indexeddb_indexeddatabasemanager_h__
 
 #include "mozilla/dom/indexedDB/IndexedDatabase.h"
 
 #include "nsIIndexedDatabaseManager.h"
 #include "nsIObserver.h"
 
 #include "mozilla/Atomics.h"
+#include "mozilla/dom/quota/PersistenceType.h"
 #include "mozilla/Mutex.h"
 #include "nsClassHashtable.h"
 #include "nsHashKeys.h"
 
 #define INDEXEDDB_MANAGER_CONTRACTID "@mozilla.org/dom/indexeddb/manager;1"
 
 class nsIAtom;
 class nsPIDOMWindow;
 class nsEventChainPostVisitor;
 
 namespace mozilla {
 namespace dom {
 class TabContext;
+namespace quota {
+class OriginOrPatternString;
+}
 }
 }
 
 BEGIN_INDEXEDDB_NAMESPACE
 
 class FileManager;
+class FileManagerInfo;
 
 class IndexedDatabaseManager MOZ_FINAL : public nsIIndexedDatabaseManager,
                                          public nsIObserver
 {
+  typedef mozilla::dom::quota::OriginOrPatternString OriginOrPatternString;
+  typedef mozilla::dom::quota::PersistenceType PersistenceType;
+
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIINDEXEDDATABASEMANAGER
   NS_DECL_NSIOBSERVER
 
   // Returns a non-owning reference.
   static IndexedDatabaseManager*
   GetOrCreate();
@@ -72,41 +80,45 @@ public:
   ;
 #else
   {
     return !!sLowDiskSpaceMode;
   }
 #endif
 
   already_AddRefed<FileManager>
-  GetFileManager(const nsACString& aOrigin,
+  GetFileManager(PersistenceType aPersistenceType,
+                 const nsACString& aOrigin,
                  const nsAString& aDatabaseName);
 
   void
   AddFileManager(FileManager* aFileManager);
 
   void
   InvalidateAllFileManagers();
 
   void
-  InvalidateFileManagersForPattern(const nsACString& aPattern);
+  InvalidateFileManagers(PersistenceType aPersistenceType,
+                         const OriginOrPatternString& aOriginOrPattern);
 
   void
-  InvalidateFileManager(const nsACString& aOrigin,
+  InvalidateFileManager(PersistenceType aPersistenceType,
+                        const nsACString& aOrigin,
                         const nsAString& aDatabaseName);
 
   nsresult
   AsyncDeleteFile(FileManager* aFileManager,
                   int64_t aFileId);
 
   // Don't call this method in real code, it blocks the main thread!
   // It is intended to be used by mochitests to test correctness of the special
   // reference counting of stored blobs/files.
   nsresult
-  BlockAndGetFileReferences(const nsACString& aOrigin,
+  BlockAndGetFileReferences(PersistenceType aPersistenceType,
+                            const nsACString& aOrigin,
                             const nsAString& aDatabaseName,
                             int64_t aFileId,
                             int32_t* aRefCnt,
                             int32_t* aDBRefCnt,
                             int32_t* aSliceRefCnt,
                             bool* aResult);
 
   static mozilla::Mutex&
@@ -131,20 +143,24 @@ private:
   ~IndexedDatabaseManager();
 
   nsresult
   Init();
 
   void
   Destroy();
 
+  static PLDHashOperator
+  InvalidateAndRemoveFileManagers(const nsACString& aKey,
+                                  nsAutoPtr<FileManagerInfo>& aValue,
+                                  void* aUserArg);
+
   // Maintains a list of all file managers per origin. This list isn't
   // protected by any mutex but it is only ever touched on the IO thread.
-  nsClassHashtable<nsCStringHashKey,
-                   nsTArray<nsRefPtr<FileManager> > > mFileManagers;
+  nsClassHashtable<nsCStringHashKey, FileManagerInfo> mFileManagerInfos;
 
   // Lock protecting FileManager.mFileInfos and nsDOMFileBase.mFileInfos
   // It's s also used to atomically update FileInfo.mRefCnt, FileInfo.mDBRefCnt
   // and FileInfo.mSliceRefCnt
   mozilla::Mutex mFileMutex;
 
   static bool sIsMainProcess;
   static mozilla::Atomic<int32_t> sLowDiskSpaceMode;
--- a/dom/indexedDB/OpenDatabaseHelper.cpp
+++ b/dom/indexedDB/OpenDatabaseHelper.cpp
@@ -1499,21 +1499,24 @@ class DeleteDatabaseHelper : public Asyn
                              public AcquireListener
 {
   friend class VersionChangeEventsRunnable;
 public:
   DeleteDatabaseHelper(IDBOpenDBRequest* aRequest,
                        OpenDatabaseHelper* aHelper,
                        uint64_t aCurrentVersion,
                        const nsAString& aName,
-                       const nsACString& aASCIIOrigin)
+                       const nsACString& aGroup,
+                       const nsACString& aASCIIOrigin,
+                       PersistenceType aPersistenceType)
   : AsyncConnectionHelper(static_cast<IDBDatabase*>(nullptr), aRequest),
     mOpenHelper(aHelper), mOpenRequest(aRequest),
     mCurrentVersion(aCurrentVersion), mName(aName),
-    mASCIIOrigin(aASCIIOrigin)
+    mGroup(aGroup), mASCIIOrigin(aASCIIOrigin),
+    mPersistenceType(aPersistenceType)
   { }
 
   NS_DECL_ISUPPORTS_INHERITED
 
   nsresult GetSuccessResult(JSContext* aCx,
                             JS::MutableHandle<JS::Value> aVal);
 
   void ReleaseMainThreadObjects()
@@ -1562,17 +1565,19 @@ protected:
   }
 
 private:
   // In-params
   nsRefPtr<OpenDatabaseHelper> mOpenHelper;
   nsRefPtr<IDBOpenDBRequest> mOpenRequest;
   uint64_t mCurrentVersion;
   nsString mName;
+  nsCString mGroup;
   nsCString mASCIIOrigin;
+  PersistenceType mPersistenceType;
 };
 
 // Responsible for firing "versionchange" events at all live and non-closed
 // databases, and for firing a "blocked" event at the requesting database if any
 // databases fail to close.
 class VersionChangeEventsRunnable : public nsRunnable
 {
 public:
@@ -1680,40 +1685,70 @@ private:
 
 } // anonymous namespace
 
 NS_IMPL_ISUPPORTS1(OpenDatabaseHelper, nsIRunnable)
 
 nsresult
 OpenDatabaseHelper::Init()
 {
-  mDatabaseId = QuotaManager::GetStorageId(mASCIIOrigin, mName);
+  mDatabaseId =
+    QuotaManager::GetStorageId(mPersistenceType, mASCIIOrigin, mName);
   NS_ENSURE_TRUE(mDatabaseId, NS_ERROR_FAILURE);
 
   return NS_OK;
 }
 
 nsresult
+OpenDatabaseHelper::WaitForOpenAllowed()
+{
+  NS_ASSERTION(mState == eCreated, "We've already been dispatched?");
+  NS_ASSERTION(NS_IsMainThread(), "All hell is about to break lose!");
+
+  mState = eOpenPending;
+
+  QuotaManager* quotaManager = QuotaManager::Get();
+  NS_ASSERTION(quotaManager, "This should never be null!");
+
+  return quotaManager->
+    WaitForOpenAllowed(OriginOrPatternString::FromOrigin(mASCIIOrigin),
+                       Nullable<PersistenceType>(mPersistenceType), mDatabaseId,
+                       this);
+}
+
+nsresult
 OpenDatabaseHelper::Dispatch(nsIEventTarget* aTarget)
 {
-  NS_ASSERTION(mState == eCreated, "We've already been dispatched?");
+  NS_ASSERTION(mState == eCreated || mState == eOpenPending,
+               "We've already been dispatched?");
+
   mState = eDBWork;
 
   return aTarget->Dispatch(this, NS_DISPATCH_NORMAL);
 }
 
 nsresult
+OpenDatabaseHelper::DispatchToIOThread()
+{
+  QuotaManager* quotaManager = QuotaManager::Get();
+  NS_ASSERTION(quotaManager, "This should never be null!");
+
+  return Dispatch(quotaManager->IOThread());
+}
+
+nsresult
 OpenDatabaseHelper::RunImmediately()
 {
   NS_ASSERTION(mState == eCreated, "We've already been dispatched?");
   NS_ASSERTION(NS_FAILED(mResultCode),
                "Should only be short-circuiting if we failed!");
   NS_ASSERTION(NS_IsMainThread(), "All hell is about to break lose!");
 
   mState = eFiringEvents;
+
   return this->Run();
 }
 
 nsresult
 OpenDatabaseHelper::DoDatabaseWork()
 {
   AssertIsOnIOThread();
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
@@ -1734,18 +1769,18 @@ OpenDatabaseHelper::DoDatabaseWork()
   AutoEnterWindow autoWindow(window);
 
   nsCOMPtr<nsIFile> dbDirectory;
 
   QuotaManager* quotaManager = QuotaManager::Get();
   NS_ASSERTION(quotaManager, "This should never be null!");
 
   nsresult rv =
-    quotaManager->EnsureOriginIsInitialized(mASCIIOrigin,
-                                            mTrackingQuota,
+    quotaManager->EnsureOriginIsInitialized(mPersistenceType, mGroup,
+                                            mASCIIOrigin, mTrackingQuota,
                                             getter_AddRefs(dbDirectory));
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   rv = dbDirectory->Append(NS_LITERAL_STRING(IDB_DIRECTORY_NAME));
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   bool exists;
   rv = dbDirectory->Exists(&exists);
@@ -1780,17 +1815,18 @@ OpenDatabaseHelper::DoDatabaseWork()
   nsCOMPtr<nsIFile> fmDirectory;
   rv = dbDirectory->Clone(getter_AddRefs(fmDirectory));
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   rv = fmDirectory->Append(filename);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   nsCOMPtr<mozIStorageConnection> connection;
-  rv = CreateDatabaseConnection(dbFile, fmDirectory, mName, mASCIIOrigin,
+  rv = CreateDatabaseConnection(dbFile, fmDirectory, mName, mPersistenceType,
+                                mGroup, mASCIIOrigin,
                                 getter_AddRefs(connection));
   if (NS_FAILED(rv) &&
       NS_ERROR_GET_MODULE(rv) != NS_ERROR_MODULE_DOM_INDEXEDDB) {
     rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = IDBFactory::LoadDatabaseInformation(connection, mDatabaseId,
@@ -1832,19 +1868,21 @@ OpenDatabaseHelper::DoDatabaseWork()
 
   if (mCurrentVersion != mRequestedVersion) {
     mState = eSetVersionPending;
   }
 
   IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
   NS_ASSERTION(mgr, "This should never be null!");
 
-  nsRefPtr<FileManager> fileManager = mgr->GetFileManager(mASCIIOrigin, mName);
+  nsRefPtr<FileManager> fileManager =
+    mgr->GetFileManager(mPersistenceType, mASCIIOrigin, mName);
   if (!fileManager) {
-    fileManager = new FileManager(mASCIIOrigin, mPrivilege, mName);
+    fileManager = new FileManager(mPersistenceType, mGroup, mASCIIOrigin,
+                                  mPrivilege, mName);
 
     rv = fileManager->Init(fmDirectory, connection);
     NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
     mgr->AddFileManager(fileManager);
   }
 
   mFileManager = fileManager.forget();
@@ -1853,16 +1891,18 @@ OpenDatabaseHelper::DoDatabaseWork()
 }
 
 // static
 nsresult
 OpenDatabaseHelper::CreateDatabaseConnection(
                                         nsIFile* aDBFile,
                                         nsIFile* aFMDirectory,
                                         const nsAString& aName,
+                                        PersistenceType aPersistenceType,
+                                        const nsACString& aGroup,
                                         const nsACString& aOrigin,
                                         mozIStorageConnection** aConnection)
 {
   AssertIsOnIOThread();
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
 
   PROFILER_LABEL("IndexedDB", "OpenDatabaseHelper::CreateDatabaseConnection");
 
@@ -1875,17 +1915,17 @@ OpenDatabaseHelper::CreateDatabaseConnec
 
     if (!exists) {
       NS_WARNING("Refusing to create database because disk space is low!");
       return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
     }
   }
 
   nsCOMPtr<nsIFileURL> dbFileUrl =
-    IDBFactory::GetDatabaseFileURL(aDBFile, aOrigin);
+    IDBFactory::GetDatabaseFileURL(aDBFile, aPersistenceType, aGroup, aOrigin);
   NS_ENSURE_TRUE(dbFileUrl, NS_ERROR_FAILURE);
 
   nsCOMPtr<mozIStorageService> ss =
     do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
   NS_ENSURE_TRUE(ss, NS_ERROR_FAILURE);
 
   nsCOMPtr<mozIStorageConnection> connection;
   rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(connection));
@@ -1945,17 +1985,22 @@ OpenDatabaseHelper::CreateDatabaseConnec
   if (schemaVersion != kSQLiteSchemaVersion) {
 #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
     if (!schemaVersion) {
       // Have to do this before opening a transaction.
       rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
         // Turn on auto_vacuum mode to reclaim disk space on mobile devices.
         "PRAGMA auto_vacuum = FULL; "
       ));
-      NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+      if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) {
+        // mozstorage translates SQLITE_FULL to NS_ERROR_FILE_NO_DEVICE_SPACE,
+        // which we know better as NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR.
+        rv = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
+      }
+      NS_ENSURE_SUCCESS(rv, rv);
     }
 #endif
 
     mozStorageTransaction transaction(connection, false,
                                   mozIStorageConnection::TRANSACTION_IMMEDIATE);
 
     if (!schemaVersion) {
       // Brand new file, initialize our tables.
@@ -2092,17 +2137,17 @@ OpenDatabaseHelper::StartDelete()
   // In case we fail, fire error events
   mState = eFiringEvents;
 
   nsresult rv = EnsureSuccessResult();
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsRefPtr<DeleteDatabaseHelper> helper =
     new DeleteDatabaseHelper(mOpenDBRequest, this, mCurrentVersion, mName,
-                             mASCIIOrigin);
+                             mGroup, mASCIIOrigin, mPersistenceType);
 
   QuotaManager* quotaManager = QuotaManager::Get();
   NS_ASSERTION(quotaManager, "This should never be null!");
 
   rv = quotaManager->AcquireExclusiveAccess(
          mDatabase, mDatabase->Origin(), helper,
          &VersionChangeEventsRunnable::QueueVersionChange<DeleteDatabaseHelper>,
          helper);
@@ -2117,16 +2162,20 @@ OpenDatabaseHelper::StartDelete()
 NS_IMETHODIMP
 OpenDatabaseHelper::Run()
 {
   NS_ASSERTION(mState != eCreated, "Dispatch was not called?!?");
 
   if (NS_IsMainThread()) {
     PROFILER_MAIN_THREAD_LABEL("IndexedDB", "OpenDatabaseHelper::Run");
 
+    if (mState == eOpenPending) {
+      return DispatchToIOThread();
+    }
+
     // If we need to queue up a SetVersionHelper, do that here.
     if (mState == eSetVersionPending) {
       nsresult rv = StartSetVersion();
 
       if (NS_SUCCEEDED(rv)) {
         return rv;
       }
 
@@ -2194,19 +2243,20 @@ OpenDatabaseHelper::Run()
       DispatchErrorEvent();
     } else {
       DispatchSuccessEvent();
     }
 
     QuotaManager* quotaManager = QuotaManager::Get();
     NS_ASSERTION(quotaManager, "This should never be null!");
 
-    quotaManager->AllowNextSynchronizedOp(
-                                OriginOrPatternString::FromOrigin(mASCIIOrigin),
-                                mDatabaseId);
+    quotaManager->
+      AllowNextSynchronizedOp(OriginOrPatternString::FromOrigin(mASCIIOrigin),
+                              Nullable<PersistenceType>(mPersistenceType),
+                              mDatabaseId);
 
     ReleaseMainThreadObjects();
 
     return NS_OK;
   }
 
   PROFILER_LABEL("IndexedDB", "OpenDatabaseHelper::Run");
 
@@ -2237,16 +2287,17 @@ OpenDatabaseHelper::EnsureSuccessResult(
 
   nsRefPtr<DatabaseInfo> dbInfo;
   if (DatabaseInfo::Get(mDatabaseId, getter_AddRefs(dbInfo))) {
 
 #ifdef DEBUG
     {
       NS_ASSERTION(dbInfo->name == mName &&
                    dbInfo->version == mCurrentVersion &&
+                   dbInfo->persistenceType == mPersistenceType &&
                    dbInfo->id == mDatabaseId &&
                    dbInfo->filePath == mDatabaseFilePath,
                    "Metadata mismatch!");
 
       uint32_t objectStoreCount = mObjectStores.Length();
       for (uint32_t index = 0; index < objectStoreCount; index++) {
         nsRefPtr<ObjectStoreInfo>& info = mObjectStores[index];
 
@@ -2279,17 +2330,19 @@ OpenDatabaseHelper::EnsureSuccessResult(
     }
 #endif
 
   }
   else {
     nsRefPtr<DatabaseInfo> newInfo(new DatabaseInfo());
 
     newInfo->name = mName;
+    newInfo->group = mGroup;
     newInfo->origin = mASCIIOrigin;
+    newInfo->persistenceType = mPersistenceType;
     newInfo->id = mDatabaseId;
     newInfo->filePath = mDatabaseFilePath;
 
     if (!DatabaseInfo::Put(newInfo)) {
       NS_ERROR("Failed to add to hash!");
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
 
@@ -2571,17 +2624,18 @@ DeleteDatabaseHelper::DoDatabaseWork(moz
   PROFILER_LABEL("IndexedDB", "DeleteDatabaseHelper::DoDatabaseWork");
 
   const StoragePrivilege& privilege = mOpenHelper->Privilege();
 
   QuotaManager* quotaManager = QuotaManager::Get();
   NS_ASSERTION(quotaManager, "This should never fail!");
 
   nsCOMPtr<nsIFile> directory;
-  nsresult rv = quotaManager->GetDirectoryForOrigin(mASCIIOrigin,
+  nsresult rv = quotaManager->GetDirectoryForOrigin(mPersistenceType,
+                                                    mASCIIOrigin,
                                                     getter_AddRefs(directory));
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   NS_ASSERTION(directory, "What?");
 
   rv = directory->Append(NS_LITERAL_STRING(IDB_DIRECTORY_NAME));
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
@@ -2610,17 +2664,18 @@ DeleteDatabaseHelper::DoDatabaseWork(moz
 
     rv = dbFile->Remove(false);
     NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
     if (privilege != Chrome) {
       QuotaManager* quotaManager = QuotaManager::Get();
       NS_ASSERTION(quotaManager, "Shouldn't be null!");
 
-      quotaManager->DecreaseUsageForOrigin(mASCIIOrigin, fileSize);
+      quotaManager->DecreaseUsageForOrigin(mPersistenceType, mGroup,
+                                           mASCIIOrigin, fileSize);
     }
   }
 
   nsCOMPtr<nsIFile> dbJournalFile;
   rv = directory->Clone(getter_AddRefs(dbJournalFile));
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   rv = dbJournalFile->Append(filename + NS_LITERAL_STRING(".sqlite-journal"));
@@ -2659,24 +2714,25 @@ DeleteDatabaseHelper::DoDatabaseWork(moz
 
     rv = fmDirectory->Remove(true);
     NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
     if (privilege != Chrome) {
       QuotaManager* quotaManager = QuotaManager::Get();
       NS_ASSERTION(quotaManager, "Shouldn't be null!");
 
-      quotaManager->DecreaseUsageForOrigin(mASCIIOrigin, usage);
+      quotaManager->DecreaseUsageForOrigin(mPersistenceType, mGroup,
+                                           mASCIIOrigin, usage);
     }
   }
 
   IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
   NS_ASSERTION(mgr, "This should never fail!");
 
-  mgr->InvalidateFileManager(mASCIIOrigin, mName);
+  mgr->InvalidateFileManager(mPersistenceType, mASCIIOrigin, mName);
 
   return NS_OK;
 }
 
 nsresult
 DeleteDatabaseHelper::GetSuccessResult(JSContext* aCx, JS::MutableHandle<JS::Value> aVal)
 {
   return NS_OK;
--- a/dom/indexedDB/OpenDatabaseHelper.h
+++ b/dom/indexedDB/OpenDatabaseHelper.h
@@ -26,44 +26,50 @@ class ContentParent;
 BEGIN_INDEXEDDB_NAMESPACE
 
 class CheckPermissionsHelper;
 
 class OpenDatabaseHelper : public HelperBase
 {
   friend class CheckPermissionsHelper;
 
+  typedef mozilla::dom::quota::PersistenceType PersistenceType;
   typedef mozilla::dom::quota::StoragePrivilege StoragePrivilege;
 
 public:
   OpenDatabaseHelper(IDBOpenDBRequest* aRequest,
                      const nsAString& aName,
+                     const nsACString& aGroup,
                      const nsACString& aASCIIOrigin,
                      uint64_t aRequestedVersion,
+                     PersistenceType aPersistenceType,
                      bool aForDeletion,
                      mozilla::dom::ContentParent* aContentParent,
                      StoragePrivilege aPrivilege)
     : HelperBase(aRequest), mOpenDBRequest(aRequest), mName(aName),
-      mASCIIOrigin(aASCIIOrigin), mRequestedVersion(aRequestedVersion),
+      mGroup(aGroup), mASCIIOrigin(aASCIIOrigin),
+      mRequestedVersion(aRequestedVersion), mPersistenceType(aPersistenceType),
       mForDeletion(aForDeletion), mPrivilege(aPrivilege), mDatabaseId(nullptr),
       mContentParent(aContentParent), mCurrentVersion(0), mLastObjectStoreId(0),
       mLastIndexId(0), mState(eCreated), mResultCode(NS_OK),
       mLoadDBMetadata(false),
       mTrackingQuota(aPrivilege != mozilla::dom::quota::Chrome)
   {
     NS_ASSERTION(!aForDeletion || !aRequestedVersion,
                  "Can't be for deletion and request a version!");
   }
 
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIRUNNABLE
 
   nsresult Init();
 
+  nsresult WaitForOpenAllowed();
   nsresult Dispatch(nsIEventTarget* aDatabaseThread);
+  nsresult DispatchToIOThread();
   nsresult RunImmediately();
 
   void SetError(nsresult rv)
   {
     NS_ASSERTION(NS_FAILED(rv), "Why are you telling me?");
     mResultCode = rv;
   }
 
@@ -91,16 +97,18 @@ public:
   {
     return mPrivilege;
   }
 
   static
   nsresult CreateDatabaseConnection(nsIFile* aDBFile,
                                     nsIFile* aFMDirectory,
                                     const nsAString& aName,
+                                    PersistenceType aPersistenceType,
+                                    const nsACString& aGroup,
                                     const nsACString& aOrigin,
                                     mozIStorageConnection** aConnection);
 
 protected:
   // Methods only called on the main thread
   nsresult EnsureSuccessResult();
   nsresult StartSetVersion();
   nsresult StartDelete();
@@ -117,34 +125,37 @@ protected:
   }
 
   // Methods only called on the DB thread
   nsresult DoDatabaseWork();
 
   // In-params.
   nsRefPtr<IDBOpenDBRequest> mOpenDBRequest;
   nsString mName;
+  nsCString mGroup;
   nsCString mASCIIOrigin;
   uint64_t mRequestedVersion;
+  PersistenceType mPersistenceType;
   bool mForDeletion;
   StoragePrivilege mPrivilege;
   nsCOMPtr<nsIAtom> mDatabaseId;
   mozilla::dom::ContentParent* mContentParent;
 
   // Out-params.
   nsTArray<nsRefPtr<ObjectStoreInfo> > mObjectStores;
   uint64_t mCurrentVersion;
   nsString mDatabaseFilePath;
   int64_t mLastObjectStoreId;
   int64_t mLastIndexId;
   nsRefPtr<IDBDatabase> mDatabase;
 
   // State variables
   enum OpenDatabaseState {
     eCreated = 0, // Not yet dispatched to the DB thread
+    eOpenPending, // Waiting for open allowed/open allowed
     eDBWork, // Waiting to do/doing work on the DB thread
     eFiringEvents, // Waiting to fire/firing events on the main thread
     eSetVersionPending, // Waiting on a SetVersionHelper
     eSetVersionCompleted, // SetVersionHelper is done
     eDeletePending, // Waiting on a DeleteDatabaseHelper
     eDeleteCompleted, // DeleteDatabaseHelper is done
   };
   OpenDatabaseState mState;
--- a/dom/indexedDB/ipc/IndexedDBChild.cpp
+++ b/dom/indexedDB/ipc/IndexedDBChild.cpp
@@ -205,31 +205,35 @@ IndexedDBChild::ActorDestroy(ActorDestro
     mFactory->SetActor(static_cast<IndexedDBChild*>(NULL));
 #ifdef DEBUG
     mFactory = NULL;
 #endif
   }
 }
 
 PIndexedDBDatabaseChild*
-IndexedDBChild::AllocPIndexedDBDatabaseChild(const nsString& aName,
-                                             const uint64_t& aVersion)
+IndexedDBChild::AllocPIndexedDBDatabaseChild(
+                                        const nsString& aName,
+                                        const uint64_t& aVersion,
+                                        const PersistenceType& aPersistenceType)
 {
   return new IndexedDBDatabaseChild(aName, aVersion);
 }
 
 bool
 IndexedDBChild::DeallocPIndexedDBDatabaseChild(PIndexedDBDatabaseChild* aActor)
 {
   delete aActor;
   return true;
 }
 
 PIndexedDBDeleteDatabaseRequestChild*
-IndexedDBChild::AllocPIndexedDBDeleteDatabaseRequestChild(const nsString& aName)
+IndexedDBChild::AllocPIndexedDBDeleteDatabaseRequestChild(
+                                        const nsString& aName,
+                                        const PersistenceType& aPersistenceType)
 {
   MOZ_CRASH("Caller is supposed to manually construct a request!");
 }
 
 bool
 IndexedDBChild::DeallocPIndexedDBDeleteDatabaseRequestChild(
                                    PIndexedDBDeleteDatabaseRequestChild* aActor)
 {
@@ -280,17 +284,18 @@ IndexedDBDatabaseChild::EnsureDatabase(
                            const DatabaseInfoGuts& aDBInfo,
                            const InfallibleTArray<ObjectStoreInfoGuts>& aOSInfo)
 {
   nsCOMPtr<nsIAtom> databaseId;
   if (mDatabase) {
     databaseId = mDatabase->Id();
   }
   else {
-    databaseId = QuotaManager::GetStorageId(aDBInfo.origin, aDBInfo.name);
+    databaseId = QuotaManager::GetStorageId(aDBInfo.persistenceType,
+                                            aDBInfo.origin, aDBInfo.name);
   }
   NS_ENSURE_TRUE(databaseId, false);
 
   nsRefPtr<DatabaseInfo> dbInfo;
   if (DatabaseInfo::Get(databaseId, getter_AddRefs(dbInfo))) {
     dbInfo->version = aDBInfo.version;
   }
   else {
--- a/dom/indexedDB/ipc/IndexedDBChild.h
+++ b/dom/indexedDB/ipc/IndexedDBChild.h
@@ -60,24 +60,28 @@ public:
   void
   Disconnect();
 
 protected:
   virtual void
   ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
 
   virtual PIndexedDBDatabaseChild*
-  AllocPIndexedDBDatabaseChild(const nsString& aName, const uint64_t& aVersion)
+  AllocPIndexedDBDatabaseChild(const nsString& aName, const uint64_t& aVersion,
+                               const PersistenceType& aPersistenceType)
                                MOZ_OVERRIDE;
 
   virtual bool
   DeallocPIndexedDBDatabaseChild(PIndexedDBDatabaseChild* aActor) MOZ_OVERRIDE;
 
   virtual PIndexedDBDeleteDatabaseRequestChild*
-  AllocPIndexedDBDeleteDatabaseRequestChild(const nsString& aName) MOZ_OVERRIDE;
+  AllocPIndexedDBDeleteDatabaseRequestChild(
+                                        const nsString& aName,
+                                        const PersistenceType& aPersistenceType)
+                                        MOZ_OVERRIDE;
 
   virtual bool
   DeallocPIndexedDBDeleteDatabaseRequestChild(
                                    PIndexedDBDeleteDatabaseRequestChild* aActor)
                                    MOZ_OVERRIDE;
 };
 
 /*******************************************************************************
--- a/dom/indexedDB/ipc/IndexedDBParent.cpp
+++ b/dom/indexedDB/ipc/IndexedDBParent.cpp
@@ -139,51 +139,53 @@ IndexedDBParent::CheckPermissionInternal
 void
 IndexedDBParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   // Nothing really needs to be done here...
 }
 
 bool
 IndexedDBParent::RecvPIndexedDBDatabaseConstructor(
-                                               PIndexedDBDatabaseParent* aActor,
-                                               const nsString& aName,
-                                               const uint64_t& aVersion)
+                                        PIndexedDBDatabaseParent* aActor,
+                                        const nsString& aName,
+                                        const uint64_t& aVersion,
+                                        const PersistenceType& aPersistenceType)
 {
   if (!CheckReadPermission(aName)) {
     return false;
   }
 
   if (IsDisconnected()) {
     // We're shutting down, ignore this request.
     return true;
   }
 
   if (!mFactory) {
     return true;
   }
 
   nsRefPtr<IDBOpenDBRequest> request;
-  nsresult rv =
-    mFactory->OpenInternal(aName, aVersion, false, getter_AddRefs(request));
+  nsresult rv = mFactory->OpenInternal(aName, aVersion, aPersistenceType, false,
+                                       getter_AddRefs(request));
   NS_ENSURE_SUCCESS(rv, false);
 
   IndexedDBDatabaseParent* actor =
     static_cast<IndexedDBDatabaseParent*>(aActor);
 
   rv = actor->SetOpenRequest(request);
   NS_ENSURE_SUCCESS(rv, false);
 
   return true;
 }
 
 bool
 IndexedDBParent::RecvPIndexedDBDeleteDatabaseRequestConstructor(
                                   PIndexedDBDeleteDatabaseRequestParent* aActor,
-                                  const nsString& aName)
+                                  const nsString& aName,
+                                  const PersistenceType& aPersistenceType)
 {
   if (!CheckWritePermission(aName)) {
     return false;
   }
 
   if (IsDisconnected()) {
     // We're shutting down, ignore this request.
     return true;
@@ -193,42 +195,46 @@ IndexedDBParent::RecvPIndexedDBDeleteDat
     return true;
   }
 
   IndexedDBDeleteDatabaseRequestParent* actor =
     static_cast<IndexedDBDeleteDatabaseRequestParent*>(aActor);
 
   nsRefPtr<IDBOpenDBRequest> request;
 
-  nsresult rv =
-    mFactory->OpenInternal(aName, 0, true, getter_AddRefs(request));
+  nsresult rv = mFactory->OpenInternal(aName, 0, aPersistenceType, true,
+                                       getter_AddRefs(request));
   NS_ENSURE_SUCCESS(rv, false);
 
   rv = actor->SetOpenRequest(request);
   NS_ENSURE_SUCCESS(rv, false);
 
   return true;
 }
 
 PIndexedDBDatabaseParent*
-IndexedDBParent::AllocPIndexedDBDatabaseParent(const nsString& aName,
-                                               const uint64_t& aVersion)
+IndexedDBParent::AllocPIndexedDBDatabaseParent(
+                                        const nsString& aName,
+                                        const uint64_t& aVersion,
+                                        const PersistenceType& aPersistenceType)
 {
   return new IndexedDBDatabaseParent();
 }
 
 bool
 IndexedDBParent::DeallocPIndexedDBDatabaseParent(PIndexedDBDatabaseParent* aActor)
 {
   delete aActor;
   return true;
 }
 
 PIndexedDBDeleteDatabaseRequestParent*
-IndexedDBParent::AllocPIndexedDBDeleteDatabaseRequestParent(const nsString& aName)
+IndexedDBParent::AllocPIndexedDBDeleteDatabaseRequestParent(
+                                        const nsString& aName,
+                                        const PersistenceType& aPersistenceType)
 {
   return new IndexedDBDeleteDatabaseRequestParent(mFactory);
 }
 
 bool
 IndexedDBParent::DeallocPIndexedDBDeleteDatabaseRequestParent(
                                   PIndexedDBDeleteDatabaseRequestParent* aActor)
 {
--- a/dom/indexedDB/ipc/IndexedDBParent.h
+++ b/dom/indexedDB/ipc/IndexedDBParent.h
@@ -200,32 +200,40 @@ protected:
                           const nsDependentCString& aPermission);
 
   virtual void
   ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
 
   virtual bool
   RecvPIndexedDBDatabaseConstructor(PIndexedDBDatabaseParent* aActor,
                                     const nsString& aName,
-                                    const uint64_t& aVersion) MOZ_OVERRIDE;
+                                    const uint64_t& aVersion,
+                                    const PersistenceType& aPersistenceType)
+                                    MOZ_OVERRIDE;
 
   virtual bool
   RecvPIndexedDBDeleteDatabaseRequestConstructor(
                                   PIndexedDBDeleteDatabaseRequestParent* aActor,
-                                  const nsString& aName) MOZ_OVERRIDE;
+                                  const nsString& aName,
+                                  const PersistenceType& aPersistenceType)
+                                  MOZ_OVERRIDE;
 
   virtual PIndexedDBDatabaseParent*
-  AllocPIndexedDBDatabaseParent(const nsString& aName, const uint64_t& aVersion)
+  AllocPIndexedDBDatabaseParent(const nsString& aName, const uint64_t& aVersion,
+                                const PersistenceType& aPersistenceType)
                                 MOZ_OVERRIDE;
 
   virtual bool
   DeallocPIndexedDBDatabaseParent(PIndexedDBDatabaseParent* aActor) MOZ_OVERRIDE;
 
   virtual PIndexedDBDeleteDatabaseRequestParent*
-  AllocPIndexedDBDeleteDatabaseRequestParent(const nsString& aName) MOZ_OVERRIDE;
+  AllocPIndexedDBDeleteDatabaseRequestParent(
+                                        const nsString& aName,
+                                        const PersistenceType& aPersistenceType)
+                                        MOZ_OVERRIDE;
 
   virtual bool
   DeallocPIndexedDBDeleteDatabaseRequestParent(
                                   PIndexedDBDeleteDatabaseRequestParent* aActor)
                                   MOZ_OVERRIDE;
 };
 
 /*******************************************************************************
--- a/dom/indexedDB/ipc/PIndexedDB.ipdl
+++ b/dom/indexedDB/ipc/PIndexedDB.ipdl
@@ -2,30 +2,36 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PBrowser;
 include protocol PContent;
 include protocol PIndexedDBDatabase;
 include protocol PIndexedDBDeleteDatabaseRequest;
 
+include "mozilla/dom/indexedDB/SerializationHelpers.h";
+
+using mozilla::dom::quota::PersistenceType;
+
 namespace mozilla {
 namespace dom {
 namespace indexedDB {
 
 protocol PIndexedDB
 {
   manager PBrowser or PContent;
 
   manages PIndexedDBDatabase;
   manages PIndexedDBDeleteDatabaseRequest;
 
 parent:
   __delete__();
 
-  PIndexedDBDatabase(nsString name, uint64_t version);
+  PIndexedDBDatabase(nsString name, uint64_t version,
+                     PersistenceType persistenceType);
 
-  PIndexedDBDeleteDatabaseRequest(nsString name);
+  PIndexedDBDeleteDatabaseRequest(nsString name,
+                                  PersistenceType persistenceType);
 };
 
 } // namespace indexedDB
 } // namespace dom
 } // namespace mozilla
--- a/dom/indexedDB/ipc/SerializationHelpers.h
+++ b/dom/indexedDB/ipc/SerializationHelpers.h
@@ -11,16 +11,23 @@
 #include "mozilla/dom/indexedDB/Key.h"
 #include "mozilla/dom/indexedDB/KeyPath.h"
 #include "mozilla/dom/indexedDB/IDBCursor.h"
 #include "mozilla/dom/indexedDB/IDBTransaction.h"
 
 namespace IPC {
 
 template <>
+struct ParamTraits<mozilla::dom::quota::PersistenceType> :
+  public EnumSerializer<mozilla::dom::quota::PersistenceType,
+                        mozilla::dom::quota::PERSISTENCE_TYPE_PERSISTENT,
+                        mozilla::dom::quota::PERSISTENCE_TYPE_INVALID>
+{ };
+
+template <>
 struct ParamTraits<mozilla::dom::indexedDB::Key>
 {
   typedef mozilla::dom::indexedDB::Key paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
     WriteParam(aMsg, aParam.mBuffer);
   }
@@ -149,34 +156,39 @@ struct ParamTraits<mozilla::dom::indexed
 template <>
 struct ParamTraits<mozilla::dom::indexedDB::DatabaseInfoGuts>
 {
   typedef mozilla::dom::indexedDB::DatabaseInfoGuts paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
     WriteParam(aMsg, aParam.name);
+    WriteParam(aMsg, aParam.group);
     WriteParam(aMsg, aParam.origin);
     WriteParam(aMsg, aParam.version);
+    WriteParam(aMsg, aParam.persistenceType);
     WriteParam(aMsg, aParam.nextObjectStoreId);
     WriteParam(aMsg, aParam.nextIndexId);
   }
 
   static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
   {
     return ReadParam(aMsg, aIter, &aResult->name) &&
+           ReadParam(aMsg, aIter, &aResult->group) &&
            ReadParam(aMsg, aIter, &aResult->origin) &&
            ReadParam(aMsg, aIter, &aResult->version) &&
+           ReadParam(aMsg, aIter, &aResult->persistenceType) &&
            ReadParam(aMsg, aIter, &aResult->nextObjectStoreId) &&
            ReadParam(aMsg, aIter, &aResult->nextIndexId);
   }
 
   static void Log(const paramType& aParam, std::wstring* aLog)
   {
     LogParam(aParam.name, aLog);
+    LogParam(aParam.group, aLog);
     LogParam(aParam.origin, aLog);
     LogParam(aParam.version, aLog);
     LogParam(aParam.nextObjectStoreId, aLog);
     LogParam(aParam.nextIndexId, aLog);
   }
 };
 
 template <>
--- a/dom/indexedDB/test/Makefile.in
+++ b/dom/indexedDB/test/Makefile.in
@@ -74,16 +74,17 @@ MOCHITEST_FILES = \
   test_objectStore_remove_values.html \
   test_object_identity.html \
   test_odd_result_order.html \
   test_open_empty_db.html \
   test_open_for_principal.html \
   test_open_objectStore.html \
   test_optionalArguments.html \
   test_overlapping_transactions.html \
+  test_persistenceType.html \
   test_put_get_values.html \
   test_put_get_values_autoIncrement.html \
   test_readonly_transactions.html \
   test_remove_index.html \
   test_remove_objectStore.html \
   test_request_readyState.html \
   test_success_events_after_abort.html \
   test_third_party.html \
--- a/dom/indexedDB/test/file.js
+++ b/dom/indexedDB/test/file.js
@@ -185,36 +185,31 @@ function getUsage(usageHandler)
   sb.usageHandler = usageHandler;
   var cb = SpecialPowers.Cu.evalInSandbox((function(uri, usage, fileUsage) {
                                            usageHandler(usage, fileUsage); }).toSource(), sb);
 
   let uri = SpecialPowers.wrap(window).document.documentURIObject;
   quotaManager.getUsageForURI(uri, cb);
 }
 
-function scheduleGC()
-{
-  SpecialPowers.exactGC(window, continueToNextStep);
-}
-
 function getFileId(file)
 {
   return utils.getFileId(file);
 }
 
 function hasFileInfo(name, id)
 {
   return utils.getFileReferences(name, id);
 }
 
 function getFileRefCount(name, id)
 {
   let count = {};
-  utils.getFileReferences(name, id, count);
+  utils.getFileReferences(name, id, null, count);
   return count.value;
 }
 
 function getFileDBRefCount(name, id)
 {
   let count = {};
-  utils.getFileReferences(name, id, {}, count);
+  utils.getFileReferences(name, id, null, {}, count);
   return count.value;
 }
--- a/dom/indexedDB/test/helpers.js
+++ b/dom/indexedDB/test/helpers.js
@@ -79,25 +79,27 @@ if (!window.runTest) {
 
     if (limitedQuota) {
       denyUnlimitedQuota();
     }
     else {
       allowUnlimitedQuota();
     }
 
+    enableExperimental();
     enableArchiveReader();
 
     clearAllDatabases(function () { testGenerator.next(); });
   }
 }
 
 function finishTest()
 {
   resetUnlimitedQuota();
+  resetExperimental();
   resetArchiveReader();
   SpecialPowers.notifyObserversInParentProcess(null, "disk-space-watcher",
                                                "free");
 
   SimpleTest.executeSoon(function() {
     testGenerator.close();
     //clearAllDatabases(function() { SimpleTest.finish(); });
     SimpleTest.finish();
@@ -248,13 +250,28 @@ function enableArchiveReader()
   SpecialPowers.setBoolPref("dom.archivereader.enabled", true);
 }
 
 function resetArchiveReader()
 {
   SpecialPowers.setBoolPref("dom.archivereader.enabled", archiveReaderEnabled);
 }
 
+function enableExperimental()
+{
+  SpecialPowers.setBoolPref("dom.indexedDB.experimental", true);
+}
+
+function resetExperimental()
+{
+  SpecialPowers.clearUserPref("dom.indexedDB.experimental");
+}
+
 function gc()
 {
   SpecialPowers.forceGC();
   SpecialPowers.forceCC();
 }
+
+function scheduleGC()
+{
+  SpecialPowers.exactGC(window, continueToNextStep);
+}
--- a/dom/indexedDB/test/test_file_replace.html
+++ b/dom/indexedDB/test/test_file_replace.html
@@ -41,17 +41,17 @@
 
     event = yield undefined;
 
     is(event.type, "success", "Got correct event type");
 
     for (let id = 1; id <= 100; id++) {
       let refs = {};
       let dbRefs = {};
-      let hasFileInfo = utils.getFileReferences(name, id, refs, dbRefs);
+      let hasFileInfo = utils.getFileReferences(name, id, null, refs, dbRefs);
       ok(hasFileInfo, "Has file info");
       is(refs.value, 1, "Correct ref count");
       is(dbRefs.value, id / 100 >> 0, "Correct db ref count");
     }
 
     finishTest();
     yield undefined;
   }
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/test_persistenceType.html
@@ -0,0 +1,108 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>Indexed Database Property Test</title>
+
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+  <script type="text/javascript;version=1.7">
+    function testSteps()
+    {
+      const name = window.location.pathname;
+      const version = 1;
+
+      const objectStoreName = "Foo";
+      const data = { key: 1, value: "bar" };
+
+      try {
+        indexedDB.open(name, { version: version, storage: "unknown" });
+        ok(false, "Should have thrown!");
+      }
+      catch (e) {
+        ok(e instanceof TypeError, "Got TypeError.");
+        is(e.name, "TypeError", "Good error name.");
+      }
+
+      let request = indexedDB.open(name, { version: version,
+                                           storage: "persistent" });
+      request.onerror = errorHandler;
+      request.onupgradeneeded = grabEventAndContinueHandler;
+      request.onsuccess = grabEventAndContinueHandler;
+      let event = yield undefined;
+
+      is(event.type, "upgradeneeded", "Got correct event type");
+
+      let db = event.target.result;
+      db.onerror = errorHandler;
+
+      let objectStore = db.createObjectStore(objectStoreName, { });
+
+      event = yield undefined;
+
+      is(event.type, "success", "Got correct event type");
+
+      is(db.name, name, "Correct name");
+      is(db.version, version, "Correct version");
+      is(db.storage, "persistent", "Correct persistence type");
+
+      objectStore = db.transaction([objectStoreName], "readwrite")
+                      .objectStore(objectStoreName);
+
+      request = objectStore.get(data.key);
+      request.onsuccess = grabEventAndContinueHandler;
+      event = yield undefined;
+
+      is(event.target.result, null, "Got no data");
+
+      request = objectStore.add(data.value, data.key);
+      request.onsuccess = grabEventAndContinueHandler;
+      event = yield undefined;
+
+      is(event.target.result, data.key, "Got correct key");
+
+      request = indexedDB.open(name, { version: version,
+                                       storage: "temporary" });
+      request.onerror = errorHandler;
+      request.onupgradeneeded = grabEventAndContinueHandler;
+      request.onsuccess = grabEventAndContinueHandler;
+      let event = yield undefined;
+
+      is(event.type, "upgradeneeded", "Got correct event type");
+
+      db = event.target.result;
+      db.onerror = errorHandler;
+
+      objectStore = db.createObjectStore(objectStoreName, { });
+
+      event = yield undefined;
+
+      is(event.type, "success", "Got correct event type");
+
+      is(db.name, name, "Correct name");
+      is(db.version, version, "Correct version");
+      is(db.storage, "temporary", "Correct persistence type");
+
+      objectStore = db.transaction([objectStoreName])
+                      .objectStore(objectStoreName);
+
+      request = objectStore.get(data.key);
+      request.onsuccess = grabEventAndContinueHandler;
+      event = yield undefined;
+
+      is(event.target.result, null, "Got no data");
+
+      finishTest();
+      yield undefined;
+    }
+  </script>
+  <script type="text/javascript;version=1.7" src="helpers.js"></script>
+
+</head>
+
+<body onload="runTest();"></body>
+
+</html>
--- a/dom/indexedDB/test/unit/Makefile.in
+++ b/dom/indexedDB/test/unit/Makefile.in
@@ -41,27 +41,29 @@ MOCHITEST_FILES = \
   test_objectStore_inline_autoincrement_key_added_on_put.js \
   test_objectStore_remove_values.js \
   test_odd_result_order.js \
   test_open_empty_db.js \
   test_open_for_principal.js \
   test_open_objectStore.js \
   test_optionalArguments.js \
   test_overlapping_transactions.js \
+  test_persistenceType.js \
   test_put_get_values.js \
   test_put_get_values_autoIncrement.js \
   test_readonly_transactions.js \
   test_remove_index.js \
   test_remove_objectStore.js \
   test_request_readyState.js \
   test_setVersion.js \
   test_setVersion_abort.js \
   test_setVersion_events.js \
   test_setVersion_exclusion.js \
   test_success_events_after_abort.js \
+  test_temporary_storage.js \
   test_traffic_jam.js \
   test_transaction_abort.js \
   test_transaction_abort_hang.js \
   test_transaction_lifetimes.js \
   test_transaction_lifetimes_nested.js \
   test_transaction_ordering.js \
   test_unique_index_update.js \
   test_writer_starvation.js \
--- a/dom/indexedDB/test/unit/head.js
+++ b/dom/indexedDB/test/unit/head.js
@@ -42,26 +42,30 @@ function runTest()
 {
   // XPCShell does not get a profile by default.
   do_get_profile();
 
   var idbManager = Cc["@mozilla.org/dom/indexeddb/manager;1"].
                    getService(Ci.nsIIndexedDatabaseManager);
   idbManager.initWindowless(this);
 
+  enableExperimental();
+
   do_test_pending();
   testGenerator.next();
 }
 
 function finishTest()
 {
+  resetExperimental();
+  SpecialPowers.notifyObserversInParentProcess(null, "disk-space-watcher",
+                                               "free");
+
   do_execute_soon(function(){
     testGenerator.close();
-    SpecialPowers.notifyObserversInParentProcess(null, "disk-space-watcher",
-                                                 "free");
     do_test_finished();
   })
 }
 
 function grabEventAndContinueHandler(event)
 {
   testGenerator.send(event);
 }
@@ -174,22 +178,37 @@ function allowUnlimitedQuota(url)
   throw "allowUnlimitedQuota";
 }
 
 function disallowUnlimitedQuota(url)
 {
   throw "disallowUnlimitedQuota";
 }
 
+function enableExperimental()
+{
+  SpecialPowers.setBoolPref("dom.indexedDB.experimental", true);
+}
+
+function resetExperimental()
+{
+  SpecialPowers.clearUserPref("dom.indexedDB.experimental");
+}
+
 function gc()
 {
   Components.utils.forceGC();
   Components.utils.forceCC();
 }
 
+function scheduleGC()
+{
+  SpecialPowers.exactGC(null, continueToNextStep);
+}
+
 function setTimeout(fun, timeout) {
   let timer = Components.classes["@mozilla.org/timer;1"]
                         .createInstance(Components.interfaces.nsITimer);
   var event = {
     notify: function (timer) {
       fun();
     }
   };
@@ -209,10 +228,48 @@ var SpecialPowers = {
                    .getService(Ci.nsIObserverService);
     obsvc.notifyObservers(subject, topic, data);
   },
   notifyObserversInParentProcess: function(subject, topic, data) {
     if (subject) {
       throw new Error("Can't send subject to another process!");
     }
     return this.notifyObservers(subject, topic, data);
+  },
+  getBoolPref: function(prefName) {
+    return this._getPrefs().getBoolPref(prefName);
+  },
+  setBoolPref: function(prefName, value) {
+    this._getPrefs().setBoolPref(prefName, value);
+  },
+  setIntPref: function(prefName, value) {
+    this._getPrefs().setIntPref(prefName, value);
+  },
+  clearUserPref: function(prefName) {
+    this._getPrefs().clearUserPref(prefName);
+  },
+  // Copied (and slightly adjusted) from specialpowersAPI.js
+  exactGC: function(win, callback) {
+    let count = 0;
+
+    function doPreciseGCandCC() {
+      function scheduledGCCallback() {
+        Components.utils.forceCC();
+
+        if (++count < 2) {
+          doPreciseGCandCC();
+        } else {
+          callback();
+        }
+      }
+
+      Components.utils.schedulePreciseGC(scheduledGCCallback);
+    }
+
+    doPreciseGCandCC();
+  },
+
+  _getPrefs: function() {
+    var prefService =
+      Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService);
+    return prefService.getBranch(null);
   }
 };
--- a/dom/indexedDB/test/unit/test_invalid_version.js
+++ b/dom/indexedDB/test/unit/test_invalid_version.js
@@ -22,11 +22,29 @@ function testSteps()
     indexedDB.open(name, -1);
     ok(false, "Should have thrown!");
   }
   catch (e) {
     ok(e instanceof TypeError, "Got TypeError.");
     is(e.name, "TypeError", "Good error name.");
   }
 
+  try {
+    indexedDB.open(name, { version: 0 });
+    ok(false, "Should have thrown!");
+  }
+  catch (e) {
+    ok(e instanceof TypeError, "Got TypeError.");
+    is(e.name, "TypeError", "Good error name.");
+  }
+
+  try {
+    indexedDB.open(name, { version: -1 });
+    ok(false, "Should have thrown!");
+  }
+  catch (e) {
+    ok(e instanceof TypeError, "Got TypeError.");
+    is(e.name, "TypeError", "Good error name.");
+  }
+
   finishTest();
   yield undefined;
 }
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/unit/test_persistenceType.js
@@ -0,0 +1,86 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var testGenerator = testSteps();
+
+function testSteps()
+{
+  const name = "Splendid Test";
+  const version = 1;
+
+  const objectStoreName = "Foo";
+  const data = { key: 1, value: "bar" };
+
+  try {
+    indexedDB.open(name, { version: version, storage: "unknown" });
+    ok(false, "Should have thrown!");
+  }
+  catch (e) {
+    ok(e instanceof TypeError, "Got TypeError.");
+    is(e.name, "TypeError", "Good error name.");
+  }
+
+  let request = indexedDB.open(name, { version: version,
+                                       storage: "persistent" });
+  request.onerror = errorHandler;
+  request.onupgradeneeded = grabEventAndContinueHandler;
+  request.onsuccess = grabEventAndContinueHandler;
+  let event = yield undefined;
+
+  is(event.type, "upgradeneeded", "Got correct event type");
+
+  let db = event.target.result;
+  db.onerror = errorHandler;
+
+  let objectStore = db.createObjectStore(objectStoreName, { });
+
+  event = yield undefined;
+
+  is(event.type, "success", "Got correct event type");
+
+  is(db.name, name, "Correct name");
+  is(db.version, version, "Correct version");
+  is(db.storage, "persistent", "Correct persistence type");
+
+  objectStore = db.transaction([objectStoreName], "readwrite")
+                  .objectStore(objectStoreName);
+
+  request = objectStore.get(data.key);
+  request.onsuccess = grabEventAndContinueHandler;
+  event = yield undefined;
+
+  is(event.target.result, null, "Got no data");
+
+  request = objectStore.add(data.value, data.key);
+  request.onsuccess = grabEventAndContinueHandler;
+  event = yield undefined;
+
+  is(event.target.result, data.key, "Got correct key");
+
+  request = indexedDB.open(name, { version: version,
+                                   storage: "temporary" });
+  request.onerror = errorHandler;
+  request.onupgradeneeded = grabEventAndContinueHandler;
+  request.onsuccess = grabEventAndContinueHandler;
+  let event = yield undefined;
+
+  is(event.type, "success", "Got correct event type");
+
+  is(db.name, name, "Correct name");
+  is(db.version, version, "Correct version");
+  is(db.storage, "persistent", "Correct persistence type");
+
+  objectStore = db.transaction([objectStoreName])
+                  .objectStore(objectStoreName);
+
+  request = objectStore.get(data.key);
+  request.onsuccess = grabEventAndContinueHandler;
+  event = yield undefined;
+
+  is(event.target.result, data.value, "Got correct data");
+
+  finishTest();
+  yield undefined;
+}
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/unit/test_temporary_storage.js
@@ -0,0 +1,225 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var testGenerator = testSteps();
+
+function testSteps()
+{
+  const name = this.window ? window.location.pathname : "Splendid Test";
+
+  const urls = [
+    { url: "http://www.alpha.com",        flags: [true, true, false, false] },
+    { url: "http://www.beta.com",         flags: [true, false, false, false] },
+    { url: "http://www.gamma.com",        flags: [true, true, false, false] },
+    { url: "http://www.delta.com",        flags: [true, true, false, false] },
+    { url: "http://www.epsilon.com",      flags: [true, true, false, false] },
+    { url: "http://www2.alpha.com",       flags: [true, true, false, false] },
+    { url: "http://www2.beta.com",        flags: [true, true, false, false] },
+    { url: "http://www2.gamma.com",       flags: [true, true, true, false] },
+    { url: "http://www2.delta.com",       flags: [true, true, true, true] },
+    { url: "http://www2.epsilon.com",     flags: [true, true, true, true] },
+    { url: "http://joe.blog.alpha.com",   flags: [true, true, true, true] },
+    { url: "http://joe.blog.beta.com",    flags: [true, true, true, true] },
+    { url: "http://joe.blog.gamma.com",   flags: [true, true, true, true] },
+    { url: "http://joe.blog.delta.com",   flags: [true, true, true, true] },
+    { url: "http://joe.blog.epsilon.com", flags: [true, true, true, true] },
+    { url: "http://www.rudolf.org",       flags: [true, true, true, true] },
+    { url: "http://www.pauline.org",      flags: [true, true, true, true] },
+    { url: "http://www.marie.org",        flags: [true, true, true, true] },
+    { url: "http://www.john.org",         flags: [true, true, true, true] },
+    { url: "http://www.ema.org",          flags: [true, true, true, true] },
+    { url: "http://www.trigger.com",      flags: [false, true, true, true] }
+  ];
+  const lastIndex = urls.length - 1;
+  const lastUrl = urls[lastIndex].url;
+
+  let quotaManager =
+    Components.classes["@mozilla.org/dom/quota/manager;1"]
+              .getService(Components.interfaces.nsIQuotaManager);
+
+  let ioService = Components.classes["@mozilla.org/network/io-service;1"]
+                            .getService(Components.interfaces.nsIIOService);
+
+  let dbSize = 0;
+
+  let databases = [];
+
+  function setLimit(limit) {
+    if (limit) {
+      SpecialPowers.setIntPref("dom.quotaManager.temporaryStorage.fixedLimit",
+                               limit);
+      return;
+    }
+    SpecialPowers.clearUserPref("dom.quotaManager.temporaryStorage.fixedLimit");
+  }
+
+  function getPrincipal(url) {
+    let uri = ioService.newURI(url, null, null);
+    return Components.classes["@mozilla.org/scriptsecuritymanager;1"]
+                     .getService(Components.interfaces.nsIScriptSecurityManager)
+                     .getNoAppCodebasePrincipal(uri);
+  }
+
+  function getUsageForUrl(url, usageHandler) {
+    let uri = ioService.newURI(url, null, null);
+    function callback(uri, usage, fileUsage) {
+      usageHandler(usage, fileUsage);
+    }
+    quotaManager.getUsageForURI(uri, callback);
+  }
+
+  function grabUsageAndContinueHandler(usage, fileUsage) {
+    testGenerator.send(usage);
+  }
+
+  function checkUsage(stageIndex) {
+    let handledIndex = 0;
+
+    function usageHandler(usage, fileUsage) {
+      if (urls[handledIndex].flags[stageIndex - 1]) {
+        ok(usage > 0, "Correct usage");
+      }
+      else {
+        ok(usage == 0, "Correct usage");
+      }
+      if (++handledIndex == urls.length) {
+        continueToNextStep();
+      }
+    }
+
+    for (let i = 0; i < urls.length; i++) {
+      getUsageForUrl(urls[i].url, usageHandler);
+    }
+  }
+
+  // Enable clear() and test()
+  let testingEnabled =
+    SpecialPowers.getBoolPref("dom.quotaManager.testing");
+  SpecialPowers.setBoolPref("dom.quotaManager.testing", true)
+
+  // Calibration
+  let request = indexedDB.openForPrincipal(getPrincipal(lastUrl), name,
+                                           { storage: "temporary" });
+  request.onerror = errorHandler;
+  request.onsuccess = grabEventAndContinueHandler;
+  let event = yield undefined;
+
+  getUsageForUrl(lastUrl, grabUsageAndContinueHandler);
+  dbSize = yield undefined;
+
+  setLimit(lastIndex * dbSize / 1024);
+  quotaManager.clear();
+
+  // Stage 1
+  for (let i = 0; i < lastIndex; i++) {
+    let data = urls[i];
+
+    request = indexedDB.openForPrincipal(getPrincipal(data.url), name,
+                                         { storage: "temporary" });
+    request.onerror = errorHandler;
+    request.onupgradeneeded = grabEventAndContinueHandler;
+    request.onsuccess = grabEventAndContinueHandler;
+    event = yield undefined;
+
+    is(event.type, "upgradeneeded", "Got correct event type");
+
+    let db = event.target.result;
+    db.createObjectStore("foo", { });
+
+    event = yield undefined;
+
+    is(event.type, "success", "Got correct event type");
+
+    databases.push(event.target.result);
+  }
+
+  request = indexedDB.openForPrincipal(getPrincipal(lastUrl), name,
+                                       { storage: "temporary" });
+  request.addEventListener("error", new ExpectError("QuotaExceededError"));
+  request.onsuccess = unexpectedSuccessHandler;
+  event = yield undefined;
+
+  checkUsage(1);
+  yield undefined;
+
+  // Stage 2
+  for (let i = 1; i < urls.length; i++) {
+    databases[i] = null;
+
+    scheduleGC();
+    yield undefined;
+
+    // The origin access time is set to the current system time when the first
+    // database for an origin is registered or the last one is unregistered.
+    // The registration happens when the database object is being created and
+    // the unregistration when it is unlinked/garbage collected.
+    // Some older windows systems have the system time limited to a maximum
+    // resolution of 10 or 15 milliseconds, so without a pause here we would
+    // end up with origins with the same access time which would cause random
+    // failures.
+    setTimeout(function() { testGenerator.next(); }, 20);
+    yield undefined;
+  }
+
+  request = indexedDB.openForPrincipal(getPrincipal(lastUrl), name,
+                                       { storage: "temporary" });
+  request.onerror = errorHandler;
+  request.onupgradeneeded = grabEventAndContinueHandler;
+  request.onsuccess = grabEventAndContinueHandler;
+  event = yield undefined;
+
+  is(event.type, "upgradeneeded", "Got correct event type");
+
+  let db = event.target.result;
+  db.createObjectStore("foo", { });
+
+  event = yield undefined;
+
+  is(event.type, "success", "Got correct event type");
+
+  checkUsage(2);
+  yield undefined;
+
+  // Stage 3
+  setLimit(14 * dbSize / 1024);
+  quotaManager.reset();
+
+  request = indexedDB.openForPrincipal(getPrincipal(lastUrl), name,
+                                       { storage: "temporary" });
+  request.onerror = errorHandler;
+  request.onsuccess = grabEventAndContinueHandler;
+  event = yield undefined;
+
+  is(event.type, "success", "Got correct event type");
+
+  let db = event.target.result;
+
+  checkUsage(3);
+  yield undefined;
+
+  // Stage 4
+  let trans = db.transaction(["foo"], "readwrite");
+
+  let blob = Blob(["bar"]);
+  request = trans.objectStore("foo").add(blob, 42);
+  request.onerror = errorHandler;
+  request.onsuccess = grabEventAndContinueHandler;
+  event = yield undefined;
+
+  trans.oncomplete = grabEventAndContinueHandler;
+  event = yield undefined;
+
+  checkUsage(4);
+  yield undefined;
+
+  // Cleanup
+  setLimit();
+  quotaManager.reset();
+
+  SpecialPowers.setBoolPref("dom.quotaManager.testing", testingEnabled);
+
+  finishTest();
+  yield undefined;
+}
--- a/dom/indexedDB/test/unit/xpcshell.ini
+++ b/dom/indexedDB/test/unit/xpcshell.ini
@@ -42,27 +42,29 @@ tail =
 [test_objectStore_inline_autoincrement_key_added_on_put.js]
 [test_objectStore_remove_values.js]
 [test_odd_result_order.js]
 [test_open_empty_db.js]
 [test_open_for_principal.js]
 [test_open_objectStore.js]
 [test_optionalArguments.js]
 [test_overlapping_transactions.js]
+[test_persistenceType.js]
 [test_put_get_values.js]
 [test_put_get_values_autoIncrement.js]
 [test_readonly_transactions.js]
 [test_remove_index.js]
 [test_remove_objectStore.js]
 [test_request_readyState.js]
 [test_setVersion.js]
 [test_setVersion_abort.js]
 [test_setVersion_events.js]
 [test_setVersion_exclusion.js]
 [test_success_events_after_abort.js]
+[test_temporary_storage.js]
 [test_traffic_jam.js]
 [test_transaction_abort.js]
 [test_transaction_abort_hang.js]
 [test_transaction_lifetimes.js]
 [test_transaction_lifetimes_nested.js]
 [test_transaction_ordering.js]
 [test_unique_index_update.js]
 [test_writer_starvation.js]
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -37,17 +37,17 @@ interface nsIDOMBlob;
 interface nsIDOMFile;
 interface nsIFile;
 interface nsIDOMTouch;
 interface nsIDOMClientRect;
 interface nsIURI;
 interface nsIDOMEventTarget;
 interface nsIRunnable;
 
-[scriptable, uuid(d6e733ef-492b-4e67-b723-28571c2959f0)]
+[scriptable, uuid(d18a8d69-7609-4165-ae20-af8aead36833)]
 interface nsIDOMWindowUtils : nsISupports {
 
   /**
    * Image animation mode of the window. When this attribute's value
    * is changed, the implementation should set all images in the window
    * to the given value. That is, when set to kDontAnimMode, all images
    * will stop animating. The attribute's value must be one of the
    * animationMode values from imgIContainer.
@@ -1224,17 +1224,19 @@ interface nsIDOMWindowUtils : nsISupport
    * Get internal id of the stored blob, file or file handle.
    */
   [implicit_jscontext] long long getFileId(in jsval aFile);
 
   /**
    * Get file ref count info for given database and file id.
    *
    */
+  [implicit_jscontext]
   boolean getFileReferences(in AString aDatabaseName, in long long aId,
+                            [optional] in jsval aOptions,
                             [optional] out long aRefCnt,
                             [optional] out long aDBRefCnt,
                             [optional] out long aSliceRefCnt);
 
   /**
    * Return whether incremental GC has been disabled due to a binary add-on.
    */
   [implicit_jscontext]
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -395,26 +395,26 @@ ContentChild::InitXPCOM()
 
 PMemoryReportRequestChild*
 ContentChild::AllocPMemoryReportRequestChild()
 {
     return new MemoryReportRequestChild();
 }
 
 // This is just a wrapper for InfallibleTArray<MemoryReport> that implements
-// nsISupports, so it can be passed to nsIMemoryMultiReporter::CollectReports.
+// nsISupports, so it can be passed to nsIMemoryReporter::CollectReports.
 class MemoryReportsWrapper MOZ_FINAL : public nsISupports {
 public:
     NS_DECL_ISUPPORTS
     MemoryReportsWrapper(InfallibleTArray<MemoryReport> *r) : mReports(r) { }
     InfallibleTArray<MemoryReport> *mReports;
 };
 NS_IMPL_ISUPPORTS0(MemoryReportsWrapper)
 
-class MemoryReportCallback MOZ_FINAL : public nsIMemoryMultiReporterCallback
+class MemoryReportCallback MOZ_FINAL : public nsIMemoryReporterCallback
 {
 public:
     NS_DECL_ISUPPORTS
 
     MemoryReportCallback(const nsACString &aProcess)
     : mProcess(aProcess)
     {
     }
@@ -432,64 +432,39 @@ public:
         wrappedReports->mReports->AppendElement(memreport);
         return NS_OK;
     }
 private:
     const nsCString mProcess;
 };
 NS_IMPL_ISUPPORTS1(
   MemoryReportCallback
-, nsIMemoryMultiReporterCallback
+, nsIMemoryReporterCallback
 )
 
 bool
 ContentChild::RecvPMemoryReportRequestConstructor(PMemoryReportRequestChild* child)
 {
-    
     nsCOMPtr<nsIMemoryReporterManager> mgr = do_GetService("@mozilla.org/memory-reporter-manager;1");
 
     InfallibleTArray<MemoryReport> reports;
 
     nsPrintfCString process("Content (%d)", getpid());
 
-    // First do the vanilla memory reporters.
+    // Run each reporter.  The callback will turn each measurement into a
+    // MemoryReport.
     nsCOMPtr<nsISimpleEnumerator> e;
     mgr->EnumerateReporters(getter_AddRefs(e));
+    nsRefPtr<MemoryReportsWrapper> wrappedReports =
+        new MemoryReportsWrapper(&reports);
+    nsRefPtr<MemoryReportCallback> cb = new MemoryReportCallback(process);
     bool more;
     while (NS_SUCCEEDED(e->HasMoreElements(&more)) && more) {
       nsCOMPtr<nsIMemoryReporter> r;
       e->GetNext(getter_AddRefs(r));
-
-      nsCString path;
-      int32_t kind;
-      int32_t units;
-      int64_t amount;
-      nsCString desc;
-
-      if (NS_SUCCEEDED(r->GetPath(path)) &&
-          NS_SUCCEEDED(r->GetKind(&kind)) &&
-          NS_SUCCEEDED(r->GetUnits(&units)) &&
-          NS_SUCCEEDED(r->GetAmount(&amount)) &&
-          NS_SUCCEEDED(r->GetDescription(desc)))
-      {
-        MemoryReport memreport(process, path, kind, units, amount, desc);
-        reports.AppendElement(memreport);
-      }
-    }
-
-    // Then do the memory multi-reporters, by calling CollectReports on each
-    // one, whereupon the callback will turn each measurement into a
-    // MemoryReport.
-    mgr->EnumerateMultiReporters(getter_AddRefs(e));
-    nsRefPtr<MemoryReportsWrapper> wrappedReports =
-        new MemoryReportsWrapper(&reports);
-    nsRefPtr<MemoryReportCallback> cb = new MemoryReportCallback(process);
-    while (NS_SUCCEEDED(e->HasMoreElements(&more)) && more) {
-      nsCOMPtr<nsIMemoryMultiReporter> r;
-      e->GetNext(getter_AddRefs(r));
       r->CollectReports(cb, wrappedReports);
     }
 
     child->Send__delete__(child, reports);
     return true;
 }
 
 bool
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -156,44 +156,64 @@ using namespace mozilla::layers;
 using namespace mozilla::net;
 using namespace mozilla::jsipc;
 
 namespace mozilla {
 namespace dom {
 
 #define NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC "ipc:network:set-offline"
 
-// This represents a single measurement taken by a memory reporter in a child
-// process and passed to this one.  Its process is non-empty, and its amount is
-// fixed.
-class ChildMemoryReporter MOZ_FINAL : public MemoryReporterBase
+// This represents all the memory reports provided by a child process.
+class ChildReporter MOZ_FINAL : public nsIMemoryReporter
 {
 public:
-    ChildMemoryReporter(const char* aProcess, const char* aPath, int32_t aKind,
-                        int32_t aUnits, int64_t aAmount,
-                        const char* aDescription)
-      : MemoryReporterBase(aPath, aKind, aUnits, aDescription)
-      , mProcess(aProcess)
-      , mAmount(aAmount)
+    ChildReporter(const InfallibleTArray<MemoryReport>& childReports)
     {
+        for (uint32_t i = 0; i < childReports.Length(); i++) {
+            MemoryReport r(childReports[i].process(),
+                           childReports[i].path(),
+                           childReports[i].kind(),
+                           childReports[i].units(),
+                           childReports[i].amount(),
+                           childReports[i].desc());
+
+            // Child reports have a non-empty process.
+            MOZ_ASSERT(!r.process().IsEmpty());
+
+            mChildReports.AppendElement(r);
+        }
     }
 
-    NS_IMETHOD GetProcess(nsACString& aProcess)
+    NS_DECL_ISUPPORTS
+
+    NS_IMETHOD GetName(nsACString& name)
     {
-      aProcess.Assign(mProcess);
-      return NS_OK;
+        name.AssignLiteral("content-child");
+        return NS_OK;
     }
 
-private:
-    int64_t Amount() { return mAmount; }
-
-    nsCString mProcess;
-    int64_t   mAmount;
+    NS_IMETHOD CollectReports(nsIMemoryReporterCallback* aCb,
+                              nsISupports* aClosure)
+    {
+        for (uint32_t i = 0; i < mChildReports.Length(); i++) {
+            nsresult rv;
+            MemoryReport r = mChildReports[i];
+            rv = aCb->Callback(r.process(), r.path(), r.kind(), r.units(),
+                               r.amount(), r.desc(), aClosure);
+            NS_ENSURE_SUCCESS(rv, rv);
+        }
+        return NS_OK;
+    }
+
+  private:
+    InfallibleTArray<MemoryReport> mChildReports;
 };
 
+NS_IMPL_ISUPPORTS1(ChildReporter, nsIMemoryReporter)
+
 class MemoryReportRequestParent : public PMemoryReportRequestParent
 {
 public:
     MemoryReportRequestParent();
     virtual ~MemoryReportRequestParent();
 
     virtual bool Recv__delete__(const InfallibleTArray<MemoryReport>& report);
 private:
@@ -204,49 +224,49 @@ private:
 };
 
 MemoryReportRequestParent::MemoryReportRequestParent()
 {
     MOZ_COUNT_CTOR(MemoryReportRequestParent);
 }
 
 bool
-MemoryReportRequestParent::Recv__delete__(const InfallibleTArray<MemoryReport>& report)
+MemoryReportRequestParent::Recv__delete__(const InfallibleTArray<MemoryReport>& childReports)
 {
-    Owner()->SetChildMemoryReporters(report);
+    Owner()->SetChildMemoryReports(childReports);
     return true;
 }
 
 MemoryReportRequestParent::~MemoryReportRequestParent()
 {
     MOZ_COUNT_DTOR(MemoryReportRequestParent);
 }
 
 /**
  * A memory reporter for ContentParent objects themselves.
  */
-class ContentParentMemoryReporter MOZ_FINAL : public nsIMemoryMultiReporter
+class ContentParentMemoryReporter MOZ_FINAL : public nsIMemoryReporter
 {
 public:
     NS_DECL_ISUPPORTS
-    NS_DECL_NSIMEMORYMULTIREPORTER
+    NS_DECL_NSIMEMORYREPORTER
     NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(MallocSizeOf)
 };
 
-NS_IMPL_ISUPPORTS1(ContentParentMemoryReporter, nsIMemoryMultiReporter)
+NS_IMPL_ISUPPORTS1(ContentParentMemoryReporter, nsIMemoryReporter)
 
 NS_IMETHODIMP
 ContentParentMemoryReporter::GetName(nsACString& aName)
 {
     aName.AssignLiteral("ContentParents");
     return NS_OK;
 }
 
 NS_IMETHODIMP
-ContentParentMemoryReporter::CollectReports(nsIMemoryMultiReporterCallback* cb,
+ContentParentMemoryReporter::CollectReports(nsIMemoryReporterCallback* cb,
                                             nsISupports* aClosure)
 {
     nsAutoTArray<ContentParent*, 16> cps;
     ContentParent::GetAllEvenIfDead(cps);
 
     for (uint32_t i = 0; i < cps.Length(); i++) {
         ContentParent* cp = cps[i];
         AsyncChannel* channel = cp->GetIPCChannel();
@@ -353,17 +373,17 @@ ContentParent::MaybeTakePreallocatedAppP
 /*static*/ void
 ContentParent::StartUp()
 {
     if (XRE_GetProcessType() != GeckoProcessType_Default) {
         return;
     }
 
     nsRefPtr<ContentParentMemoryReporter> mr = new ContentParentMemoryReporter();
-    NS_RegisterMemoryMultiReporter(mr);
+    NS_RegisterMemoryReporter(mr);
 
     sCanLaunchSubprocesses = true;
 
     // Try to preallocate a process that we can transform into an app later.
     PreallocatedProcessManager::AllocateAfterDelay();
 }
 
 /*static*/ void
@@ -872,17 +892,17 @@ ContentParent::ShutDownProcess(bool aClo
     // NB: must MarkAsDead() here so that this isn't accidentally
     // returned from Get*() while in the midst of shutdown.
     MarkAsDead();
 
     // A ContentParent object might not get freed until after XPCOM shutdown has
     // shut down the cycle collector.  But by then it's too late to release any
     // CC'ed objects, so we need to null them out here, while we still can.  See
     // bug 899761.
-    mMemoryReporters.Clear();
+    mChildReporter = nullptr;
     if (mMessageManager) {
       mMessageManager->Disconnect();
       mMessageManager = nullptr;
     }
 }
 
 void
 ContentParent::MarkAsDead()
@@ -1023,18 +1043,18 @@ ContentParent::ActorDestroy(ActorDestroy
         obs->RemoveObserver(static_cast<nsIObserver*>(this), "a11y-init-or-shutdown");
 #endif
     }
 
     if (ppm) {
       ppm->Disconnect();
     }
 
-    // clear the child memory reporters
-    ClearChildMemoryReporters();
+    // unregister the child memory reporter
+    UnregisterChildMemoryReporter();
 
     // remove the global remote preferences observers
     Preferences::RemoveObserver(this, "");
 
     RecvRemoveGeolocationListener();
 
     mConsoleService = nullptr;
 
@@ -2136,52 +2156,39 @@ ContentParent::AllocPMemoryReportRequest
 bool
 ContentParent::DeallocPMemoryReportRequestParent(PMemoryReportRequestParent* actor)
 {
   delete actor;
   return true;
 }
 
 void
-ContentParent::SetChildMemoryReporters(const InfallibleTArray<MemoryReport>& report)
+ContentParent::SetChildMemoryReports(const InfallibleTArray<MemoryReport>& childReports)
 {
     nsCOMPtr<nsIMemoryReporterManager> mgr =
         do_GetService("@mozilla.org/memory-reporter-manager;1");
-    for (int32_t i = 0; i < mMemoryReporters.Count(); i++)
-        mgr->UnregisterReporter(mMemoryReporters[i]);
-
-    for (uint32_t i = 0; i < report.Length(); i++) {
-        nsCString process  = report[i].process();
-        nsCString path     = report[i].path();
-        int32_t   kind     = report[i].kind();
-        int32_t   units    = report[i].units();
-        int64_t   amount   = report[i].amount();
-        nsCString desc     = report[i].desc();
-
-        nsRefPtr<ChildMemoryReporter> r =
-            new ChildMemoryReporter(process.get(), path.get(), kind, units,
-                                    amount, desc.get());
-
-        mMemoryReporters.AppendObject(r);
-        mgr->RegisterReporter(r);
-    }
+
+    if (mChildReporter)
+        mgr->UnregisterReporter(mChildReporter);
+
+    mChildReporter = new ChildReporter(childReports);
+    mgr->RegisterReporter(mChildReporter);
 
     nsCOMPtr<nsIObserverService> obs =
         do_GetService("@mozilla.org/observer-service;1");
     if (obs)
         obs->NotifyObservers(nullptr, "child-memory-reporter-update", nullptr);
 }
 
 void
-ContentParent::ClearChildMemoryReporters()
+ContentParent::UnregisterChildMemoryReporter()
 {
     nsCOMPtr<nsIMemoryReporterManager> mgr =
         do_GetService("@mozilla.org/memory-reporter-manager;1");
-    for (int32_t i = 0; i < mMemoryReporters.Count(); i++)
-        mgr->UnregisterReporter(mMemoryReporters[i]);
+    mgr->UnregisterReporter(mChildReporter);
 }
 
 PTestShellParent*
 ContentParent::AllocPTestShellParent()
 {
   return new TestShellParent();
 }
 
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -138,18 +138,19 @@ public:
     jsipc::JavaScriptParent *GetCPOWManager();
 
     void ReportChildAlreadyBlocked();
     bool RequestRunToCompletion();
 
     bool IsAlive();
     bool IsForApp();
 
-    void SetChildMemoryReporters(const InfallibleTArray<MemoryReport>& report);
-    void ClearChildMemoryReporters();
+    void SetChildMemoryReports(const InfallibleTArray<MemoryReport>&
+                               childReports);
+    void UnregisterChildMemoryReporter();
 
     GeckoChildProcessHost* Process() {
         return mSubprocess;
     }
 
     int32_t Pid();
 
     bool NeedsPermissionsUpdate() {
@@ -444,21 +445,23 @@ private:
     // details.
 
     GeckoChildProcessHost* mSubprocess;
     base::ChildPrivileges mOSPrivileges;
 
     uint64_t mChildID;
     int32_t mGeolocationWatchID;
 
-    // This is a cache of all of the memory reporters
-    // registered in the child process.  To update this, one
-    // can broadcast the topic "child-memory-reporter-request" using
-    // the nsIObserverService.
-    nsCOMArray<nsIMemoryReporter> mMemoryReporters;
+    // This is a reporter holding the reports from the child's last
+    // "child-memory-reporter-update" notification.  To update this, one can
+    // broadcast the topic "child-memory-reporter-request" using the
+    // nsIObserverService.
+    //
+    // Note that this assumes there is at most one child process at a time!
+    nsCOMPtr<nsIMemoryReporter> mChildReporter;
 
     nsString mAppManifestURL;
 
     /**
      * We cache mAppName instead of looking it up using mAppManifestURL when we
      * need it because it turns out that getting an app from the apps service is
      * expensive.
      */
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -246,17 +246,17 @@ parent:
      *   cache group update. But we must cache the document (identified by the
      *   documentURI). This argument will ensure that a previously uncached 
      *   document will get cached and that we don't re-cache a document that 
      *   has already been cached (stickDocument=false).
      */
     POfflineCacheUpdate(URIParams manifestURI, URIParams documentURI,
                         bool stickDocument);
 
-    sync PIndexedDB(nsCString asciiOrigin)
+    sync PIndexedDB(nsCString group, nsCString asciiOrigin)
         returns (bool allowed);
 
     /**
      * window.open from inside <iframe mozbrowser> is special.  When the child
      * process calls window.open, it creates a new PBrowser (in its own
      * process), then calls BrowserFrameOpenWindow on it.
      *
      * The parent process gets a chance to accept or reject the window.open
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -1689,35 +1689,35 @@ TabChild::RecvHandleDoubleTap(const CSSI
 
 bool
 TabChild::RecvHandleSingleTap(const CSSIntPoint& aPoint)
 {
   if (!mGlobal || !mTabChildGlobal) {
     return true;
   }
 
-  RecvMouseEvent(NS_LITERAL_STRING("mousemove"), aPoint.x, aPoint.y, 0, 1, 0, false);
-  RecvMouseEvent(NS_LITERAL_STRING("mousedown"), aPoint.x, aPoint.y, 0, 1, 0, false);
-  RecvMouseEvent(NS_LITERAL_STRING("mouseup"), aPoint.x, aPoint.y, 0, 1, 0, false);
+  DispatchMouseEvent(NS_LITERAL_STRING("mousemove"), aPoint, 0, 1, 0, false,
+                     nsIDOMMouseEvent::MOZ_SOURCE_TOUCH);
+  DispatchMouseEvent(NS_LITERAL_STRING("mousedown"), aPoint, 0, 1, 0, false,
+                     nsIDOMMouseEvent::MOZ_SOURCE_TOUCH);
+  DispatchMouseEvent(NS_LITERAL_STRING("mouseup"), aPoint, 0, 1, 0, false,
+                     nsIDOMMouseEvent::MOZ_SOURCE_TOUCH);
 
   return true;
 }
 
 bool
 TabChild::RecvHandleLongTap(const CSSIntPoint& aPoint)
 {
   if (!mGlobal || !mTabChildGlobal) {
     return true;
   }
 
-  RecvMouseEvent(NS_LITERAL_STRING("contextmenu"), aPoint.x, aPoint.y,
-                 2 /* Right button */,
-                 1 /* Click count */,
-                 0 /* Modifiers */,
-                 false /* Ignore root scroll frame */);
+  DispatchMouseEvent(NS_LITERAL_STRING("contextmenu"), aPoint, 2, 1, 0, false,
+                     nsIDOMMouseEvent::MOZ_SOURCE_TOUCH);
 
   return true;
 }
 
 bool
 TabChild::RecvActivate()
 {
   nsCOMPtr<nsIWebBrowserFocus> browser = do_QueryInterface(mWebNav);
@@ -1737,17 +1737,17 @@ TabChild::RecvMouseEvent(const nsString&
                          const float&    aX,
                          const float&    aY,
                          const int32_t&  aButton,
                          const int32_t&  aClickCount,
                          const int32_t&  aModifiers,
                          const bool&     aIgnoreRootScrollFrame)
 {
   DispatchMouseEvent(aType, CSSPoint(aX, aY), aButton, aClickCount, aModifiers,
-                     aIgnoreRootScrollFrame);
+                     aIgnoreRootScrollFrame, nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN);
   return true;
 }
 
 bool
 TabChild::RecvRealMouseEvent(const nsMouseEvent& event)
 {
   nsMouseEvent localEvent(event);
   DispatchWidgetEvent(localEvent);
@@ -1885,17 +1885,18 @@ TabChild::FireContextMenuEvent()
   }
 
   MOZ_ASSERT(mTapHoldTimer && mActivePointerId >= 0);
   bool defaultPrevented = DispatchMouseEvent(NS_LITERAL_STRING("contextmenu"),
                                              mGestureDownPoint / CSSToLayoutDeviceScale(scale),
                                              2 /* Right button */,
                                              1 /* Click count */,
                                              0 /* Modifiers */,
-                                             false /* Ignore root scroll frame */);
+                                             false /* Ignore root scroll frame */,
+                                             nsIDOMMouseEvent::MOZ_SOURCE_TOUCH);
 
   // Fire a click event if someone didn't call preventDefault() on the context
   // menu event.
   if (defaultPrevented) {
     CancelTapTracking();
   } else if (mTapHoldTimer) {
     mTapHoldTimer->Cancel();
     mTapHoldTimer = nullptr;
@@ -2390,29 +2391,30 @@ TabChild::NotifyPainted()
 
 bool
 TabChild::IsAsyncPanZoomEnabled()
 {
     return mScrolling == ASYNC_PAN_ZOOM;
 }
 
 bool
-TabChild::DispatchMouseEvent(const nsString& aType,
-                             const CSSPoint& aPoint,
-                             const int32_t&  aButton,
-                             const int32_t&  aClickCount,
-                             const int32_t&  aModifiers,
-                             const bool&     aIgnoreRootScrollFrame)
+TabChild::DispatchMouseEvent(const nsString&       aType,
+                             const CSSPoint&       aPoint,
+                             const int32_t&        aButton,
+                             const int32_t&        aClickCount,
+                             const int32_t&        aModifiers,
+                             const bool&           aIgnoreRootScrollFrame,
+                             const unsigned short& aInputSourceArg)
 {
   nsCOMPtr<nsIDOMWindowUtils> utils(GetDOMWindowUtils());
   NS_ENSURE_TRUE(utils, true);
   
   bool defaultPrevented = false;
   utils->SendMouseEvent(aType, aPoint.x, aPoint.y, aButton, aClickCount, aModifiers,
-                        aIgnoreRootScrollFrame, 0, 0, &defaultPrevented);
+                        aIgnoreRootScrollFrame, 0, aInputSourceArg, &defaultPrevented);
   return defaultPrevented;
 }
 
 void
 TabChild::MakeVisible()
 {
     if (mWidget) {
         mWidget->Show(true);
@@ -2440,17 +2442,19 @@ TabChild::GetMessageManager(nsIContentFr
     NS_ADDREF(*aResult = mTabChildGlobal);
     return NS_OK;
   }
   *aResult = nullptr;
   return NS_ERROR_FAILURE;
 }
 
 PIndexedDBChild*
-TabChild::AllocPIndexedDBChild(const nsCString& aASCIIOrigin, bool* /* aAllowed */)
+TabChild::AllocPIndexedDBChild(
+                            const nsCString& aGroup,
+                            const nsCString& aASCIIOrigin, bool* /* aAllowed */)
 {
   NS_NOTREACHED("Should never get here!");
   return NULL;
 }
 
 bool
 TabChild::DeallocPIndexedDBChild(PIndexedDBChild* aActor)
 {
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -306,22 +306,23 @@ public:
 
     void NotifyPainted();
 
     bool IsAsyncPanZoomEnabled();
 
     /** Return a boolean indicating if the page has called preventDefault on
      *  the event.
      */
-    bool DispatchMouseEvent(const nsString& aType,
-                            const CSSPoint& aPoint,
-                            const int32_t&  aButton,
-                            const int32_t&  aClickCount,
-                            const int32_t&  aModifiers,
-                            const bool&     aIgnoreRootScrollFrame);
+    bool DispatchMouseEvent(const nsString&       aType,
+                            const CSSPoint&       aPoint,
+                            const int32_t&        aButton,
+                            const int32_t&        aClickCount,
+                            const int32_t&        aModifiers,
+                            const bool&           aIgnoreRootScrollFrame,
+                            const unsigned short& aInputSourceArg);
 
     /**
      * Signal to this TabChild that it should be made visible:
      * activated widget, retained layer tree, etc.  (Respectively,
      * made not visible.)
      */
     void MakeVisible();
     void MakeHidden();
@@ -346,17 +347,18 @@ protected:
                                                       TextureFactoryIdentifier* aTextureFactoryIdentifier,
                                                       uint64_t* aLayersId) MOZ_OVERRIDE;
     virtual bool DeallocPRenderFrameChild(PRenderFrameChild* aFrame) MOZ_OVERRIDE;
     virtual bool RecvDestroy() MOZ_OVERRIDE;
     virtual bool RecvSetUpdateHitRegion(const bool& aEnabled) MOZ_OVERRIDE;
 
     nsEventStatus DispatchWidgetEvent(nsGUIEvent& event);
 
-    virtual PIndexedDBChild* AllocPIndexedDBChild(const nsCString& aASCIIOrigin,
+    virtual PIndexedDBChild* AllocPIndexedDBChild(const nsCString& aGroup,
+                                                  const nsCString& aASCIIOrigin,
                                                   bool* /* aAllowed */);
 
     virtual bool DeallocPIndexedDBChild(PIndexedDBChild* aActor);
 
 private:
     /**
      * Create a new TabChild object.
      *
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -1190,30 +1190,33 @@ TabParent::ReceiveMessage(const nsString
                             aCloneData,
                             aCpows,
                             aJSONRetVal);
   }
   return true;
 }
 
 PIndexedDBParent*
-TabParent::AllocPIndexedDBParent(const nsCString& aASCIIOrigin, bool* /* aAllowed */)
+TabParent::AllocPIndexedDBParent(
+                            const nsCString& aGroup,
+                            const nsCString& aASCIIOrigin, bool* /* aAllowed */)
 {
   return new IndexedDBParent(this);
 }
 
 bool
 TabParent::DeallocPIndexedDBParent(PIndexedDBParent* aActor)
 {
   delete aActor;
   return true;
 }
 
 bool
 TabParent::RecvPIndexedDBConstructor(PIndexedDBParent* aActor,
+                                     const nsCString& aGroup,
                                      const nsCString& aASCIIOrigin,
                                      bool* aAllowed)
 {
   nsRefPtr<IndexedDatabaseManager> mgr = IndexedDatabaseManager::GetOrCreate();
   NS_ENSURE_TRUE(mgr, false);
 
   if (!IndexedDatabaseManager::IsMainProcess()) {
     NS_RUNTIMEABORT("Not supported yet!");
@@ -1258,17 +1261,17 @@ TabParent::RecvPIndexedDBConstructor(PIn
     *aAllowed = false;
     return true;
   }
 
   ContentParent* contentParent = Manager();
   NS_ASSERTION(contentParent, "Null manager?!");
 
   nsRefPtr<IDBFactory> factory;
-  rv = IDBFactory::Create(window, aASCIIOrigin, contentParent,
+  rv = IDBFactory::Create(window, aGroup, aASCIIOrigin, contentParent,
                           getter_AddRefs(factory));
   NS_ENSURE_SUCCESS(rv, false);
 
   if (!factory) {
     *aAllowed = false;
     return true;
   }
 
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -243,23 +243,26 @@ protected:
                         const StructuredCloneData* aCloneData,
                         CpowHolder* aCpows,
                         InfallibleTArray<nsString>* aJSONRetVal = nullptr);
 
     virtual bool Recv__delete__() MOZ_OVERRIDE;
 
     virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
 
-    virtual PIndexedDBParent* AllocPIndexedDBParent(const nsCString& aASCIIOrigin,
-                                                    bool* /* aAllowed */);
+    virtual PIndexedDBParent* AllocPIndexedDBParent(
+                                                  const nsCString& aGroup,
+                                                  const nsCString& aASCIIOrigin,
+                                                  bool* /* aAllowed */);
 
     virtual bool DeallocPIndexedDBParent(PIndexedDBParent* aActor);
 
     virtual bool
     RecvPIndexedDBConstructor(PIndexedDBParent* aActor,
+                              const nsCString& aGroup,
                               const nsCString& aASCIIOrigin,
                               bool* aAllowed);
 
     Element* mFrameElement;
     nsCOMPtr<nsIBrowserDOMWindow> mBrowserDOMWindow;
 
     struct DelayedDialogData
     {
--- a/dom/permission/tests/unit/test_bug808734.js
+++ b/dom/permission/tests/unit/test_bug808734.js
@@ -16,17 +16,18 @@ var gData = [
   access: READWRITE,
   expected: ["settings-read", "settings-write",
              "indexedDB-chrome-settings-read",
              "indexedDB-chrome-settings-write"] 
 },
 // test substitute
 {
   permission: "storage",
-  expected: ["indexedDB-unlimited", "offline-app", "pin-app"]
+  expected: ["indexedDB-unlimited", "offline-app", "pin-app",
+             "default-persistent-storage"]
 },
 // test unknown access
 {
   permission: "contacts",
   access: UNKNOWN,
   expected: []
 },
 // test unknown permission
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -139,17 +139,17 @@ NPObjWrapper_Call(JSContext *cx, unsigne
 
 static bool
 NPObjWrapper_Construct(JSContext *cx, unsigned argc, JS::Value *vp);
 
 static bool
 CreateNPObjectMember(NPP npp, JSContext *cx, JSObject *obj, NPObject *npobj,
                      JS::Handle<jsid> id, NPVariant* getPropertyResult, JS::Value *vp);
 
-JSClass sNPObjectJSWrapperClass =
+const JSClass sNPObjectJSWrapperClass =
   {
     NPRUNTIME_JSCLASS_NAME,
     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_NEW_RESOLVE | JSCLASS_NEW_ENUMERATE,
     NPObjWrapper_AddProperty,
     NPObjWrapper_DelProperty,
     NPObjWrapper_GetProperty,
     NPObjWrapper_SetProperty,
     (JSEnumerateOp)NPObjWrapper_newEnumerate,
@@ -176,17 +176,17 @@ static void
 NPObjectMember_Finalize(JSFreeOp *fop, JSObject *obj);
 
 static bool
 NPObjectMember_Call(JSContext *cx, unsigned argc, JS::Value *vp);
 
 static void
 NPObjectMember_Trace(JSTracer *trc, JSObject *obj);
 
-static JSClass sNPObjectMemberClass =
+static const JSClass sNPObjectMemberClass =
   {
     "NPObject Ambiguous Member class", JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS,
     JS_PropertyStub, JS_DeletePropertyStub,
     JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub,
     JS_ResolveStub, NPObjectMember_Convert,
     NPObjectMember_Finalize, nullptr, NPObjectMember_Call,
     nullptr, nullptr, NPObjectMember_Trace
   };
@@ -963,17 +963,17 @@ nsJSObjWrapper::GetNewOrUsed(NPP npp, JS
       return nullptr;
     }
   }
 
   // No need to enter the right compartment here as we only get the
   // class and private from the JSObject, neither of which cares about
   // compartments.
 
-  JSClass *clazz = JS_GetClass(obj);
+  const JSClass *clazz = JS_GetClass(obj);
 
   if (clazz == &sNPObjectJSWrapperClass) {
     // obj is one of our own, its private data is the NPObject we're
     // looking for.
 
     NPObject *npobj = (NPObject *)::JS_GetPrivate(obj);
 
     // If the private is null, that means that the object has already been torn
--- a/dom/plugins/base/nsJSNPRuntime.h
+++ b/dom/plugins/base/nsJSNPRuntime.h
@@ -25,17 +25,17 @@ public:
   {
   }
 
   JSObject *mJSObj;
 
   const NPP mNpp;
 };
 
-extern JSClass sNPObjectJSWrapperClass;
+extern const JSClass sNPObjectJSWrapperClass;
 
 class nsJSObjWrapper : public NPObject,
                        public nsJSObjWrapperKey
 {
 public:
   static NPObject *GetNewOrUsed(NPP npp, JSContext *cx,
                                 JS::Handle<JSObject*> obj);
 
--- a/dom/promise/Promise.cpp
+++ b/dom/promise/Promise.cpp
@@ -4,17 +4,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/Promise.h"
 
 #include "jsfriendapi.h"
 #include "mozilla/dom/OwningNonNull.h"
 #include "mozilla/dom/PromiseBinding.h"
-#include "mozilla/dom/PromiseResolver.h"
 #include "mozilla/Preferences.h"
 #include "PromiseCallback.h"
 #include "nsContentUtils.h"
 #include "nsPIDOMWindow.h"
 #include "WorkerPrivate.h"
 #include "nsJSPrincipals.h"
 #include "nsJSUtils.h"
 #include "nsPIDOMWindow.h"
@@ -47,33 +46,83 @@ public:
     mPromise->RunTask();
     return NS_OK;
   }
 
 private:
   nsRefPtr<Promise> mPromise;
 };
 
+// This class processes the promise's callbacks with promise's result.
+class PromiseResolverTask MOZ_FINAL : public nsRunnable
+{
+public:
+  PromiseResolverTask(Promise* aPromise,
+                      JS::Handle<JS::Value> aValue,
+                      Promise::PromiseState aState)
+    : mPromise(aPromise)
+    , mValue(aValue)
+    , mState(aState)
+  {
+    MOZ_ASSERT(aPromise);
+    MOZ_ASSERT(mState != Promise::Pending);
+    MOZ_COUNT_CTOR(PromiseResolverTask);
+
+    JSContext* cx = nsContentUtils::GetSafeJSContext();
+
+    /* It's safe to use unsafeGet() here: the unsafeness comes from the
+     * possibility of updating the value of mJSObject without triggering the
+     * barriers.  However if the value will always be marked, post barriers
+     * unnecessary. */
+    JS_AddNamedValueRootRT(JS_GetRuntime(cx), mValue.unsafeGet(),
+                           "PromiseResolverTask.mValue");
+  }
+
+  ~PromiseResolverTask()
+  {
+    MOZ_COUNT_DTOR(PromiseResolverTask);
+
+    JSContext* cx = nsContentUtils::GetSafeJSContext();
+
+    /* It's safe to use unsafeGet() here: the unsafeness comes from the
+     * possibility of updating the value of mJSObject without triggering the
+     * barriers.  However if the value will always be marked, post barriers
+     * unnecessary. */
+    JS_RemoveValueRootRT(JS_GetRuntime(cx), mValue.unsafeGet());
+  }
+
+  NS_IMETHOD Run()
+  {
+    mPromise->RunResolveTask(
+      JS::Handle<JS::Value>::fromMarkedLocation(mValue.address()),
+      mState, Promise::SyncTask);
+    return NS_OK;
+  }
+
+private:
+  nsRefPtr<Promise> mPromise;
+  JS::Heap<JS::Value> mValue;
+  Promise::PromiseState mState;
+};
+
 // Promise
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(Promise)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Promise)
   tmp->MaybeReportRejected();
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mResolver)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mResolveCallbacks);
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mRejectCallbacks);
   tmp->mResult = JS::UndefinedValue();
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Promise)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResolver)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResolveCallbacks);
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRejectCallbacks);
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Promise)
   NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mResult)
   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
@@ -88,22 +137,21 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
 NS_INTERFACE_MAP_END
 
 Promise::Promise(nsPIDOMWindow* aWindow)
   : mWindow(aWindow)
   , mResult(JS::UndefinedValue())
   , mState(Pending)
   , mTaskPending(false)
   , mHadRejectCallback(false)
+  , mResolvePending(false)
 {
   MOZ_COUNT_CTOR(Promise);
   mozilla::HoldJSObjects(this);
   SetIsDOMBinding();
-
-  mResolver = new PromiseResolver(this);
 }
 
 Promise::~Promise()
 {
   MaybeReportRejected();
   mResult = JS::UndefinedValue();
   mozilla::DropJSObjects(this);
   MOZ_COUNT_DTOR(Promise);
@@ -147,40 +195,117 @@ EnterCompartment(Maybe<JSAutoCompartment
 {
   // FIXME Bug 878849
   if (aValue.WasPassed() && aValue.Value().isObject()) {
     JS::Rooted<JSObject*> rooted(aCx, &aValue.Value().toObject());
     aAc.construct(aCx, rooted);
   }
 }
 
+enum {
+  SLOT_PROMISE = 0,
+  SLOT_TASK
+};
+
+/* static */ bool
+Promise::JSCallback(JSContext *aCx, unsigned aArgc, JS::Value *aVp)
+{
+  JS::CallArgs args = CallArgsFromVp(aArgc, aVp);
+
+  JS::Rooted<JS::Value> v(aCx,
+                          js::GetFunctionNativeReserved(&args.callee(),
+                                                        SLOT_PROMISE));
+  MOZ_ASSERT(v.isObject());
+
+  Promise* promise;
+  if (NS_FAILED(UNWRAP_OBJECT(Promise, aCx, &v.toObject(), promise))) {
+    return Throw(aCx, NS_ERROR_UNEXPECTED);
+  }
+
+  Optional<JS::Handle<JS::Value> > value(aCx);
+  if (aArgc) {
+    value.Value() = args[0];
+  }
+
+  v = js::GetFunctionNativeReserved(&args.callee(), SLOT_TASK);
+  PromiseCallback::Task task = static_cast<PromiseCallback::Task>(v.toInt32());
+
+  if (task == PromiseCallback::Resolve) {
+    promise->MaybeResolve(aCx, value);
+  } else {
+    promise->MaybeReject(aCx, value);
+  }
+
+  return true;
+}
+
+/* static */ JSObject*
+Promise::CreateFunction(JSContext* aCx, JSObject* aParent, Promise* aPromise,
+                        int32_t aTask)
+{
+  JSFunction* func = js::NewFunctionWithReserved(aCx, JSCallback,
+                                                 1 /* nargs */, 0 /* flags */,
+                                                 aParent, nullptr);
+  if (!func) {
+    return nullptr;
+  }
+
+  JS::Rooted<JSObject*> obj(aCx, JS_GetFunctionObject(func));
+
+  JS::Rooted<JS::Value> promiseObj(aCx);
+  if (!dom::WrapNewBindingObject(aCx, obj, aPromise, &promiseObj)) {
+    return nullptr;
+  }
+
+  js::SetFunctionNativeReserved(obj, SLOT_PROMISE, promiseObj);
+  js::SetFunctionNativeReserved(obj, SLOT_TASK, JS::Int32Value(aTask));
+
+  return obj;
+}
+
 /* static */ already_AddRefed<Promise>
 Promise::Constructor(const GlobalObject& aGlobal,
                      PromiseInit& aInit, ErrorResult& aRv)
 {
   JSContext* cx = aGlobal.GetContext();
   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
   if (!window) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
   nsRefPtr<Promise> promise = new Promise(window);
 
-  aInit.Call(promise, *promise->mResolver, aRv,
+  JS::Rooted<JSObject*> resolveFunc(cx,
+                                    CreateFunction(cx, aGlobal.Get(), promise,
+                                                   PromiseCallback::Resolve));
+  if (!resolveFunc) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return nullptr;
+  }
+
+  JS::Rooted<JSObject*> rejectFunc(cx,
+                                   CreateFunction(cx, aGlobal.Get(), promise,
+                                                  PromiseCallback::Reject));
+  if (!rejectFunc) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return nullptr;
+  }
+
+  aInit.Call(promise, resolveFunc, rejectFunc, aRv,
              CallbackObject::eRethrowExceptions);
   aRv.WouldReportJSException();
 
   if (aRv.IsJSException()) {
     Optional<JS::Handle<JS::Value> > value(cx);
     aRv.StealJSException(cx, &value.Value());
 
     Maybe<JSAutoCompartment> ac;
     EnterCompartment(ac, cx, value);
-    promise->mResolver->Reject(cx, value);
+    promise->MaybeReject(cx, value);
   }
 
   return promise.forget();
 }
 
 /* static */ already_AddRefed<Promise>
 Promise::Resolve(const GlobalObject& aGlobal, JSContext* aCx,
                  JS::Handle<JS::Value> aValue, ErrorResult& aRv)
@@ -189,52 +314,52 @@ Promise::Resolve(const GlobalObject& aGl
   if (!window) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
   nsRefPtr<Promise> promise = new Promise(window);
 
   Optional<JS::Handle<JS::Value> > value(aCx, aValue);
-  promise->mResolver->Resolve(aCx, value);
+  promise->MaybeResolve(aCx, value);
   return promise.forget();
 }
 
 /* static */ already_AddRefed<Promise>
 Promise::Reject(const GlobalObject& aGlobal, JSContext* aCx,
                 JS::Handle<JS::Value> aValue, ErrorResult& aRv)
 {
   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
   if (!window) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
   nsRefPtr<Promise> promise = new Promise(window);
 
   Optional<JS::Handle<JS::Value> > value(aCx, aValue);
-  promise->mResolver->Reject(aCx, value);
+  promise->MaybeReject(aCx, value);
   return promise.forget();
 }
 
 already_AddRefed<Promise>
 Promise::Then(const Optional<OwningNonNull<AnyCallback> >& aResolveCallback,
               const Optional<OwningNonNull<AnyCallback> >& aRejectCallback)
 {
   nsRefPtr<Promise> promise = new Promise(GetParentObject());
 
   nsRefPtr<PromiseCallback> resolveCb =
-    PromiseCallback::Factory(promise->mResolver,
+    PromiseCallback::Factory(promise,
                              aResolveCallback.WasPassed()
                                ? &aResolveCallback.Value()
                                : nullptr,
                              PromiseCallback::Resolve);
 
   nsRefPtr<PromiseCallback> rejectCb =
-    PromiseCallback::Factory(promise->mResolver,
+    PromiseCallback::Factory(promise,
                              aRejectCallback.WasPassed()
                                ? &aRejectCallback.Value()
                                : nullptr,
                              PromiseCallback::Reject);
 
   AppendCallbacks(resolveCb, rejectCb);
 
   return promise.forget();
@@ -255,19 +380,19 @@ Promise::AppendCallbacks(PromiseCallback
     mResolveCallbacks.AppendElement(aResolveCallback);
   }
 
   if (aRejectCallback) {
     mHadRejectCallback = true;
     mRejectCallbacks.AppendElement(aRejectCallback);
   }
 
-  // If promise's state is resolved, queue a task to process promise's resolve
-  // callbacks with promise's result. If promise's state is rejected, queue a task
-  // to process promise's reject callbacks with promise's result.
+  // If promise's state is resolved, queue a task to process our resolve
+  // callbacks with promise's result. If promise's state is rejected, queue a
+  // task to process our reject callbacks with promise's result.
   if (mState != Pending && !mTaskPending) {
     nsRefPtr<PromiseTask> task = new PromiseTask(this);
     NS_DispatchToCurrentThread(task);
     mTaskPending = true;
   }
 }
 
 void
@@ -310,10 +435,96 @@ Promise::MaybeReportRejected()
   NS_DispatchToCurrentThread(
     new AsyncErrorReporter(JS_GetObjectRuntime(&mResult.toObject()),
                            report,
                            nullptr,
                            nsContentUtils::GetObjectPrincipal(&mResult.toObject()),
                            win));
 }
 
+void
+Promise::MaybeResolve(JSContext* aCx,
+                      const Optional<JS::Handle<JS::Value> >& aValue,
+                      PromiseTaskSync aAsynchronous)
+{
+  if (mResolvePending) {
+    return;
+  }
+
+  ResolveInternal(aCx, aValue, aAsynchronous);
+}
+
+void
+Promise::MaybeReject(JSContext* aCx,
+                     const Optional<JS::Handle<JS::Value> >& aValue,
+                     PromiseTaskSync aAsynchronous)
+{
+  if (mResolvePending) {
+    return;
+  }
+
+  RejectInternal(aCx, aValue, aAsynchronous);
+}
+
+void
+Promise::ResolveInternal(JSContext* aCx,
+                         const Optional<JS::Handle<JS::Value> >& aValue,
+                         PromiseTaskSync aAsynchronous)
+{
+  mResolvePending = true;
+
+  // TODO: Bug 879245 - Then-able objects
+  if (aValue.WasPassed() && aValue.Value().isObject()) {
+    JS::Rooted<JSObject*> valueObj(aCx, &aValue.Value().toObject());
+    Promise* nextPromise;
+    nsresult rv = UNWRAP_OBJECT(Promise, aCx, valueObj, nextPromise);
+
+    if (NS_SUCCEEDED(rv)) {
+      nsRefPtr<PromiseCallback> resolveCb = new ResolvePromiseCallback(this);
+      nsRefPtr<PromiseCallback> rejectCb = new RejectPromiseCallback(this);
+      nextPromise->AppendCallbacks(resolveCb, rejectCb);
+      return;
+    }
+  }
+
+  // If the synchronous flag is set, process our resolve callbacks with
+  // value. Otherwise, the synchronous flag is unset, queue a task to process
+  // own resolve callbacks with value. Otherwise, the synchronous flag is
+  // unset, queue a task to process our resolve callbacks with value.
+  RunResolveTask(aValue.WasPassed() ? aValue.Value() : JS::UndefinedHandleValue,
+                 Resolved, aAsynchronous);
+}
+
+void
+Promise::RejectInternal(JSContext* aCx,
+                        const Optional<JS::Handle<JS::Value> >& aValue,
+                        PromiseTaskSync aAsynchronous)
+{
+  mResolvePending = true;
+
+  // If the synchronous flag is set, process our reject callbacks with
+  // value. Otherwise, the synchronous flag is unset, queue a task to process
+  // promise's reject callbacks with value.
+  RunResolveTask(aValue.WasPassed() ? aValue.Value() : JS::UndefinedHandleValue,
+                 Rejected, aAsynchronous);
+}
+
+void
+Promise::RunResolveTask(JS::Handle<JS::Value> aValue,
+                        PromiseState aState,
+                        PromiseTaskSync aAsynchronous)
+{
+  // If the synchronous flag is unset, queue a task to process our
+  // accept callbacks with value.
+  if (aAsynchronous == AsyncTask) {
+    nsRefPtr<PromiseResolverTask> task =
+      new PromiseResolverTask(this, aValue, aState);
+    NS_DispatchToCurrentThread(task);
+    return;
+  }
+
+  SetResult(aValue);
+  SetState(aState);
+  RunTask();
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/promise/Promise.h
+++ b/dom/promise/Promise.h
@@ -18,24 +18,25 @@
 #include "js/TypeDecls.h"
 
 namespace mozilla {
 namespace dom {
 
 class PromiseInit;
 class PromiseCallback;
 class AnyCallback;
-class PromiseResolver;
 
 class Promise MOZ_FINAL : public nsISupports,
                           public nsWrapperCache
 {
   friend class PromiseTask;
-  friend class PromiseResolver;
   friend class PromiseResolverTask;
+  friend class ResolvePromiseCallback;
+  friend class RejectPromiseCallback;
+  friend class WrapperPromiseCallback;
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Promise)
 
   Promise(nsPIDOMWindow* aWindow);
   ~Promise();
 
@@ -74,16 +75,21 @@ public:
 
 private:
   enum PromiseState {
     Pending,
     Resolved,
     Rejected
   };
 
+  enum PromiseTaskSync {
+    SyncTask,
+    AsyncTask
+  };
+
   void SetState(PromiseState aState)
   {
     MOZ_ASSERT(mState == Pending);
     MOZ_ASSERT(aState != Pending);
     mState = aState;
   }
 
   void SetResult(JS::Handle<JS::Value> aValue)
@@ -92,32 +98,58 @@ private:
   }
 
   // This method processes promise's resolve/reject callbacks with promise's
   // result. It's executed when the resolver.resolve() or resolver.reject() is
   // called or when the promise already has a result and new callbacks are
   // appended by then(), catch() or done().
   void RunTask();
 
+  void RunResolveTask(JS::Handle<JS::Value> aValue,
+                      Promise::PromiseState aState,
+                      PromiseTaskSync aAsynchronous);
+
   void AppendCallbacks(PromiseCallback* aResolveCallback,
                        PromiseCallback* aRejectCallback);
 
   // If we have been rejected and our mResult is a JS exception,
   // report it to the error console.
   void MaybeReportRejected();
 
-  nsRefPtr<nsPIDOMWindow> mWindow;
+  void MaybeResolve(JSContext* aCx,
+                    const Optional<JS::Handle<JS::Value> >& aValue,
+                    PromiseTaskSync aSync = AsyncTask);
+  void MaybeReject(JSContext* aCx,
+                   const Optional<JS::Handle<JS::Value> >& aValue,
+                   PromiseTaskSync aSync = AsyncTask);
+
+  void ResolveInternal(JSContext* aCx,
+                       const Optional<JS::Handle<JS::Value> >& aValue,
+                       PromiseTaskSync aSync = AsyncTask);
 
-  nsRefPtr<PromiseResolver> mResolver;
+  void RejectInternal(JSContext* aCx,
+                      const Optional<JS::Handle<JS::Value> >& aValue,
+                      PromiseTaskSync aSync = AsyncTask);
+
+  // Static methods for the PromiseInit functions.
+  static bool
+  JSCallback(JSContext *aCx, unsigned aArgc, JS::Value *aVp);
+  static JSObject*
+  CreateFunction(JSContext* aCx, JSObject* aParent, Promise* aPromise,
+                int32_t aTask);
+
+  nsRefPtr<nsPIDOMWindow> mWindow;
 
   nsTArray<nsRefPtr<PromiseCallback> > mResolveCallbacks;
   nsTArray<nsRefPtr<PromiseCallback> > mRejectCallbacks;
 
   JS::Heap<JS::Value> mResult;
   PromiseState mState;
   bool mTaskPending;
   bool mHadRejectCallback;
+
+  bool mResolvePending;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_Promise_h
--- a/dom/promise/PromiseCallback.cpp
+++ b/dom/promise/PromiseCallback.cpp
@@ -1,17 +1,16 @@
 /* -*- 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 "PromiseCallback.h"
 #include "mozilla/dom/Promise.h"
-#include "mozilla/dom/PromiseResolver.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(PromiseCallback)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(PromiseCallback)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PromiseCallback)
@@ -46,100 +45,100 @@ EnterCompartment(Maybe<JSAutoCompartment
     aAc.construct(aCx, rooted);
   }
 }
 
 // ResolvePromiseCallback
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED_1(ResolvePromiseCallback,
                                      PromiseCallback,
-                                     mResolver)
+                                     mPromise)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ResolvePromiseCallback)
 NS_INTERFACE_MAP_END_INHERITING(PromiseCallback)
 
 NS_IMPL_ADDREF_INHERITED(ResolvePromiseCallback, PromiseCallback)
 NS_IMPL_RELEASE_INHERITED(ResolvePromiseCallback, PromiseCallback)
 
-ResolvePromiseCallback::ResolvePromiseCallback(PromiseResolver* aResolver)
-  : mResolver(aResolver)
+ResolvePromiseCallback::ResolvePromiseCallback(Promise* aPromise)
+  : mPromise(aPromise)
 {
-  MOZ_ASSERT(aResolver);
+  MOZ_ASSERT(aPromise);
   MOZ_COUNT_CTOR(ResolvePromiseCallback);
 }
 
 ResolvePromiseCallback::~ResolvePromiseCallback()
 {
   MOZ_COUNT_DTOR(ResolvePromiseCallback);
 }
 
 void
 ResolvePromiseCallback::Call(const Optional<JS::Handle<JS::Value> >& aValue)
 {
   // Run resolver's algorithm with value and the synchronous flag set.
   AutoJSContext cx;
   Maybe<JSAutoCompartment> ac;
   EnterCompartment(ac, cx, aValue);
 
-  mResolver->ResolveInternal(cx, aValue, PromiseResolver::SyncTask);
+  mPromise->ResolveInternal(cx, aValue, Promise::SyncTask);
 }
 
 // RejectPromiseCallback
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED_1(RejectPromiseCallback,
                                      PromiseCallback,
-                                     mResolver)
+                                     mPromise)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(RejectPromiseCallback)
 NS_INTERFACE_MAP_END_INHERITING(PromiseCallback)
 
 NS_IMPL_ADDREF_INHERITED(RejectPromiseCallback, PromiseCallback)
 NS_IMPL_RELEASE_INHERITED(RejectPromiseCallback, PromiseCallback)
 
-RejectPromiseCallback::RejectPromiseCallback(PromiseResolver* aResolver)
-  : mResolver(aResolver)
+RejectPromiseCallback::RejectPromiseCallback(Promise* aPromise)
+  : mPromise(aPromise)
 {
-  MOZ_ASSERT(aResolver);
+  MOZ_ASSERT(aPromise);
   MOZ_COUNT_CTOR(RejectPromiseCallback);
 }
 
 RejectPromiseCallback::~RejectPromiseCallback()
 {
   MOZ_COUNT_DTOR(RejectPromiseCallback);
 }
 
 void
 RejectPromiseCallback::Call(const Optional<JS::Handle<JS::Value> >& aValue)
 {
   // Run resolver's algorithm with value and the synchronous flag set.
   AutoJSContext cx;
   Maybe<JSAutoCompartment> ac;
   EnterCompartment(ac, cx, aValue);
 
-  mResolver->RejectInternal(cx, aValue, PromiseResolver::SyncTask);
+  mPromise->RejectInternal(cx, aValue, Promise::SyncTask);
 }
 
 // WrapperPromiseCallback
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED_2(WrapperPromiseCallback,
                                      PromiseCallback,
-                                     mNextResolver, mCallback)
+                                     mNextPromise, mCallback)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(WrapperPromiseCallback)
 NS_INTERFACE_MAP_END_INHERITING(PromiseCallback)
 
 NS_IMPL_ADDREF_INHERITED(WrapperPromiseCallback, PromiseCallback)
 NS_IMPL_RELEASE_INHERITED(WrapperPromiseCallback, PromiseCallback)
 
-WrapperPromiseCallback::WrapperPromiseCallback(PromiseResolver* aNextResolver,
+WrapperPromiseCallback::WrapperPromiseCallback(Promise* aNextPromise,
                                                AnyCallback* aCallback)
-  : mNextResolver(aNextResolver)
+  : mNextPromise(aNextPromise)
   , mCallback(aCallback)
 {
-  MOZ_ASSERT(aNextResolver);
+  MOZ_ASSERT(aNextPromise);
   MOZ_COUNT_CTOR(WrapperPromiseCallback);
 }
 
 WrapperPromiseCallback::~WrapperPromiseCallback()
 {
   MOZ_COUNT_DTOR(WrapperPromiseCallback);
 }
 
@@ -150,36 +149,36 @@ WrapperPromiseCallback::Call(const Optio
   Maybe<JSAutoCompartment> ac;
   EnterCompartment(ac, cx, aValue);
 
   ErrorResult rv;
 
   // If invoking callback threw an exception, run resolver's reject with the
   // thrown exception as argument and the synchronous flag set.
   Optional<JS::Handle<JS::Value> > value(cx,
-    mCallback->Call(mNextResolver->GetParentObject(), aValue, rv,
+    mCallback->Call(mNextPromise->GetParentObject(), aValue, rv,
                     CallbackObject::eRethrowExceptions));
 
   rv.WouldReportJSException();
 
   if (rv.Failed() && rv.IsJSException()) {
     Optional<JS::Handle<JS::Value> > value(cx);
     rv.StealJSException(cx, &value.Value());
 
     Maybe<JSAutoCompartment> ac2;
     EnterCompartment(ac2, cx, value);
-    mNextResolver->RejectInternal(cx, value, PromiseResolver::SyncTask);
+    mNextPromise->RejectInternal(cx, value, Promise::SyncTask);
     return;
   }
 
   // Otherwise, run resolver's resolve with value and the synchronous flag
   // set.
   Maybe<JSAutoCompartment> ac2;
   EnterCompartment(ac2, cx, value);
-  mNextResolver->ResolveInternal(cx, value, PromiseResolver::SyncTask);
+  mNextPromise->ResolveInternal(cx, value, Promise::SyncTask);
 }
 
 // SimpleWrapperPromiseCallback
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED_2(SimpleWrapperPromiseCallback,
                                      PromiseCallback,
                                      mPromise, mCallback)
 
@@ -206,33 +205,33 @@ SimpleWrapperPromiseCallback::~SimpleWra
 void
 SimpleWrapperPromiseCallback::Call(const Optional<JS::Handle<JS::Value> >& aValue)
 {
   ErrorResult rv;
   mCallback->Call(mPromise, aValue, rv);
 }
 
 /* static */ PromiseCallback*
-PromiseCallback::Factory(PromiseResolver* aNextResolver,
-                         AnyCallback* aCallback, Task aTask)
+PromiseCallback::Factory(Promise* aNextPromise, AnyCallback* aCallback,
+                         Task aTask)
 {
-  MOZ_ASSERT(aNextResolver);
+  MOZ_ASSERT(aNextPromise);
 
   // If we have a callback and a next resolver, we have to exec the callback and
   // then propagate the return value to the next resolver->resolve().
   if (aCallback) {
-    return new WrapperPromiseCallback(aNextResolver, aCallback);
+    return new WrapperPromiseCallback(aNextPromise, aCallback);
   }
 
   if (aTask == Resolve) {
-    return new ResolvePromiseCallback(aNextResolver);
+    return new ResolvePromiseCallback(aNextPromise);
   }
 
   if (aTask == Reject) {
-    return new RejectPromiseCallback(aNextResolver);
+    return new RejectPromiseCallback(aNextPromise);
   }
 
   MOZ_ASSERT(false, "This should not happen");
   return nullptr;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/promise/PromiseCallback.h
+++ b/dom/promise/PromiseCallback.h
@@ -8,18 +8,16 @@
 #define mozilla_dom_PromiseCallback_h
 
 #include "mozilla/dom/Promise.h"
 #include "nsCycleCollectionParticipant.h"
 
 namespace mozilla {
 namespace dom {
 
-class PromiseResolver;
-
 // This is the base class for any PromiseCallback.
 // It's a logical step in the promise chain of callbacks.
 class PromiseCallback : public nsISupports
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS(PromiseCallback)
 
@@ -30,38 +28,36 @@ public:
 
   enum Task {
     Resolve,
     Reject
   };
 
   // This factory returns a PromiseCallback object with refcount of 0.
   static PromiseCallback*
-  Factory(PromiseResolver* aNextResolver, AnyCallback* aCallback,
-          Task aTask);
+  Factory(Promise* aNextPromise, AnyCallback* aCallback, Task aTask);
 };
 
 // WrapperPromiseCallback execs a JS Callback with a value, and then the return
-// value is sent to the aNextResolver->resolve() or to aNextResolver->Reject()
-// if the JS Callback throws.
+// value is sent to the aNextPromise->resolveFunction() or to
+// aNextPromise->RejectFunction() if the JS Callback throws.
 class WrapperPromiseCallback MOZ_FINAL : public PromiseCallback
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(WrapperPromiseCallback,
                                            PromiseCallback)
 
   void Call(const Optional<JS::Handle<JS::Value> >& aValue) MOZ_OVERRIDE;
 
-  WrapperPromiseCallback(PromiseResolver* aNextResolver,
-                         AnyCallback* aCallback);
+  WrapperPromiseCallback(Promise* aNextPromise, AnyCallback* aCallback);
   ~WrapperPromiseCallback();
 
 private:
-  nsRefPtr<PromiseResolver> mNextResolver;
+  nsRefPtr<Promise> mNextPromise;
   nsRefPtr<AnyCallback> mCallback;
 };
 
 // SimpleWrapperPromiseCallback execs a JS Callback with a value.
 class SimpleWrapperPromiseCallback MOZ_FINAL : public PromiseCallback
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
@@ -74,48 +70,48 @@ public:
                                AnyCallback* aCallback);
   ~SimpleWrapperPromiseCallback();
 
 private:
   nsRefPtr<Promise> mPromise;
   nsRefPtr<AnyCallback> mCallback;
 };
 
-// ResolvePromiseCallback calls aResolver->Resolve() with the value received by
-// Call().
+// ResolvePromiseCallback calls aPromise->ResolveFunction() with the value
+// received by Call().
 class ResolvePromiseCallback MOZ_FINAL : public PromiseCallback
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ResolvePromiseCallback,
                                            PromiseCallback)
 
   void Call(const Optional<JS::Handle<JS::Value> >& aValue) MOZ_OVERRIDE;
 
-  ResolvePromiseCallback(PromiseResolver* aResolver);
+  ResolvePromiseCallback(Promise* aPromise);
   ~ResolvePromiseCallback();
 
 private:
-  nsRefPtr<PromiseResolver> mResolver;
+  nsRefPtr<Promise> mPromise;
 };
 
-// RejectPromiseCallback calls aResolver->Reject() with the value received by
-// Call().
+// RejectPromiseCallback calls aPromise->RejectFunction() with the value
+// received by Call().
 class RejectPromiseCallback MOZ_FINAL : public PromiseCallback
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(RejectPromiseCallback,
                                            PromiseCallback)
 
   void Call(const Optional<JS::Handle<JS::Value> >& aValue) MOZ_OVERRIDE;
 
-  RejectPromiseCallback(PromiseResolver* aResolver);
+  RejectPromiseCallback(Promise* aPromise);
   ~RejectPromiseCallback();
 
 private:
-  nsRefPtr<PromiseResolver> mResolver;
+  nsRefPtr<Promise> mPromise;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_PromiseCallback_h
deleted file mode 100644
--- a/dom/promise/PromiseResolver.cpp
+++ /dev/null
@@ -1,173 +0,0 @@
-/* -*- 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 "mozilla/dom/PromiseResolver.h"
-#include "mozilla/dom/PromiseBinding.h"
-#include "mozilla/dom/Promise.h"
-#include "nsThreadUtils.h"
-#include "PromiseCallback.h"
-
-namespace mozilla {
-namespace dom {
-
-// PromiseResolverTask
-
-// This class processes the promise's callbacks with promise's result.
-class PromiseResolverTask MOZ_FINAL : public nsRunnable
-{
-public:
-  PromiseResolverTask(PromiseResolver* aResolver,
-                      const JS::Handle<JS::Value> aValue,
-                      Promise::PromiseState aState)
-    : mResolver(aResolver)
-    , mValue(aValue)
-    , mState(aState)
-  {
-    MOZ_ASSERT(aResolver);
-    MOZ_ASSERT(mState != Promise::Pending);
-    MOZ_COUNT_CTOR(PromiseResolverTask);
-
-    JSContext* cx = nsContentUtils::GetSafeJSContext();
-    JS_AddNamedValueRootRT(JS_GetRuntime(cx), &mValue,
-                           "PromiseResolverTask.mValue");
-  }
-
-  ~PromiseResolverTask()
-  {
-    MOZ_COUNT_DTOR(PromiseResolverTask);
-
-    JSContext* cx = nsContentUtils::GetSafeJSContext();
-    JS_RemoveValueRootRT(JS_GetRuntime(cx), &mValue);
-  }
-
-  NS_IMETHOD Run()
-  {
-    mResolver->RunTask(JS::Handle<JS::Value>::fromMarkedLocation(&mValue),
-                       mState, PromiseResolver::SyncTask);
-    return NS_OK;
-  }
-
-private:
-  nsRefPtr<PromiseResolver> mResolver;
-  JS::Value mValue;
-  Promise::PromiseState mState;
-};
-
-// PromiseResolver
-
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(PromiseResolver, mPromise)
-
-NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(PromiseResolver, AddRef)
-NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(PromiseResolver, Release)
-
-PromiseResolver::PromiseResolver(Promise* aPromise)
-  : mPromise(aPromise)
-  , mResolvePending(false)
-{
-  MOZ_COUNT_CTOR(PromiseResolver);
-  SetIsDOMBinding();
-}
-
-PromiseResolver::~PromiseResolver()
-{
-  MOZ_COUNT_DTOR(PromiseResolver);
-}
-
-JSObject*
-PromiseResolver::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
-{
-  return PromiseResolverBinding::Wrap(aCx, aScope, this);
-}
-
-void
-PromiseResolver::Resolve(JSContext* aCx,
-                         const Optional<JS::Handle<JS::Value> >& aValue,
-                         PromiseTaskSync aAsynchronous)
-{
-  if (mResolvePending) {
-    return;
-  }
-
-  ResolveInternal(aCx, aValue, aAsynchronous);
-}
-
-void
-PromiseResolver::ResolveInternal(JSContext* aCx,
-                                 const Optional<JS::Handle<JS::Value> >& aValue,
-                                 PromiseTaskSync aAsynchronous)
-{
-  mResolvePending = true;
-
-  // TODO: Bug 879245 - Then-able objects
-  if (aValue.WasPassed() && aValue.Value().isObject()) {
-    JS::Rooted<JSObject*> valueObj(aCx, &aValue.Value().toObject());
-    Promise* nextPromise;
-    nsresult rv = UNWRAP_OBJECT(Promise, aCx, valueObj, nextPromise);
-
-    if (NS_SUCCEEDED(rv)) {
-      nsRefPtr<PromiseCallback> resolveCb = new ResolvePromiseCallback(this);
-      nsRefPtr<PromiseCallback> rejectCb = new RejectPromiseCallback(this);
-      nextPromise->AppendCallbacks(resolveCb, rejectCb);
-      return;
-    }
-  }
-
-  // If the synchronous flag is set, process promise's resolve callbacks with
-  // value. Otherwise, the synchronous flag is unset, queue a task to process
-  // promise's resolve callbacks with value. Otherwise, the synchronous flag is
-  // unset, queue a task to process promise's resolve callbacks with value.
-  RunTask(aValue.WasPassed() ? aValue.Value() : JS::UndefinedHandleValue,
-          Promise::Resolved, aAsynchronous);
-}
-
-void
-PromiseResolver::Reject(JSContext* aCx,
-                        const Optional<JS::Handle<JS::Value> >& aValue,
-                        PromiseTaskSync aAsynchronous)
-{
-  if (mResolvePending) {
-    return;
-  }
-
-  RejectInternal(aCx, aValue, aAsynchronous);
-}
-
-void
-PromiseResolver::RejectInternal(JSContext* aCx,
-                                const Optional<JS::Handle<JS::Value> >& aValue,
-                                PromiseTaskSync aAsynchronous)
-{
-  mResolvePending = true;
-
-  // If the synchronous flag is set, process promise's reject callbacks with
-  // value. Otherwise, the synchronous flag is unset, queue a task to process
-  // promise's reject callbacks with value.
-  RunTask(aValue.WasPassed() ? aValue.Value() : JS::UndefinedHandleValue,
-          Promise::Rejected, aAsynchronous);
-}
-
-void
-PromiseResolver::RunTask(JS::Handle<JS::Value> aValue,
-                         Promise::PromiseState aState,
-                         PromiseTaskSync aAsynchronous)
-{
-  // If the synchronous flag is unset, queue a task to process promise's
-  // accept callbacks with value.
-  if (aAsynchronous == AsyncTask) {
-    nsRefPtr<PromiseResolverTask> task =
-      new PromiseResolverTask(this, aValue, aState);
-    NS_DispatchToCurrentThread(task);
-    return;
-  }
-
-  mPromise->SetResult(aValue);
-  mPromise->SetState(aState);
-  mPromise->RunTask();
-  mPromise = nullptr;
-}
-
-} // namespace dom
-} // namespace mozilla
deleted file mode 100644
--- a/dom/promise/PromiseResolver.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/* -*- 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_PromiseResolver_h
-#define mozilla_dom_PromiseResolver_h
-
-#include "mozilla/dom/Promise.h"
-#include "mozilla/Attributes.h"
-#include "mozilla/dom/BindingDeclarations.h"
-#include "nsCycleCollectionParticipant.h"
-#include "nsWrapperCache.h"
-#include "js/TypeDecls.h"
-
-namespace mozilla {
-namespace dom {
-
-class PromiseResolver MOZ_FINAL : public nsWrapperCache
-{
-  friend class PromiseResolverTask;
-  friend class WrapperPromiseCallback;
-  friend class ResolvePromiseCallback;
-  friend class RejectPromiseCallback;
-
-private:
-  enum PromiseTaskSync {
-    SyncTask,
-    AsyncTask
-  };
-
-public:
-  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(PromiseResolver)
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(PromiseResolver)
-
-  PromiseResolver(Promise* aPromise);
-  virtual ~PromiseResolver();
-
-  Promise* GetParentObject() const
-  {
-    return mPromise;
-  }
-
-  virtual JSObject*
-  WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
-
-  void Resolve(JSContext* aCx, const Optional<JS::Handle<JS::Value> >& aValue,
-               PromiseTaskSync aSync = AsyncTask);
-
-  void Reject(JSContext* aCx, const Optional<JS::Handle<JS::Value> >& aValue,
-              PromiseTaskSync aSync = AsyncTask);
-
-private:
-  void ResolveInternal(JSContext* aCx,
-                       const Optional<JS::Handle<JS::Value> >& aValue,
-                       PromiseTaskSync aSync = AsyncTask);
-
-  void RejectInternal(JSContext* aCx,
-                      const Optional<JS::Handle<JS::Value> >& aValue,
-                      PromiseTaskSync aSync = AsyncTask);
-
-  void RunTask(JS::Handle<JS::Value> aValue,
-               Promise::PromiseState aState, PromiseTaskSync aSync);
-
-  nsRefPtr<Promise> mPromise;
-
-  bool mResolvePending;
-};
-
-} // namespace dom
-} // namespace mozilla
-
-#endif // mozilla_dom_PromiseResolver_h
--- a/dom/promise/moz.build
+++ b/dom/promise/moz.build
@@ -7,22 +7,20 @@
 TEST_DIRS += ['tests']
 
 XPIDL_MODULE = 'dom_promise'
 
 MODULE = 'dom'
 
 EXPORTS.mozilla.dom += [
     'Promise.h',
-    'PromiseResolver.h',
 ]
 
 CPP_SOURCES += [
     'Promise.cpp',
-    'PromiseResolver.cpp',
     'PromiseCallback.cpp',
 ]
 
 FAIL_ON_WARNINGS = True
 
 LIBXUL_LIBRARY = True
 
 LIBRARY_NAME = 'dompromise_s'
--- a/dom/promise/tests/test_bug883683.html
+++ b/dom/promise/tests/test_bug883683.html
@@ -18,20 +18,20 @@
 
 function runTest() {
   [{}, {}, {}, {}, {}].reduce(Promise.reject);
   ok(true, "No leaks with reject?");
 
   [{}, {}, {}, {}, {}].reduce(Promise.resolve);
   ok(true, "No leaks with resolve?");
 
-  [{}, {}, {}, {}, {}].reduce(function(a, b, c, d) { return new Promise(function(r) { throw a; }); });
+  [{}, {}, {}, {}, {}].reduce(function(a, b, c, d) { return new Promise(function(r1, r2) { throw a; }); });
   ok(true, "No leaks with exception?");
 
-  [{}, {}, {}, {}, {}].reduce(function(a, b, c, d) { return new Promise(function(r) { }); });
+  [{}, {}, {}, {}, {}].reduce(function(a, b, c, d) { return new Promise(function(r1, r2) { }); });
   ok(true, "No leaks with empty promise?");
 
   SimpleTest.finish();
 }
 
 SimpleTest.waitForExplicitFinish();
 SpecialPowers.pushPrefEnv({"set": [["dom.promise.enabled", true]]}, runTest);
 // -->
--- a/dom/promise/tests/test_promise.html
+++ b/dom/promise/tests/test_promise.html
@@ -14,96 +14,95 @@
 
 </div>
 <pre id="test">
 <script type="application/javascript"><!--
 
 function promiseResolve() {
   ok(Promise, "Promise object should exist");
 
-  var promise = new Promise(function(resolver) {
-    ok(resolver, "PromiseResolver exists");
-    ok("reject" in resolver, "PromiseResolver.reject exists");
-    ok("resolve" in resolver, "PromiseResolver.resolve exists");
+  var promise = new Promise(function(resolve, reject) {
+    ok(resolve, "Promise.resolve exists");
+    ok(reject, "Promise.reject exists");
 
-    resolver.resolve(42);
+    resolve(42);
   }).then(function(what) {
     ok(true, "Then - resolveCb has been called");
     is(what, 42, "ResolveCb received 42");
     runTest();
   }, function() {
     ok(false, "Then - rejectCb has been called");
     runTest();
   });
 }
 
 function promiseReject() {
-  var promise = new Promise(function(resolver) {
-    resolver.reject(42);
+  var promise = new Promise(function(resolve, reject) {
+    reject(42);
   }).then(function(what) {
     ok(false, "Then - resolveCb has been called");
     runTest();
   }, function(what) {
     ok(true, "Then - rejectCb has been called");
     is(what, 42, "RejectCb received 42");
     runTest();
   });
 }
 
 function promiseException() {
-  var promise = new Promise(function(resolver) {
+  var promise = new Promise(function(resolve, reject) {
     throw 42;
   }).then(function(what) {
     ok(false, "Then - resolveCb has been called");
     runTest();
   }, function(what) {
     ok(true, "Then - rejectCb has been called");
     is(what, 42, "RejectCb received 42");
     runTest();
   });
 }
 
 function promiseGC() {
-  var resolver;
-  var promise = new Promise(function(r) {
-    resolver = r;
+  var resolve;
+  var promise = new Promise(function(r1, r2) {
+    resolve = r1;
   }).then(function(what) {
     ok(true, "Then - promise is still alive");
     runTest();
   });
 
   promise = null;
 
   SpecialPowers.gc();
   SpecialPowers.forceGC();
   SpecialPowers.forceCC();
 
-  resolver.resolve(42);
+  resolve(42);
 }
 
 function promiseAsync() {
   var global = "foo";
-  var f = new Promise(function(r) {
+  var f = new Promise(function(r1, r2) {
     is(global, "foo", "Global should be foo");
-    r.resolve(42);
+    r1(42);
     is(global, "foo", "Global should still be foo");
     setTimeout(function() {
       is(global, "bar", "Global should still be bar!");
       runTest();
     }, 0);
   }).then(function() {
     global = "bar";
   });
   is(global, "foo", "Global should still be foo (2)");
 }
 
 function promiseDoubleThen() {
   var steps = 0;
-  var promise = new Promise(function(resolver) {
-    resolver.resolve(42);
+  var promise = new Promise(function(r1, r2) {
+    r1(42);
   });
 
   promise.then(function(what) {
     ok(true, "Then.resolve has been called");
     is(what, 42, "Value == 42");
     steps++;
   }, function(what) {
     ok(false, "Then.reject has been called");
@@ -115,32 +114,32 @@ function promiseDoubleThen() {
     is(what, 42, "Value == 42");
     runTest();
   }, function(what) {
     ok(false, "Then.reject has been called");
   });
 }
 
 function promiseThenException() {
-  var promise = new Promise(function(resolver) {
-    resolver.resolve(42);
+  var promise = new Promise(function(resolve, reject) {
+    resolve(42);
   });
 
   promise.then(function(what) {
     ok(true, "Then.resolve has been called");
     throw "booh";
   }).catch(function(e) {
     ok(true, "window.onerror has been called!");
     runTest();
   });
 }
 
 function promiseThenCatchThen() {
-  var promise = new Promise(function(resolver) {
-    resolver.resolve(42);
+  var promise = new Promise(function(resolve, reject) {
+    resolve(42);
   });
 
   var promise2 = promise.then(function(what) {
     ok(true, "Then.resolve has been called");
     is(what, 42, "Value == 42");
     return what + 1;
   }, function(what) {
     ok(false, "Then.reject has been called");
@@ -161,18 +160,18 @@ function promiseThenCatchThen() {
     is(what, 44, "Value == 44");
     runTest();
   }, function(what) {
     ok(false, "Then.reject has been called");
   });
 }
 
 function promiseRejectThenCatchThen() {
-  var promise = new Promise(function(resolver) {
-    resolver.reject(42);
+  var promise = new Promise(function(resolve, reject) {
+    reject(42);
   });
 
   var promise2 = promise.then(function(what) {
     ok(false, "Then.resolve has been called");
   }, function(what) {
     ok(true, "Then.reject has been called");
     is(what, 42, "Value == 42");
     return what + 1;
@@ -189,18 +188,18 @@ function promiseRejectThenCatchThen() {
   }).then(function(what) {
     ok(true, "Then.resolve has been called");
     is(what, 44, "Value == 44");
     runTest();
   });
 }
 
 function promiseRejectThenCatchThen2() {
-  var promise = new Promise(function(resolver) {
-    resolver.reject(42);
+  var promise = new Promise(function(resolve, reject) {
+    reject(42);
   });
 
   promise.then(function(what) {
     ok(true, "Then.resolve has been called");
     is(what, 42, "Value == 42");
     return what+1;
   }).catch(function(what) {
     is(what, 42, "Value == 42");
@@ -209,18 +208,18 @@ function promiseRejectThenCatchThen2() {
   }).then(function(what) {
     ok(true, "Then.resolve has been called");
     is(what, 43, "Value == 43");
     runTest();
   });
 }
 
 function promiseRejectThenCatchExceptionThen() {
-  var promise = new Promise(function(resolver) {
-    resolver.reject(42);
+  var promise = new Promise(function(resolve, reject) {
+    reject(42);
   });
 
   promise.then(function(what) {
     ok(false, "Then.resolve has been called");
   }, function(what) {
     ok(true, "Then.reject has been called");
     is(what, 42, "Value == 42");
     throw(what + 1);
@@ -232,18 +231,18 @@ function promiseRejectThenCatchException
     ok(true, "Then.resolve has been called");
     is(what, 44, "Value == 44");
     runTest();
   });
 }
 
 function promiseThenCatchOrderingResolve() {
   var global = 0;
-  var f = new Promise(function(r) {
-    r.resolve(42);
+  var f = new Promise(function(r1, r2) {
+    r1(42);
   });
 
   f.then(function() {
     f.then(function() {
       global++;
     });
     f.catch(function() {
       global++;
@@ -255,18 +254,18 @@ function promiseThenCatchOrderingResolve
       is(global, 2, "Many steps... should return 2");
       runTest();
     }, 0);
   });
 }
 
 function promiseThenCatchOrderingReject() {
   var global = 0;
-  var f = new Promise(function(r) {
-    r.reject(42);
+  var f = new Promise(function(r1, r2) {
+    r2(42);
   })
 
   f.then(function() {}, function() {
     f.then(function() {
       global++;
     });
     f.catch(function() {
       global++;
@@ -277,61 +276,61 @@ function promiseThenCatchOrderingReject(
     setTimeout(function() {
       is(global, 2, "Many steps... should return 2");
       runTest();
     }, 0);
   });
 }
 
 function promiseNestedPromise() {
-  new Promise(function(resolver) {
-    resolver.resolve(new Promise(function(r) {
+  new Promise(function(resolve, reject) {
+    resolve(new Promise(function(resolve, reject) {
       ok(true, "Nested promise is executed");
-      r.resolve(42);
+      resolve(42);
     }));
   }).then(function(value) {
     is(value, 42, "Nested promise is executed and then == 42");
     runTest();
   });
 }
 
 function promiseNestedNestedPromise() {
-  new Promise(function(resolver) {
-    resolver.resolve(new Promise(function(r) {
+  new Promise(function(resolve, reject) {
+    resolve(new Promise(function(resolve, reject) {
       ok(true, "Nested promise is executed");
-      r.resolve(42);
+      resolve(42);
     }).then(function(what) { return what+1; }));
   }).then(function(value) {
     is(value, 43, "Nested promise is executed and then == 43");
     runTest();
   });
 }
 
 function promiseWrongNestedPromise() {
-  new Promise(function(resolver) {
-    resolver.resolve(new Promise(function(r) {
+  new Promise(function(resolve, reject) {
+    resolve(new Promise(function(r, r2) {
       ok(true, "Nested promise is executed");
-      r.resolve(42);
+      r(42);
     }));
-    resolver.reject(42);
+    reject(42);
   }).then(function(value) {
     is(value, 42, "Nested promise is executed and then == 42");
     runTest();
   }, function(value) {
      ok(false, "This is wrong");
   });
 }
 
 function promiseLoop() {
-  new Promise(function(resolver) {
-    resolver.resolve(new Promise(function(r) {
+  new Promise(function(resolve, reject) {
+    resolve(new Promise(function(r1, r2) {
       ok(true, "Nested promise is executed");
-      r.resolve(new Promise(function(r) {
+      r1(new Promise(function(r1, r2) {
         ok(true, "Nested nested promise is executed");
-        r.resolve(42);
+        r1(42);
       }));
     }));
   }).then(function(value) {
     is(value, 42, "Nested nested promise is executed and then == 42");
     runTest();
   }, function(value) {
      ok(false, "This is wrong");
   });
@@ -351,19 +350,19 @@ function promiseResolve() {
     is(what, 42, "Value == 42");
     runTest();
   }, function() {
     ok(false, "This should not be called");
   });
 }
 
 function promiseResolveNestedPromise() {
-  var promise = Promise.resolve(new Promise(function(r) {
+  var promise = Promise.resolve(new Promise(function(r, r2) {
     ok(true, "Nested promise is executed");
-    r.resolve(42);
+    r(42);
   }, function() {
     ok(false, "This should not be called");
   })).then(function(what) {
     is(what, 42, "Value == 42");
     runTest();
   }, function() {
     ok(false, "This should not be called");
   });
--- a/dom/promise/tests/test_resolve.html
+++ b/dom/promise/tests/test_resolve.html
@@ -39,23 +39,23 @@ function cbError() {
 function runTest() {
   if (!tests.length) {
     SimpleTest.finish();
     return;
   }
 
   var test = tests.pop();
 
-  new Promise(function(resolver) {
-    resolver.resolve(test);
+  new Promise(function(resolve, reject) {
+    resolve(test);
   }).then(function(what) {
     ok(test === what, "What is: " + what);
   }, cbError).then(function() {
-    new Promise(function(resolver) {
-      resolver.reject(test)
+    new Promise(function(resolve, reject) {
+      reject(test)
     }).then(cbError, function(what) {
       ok(test === what, "What is: " + what);
     }).then(runTest, cbError);
   });
 }
 
 SimpleTest.waitForExplicitFinish();
 SpecialPowers.pushPrefEnv({"set": [["dom.promise.enabled", true]]}, runTest);
--- a/dom/quota/CheckQuotaHelper.cpp
+++ b/dom/quota/CheckQuotaHelper.cpp
@@ -115,19 +115,18 @@ CheckQuotaHelper::Cancel()
 
 // static
 uint32_t
 CheckQuotaHelper::GetQuotaPermission(nsIPrincipal* aPrincipal)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(aPrincipal, "Null principal!");
 
-  if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
-    return nsIPermissionManager::ALLOW_ACTION;
-  }
+  NS_ASSERTION(!nsContentUtils::IsSystemPrincipal(aPrincipal),
+               "Chrome windows shouldn't track quota!");
 
   nsCOMPtr<nsIPermissionManager> pm =
     do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
   NS_ENSURE_TRUE(pm, nsIPermissionManager::DENY_ACTION);
 
   uint32_t permission;
   nsresult rv = pm->TestPermissionFromPrincipal(aPrincipal,
                                                 PERMISSION_INDEXEDDB_UNLIMITED,
@@ -195,17 +194,17 @@ CheckQuotaHelper::Run()
       return NS_OK;
     }
   }
 
   MutexAutoLock lock(mMutex);
 
   NS_ASSERTION(mWaiting, "Huh?!");
 
-    // This should never be used again.
+  // This should never be used again.
   mWindow = nullptr;
 
   mWaiting = false;
   mCondVar.NotifyAll();
 
   return NS_OK;
 }
 
--- a/dom/quota/CheckQuotaHelper.h
+++ b/dom/quota/CheckQuotaHelper.h
@@ -8,18 +8,18 @@
 #define mozilla_dom_quota_checkquotahelper_h__
 
 #include "QuotaCommon.h"
 
 #include "nsIInterfaceRequestor.h"
 #include "nsIObserver.h"
 #include "nsIRunnable.h"
 
+#include "mozilla/CondVar.h"
 #include "mozilla/Mutex.h"
-#include "mozilla/CondVar.h"
 
 class nsIPrincipal;
 class nsPIDOMWindow;
 
 BEGIN_QUOTA_NAMESPACE
 
 class CheckQuotaHelper MOZ_FINAL : public nsIRunnable,
                                    public nsIInterfaceRequestor,
@@ -29,21 +29,24 @@ public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIRUNNABLE
   NS_DECL_NSIINTERFACEREQUESTOR
   NS_DECL_NSIOBSERVER
 
   CheckQuotaHelper(nsPIDOMWindow* aWindow,
                    mozilla::Mutex& aMutex);
 
-  bool PromptAndReturnQuotaIsDisabled();
+  bool
+  PromptAndReturnQuotaIsDisabled();
 
-  void Cancel();
+  void
+  Cancel();
 
-  static uint32_t GetQuotaPermission(nsIPrincipal* aPrincipal);
+  static uint32_t
+  GetQuotaPermission(nsIPrincipal* aPrincipal);
 
 private:
   nsPIDOMWindow* mWindow;
 
   mozilla::Mutex& mMutex;
   mozilla::CondVar mCondVar;
   uint32_t mPromptResult;
   bool mWaiting;
--- a/dom/quota/Client.h
+++ b/dom/quota/Client.h
@@ -4,22 +4,25 @@
  * 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_quota_client_h__
 #define mozilla_dom_quota_client_h__
 
 #include "mozilla/dom/quota/QuotaCommon.h"
 
+#include "PersistenceType.h"
+
 class nsIOfflineStorage;
 class nsIRunnable;
 
 BEGIN_QUOTA_NAMESPACE
 
-class UsageRunnable;
+class OriginOrPatternString;
+class UsageInfo;
 
 // An abstract interface for quota manager clients.
 // Each storage API must provide an implementation of this interface in order
 // to participate in centralized quota and storage handling.
 class Client
 {
 public:
   NS_IMETHOD_(nsrefcnt)
@@ -65,24 +68,30 @@ public:
       return NS_ERROR_FAILURE;
     }
 
     return NS_OK;
   }
 
   // Methods which are called on the IO thred.
   virtual nsresult
-  InitOrigin(const nsACString& aOrigin, UsageRunnable* aUsageRunnable) = 0;
+  InitOrigin(PersistenceType aPersistenceType,
+             const nsACString& aGroup,
+             const nsACString& aOrigin,
+             UsageInfo* aUsageInfo) = 0;
 
   virtual nsresult
-  GetUsageForOrigin(const nsACString& aOrigin,
-                    UsageRunnable* aUsageRunnable) = 0;
+  GetUsageForOrigin(PersistenceType aPersistenceType,
+                    const nsACString& aGroup,
+                    const nsACString& aOrigin,
+                    UsageInfo* aUsageInfo) = 0;
 
   virtual void
-  OnOriginClearCompleted(const nsACString& aPattern) = 0;
+  OnOriginClearCompleted(PersistenceType aPersistenceType,
+                         const OriginOrPatternString& aOriginOrPattern) = 0;
 
   virtual void
   ReleaseIOThreadObjects() = 0;
 
   // Methods which are called on the main thred.
   virtual bool
   IsFileServiceUtilized() = 0;
 
--- a/dom/quota/FileStreams.cpp
+++ b/dom/quota/FileStreams.cpp
@@ -43,17 +43,17 @@ FileQuotaStream<FileStreamBase>::Close()
 template <class FileStreamBase>
 nsresult
 FileQuotaStream<FileStreamBase>::DoOpen()
 {
   QuotaManager* quotaManager = QuotaManager::Get();
   NS_ASSERTION(quotaManager, "Shouldn't be null!");
 
   NS_ASSERTION(!mQuotaObject, "Creating quota object more than once?");
-  mQuotaObject = quotaManager->GetQuotaObject(mOrigin,
+  mQuotaObject = quotaManager->GetQuotaObject(mPersistenceType, mGroup, mOrigin,
     FileStreamBase::mOpenParams.localFile);
 
   nsresult rv = FileStreamBase::DoOpen();
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (mQuotaObject && (FileStreamBase::mOpenParams.ioFlags & PR_TRUNCATE)) {
     mQuotaObject->UpdateSize(0);
   }
@@ -84,42 +84,48 @@ FileQuotaStreamWithWrite<FileStreamBase>
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 NS_IMPL_ISUPPORTS_INHERITED0(FileInputStream, nsFileInputStream)
 
 already_AddRefed<FileInputStream>
-FileInputStream::Create(const nsACString& aOrigin, nsIFile* aFile,
-                        int32_t aIOFlags, int32_t aPerm,
+FileInputStream::Create(PersistenceType aPersistenceType,
+                        const nsACString& aGroup, const nsACString& aOrigin,
+                        nsIFile* aFile, int32_t aIOFlags, int32_t aPerm,
                         int32_t aBehaviorFlags)
 {
-  nsRefPtr<FileInputStream> stream = new FileInputStream(aOrigin);
+  nsRefPtr<FileInputStream> stream =
+    new FileInputStream(aPersistenceType, aGroup, aOrigin);
   nsresult rv = stream->Init(aFile, aIOFlags, aPerm, aBehaviorFlags);
   NS_ENSURE_SUCCESS(rv, nullptr);
   return stream.forget();
 }
 
 NS_IMPL_ISUPPORTS_INHERITED0(FileOutputStream, nsFileOutputStream)
 
 already_AddRefed<FileOutputStream>
-FileOutputStream::Create(const nsACString& aOrigin, nsIFile* aFile,
-                         int32_t aIOFlags, int32_t aPerm,
+FileOutputStream::Create(PersistenceType aPersistenceType,
+                         const nsACString& aGroup, const nsACString& aOrigin,
+                         nsIFile* aFile, int32_t aIOFlags, int32_t aPerm,
                          int32_t aBehaviorFlags)
 {
-  nsRefPtr<FileOutputStream> stream = new FileOutputStream(aOrigin);
+  nsRefPtr<FileOutputStream> stream =
+    new FileOutputStream(aPersistenceType, aGroup, aOrigin);
   nsresult rv = stream->Init(aFile, aIOFlags, aPerm, aBehaviorFlags);
   NS_ENSURE_SUCCESS(rv, nullptr);
   return stream.forget();
 }
 
 NS_IMPL_ISUPPORTS_INHERITED0(FileStream, nsFileStream)
 
 already_AddRefed<FileStream>
-FileStream::Create(const nsACString& aOrigin, nsIFile* aFile, int32_t aIOFlags,
+FileStream::Create(PersistenceType aPersistenceType, const nsACString& aGroup,
+                   const nsACString& aOrigin, nsIFile* aFile, int32_t aIOFlags,
                    int32_t aPerm, int32_t aBehaviorFlags)
 {
-  nsRefPtr<FileStream> stream = new FileStream(aOrigin);
+  nsRefPtr<FileStream> stream =
+    new FileStream(aPersistenceType, aGroup, aOrigin);
   nsresult rv = stream->Init(aFile, aIOFlags, aPerm, aBehaviorFlags);
   NS_ENSURE_SUCCESS(rv, nullptr);
   return stream.forget();
 }
--- a/dom/quota/FileStreams.h
+++ b/dom/quota/FileStreams.h
@@ -6,108 +6,120 @@
 
 #ifndef mozilla_dom_quota_filestreams_h__
 #define mozilla_dom_quota_filestreams_h__
 
 #include "QuotaCommon.h"
 
 #include "nsFileStreams.h"
 
+#include "PersistenceType.h"
 #include "QuotaObject.h"
 
 BEGIN_QUOTA_NAMESPACE
 
 template <class FileStreamBase>
 class FileQuotaStream : public Fi