Merge inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Sun, 10 Apr 2016 16:20:04 -0400
changeset 330408 29d5a4175c8b74f45482276a53985cf2568b4be2
parent 330270 9332103718ecc5d23f4523f2faedd929e0fb46ee (current diff)
parent 330407 677502617cb90c9243eb46ea10f1cd016eedd3e3 (diff)
child 330409 95f5751ef3200b208d6f38d13b27c268e7e18152
child 330411 14eb89c4134db16845dedf5fddd2fb0a7f70497f
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone48.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. a=merge
devtools/server/actors/storage.js
ipc/app/app-common.mozbuild
ipc/app/firefox-webcontent.exe.manifest
ipc/app/plugin-container/Makefile.in
ipc/app/plugin-container/module.ver
ipc/app/plugin-container/moz.build
ipc/app/plugin-container/plugin-container.exe.manifest
ipc/contentproc/firefox-webcontent.cpp
testing/web-platform/meta/WebCryptoAPI/getRandomValues.worker.js.ini
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 1114647 - Renamed the content process
+Bug 1186060 - Switch Windows compiler from VS2013 to VS2015
--- a/accessible/atk/AccessibleWrap.cpp
+++ b/accessible/atk/AccessibleWrap.cpp
@@ -281,17 +281,17 @@ AccessibleWrap::Shutdown()
 }
 
 void
 AccessibleWrap::GetNativeInterface(void** aOutAccessible)
 {
   *aOutAccessible = nullptr;
 
   if (!mAtkObject) {
-    if (IsDefunct() || !nsAccUtils::IsEmbeddedObject(this)) {
+    if (IsDefunct() || IsText()) {
       // We don't create ATK objects for node which has been shutdown or
       // plain text leaves
       return;
     }
 
     GType type = GetMaiAtkType(CreateMaiInterfaces());
     if (!type)
       return;
--- a/accessible/base/AccTypes.h
+++ b/accessible/base/AccTypes.h
@@ -78,16 +78,17 @@ enum AccGenericType {
   eLandmark = 1 << 7,
   eList = 1 << 8,
   eListControl = 1 << 9,
   eMenuButton = 1 << 10,
   eSelect = 1 << 11,
   eTable = 1 << 12,
   eTableCell = 1 << 13,
   eTableRow = 1 << 14,
+  eText = 1 << 15,
 
-  eLastAccGenericType = eTableRow
+  eLastAccGenericType = eText
 };
 
 } // namespace a11y
 } // namespace mozilla
 
 #endif // mozilla_a11y_AccTypes_h
--- a/accessible/base/Filters.cpp
+++ b/accessible/base/Filters.cpp
@@ -48,11 +48,10 @@ uint32_t
 filters::GetCell(Accessible* aAccessible)
 {
   return aAccessible->IsTableCell() ? eMatch : eSkipSubtree;
 }
 
 uint32_t
 filters::GetEmbeddedObject(Accessible* aAccessible)
 {
-  return nsAccUtils::IsEmbeddedObject(aAccessible) ?
-    eMatch | eSkipSubtree : eSkipSubtree;
+  return aAccessible->IsText() ? eSkipSubtree : eMatch | eSkipSubtree;
 }
--- a/accessible/base/TextAttrs.cpp
+++ b/accessible/base/TextAttrs.cpp
@@ -44,30 +44,30 @@ TextAttrsMgr::GetAttributes(nsIPersisten
                   ((mOffsetAcc && mOffsetAccIdx != -1 &&
                     aStartOffset && aEndOffset) ||
                   (!mOffsetAcc && mOffsetAccIdx == -1 &&
                     !aStartOffset && !aEndOffset &&
                    mIncludeDefAttrs && aAttributes)),
                   "Wrong usage of TextAttrsMgr!");
 
   // Embedded objects are combined into own range with empty attributes set.
-  if (mOffsetAcc && nsAccUtils::IsEmbeddedObject(mOffsetAcc)) {
+  if (mOffsetAcc && !mOffsetAcc->IsText()) {
     for (int32_t childIdx = mOffsetAccIdx - 1; childIdx >= 0; childIdx--) {
       Accessible* currAcc = mHyperTextAcc->GetChildAt(childIdx);
-      if (!nsAccUtils::IsEmbeddedObject(currAcc))
+      if (currAcc->IsText())
         break;
 
       (*aStartOffset)--;
     }
 
     uint32_t childCount = mHyperTextAcc->ChildCount();
     for (uint32_t childIdx = mOffsetAccIdx + 1; childIdx < childCount;
          childIdx++) {
       Accessible* currAcc = mHyperTextAcc->GetChildAt(childIdx);
-      if (!nsAccUtils::IsEmbeddedObject(currAcc))
+      if (currAcc->IsText())
         break;
 
       (*aEndOffset)++;
     }
 
     return;
   }
 
@@ -157,17 +157,17 @@ TextAttrsMgr::GetRange(TextAttr* aAttrAr
                        uint32_t* aStartOffset, uint32_t* aEndOffset)
 {
   // Navigate backward from anchor accessible to find start offset.
   for (int32_t childIdx = mOffsetAccIdx - 1; childIdx >= 0; childIdx--) {
     Accessible* currAcc = mHyperTextAcc->GetChildAt(childIdx);
 
     // Stop on embedded accessible since embedded accessibles are combined into
     // own range.
-    if (nsAccUtils::IsEmbeddedObject(currAcc))
+    if (!currAcc->IsText())
       break;
 
     bool offsetFound = false;
     for (uint32_t attrIdx = 0; attrIdx < aAttrArrayLen; attrIdx++) {
       TextAttr* textAttr = aAttrArray[attrIdx];
       if (!textAttr->Equal(currAcc)) {
         offsetFound = true;
         break;
@@ -179,17 +179,17 @@ TextAttrsMgr::GetRange(TextAttr* aAttrAr
 
     *(aStartOffset) -= nsAccUtils::TextLength(currAcc);
   }
 
   // Navigate forward from anchor accessible to find end offset.
   uint32_t childLen = mHyperTextAcc->ChildCount();
   for (uint32_t childIdx = mOffsetAccIdx + 1; childIdx < childLen; childIdx++) {
     Accessible* currAcc = mHyperTextAcc->GetChildAt(childIdx);
-    if (nsAccUtils::IsEmbeddedObject(currAcc))
+    if (!currAcc->IsText())
       break;
 
     bool offsetFound = false;
     for (uint32_t attrIdx = 0; attrIdx < aAttrArrayLen; attrIdx++) {
       TextAttr* textAttr = aAttrArray[attrIdx];
 
       // Alter the end offset when text attribute changes its value and stop
       // the search.
--- a/accessible/base/TextRange.cpp
+++ b/accessible/base/TextRange.cpp
@@ -61,18 +61,19 @@ TextRange::TextRange(HyperTextAccessible
 void
 TextRange::EmbeddedChildren(nsTArray<Accessible*>* aChildren) const
 {
   if (mStartContainer == mEndContainer) {
     int32_t startIdx = mStartContainer->GetChildIndexAtOffset(mStartOffset);
     int32_t endIdx = mStartContainer->GetChildIndexAtOffset(mEndOffset);
     for (int32_t idx = startIdx; idx <= endIdx; idx++) {
       Accessible* child = mStartContainer->GetChildAt(idx);
-      if (nsAccUtils::IsEmbeddedObject(child))
+      if (!child->IsText()) {
         aChildren->AppendElement(child);
+      }
     }
     return;
   }
 
   Accessible* p1 = mStartContainer->GetChildAtOffset(mStartOffset);
   Accessible* p2 = mEndContainer->GetChildAtOffset(mEndOffset);
 
   uint32_t pos1 = 0, pos2 = 0;
@@ -82,39 +83,42 @@ TextRange::EmbeddedChildren(nsTArray<Acc
 
   // Traverse the tree up to the container and collect embedded objects.
   for (uint32_t idx = 0; idx < pos1 - 1; idx++) {
     Accessible* parent = parents1[idx + 1];
     Accessible* child = parents1[idx];
     uint32_t childCount = parent->ChildCount();
     for (uint32_t childIdx = child->IndexInParent(); childIdx < childCount; childIdx++) {
       Accessible* next = parent->GetChildAt(childIdx);
-      if (nsAccUtils::IsEmbeddedObject(next))
+      if (!next->IsText()) {
         aChildren->AppendElement(next);
+      }
     }
   }
 
   // Traverse through direct children in the container.
   int32_t endIdx = parents2[pos2 - 1]->IndexInParent();
   int32_t childIdx = parents1[pos1 - 1]->IndexInParent() + 1;
   for (; childIdx < endIdx; childIdx++) {
     Accessible* next = container->GetChildAt(childIdx);
-    if (nsAccUtils::IsEmbeddedObject(next))
+    if (!next->IsText()) {
       aChildren->AppendElement(next);
+    }
   }
 
   // Traverse down from the container to end point.
   for (int32_t idx = pos2 - 2; idx > 0; idx--) {
     Accessible* parent = parents2[idx];
     Accessible* child = parents2[idx - 1];
     int32_t endIdx = child->IndexInParent();
     for (int32_t childIdx = 0; childIdx < endIdx; childIdx++) {
       Accessible* next = parent->GetChildAt(childIdx);
-      if (nsAccUtils::IsEmbeddedObject(next))
+      if (!next->IsText()) {
         aChildren->AppendElement(next);
+      }
     }
   }
 }
 
 void
 TextRange::Text(nsAString& aText) const
 {
   Accessible* current = mStartContainer->GetChildAtOffset(mStartOffset);
--- a/accessible/base/nsAccUtils.cpp
+++ b/accessible/base/nsAccUtils.cpp
@@ -387,31 +387,32 @@ nsAccUtils::IsTextInterfaceSupportCorrec
   // early and fire mutation events before we need to
   if (aAccessible->IsDoc())
     return true;
 
   bool foundText = false;
   uint32_t childCount = aAccessible->ChildCount();
   for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
     Accessible* child = aAccessible->GetChildAt(childIdx);
-    if (!IsEmbeddedObject(child)) {
+    if (child->IsText()) {
       foundText = true;
       break;
     }
   }
 
   return !foundText || aAccessible->IsHyperText();
 }
 #endif
 
 uint32_t
 nsAccUtils::TextLength(Accessible* aAccessible)
 {
-  if (IsEmbeddedObject(aAccessible))
+  if (!aAccessible->IsText()) {
     return 1;
+  }
 
   TextLeafAccessible* textLeaf = aAccessible->AsTextLeaf();
   if (textLeaf)
     return textLeaf->Text().Length();
 
   // For list bullets (or anything other accessible which would compute its own
   // text. They don't have their own frame.
   // XXX In the future, list bullets may have frame and anon content, so 
--- a/accessible/base/nsAccUtils.h
+++ b/accessible/base/nsAccUtils.h
@@ -199,27 +199,16 @@ public:
 #endif
 
   /**
    * Return text length of the given accessible, return 0 on failure.
    */
   static uint32_t TextLength(Accessible* aAccessible);
 
   /**
-   * Return true if the given accessible is embedded object.
-   */
-  static bool IsEmbeddedObject(Accessible* aAcc)
-  {
-    uint32_t role = aAcc->Role();
-    return role != roles::TEXT_LEAF &&
-           role != roles::WHITESPACE &&
-           role != roles::STATICTEXT;
-  }
-
-  /**
    * Transform nsIAccessibleStates constants to internal state constant.
    */
   static inline uint64_t To64State(uint32_t aState1, uint32_t aState2)
   {
     return static_cast<uint64_t>(aState1) +
         (static_cast<uint64_t>(aState2) << 31);
   }
 
--- a/accessible/base/nsAccessiblePivot.cpp
+++ b/accessible/base/nsAccessiblePivot.cpp
@@ -388,25 +388,25 @@ nsAccessiblePivot::MoveNextByText(TextBo
     tempStart = potentialStart > tempStart ? potentialStart : currentEnd;
 
     // The offset range we've obtained might have embedded characters in it,
     // limit the range to the start of the first occurrence of an embedded
     // character.
     Accessible* childAtOffset = nullptr;
     for (int32_t i = tempStart; i < tempEnd; i++) {
       childAtOffset = text->GetChildAtOffset(i);
-      if (childAtOffset && nsAccUtils::IsEmbeddedObject(childAtOffset)) {
+      if (childAtOffset && !childAtOffset->IsText()) {
         tempEnd = i;
         break;
       }
     }
     // If there's an embedded character at the very start of the range, we
     // instead want to traverse into it. So restart the movement with
     // the child as the starting point.
-    if (childAtOffset && nsAccUtils::IsEmbeddedObject(childAtOffset) &&
+    if (childAtOffset && !childAtOffset->IsText() &&
         tempStart == static_cast<int32_t>(childAtOffset->StartOffset())) {
       tempPosition = childAtOffset;
       tempStart = tempEnd = -1;
       continue;
     }
 
     *aResult = true;
 
@@ -519,25 +519,25 @@ nsAccessiblePivot::MovePreviousByText(Te
     tempEnd = potentialEnd < tempEnd ? potentialEnd : currentStart;
 
     // The offset range we've obtained might have embedded characters in it,
     // limit the range to the start of the last occurrence of an embedded
     // character.
     Accessible* childAtOffset = nullptr;
     for (int32_t i = tempEnd - 1; i >= tempStart; i--) {
       childAtOffset = text->GetChildAtOffset(i);
-      if (childAtOffset && nsAccUtils::IsEmbeddedObject(childAtOffset)) {
+      if (childAtOffset && !childAtOffset->IsText()) {
         tempStart = childAtOffset->EndOffset();
         break;
       }
     }
     // If there's an embedded character at the very end of the range, we
     // instead want to traverse into it. So restart the movement with
     // the child as the starting point.
-    if (childAtOffset && nsAccUtils::IsEmbeddedObject(childAtOffset) &&
+    if (childAtOffset && !childAtOffset->IsText() &&
         tempEnd == static_cast<int32_t>(childAtOffset->EndOffset())) {
       tempPosition = childAtOffset;
       tempStart = tempEnd = childAtOffset->AsHyperText()->CharacterCount();
       continue;
     }
 
     *aResult = true;
 
--- a/accessible/generic/Accessible.cpp
+++ b/accessible/generic/Accessible.cpp
@@ -2071,17 +2071,17 @@ Accessible::InsertChildAt(uint32_t aInde
 
   } else {
     if (!mChildren.InsertElementAt(aIndex, aChild))
       return false;
 
     MOZ_ASSERT(mStateFlags & eKidsMutating, "Illicit children change");
   }
 
-  if (!nsAccUtils::IsEmbeddedObject(aChild)) {
+  if (aChild->IsText()) {
     mStateFlags |= eHasTextKids;
   }
 
   aChild->BindToParent(this, aIndex);
   return true;
 }
 
 bool
@@ -2230,17 +2230,17 @@ Accessible::GetIndexOfEmbeddedChild(Acce
 ////////////////////////////////////////////////////////////////////////////////
 // HyperLinkAccessible methods
 
 bool
 Accessible::IsLink()
 {
   // Every embedded accessible within hypertext accessible implements
   // hyperlink interface.
-  return mParent && mParent->IsHyperText() && nsAccUtils::IsEmbeddedObject(this);
+  return mParent && mParent->IsHyperText() && !IsText();
 }
 
 uint32_t
 Accessible::StartOffset()
 {
   NS_PRECONDITION(IsLink(), "StartOffset is called not on hyper link!");
 
   HyperTextAccessible* hyperText = mParent ? mParent->AsHyperText() : nullptr;
--- a/accessible/generic/Accessible.h
+++ b/accessible/generic/Accessible.h
@@ -636,16 +636,18 @@ public:
   virtual TableCellAccessible* AsTableCell() { return nullptr; }
   const TableCellAccessible* AsTableCell() const
     { return const_cast<Accessible*>(this)->AsTableCell(); }
 
   bool IsTableRow() const { return HasGenericType(eTableRow); }
 
   bool IsTextField() const { return mType == eHTMLTextFieldType; }
 
+  bool IsText() const { return mGenericTypes & eText; }
+
   bool IsTextLeaf() const { return mType == eTextLeafType; }
   TextLeafAccessible* AsTextLeaf();
 
   bool IsXULLabel() const { return mType == eXULLabelType; }
   XULLabelAccessible* AsXULLabel();
 
   bool IsXULListItem() const { return mType == eXULListItemType; }
 
@@ -1094,17 +1096,17 @@ protected:
 
   RefPtr<Accessible> mParent;
   nsTArray<RefPtr<Accessible> > mChildren;
   int32_t mIndexInParent;
 
   static const uint8_t kStateFlagsBits = 13;
   static const uint8_t kContextFlagsBits = 3;
   static const uint8_t kTypeBits = 6;
-  static const uint8_t kGenericTypesBits = 15;
+  static const uint8_t kGenericTypesBits = 16;
 
   /**
    * Keep in sync with StateFlags, ContextFlags, and AccTypes.
    */
   uint32_t mStateFlags : kStateFlagsBits;
   uint32_t mContextFlags : kContextFlagsBits;
   uint32_t mType : kTypeBits;
   uint32_t mGenericTypes : kGenericTypesBits;
--- a/accessible/generic/TextLeafAccessible.cpp
+++ b/accessible/generic/TextLeafAccessible.cpp
@@ -15,16 +15,17 @@ using namespace mozilla::a11y;
 // TextLeafAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 TextLeafAccessible::
   TextLeafAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   LinkableAccessible(aContent, aDoc)
 {
   mType = eTextLeafType;
+  mGenericTypes |= eText;
   mStateFlags |= eNoKidsFromDOM;
 }
 
 TextLeafAccessible::~TextLeafAccessible()
 {
 }
 
 role
--- a/accessible/html/HTMLElementAccessibles.h
+++ b/accessible/html/HTMLElementAccessibles.h
@@ -31,16 +31,17 @@ public:
  */
 class HTMLBRAccessible : public LeafAccessible
 {
 public:
   HTMLBRAccessible(nsIContent* aContent, DocAccessible* aDoc) :
     LeafAccessible(aContent, aDoc)
   {
     mType = eHTMLBRType;
+    mGenericTypes |= eText;
   }
 
   // Accessible
   virtual a11y::role NativeRole() override;
   virtual uint64_t NativeState() override;
 
 protected:
   // Accessible
--- a/accessible/html/HTMLListAccessible.cpp
+++ b/accessible/html/HTMLListAccessible.cpp
@@ -120,16 +120,17 @@ HTMLLIAccessible::UpdateBullet(bool aHas
 
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLListBulletAccessible
 ////////////////////////////////////////////////////////////////////////////////
 HTMLListBulletAccessible::
   HTMLListBulletAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   LeafAccessible(aContent, aDoc)
 {
+  mGenericTypes |= eText;
   mStateFlags |= eSharedNode;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLListBulletAccessible: Accessible
 
 nsIFrame*
 HTMLListBulletAccessible::GetFrame() const
--- a/addon-sdk/source/lib/sdk/io/file.js
+++ b/addon-sdk/source/lib/sdk/io/file.js
@@ -137,17 +137,17 @@ exports.open = function open(filename, m
   if (/w/.test(mode)) {
     if (file.exists())
       ensureFile(file);
     var stream = Cc['@mozilla.org/network/file-output-stream;1'].
                  createInstance(Ci.nsIFileOutputStream);
     var openFlags = OPEN_FLAGS.WRONLY |
                     OPEN_FLAGS.CREATE_FILE |
                     OPEN_FLAGS.TRUNCATE;
-    var permFlags = parseInt("0644", 8); // u+rw go+r
+    var permFlags = 0o644; // u+rw go+r
     try {
       stream.init(file, openFlags, permFlags, 0);
     }
     catch (err) {
       throw friendlyError(err, filename);
     }
     return /b/.test(mode) ?
            new byteStreams.ByteWriter(stream) :
@@ -173,17 +173,17 @@ exports.remove = function remove(path) {
   var file = MozFile(path);
   ensureFile(file);
   file.remove(false);
 };
 
 exports.mkpath = function mkpath(path) {
   var file = MozFile(path);
   if (!file.exists())
-    file.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt("0755", 8)); // u+rwx go+rx
+    file.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755); // u+rwx go+rx
   else if (!file.isDirectory())
     throw new Error("The path already exists and is not a directory: " + path);
 };
 
 exports.rmdir = function rmdir(path) {
   var file = MozFile(path);
   ensureDir(file);
   try {
--- a/addon-sdk/source/lib/sdk/io/fs.js
+++ b/addon-sdk/source/lib/sdk/io/fs.js
@@ -36,17 +36,17 @@ const { createOutputTransport, createInp
 
 const { OPEN_UNBUFFERED } = Ci.nsITransport;
 
 
 const { REOPEN_ON_REWIND, DEFER_OPEN } = Ci.nsIFileInputStream;
 const { DIRECTORY_TYPE, NORMAL_FILE_TYPE } = Ci.nsIFile;
 const { NS_SEEK_SET, NS_SEEK_CUR, NS_SEEK_END } = Ci.nsISeekableStream;
 
-const FILE_PERMISSION = parseInt("0666", 8);
+const FILE_PERMISSION = 0o666;
 const PR_UINT32_MAX = 0xfffffff;
 // Values taken from:
 // http://mxr.mozilla.org/mozilla-central/source/nsprpub/pr/include/prio.h#615
 const PR_RDONLY =       0x01;
 const PR_WRONLY =       0x02;
 const PR_RDWR =         0x04;
 const PR_CREATE_FILE =  0x08;
 const PR_APPEND =       0x10;
--- a/addon-sdk/source/test/addons/content-permissions/httpd.js
+++ b/addon-sdk/source/test/addons/content-permissions/httpd.js
@@ -2041,17 +2041,17 @@ function maybeAddHeaders(file, metadata,
 
   var headerFile = file.parent;
   headerFile.append(name + HEADERS_SUFFIX);
 
   if (!headerFile.exists())
     return;
 
   const PR_RDONLY = 0x01;
-  var fis = new FileInputStream(headerFile, PR_RDONLY, parseInt("444", 8),
+  var fis = new FileInputStream(headerFile, PR_RDONLY, 0o444,
                                 Ci.nsIFileInputStream.CLOSE_ON_EOF);
 
   try
   {
     var lis = new ConverterInputStream(fis, "UTF-8", 1024, 0x0);
     lis.QueryInterface(Ci.nsIUnicharLineInputStream);
 
     var line = {value: ""};
@@ -2567,17 +2567,17 @@ ServerHandler.prototype =
 */
   _writeFileResponse: function(metadata, file, response, offset, count)
   {
     const PR_RDONLY = 0x01;
 
     var type = this._getTypeFromFile(file);
     if (type === SJS_TYPE)
     {
-      var fis = new FileInputStream(file, PR_RDONLY, parseInt("444", 8),
+      var fis = new FileInputStream(file, PR_RDONLY, 0o444,
                                     Ci.nsIFileInputStream.CLOSE_ON_EOF);
 
       try
       {
         var sis = new ScriptableInputStream(fis);
         var s = Cu.Sandbox(gGlobalObject);
         s.importFunction(dump, "dump");
 
@@ -2661,17 +2661,17 @@ ServerHandler.prototype =
                            false);
       }
       catch (e) { /* lastModifiedTime threw, ignore */ }
 
       response.setHeader("Content-Type", type, false);
       maybeAddHeaders(file, metadata, response);
       response.setHeader("Content-Length", "" + count, false);
 
-      var fis = new FileInputStream(file, PR_RDONLY, parseInt("444", 8),
+      var fis = new FileInputStream(file, PR_RDONLY, 0o444,
                                     Ci.nsIFileInputStream.CLOSE_ON_EOF);
 
       offset = offset || 0;
       count = count || file.fileSize;
       NS_ASSERT(offset === 0 || offset < file.fileSize, "bad offset");
       NS_ASSERT(count >= 0, "bad count");
       NS_ASSERT(offset + count <= file.fileSize, "bad total data size");
 
--- a/addon-sdk/source/test/addons/e10s-content/lib/httpd.js
+++ b/addon-sdk/source/test/addons/e10s-content/lib/httpd.js
@@ -2042,17 +2042,17 @@ function maybeAddHeaders(file, metadata,
 
   var headerFile = file.parent;
   headerFile.append(name + HEADERS_SUFFIX);
 
   if (!headerFile.exists())
     return;
 
   const PR_RDONLY = 0x01;
-  var fis = new FileInputStream(headerFile, PR_RDONLY, parseInt("444", 8),
+  var fis = new FileInputStream(headerFile, PR_RDONLY, 0o444,
                                 Ci.nsIFileInputStream.CLOSE_ON_EOF);
 
   try
   {
     var lis = new ConverterInputStream(fis, "UTF-8", 1024, 0x0);
     lis.QueryInterface(Ci.nsIUnicharLineInputStream);
 
     var line = {value: ""};
@@ -2568,17 +2568,17 @@ ServerHandler.prototype =
 */
   _writeFileResponse: function(metadata, file, response, offset, count)
   {
     const PR_RDONLY = 0x01;
 
     var type = this._getTypeFromFile(file);
     if (type === SJS_TYPE)
     {
-      var fis = new FileInputStream(file, PR_RDONLY, parseInt("444", 8),
+      var fis = new FileInputStream(file, PR_RDONLY, 0o444,
                                     Ci.nsIFileInputStream.CLOSE_ON_EOF);
 
       try
       {
         var sis = new ScriptableInputStream(fis);
         var s = Cu.Sandbox(gGlobalObject);
         s.importFunction(dump, "dump");
 
@@ -2662,17 +2662,17 @@ ServerHandler.prototype =
                            false);
       }
       catch (e) { /* lastModifiedTime threw, ignore */ }
 
       response.setHeader("Content-Type", type, false);
       maybeAddHeaders(file, metadata, response);
       response.setHeader("Content-Length", "" + count, false);
 
-      var fis = new FileInputStream(file, PR_RDONLY, parseInt("444", 8),
+      var fis = new FileInputStream(file, PR_RDONLY, 0o444,
                                     Ci.nsIFileInputStream.CLOSE_ON_EOF);
 
       offset = offset || 0;
       count = count || file.fileSize;
       NS_ASSERT(offset === 0 || offset < file.fileSize, "bad offset");
       NS_ASSERT(count >= 0, "bad count");
       NS_ASSERT(offset + count <= file.fileSize, "bad total data size");
 
--- a/addon-sdk/source/test/addons/places/lib/httpd.js
+++ b/addon-sdk/source/test/addons/places/lib/httpd.js
@@ -2041,17 +2041,17 @@ function maybeAddHeaders(file, metadata,
 
   var headerFile = file.parent;
   headerFile.append(name + HEADERS_SUFFIX);
 
   if (!headerFile.exists())
     return;
 
   const PR_RDONLY = 0x01;
-  var fis = new FileInputStream(headerFile, PR_RDONLY, parseInt("444", 8),
+  var fis = new FileInputStream(headerFile, PR_RDONLY, 0o444,
                                 Ci.nsIFileInputStream.CLOSE_ON_EOF);
 
   try
   {
     var lis = new ConverterInputStream(fis, "UTF-8", 1024, 0x0);
     lis.QueryInterface(Ci.nsIUnicharLineInputStream);
 
     var line = {value: ""};
@@ -2567,17 +2567,17 @@ ServerHandler.prototype =
 */
   _writeFileResponse: function(metadata, file, response, offset, count)
   {
     const PR_RDONLY = 0x01;
 
     var type = this._getTypeFromFile(file);
     if (type === SJS_TYPE)
     {
-      var fis = new FileInputStream(file, PR_RDONLY, parseInt("444", 8),
+      var fis = new FileInputStream(file, PR_RDONLY, 0o444,
                                     Ci.nsIFileInputStream.CLOSE_ON_EOF);
 
       try
       {
         var sis = new ScriptableInputStream(fis);
         var s = Cu.Sandbox(gGlobalObject);
         s.importFunction(dump, "dump");
 
@@ -2661,17 +2661,17 @@ ServerHandler.prototype =
                            false);
       }
       catch (e) { /* lastModifiedTime threw, ignore */ }
 
       response.setHeader("Content-Type", type, false);
       maybeAddHeaders(file, metadata, response);
       response.setHeader("Content-Length", "" + count, false);
 
-      var fis = new FileInputStream(file, PR_RDONLY, parseInt("444", 8),
+      var fis = new FileInputStream(file, PR_RDONLY, 0o444,
                                     Ci.nsIFileInputStream.CLOSE_ON_EOF);
 
       offset = offset || 0;
       count = count || file.fileSize;
       NS_ASSERT(offset === 0 || offset < file.fileSize, "bad offset");
       NS_ASSERT(count >= 0, "bad count");
       NS_ASSERT(offset + count <= file.fileSize, "bad total data size");
 
--- a/addon-sdk/source/test/fixtures/child-process-scripts.js
+++ b/addon-sdk/source/test/fixtures/child-process-scripts.js
@@ -61,17 +61,17 @@ function createFile (name, data) {
   return promise;
 }
 
 // TODO Use fs.chmod once implemented, bug 914606
 function makeExecutable (name) {
   let { CC } = require('chrome');
   let nsILocalFile = CC('@mozilla.org/file/local;1', 'nsILocalFile', 'initWithPath');
   let file = nsILocalFile(name);
-  file.permissions = parseInt('0777', 8);
+  file.permissions = 0o777;
 }
 
 function deleteFile (name) {
   let file = join(PROFILE_DIR, name);
   if (existsSync(file))
     unlinkSync(file);
 }
 
--- a/addon-sdk/source/test/lib/httpd.js
+++ b/addon-sdk/source/test/lib/httpd.js
@@ -2042,17 +2042,17 @@ function maybeAddHeaders(file, metadata,
 
   var headerFile = file.parent;
   headerFile.append(name + HEADERS_SUFFIX);
 
   if (!headerFile.exists())
     return;
 
   const PR_RDONLY = 0x01;
-  var fis = new FileInputStream(headerFile, PR_RDONLY, parseInt("444", 8),
+  var fis = new FileInputStream(headerFile, PR_RDONLY, 0o444,
                                 Ci.nsIFileInputStream.CLOSE_ON_EOF);
 
   try
   {
     var lis = new ConverterInputStream(fis, "UTF-8", 1024, 0x0);
     lis.QueryInterface(Ci.nsIUnicharLineInputStream);
 
     var line = {value: ""};
@@ -2568,17 +2568,17 @@ ServerHandler.prototype =
 */
   _writeFileResponse: function(metadata, file, response, offset, count)
   {
     const PR_RDONLY = 0x01;
 
     var type = this._getTypeFromFile(file);
     if (type === SJS_TYPE)
     {
-      var fis = new FileInputStream(file, PR_RDONLY, parseInt("444", 8),
+      var fis = new FileInputStream(file, PR_RDONLY, 0o444,
                                     Ci.nsIFileInputStream.CLOSE_ON_EOF);
 
       try
       {
         var sis = new ScriptableInputStream(fis);
         var s = Cu.Sandbox(gGlobalObject);
         s.importFunction(dump, "dump");
 
@@ -2662,17 +2662,17 @@ ServerHandler.prototype =
                            false);
       }
       catch (e) { /* lastModifiedTime threw, ignore */ }
 
       response.setHeader("Content-Type", type, false);
       maybeAddHeaders(file, metadata, response);
       response.setHeader("Content-Length", "" + count, false);
 
-      var fis = new FileInputStream(file, PR_RDONLY, parseInt("444", 8),
+      var fis = new FileInputStream(file, PR_RDONLY, 0o444,
                                     Ci.nsIFileInputStream.CLOSE_ON_EOF);
 
       offset = offset || 0;
       count = count || file.fileSize;
       NS_ASSERT(offset === 0 || offset < file.fileSize, "bad offset");
       NS_ASSERT(count >= 0, "bad count");
       NS_ASSERT(offset + count <= file.fileSize, "bad total data size");
 
--- a/addon-sdk/source/test/test-fs.js
+++ b/addon-sdk/source/test/test-fs.js
@@ -505,44 +505,44 @@ exports["test fs.writeFile error"] = fun
 
 exports["test fs.chmod"] = function (assert, done) {
   let content = ["hej från sverige"];
 
   fs.writeFile(chmodPath, content, function (err) {
     testPerm("0755")()
       .then(testPerm("0777"))
       .then(testPerm("0666"))
-      .then(testPerm(parseInt("0511", 8)))
-      .then(testPerm(parseInt("0200", 8)))
+      .then(testPerm(0o511))
+      .then(testPerm(0o200))
       .then(testPerm("0040"))
       .then(testPerm("0000"))
-      .then(testPermSync(parseInt("0777", 8)))
-      .then(testPermSync(parseInt("0666", 8)))
+      .then(testPermSync(0o777))
+      .then(testPermSync(0o666))
       .then(testPermSync("0511"))
       .then(testPermSync("0200"))
       .then(testPermSync("0040"))
       .then(testPermSync("0000"))
       .then(() => {
         assert.pass("Successful chmod passes");
       }, assert.fail)
       // Test invalid paths
-      .then(() => chmod("not-a-valid-file", parseInt("0755", 8)))
+      .then(() => chmod("not-a-valid-file", 0o755))
       .then(assert.fail, (err) => {
         checkPermError(err, "not-a-valid-file");
       })
-      .then(() => chmod("not-a-valid-file", parseInt("0755", 8), "sync"))
+      .then(() => chmod("not-a-valid-file", 0o755, "sync"))
       .then(assert.fail, (err) => {
         checkPermError(err, "not-a-valid-file");
       })
       // Test invalid files
-      .then(() => chmod("resource://not-a-real-file", parseInt("0755", 8)))
+      .then(() => chmod("resource://not-a-real-file", 0o755))
       .then(assert.fail, (err) => {
         checkPermError(err, "resource://not-a-real-file");
       })
-      .then(() => chmod("resource://not-a-real-file", parseInt("0755", 8), 'sync'))
+      .then(() => chmod("resource://not-a-real-file", 0o755, 'sync'))
       .then(assert.fail, (err) => {
         checkPermError(err, "resource://not-a-real-file");
       })
       .then(done, assert.fail);
   });
 
   function checkPermError (err, path) {
     assert.equal(err.message, "ENOENT, chmod " + path);
@@ -598,18 +598,18 @@ exports["test fs.chmod"] = function (ass
    */
   function normalizeMode (mode) {
     if (typeof mode === "string")
       mode = parseInt(mode, 8);
 
     if (!isWindows)
       return mode;
 
-    var ANY_READ = parseInt("0444", 8);
-    var ANY_WRITE = parseInt("0222", 8);
+    var ANY_READ = 0o444;
+    var ANY_WRITE = 0o222;
     var winMode = 0;
 
     // On Windows, if WRITE is true, then READ is also true
     if (mode & ANY_WRITE)
       winMode |= ANY_WRITE | ANY_READ;
     // Minimum permissions are READ for Windows
     else
       winMode |= ANY_READ;
--- a/b2g/config/mozconfigs/common
+++ b/b2g/config/mozconfigs/common
@@ -4,19 +4,19 @@
 
 # This file is included at the top of all b2g mozconfigs
 
 . "$topsrcdir/build/mozconfig.common"
 
 # Normally, we'd set this unconditionally, but this file is also used
 # for local builds and there is no other mozconfig in this tree that
 # is included on device builds.
-if test -d $topsrcdir/../gcc/bin; then
-    HOST_CC="$topsrcdir/../gcc/bin/gcc"
-    HOST_CXX="$topsrcdir/../gcc/bin/g++"
+if test -d $topsrcdir/gcc/bin; then
+    HOST_CC="$topsrcdir/gcc/bin/gcc"
+    HOST_CXX="$topsrcdir/gcc/bin/g++"
     ac_add_options --enable-stdcxx-compat
 fi
 
 # Allow overriding this from the environment, and don't
 # try to set it if it doesn't exist. As per above, this file is also
 # used for local builds, and we may need to override this for builds in
 # other environments.
 if test -z "$SOCORRO_SYMBOL_UPLOAD_TOKEN_FILE" -a -f /builds/crash-stats-api.token; then
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1178,17 +1178,34 @@ var gBrowserInit = {
     Services.telemetry.getHistogramById("E10S_WINDOW").add(gMultiProcessBrowser);
 
     SidebarUI.startDelayedLoad();
 
     UpdateUrlbarSearchSplitterState();
 
     if (!(isBlankPageURL(uriToLoad) || uriToLoad == "about:privatebrowsing") ||
         !focusAndSelectUrlBar()) {
-      gBrowser.selectedBrowser.focus();
+      if (gBrowser.selectedBrowser.isRemoteBrowser) {
+        // If the initial browser is remote, in order to optimize for first paint,
+        // we'll defer switching focus to that browser until it has painted.
+        let focusedElement = document.commandDispatcher.focusedElement;
+        let mm = window.messageManager;
+        mm.addMessageListener("Browser:FirstPaint", function onFirstPaint() {
+          mm.removeMessageListener("Browser:FirstPaint", onFirstPaint);
+          // If focus didn't move while we were waiting for first paint, we're okay
+          // to move to the browser.
+          if (document.commandDispatcher.focusedElement == focusedElement) {
+            gBrowser.selectedBrowser.focus();
+          }
+        });
+      } else {
+        // If the initial browser is not remote, we can focus the browser
+        // immediately with no paint performance impact.
+        gBrowser.selectedBrowser.focus();
+      }
     }
 
     // Enable/Disable auto-hide tabbar
     gBrowser.tabContainer.updateVisibility();
 
     BookmarkingUI.init();
 
     gPrefService.addObserver(gHomeButton.prefDomain, gHomeButton, false);
@@ -1568,16 +1585,20 @@ if (AppConstants.platform == "macosx") {
     gSyncUI.init();
 
     if (AppConstants.E10S_TESTING_ONLY) {
       gRemoteTabsUI.init();
     }
   };
 
   gBrowserInit.nonBrowserWindowShutdown = function() {
+    let dockSupport = Cc["@mozilla.org/widget/macdocksupport;1"]
+                      .getService(Ci.nsIMacDockSupport);
+    dockSupport.dockMenu = null;
+
     // If nonBrowserWindowDelayedStartup hasn't run yet, we have no work to do -
     // just cancel the pending timeout and return;
     if (this._delayedStartupTimeoutId) {
       clearTimeout(this._delayedStartupTimeoutId);
       return;
     }
 
     BrowserOffline.uninit();
--- a/browser/base/content/sanitize.js
+++ b/browser/base/content/sanitize.js
@@ -244,17 +244,17 @@ Sanitizer.prototype = {
             // Iterate through the cookies and delete any created after our cutoff.
             let cookiesEnum = cookieMgr.enumerator;
             while (cookiesEnum.hasMoreElements()) {
               let cookie = cookiesEnum.getNext().QueryInterface(Ci.nsICookie2);
 
               if (cookie.creationTime > range[0]) {
                 // This cookie was created after our cutoff, clear it
                 cookieMgr.remove(cookie.host, cookie.name, cookie.path,
-                                 cookie.originAttributes, false);
+                                 false, cookie.originAttributes);
 
                 if (++yieldCounter % YIELD_PERIOD == 0) {
                   yield new Promise(resolve => setTimeout(resolve, 0)); // Don't block the main thread too long
                 }
               }
             }
           }
           else {
--- a/browser/base/content/tab-content.js
+++ b/browser/base/content/tab-content.js
@@ -848,8 +848,13 @@ var RefreshBlocker = {
 
 RefreshBlocker.init();
 
 ExtensionContent.init(this);
 addEventListener("unload", () => {
   ExtensionContent.uninit(this);
   RefreshBlocker.uninit();
 });
+
+addEventListener("MozAfterPaint", function onFirstPaint() {
+  removeEventListener("MozAfterPaint", onFirstPaint);
+  sendAsyncMessage("Browser:FirstPaint");
+});
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -574,8 +574,10 @@ skip-if = !e10s || !crashreporter
 [browser_aboutTabCrashed_clearEmail.js]
 skip-if = !e10s || !crashreporter
 [browser_aboutTabCrashed_showForm.js]
 skip-if = !e10s || !crashreporter
 [browser_aboutTabCrashed_withoutDump.js]
 skip-if = !e10s
 [browser_csp_block_all_mixedcontent.js]
 tags = mcb
+[browser_newwindow_focus.js]
+skip-if = (os == "linux" && !e10s) # Bug 1263254 - Perma fails on Linux without e10s for some reason.
\ No newline at end of file
--- a/browser/base/content/test/general/browser_bug495058.js
+++ b/browser/base/content/test/general/browser_bug495058.js
@@ -1,43 +1,50 @@
-function test() {
-  waitForExplicitFinish();
-  next();
-}
+/**
+ * Tests that the right elements of a tab are focused when it is
+ * torn out into its own window.
+ */
 
-var uris = [
+const URIS = [
   "about:blank",
   "about:sessionrestore",
   "about:privatebrowsing",
 ];
 
-function next() {
-  var tab = gBrowser.addTab();
-  var uri = uris.shift();
+add_task(function*() {
+  for (let uri of URIS) {
+    let tab = gBrowser.addTab();
+    yield BrowserTestUtils.loadURI(tab.linkedBrowser, uri);
+
+    let win = gBrowser.replaceTabWithWindow(tab);
+    yield BrowserTestUtils.waitForEvent(win, "load");
 
-  if (uri == "about:blank") {
-    detach();
-  } else {
-    let browser = tab.linkedBrowser;
-    browser.addEventListener("load", function () {
-      browser.removeEventListener("load", arguments.callee, true);
-      detach();
-    }, true);
-    browser.loadURI(uri);
-  }
+    tab = win.gBrowser.selectedTab;
+
+    // By default, we'll wait for MozAfterPaint to come up through the
+    // browser element. We'll handle the e10s case in the next block.
+    let contentPainted = BrowserTestUtils.waitForEvent(tab.linkedBrowser,
+                                                       "MozAfterPaint");
+
+    let delayedStartup =
+      TestUtils.topicObserved("browser-delayed-startup-finished",
+                              subject => subject == win);
 
-  function detach() {
-    var win = gBrowser.replaceTabWithWindow(tab);
-
-    whenDelayedStartupFinished(win, function () {
-      is(win.gBrowser.currentURI.spec, uri, uri + ": uri loaded in detached tab");
-      is(win.document.activeElement, win.gBrowser.selectedBrowser, uri + ": browser is focused");
-      is(win.gURLBar.value, "", uri + ": urlbar is empty");
-      ok(win.gURLBar.placeholder, uri + ": placeholder text is present");
+    if (gMultiProcessBrowser &&
+        E10SUtils.canLoadURIInProcess(uri, Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT)) {
+      // Until bug 1261842 is fixed, the initial browser is going to be
+      // non-remote. If we're transitioning to a URL that can be loaded
+      // remotely, we'll need to wait until that remoteness switch is done
+      // before we send any framescripts down to the browser.
+      yield BrowserTestUtils.waitForEvent(tab, "TabRemotenessChange");
+      contentPainted = BrowserTestUtils.contentPainted(tab.linkedBrowser);
+    }
 
-      win.close();
-      if (uris.length)
-        next();
-      else
-        executeSoon(finish);
-    });
+    yield Promise.all([delayedStartup, contentPainted]);
+
+    Assert.equal(win.gBrowser.currentURI.spec, uri, uri + ": uri loaded in detached tab");
+    Assert.equal(win.document.activeElement, win.gBrowser.selectedBrowser, uri + ": browser is focused");
+    Assert.equal(win.gURLBar.value, "", uri + ": urlbar is empty");
+    Assert.ok(win.gURLBar.placeholder, uri + ": placeholder text is present");
+
+    yield BrowserTestUtils.closeWindow(win);
   }
-}
+});
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_newwindow_focus.js
@@ -0,0 +1,96 @@
+"use strict";
+
+/**
+ * These tests are for the auto-focus behaviour on the initial browser
+ * when a window is opened from content.
+ */
+
+const PAGE = `data:text/html,<a id="target" href="%23" onclick="window.open('http://www.example.com', '_blank', 'width=100,height=100');">Click me</a>`;
+
+/**
+ * Returns a Promise that resolves when a new window has
+ * opened, and the "load" event has fired in that window.
+ * We can't use BrowserTestUtils.domWindowOpened directly,
+ * because by the time the "then" on the Promise runs,
+ * DOMContentLoaded and load may have already run in the new
+ * window. However, we want to be very explicit about what
+ * events we're waiting for, and not rely on a quirk of our
+ * Promises infrastructure.
+ */
+function promiseNewWindow() {
+  return new Promise((resolve) => {
+    let observer = (subject, topic, data) => {
+      if (topic == "domwindowopened") {
+        Services.ww.unregisterNotification(observer);
+        let win = subject.QueryInterface(Ci.nsIDOMWindow);
+        win.addEventListener("load", function onLoad() {
+          win.removeEventListener("load", onLoad);
+          resolve(win);
+        });
+      }
+    };
+
+    Services.ww.registerNotification(observer);
+  });
+}
+
+/**
+ * Test that when a new window is opened from content, focus moves
+ * to the initial browser in that window once the window has finished
+ * painting.
+ */
+add_task(function* test_focus_browser() {
+  yield BrowserTestUtils.withNewTab({
+    url: PAGE,
+    gBrowser,
+  }, function*(browser) {
+    let newWinPromise = promiseNewWindow();
+    let delayedStartupPromise = BrowserTestUtils.waitForNewWindow();
+
+    yield BrowserTestUtils.synthesizeMouseAtCenter("#target", {}, browser);
+    let newWin = yield newWinPromise;
+    yield BrowserTestUtils.contentPainted(newWin.gBrowser.selectedBrowser);
+    yield delayedStartupPromise;
+
+    let focusedElement =
+      Services.focus.getFocusedElementForWindow(newWin, false, {});
+
+    Assert.equal(focusedElement, newWin.gBrowser.selectedBrowser,
+                 "Initial browser should be focused");
+
+    yield BrowserTestUtils.closeWindow(newWin);
+  });
+});
+
+/**
+ * Test that when a new window is opened from content and focus
+ * shifts in that window before the content has a chance to paint
+ * that we _don't_ steal focus once content has painted.
+ */
+add_task(function* test_no_steal_focus() {
+  yield BrowserTestUtils.withNewTab({
+    url: PAGE,
+    gBrowser,
+  }, function*(browser) {
+    let newWinPromise = promiseNewWindow();
+    let delayedStartupPromise = BrowserTestUtils.waitForNewWindow();
+
+    yield BrowserTestUtils.synthesizeMouseAtCenter("#target", {}, browser);
+    let newWin = yield newWinPromise;
+
+    // Because we're switching focus, we shouldn't steal it once
+    // content paints.
+    newWin.gURLBar.focus();
+
+    yield BrowserTestUtils.contentPainted(newWin.gBrowser.selectedBrowser);
+    yield delayedStartupPromise;
+
+    let focusedElement =
+      Services.focus.getFocusedElementForWindow(newWin, false, {});
+
+    Assert.equal(focusedElement, newWin.gURLBar.inputField,
+                 "URLBar should be focused");
+
+    yield BrowserTestUtils.closeWindow(newWin);
+  });
+});
--- a/browser/components/feeds/WebContentConverter.js
+++ b/browser/components/feeds/WebContentConverter.js
@@ -790,17 +790,17 @@ WebContentConverterRegistrar.prototype =
     let nums = children.map((child) => {
       let match = /^(\d+)\.uri$/.exec(child);
       return match ? match[1] : "";
     }).filter(child => !!child)
       .sort();
 
 
     // now register them
-    for (num of nums) {
+    for (let num of nums) {
       let branch = ps.getBranch(PREF_CONTENTHANDLERS_BRANCH + num + ".");
       try {
         this._registerContentHandlerHavingBranch(branch);
       } catch (ex) {
         // do nothing, the next branch might have values
       }
     }
 
--- a/browser/components/migration/MSMigrationUtils.jsm
+++ b/browser/components/migration/MSMigrationUtils.jsm
@@ -618,17 +618,17 @@ Cookies.prototype = {
       let hostLen = hostpath.indexOf("/");
       let host = hostpath.substr(0, hostLen);
       let path = hostpath.substr(hostLen);
 
       // For a non-null domain, assume it's what Mozilla considers
       // a domain cookie.  See bug 222343.
       if (host.length > 0) {
         // Fist delete any possible extant matching host cookie.
-        Services.cookies.remove(host, name, path, {}, false);
+        Services.cookies.remove(host, name, path, false, {});
         // Now make it a domain cookie.
         if (host[0] != "." && !hostIsIPAddress(host))
           host = "." + host;
       }
 
       // Fallback: expire in 1h (NB: time is in seconds since epoch, so we have
       // to divide the result of Date.now() (which is in milliseconds) by 1000).
       let expireTime = Math.floor(Date.now() / 1000) + 3600;
--- a/browser/components/preferences/cookies.js
+++ b/browser/components/preferences/cookies.js
@@ -586,17 +586,17 @@ var gCookiesWindow = {
     var psvc = Components.classes["@mozilla.org/preferences-service;1"]
                          .getService(Components.interfaces.nsIPrefBranch);
     var blockFutureCookies = false;
     if (psvc.prefHasUserValue("network.cookie.blockFutureCookies"))
       blockFutureCookies = psvc.getBoolPref("network.cookie.blockFutureCookies");
     for (var i = 0; i < deleteItems.length; ++i) {
       var item = deleteItems[i];
       this._cm.remove(item.host, item.name, item.path,
-                      item.originAttributes, blockFutureCookies);
+                      blockFutureCookies, item.originAttributes);
     }
   },
 
   deleteCookie: function () {
     // Selection Notes
     // - Selection always moves to *NEXT* adjacent item unless item
     //   is last child at a given level in which case it moves to *PREVIOUS*
     //   item
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -634,17 +634,18 @@ var SessionStoreInternal = {
     switch (aTopic) {
       case "browser-window-before-show": // catch new windows
         this.onBeforeBrowserWindowShown(aSubject);
         break;
       case "domwindowclosed": // catch closed windows
         this.onClose(aSubject);
         break;
       case "quit-application-granted":
-        this.onQuitApplicationGranted();
+        let syncShutdown = aData == "syncShutdown";
+        this.onQuitApplicationGranted(syncShutdown);
         break;
       case "browser-lastwindow-close-granted":
         this.onLastWindowCloseGranted();
         break;
       case "quit-application":
         this.onQuitApplication(aData);
         break;
       case "browser:purge-session-history": // catch sanitization
@@ -1417,17 +1418,17 @@ var SessionStoreInternal = {
         this._closedWindows.splice(winIndex, 1);
       }
     }
   },
 
   /**
    * On quit application granted
    */
-  onQuitApplicationGranted: function ssi_onQuitApplicationGranted() {
+  onQuitApplicationGranted: function ssi_onQuitApplicationGranted(syncShutdown=false) {
     // Collect an initial snapshot of window data before we do the flush
     this._forEachBrowserWindow((win) => {
       this._collectWindowData(win);
     });
 
     // Now add an AsyncShutdown blocker that'll spin the event loop
     // until the windows have all been flushed.
 
@@ -1435,20 +1436,26 @@ var SessionStoreInternal = {
     // and will help us debug things that go wrong with our AsyncShutdown
     // blocker.
     let progress = { total: -1, current: -1 };
 
     // We're going down! Switch state so that we treat closing windows and
     // tabs correctly.
     RunState.setQuitting();
 
-    AsyncShutdown.quitApplicationGranted.addBlocker(
-      "SessionStore: flushing all windows",
-      this.flushAllWindowsAsync(progress),
-      () => progress);
+    if (!syncShutdown) {
+      // We've got some time to shut down, so let's do this properly.
+      AsyncShutdown.quitApplicationGranted.addBlocker(
+        "SessionStore: flushing all windows",
+        this.flushAllWindowsAsync(progress),
+        () => progress);
+    } else {
+      // We have to shut down NOW, which means we only get to save whatever
+      // we already had cached.
+    }
   },
 
   /**
    * An async Task that iterates all open browser windows and flushes
    * any outstanding messages from their tabs. This will also close
    * all of the currently open windows while we wait for the flushes
    * to complete.
    *
--- a/browser/installer/Makefile.in
+++ b/browser/installer/Makefile.in
@@ -51,17 +51,16 @@ ifdef MOZ_D3DCOMPILER_VISTA_DLL
 DEFINES += -DMOZ_D3DCOMPILER_VISTA_DLL=$(MOZ_D3DCOMPILER_VISTA_DLL)
 endif
 ifdef MOZ_D3DCOMPILER_XP_DLL
 DEFINES += -DMOZ_D3DCOMPILER_XP_DLL=$(MOZ_D3DCOMPILER_XP_DLL)
 endif
 endif
 
 DEFINES += -DMOZ_CHILD_PROCESS_NAME=$(MOZ_CHILD_PROCESS_NAME)
-DEFINES += -DMOZ_PLUGIN_PROCESS_NAME=$(MOZ_PLUGIN_PROCESS_NAME)
 
 # Set MSVC dlls version to package, if any.
 ifdef MOZ_NO_DEBUG_RTL
 ifdef WIN32_REDIST_DIR
 DEFINES += -DMOZ_PACKAGE_MSVC_DLLS=1
 DEFINES += -DMSVC_C_RUNTIME_DLL=$(MSVC_C_RUNTIME_DLL)
 DEFINES += -DMSVC_CXX_RUNTIME_DLL=$(MSVC_CXX_RUNTIME_DLL)
 endif
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -83,17 +83,16 @@
 #ifdef XP_MACOSX
 @BINPATH@/@MOZ_CHILD_PROCESS_NAME@.app/
 @BINPATH@/@DLL_PREFIX@plugin_child_interpose@DLL_SUFFIX@
 #else
 @BINPATH@/@MOZ_CHILD_PROCESS_NAME@
 #endif
 #ifdef XP_WIN32
 @BINPATH@/plugin-hang-ui@BIN_SUFFIX@
-@BINPATH@/@MOZ_PLUGIN_PROCESS_NAME@
 #if MOZ_PACKAGE_MSVC_DLLS
 @BINPATH@/@MSVC_C_RUNTIME_DLL@
 @BINPATH@/@MSVC_CXX_RUNTIME_DLL@
 #endif
 #if MOZ_PACKAGE_WIN_UCRT_DLLS
 @BINPATH@/api-ms-win-*.dll
 @BINPATH@/ucrtbase.dll
 #endif
--- a/browser/installer/windows/nsis/installer.nsi
+++ b/browser/installer/windows/nsis/installer.nsi
@@ -471,18 +471,16 @@ Section "-Application" APP_IDX
   ; These need special handling on uninstall since they may be overwritten by
   ; an install into a different location.
   StrCpy $0 "Software\Microsoft\Windows\CurrentVersion\App Paths\${FileMainEXE}"
   ${WriteRegStr2} $TmpVal "$0" "" "$INSTDIR\${FileMainEXE}" 0
   ${WriteRegStr2} $TmpVal "$0" "Path" "$INSTDIR" 0
 
   StrCpy $0 "Software\Microsoft\MediaPlayer\ShimInclusionList\$R9"
   ${CreateRegKey} "$TmpVal" "$0" 0
-  StrCpy $0 "Software\Microsoft\MediaPlayer\ShimInclusionList\firefox-webcontent.exe"
-  ${CreateRegKey} "$TmpVal" "$0" 0
   StrCpy $0 "Software\Microsoft\MediaPlayer\ShimInclusionList\plugin-container.exe"
   ${CreateRegKey} "$TmpVal" "$0" 0
 
   ${If} $TmpVal == "HKLM"
     ; Set the permitted LSP Categories for WinVista and above
     ${SetAppLSPCategories} ${LSP_CATEGORIES}
   ${EndIf}
 
--- a/browser/installer/windows/nsis/uninstaller.nsi
+++ b/browser/installer/windows/nsis/uninstaller.nsi
@@ -350,19 +350,16 @@ Section "Uninstall"
 
   StrCpy $0 "Software\Microsoft\Windows\CurrentVersion\App Paths\${FileMainEXE}"
   ${If} $R9 == "false"
     DeleteRegKey HKLM "$0"
     DeleteRegKey HKCU "$0"
     StrCpy $0 "Software\Microsoft\MediaPlayer\ShimInclusionList\${FileMainEXE}"
     DeleteRegKey HKLM "$0"
     DeleteRegKey HKCU "$0"
-    StrCpy $0 "Software\Microsoft\MediaPlayer\ShimInclusionList\firefox-webcontent.exe"
-    DeleteRegKey HKLM "$0"
-    DeleteRegKey HKCU "$0"
     StrCpy $0 "Software\Microsoft\MediaPlayer\ShimInclusionList\plugin-container.exe"
     DeleteRegKey HKLM "$0"
     DeleteRegKey HKCU "$0"
     StrCpy $0 "Software\Classes\MIME\Database\Content Type\application/x-xpinstall;app=firefox"
     DeleteRegKey HKLM "$0"
     DeleteRegKey HKCU "$0"
   ${Else}
     ReadRegStr $R1 HKLM "$0" ""
--- a/browser/modules/PluginContent.jsm
+++ b/browser/modules/PluginContent.jsm
@@ -422,17 +422,17 @@ PluginContent.prototype = {
         break;
 
       case "PluginInstantiated":
         let key = this._getPluginInfo(plugin).pluginTag.niceName;
         Services.telemetry.getKeyedHistogramById('PLUGIN_ACTIVATION_COUNT').add(key);
         shouldShowNotification = true;
         let pluginRect = plugin.getBoundingClientRect();
         if (pluginRect.width <= 5 && pluginRect.height <= 5) {
-          Services.telemetry.getKeyedHistogramById('PLUGIN_TINY_CONTENT').add(key);
+          Services.telemetry.getHistogramById('PLUGIN_TINY_CONTENT').add(1);
         }
         break;
     }
 
     if (this._getPluginInfo(plugin).mimetype === "application/x-shockwave-flash") {
       this._recordFlashPluginTelemetry(eventType, plugin);
     }
 
--- a/build/win32/Makefile.in
+++ b/build/win32/Makefile.in
@@ -26,11 +26,10 @@ libs:: $(libs-preqs)
 endif
 
 # run the binscope tool to make sure the binary and all libraries
 # are using all available Windows OS-level security mechanisms
 # Don't do this in clang-cl since it doesn't support debug information yet.
 ifndef CLANG_CL
 check::
 	$(PYTHON) $(srcdir)/autobinscope.py $(DIST)/bin/$(MOZ_APP_NAME)$(BIN_SUFFIX) $(DIST)/crashreporter-symbols/
-	$(PYTHON) $(srcdir)/autobinscope.py $(DIST)/bin/$(MOZ_CHILD_PROCESS_NAME) $(DIST)/crashreporter-symbols/
-	$(PYTHON) $(srcdir)/autobinscope.py $(DIST)/bin/$(MOZ_PLUGIN_PROCESS_NAME) $(DIST)/crashreporter-symbols/
+	$(PYTHON) $(srcdir)/autobinscope.py $(DIST)/bin/plugin-container.exe $(DIST)/crashreporter-symbols/
 endif
--- a/build/win32/mozconfig.vs2015-win64
+++ b/build/win32/mozconfig.vs2015-win64
@@ -1,10 +1,11 @@
 if [ -z "${VSPATH}" ]; then
-    VSPATH="$(cd ${topsrcdir} && pwd)/vs2015u1"
+    TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir}
+    VSPATH="$(cd ${TOOLTOOL_DIR} && pwd)/vs2015u1"
 fi
 
 export WINDOWSSDKDIR="${VSPATH}/SDK"
 export WIN32_REDIST_DIR="${VSPATH}/VC/redist/x86/Microsoft.VC140.CRT"
 export WIN_UCRT_REDIST_DIR="${VSPATH}/SDK/Redist/ucrt/DLLs/x86"
 
 export PATH="${VSPATH}/VC/bin/amd64_x86:${VSPATH}/VC/bin/amd64:${VSPATH}/VC/bin:${VSPATH}/SDK/bin/x86:${VSPATH}/SDK/bin/x64:${VSPATH}/DIASDK/bin:${PATH}"
 export PATH="${VSPATH}/VC/redist/x86/Microsoft.VC140.CRT:${VSPATH}/VC/redist/x64/Microsoft.VC140.CRT:${VSPATH}/SDK/Redist/ucrt/DLLs/x86:${VSPATH}/SDK/Redist/ucrt/DLLs/x64:${PATH}"
--- a/build/win64/mozconfig.vs2015
+++ b/build/win64/mozconfig.vs2015
@@ -1,10 +1,11 @@
 if [ -z "${VSPATH}" ]; then
-    VSPATH="$(cd ${topsrcdir} && pwd)/vs2015u1"
+    TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir}
+    VSPATH="$(cd ${TOOLTOOL_DIR} && pwd)/vs2015u1"
 fi
 
 export WINDOWSSDKDIR="${VSPATH}/SDK"
 export WIN32_REDIST_DIR=${VSPATH}/VC/redist/x64/Microsoft.VC140.CRT
 export WIN_UCRT_REDIST_DIR="${VSPATH}/SDK/Redist/ucrt/DLLs/x64"
 
 export PATH="${VSPATH}/VC/bin/amd64:${VSPATH}/VC/bin:${VSPATH}/SDK/bin/x64:${VSPATH}/VC/redist/x64/Microsoft.VC140.CRT:${VSPATH}/SDK/Redist/ucrt/DLLs/x64:${VSPATH}/DIASDK/bin/amd64:${PATH}"
 
--- a/devtools/client/memory/actions/snapshot.js
+++ b/devtools/client/memory/actions/snapshot.js
@@ -286,22 +286,21 @@ const defaultCensusTaker = takeTreeMap;
  * to display some basic information about a snapshot.
  *
  * @param {string} value from viewState
  */
 const getCurrentCensusTaker = exports.getCurrentCensusTaker = function (currentView) {
   switch (currentView) {
     case viewState.TREE_MAP:
       return takeTreeMap;
-    break;
     case viewState.CENSUS:
       return takeCensus;
-    break;
+    default:
+      return defaultCensusTaker;
   }
-  return defaultCensusTaker;
 };
 
 /**
  * Refresh the selected snapshot's census data, if need be (for example,
  * display configuration changed).
  *
  * @param {HeapAnalysesClient} heapWorker
  */
--- a/devtools/client/responsive.html/test/browser/browser.ini
+++ b/devtools/client/responsive.html/test/browser/browser.ini
@@ -1,17 +1,17 @@
 [DEFAULT]
 tags = devtools
 subsuite = devtools
-skip-if = !e10s && debug # Bug 1262416 - Intermittent crash at MessageLoop::DeletePendingTasks
+skip-if = (!e10s && debug) # Bug 1262416 - Intermittent crash at MessageLoop::DeletePendingTasks
 support-files =
   devices.json
   head.js
   !/devtools/client/commandline/test/helpers.js
   !/devtools/client/framework/test/shared-head.js
   !/devtools/client/framework/test/shared-redux-head.js
 
 [browser_device_width.js]
-skip-if = (!e10s && debug) || (e10s && debug && os == "win") # Bug 1262432 - crashes at nsLayoutUtils::HasDisplayPort(content), Bug 1262416 - Intermittent crash at MessageLoop::DeletePendingTasks
+skip-if = (e10s && debug) # Bug 1262432 - crashes at nsLayoutUtils::HasDisplayPort(content)
 [browser_exit_button.js]
 [browser_resize_cmd.js]
 [browser_screenshot_button.js]
 [browser_viewport_basics.js]
--- a/devtools/server/actors/storage.js
+++ b/devtools/server/actors/storage.js
@@ -846,17 +846,17 @@ var cookieHelpers = {
         }
         break;
 
       case "host":
       case "name":
       case "path":
         // Remove the edited cookie.
         Services.cookies.remove(origHost, origName, origPath,
-                                cookie.originAttributes, false);
+                                false, cookie.originAttributes);
         break;
     }
 
     // Apply changes.
     cookie[field] = newValue;
 
     // cookie.isSession is not always set correctly on session cookies so we
     // need to trust cookie.expires instead.
@@ -889,18 +889,18 @@ var cookieHelpers = {
     let enumerator = Services.cookies.getCookiesFromHost(host);
     while (enumerator.hasMoreElements()) {
       let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
       if (hostMatches(cookie.host, host) && cookie.name === name) {
         Services.cookies.remove(
           cookie.host,
           cookie.name,
           cookie.path,
-          cookie.originAttributes,
-          false
+          false,
+          cookie.originAttributes
         );
       }
     }
   },
 
   addCookieObservers: function() {
     Services.obs.addObserver(cookieHelpers, "cookie-changed", false);
     return null;
--- a/devtools/shared/gcli/commands/cookie.js
+++ b/devtools/shared/gcli/commands/cookie.js
@@ -128,17 +128,17 @@ exports.items = [
       host = sanitizeHost(host);
       let enm = cookieMgr.getCookiesFromHost(host);
 
       while (enm.hasMoreElements()) {
         let cookie = enm.getNext().QueryInterface(Ci.nsICookie);
         if (isCookieAtHost(cookie, host)) {
           if (cookie.name == args.name) {
             cookieMgr.remove(cookie.host, cookie.name, cookie.path,
-                             cookie.originAttributes, false);
+                             false, cookie.originAttributes);
           }
         }
       }
     }
   },
   {
     item: "converter",
     from: "cookies",
--- a/dom/animation/test/chrome/test_running_on_compositor.html
+++ b/dom/animation/test/chrome/test_running_on_compositor.html
@@ -47,357 +47,368 @@ var omtaEnabled = SpecialPowers.DOMWindo
                   SpecialPowers.getBoolPref(OMTAPrefKey);
 
 promise_test(function(t) {
   // FIXME: When we implement Element.animate, use that here instead of CSS
   // so that we remove any dependency on the CSS mapping.
   var div = addDiv(t, { style: 'animation: anim 100s' });
   var animation = div.getAnimations()[0];
 
-  return animation.ready.then(t.step_func(function() {
+  return animation.ready.then(function() {
     assert_equals(animation.isRunningOnCompositor, omtaEnabled,
        'Animation reports that it is running on the compositor'
        + ' during playback');
 
     div.style.animationPlayState = 'paused';
 
     return animation.ready;
-  })).then(t.step_func(function() {
+  }).then(function() {
     assert_equals(animation.isRunningOnCompositor, false,
        'Animation reports that it is NOT running on the compositor'
        + ' when paused');
-  }));
+  });
 }, '');
 
 promise_test(function(t) {
   var div = addDiv(t, { style: 'animation: background 100s' });
   var animation = div.getAnimations()[0];
 
-  return animation.ready.then(t.step_func(function() {
+  return animation.ready.then(function() {
     assert_equals(animation.isRunningOnCompositor, false,
        'Animation reports that it is NOT running on the compositor'
        + ' for animation of "background"');
-  }));
+  });
 }, 'isRunningOnCompositor is false for animation of "background"');
 
 promise_test(function(t) {
   var div = addDiv(t, { style: 'animation: background_and_translate 100s' });
   var animation = div.getAnimations()[0];
 
-  return animation.ready.then(t.step_func(function() {
+  return animation.ready.then(function() {
     assert_equals(animation.isRunningOnCompositor, omtaEnabled,
        'Animation reports that it is running on the compositor'
         + ' when the animation has two properties, where one can run'
         + ' on the compositor, the other cannot');
-  }));
+  });
 }, 'isRunningOnCompositor is true if the animation has at least one ' +
    'property can run on compositor');
 
 promise_test(function(t) {
   var div = addDiv(t, { style: 'animation: anim 100s' });
   var animation = div.getAnimations()[0];
 
-  return animation.ready.then(t.step_func(function() {
+  return animation.ready.then(function() {
     animation.pause();
     return animation.ready;
-  })).then(t.step_func(function() {
+  }).then(function() {
     assert_equals(animation.isRunningOnCompositor, false,
        'Animation reports that it is NOT running on the compositor'
        + ' when animation.pause() is called');
-  }));
+  });
 }, 'isRunningOnCompositor is false when the animation.pause() is called');
 
 promise_test(function(t) {
   var div = addDiv(t, { style: 'animation: anim 100s' });
   var animation = div.getAnimations()[0];
 
-  return animation.ready.then(t.step_func(function() {
+  return animation.ready.then(function() {
     animation.finish();
     assert_equals(animation.isRunningOnCompositor, false,
        'Animation reports that it is NOT running on the compositor'
        + ' immediately after animation.finish() is called');
     // Check that we don't set the flag back again on the next tick.
     return waitForFrame();
-  })).then(t.step_func(function() {
+  }).then(function() {
     assert_equals(animation.isRunningOnCompositor, false,
        'Animation reports that it is NOT running on the compositor'
        + ' on the next tick after animation.finish() is called');
-  }));
+  });
 }, 'isRunningOnCompositor is false when the animation.finish() is called');
 
 promise_test(function(t) {
   var div = addDiv(t, { style: 'animation: anim 100s' });
   var animation = div.getAnimations()[0];
 
-  return animation.ready.then(t.step_func(function() {
+  return animation.ready.then(function() {
     animation.currentTime = 100 * MS_PER_SEC;
     assert_equals(animation.isRunningOnCompositor, false,
        'Animation reports that it is NOT running on the compositor'
        + ' immediately after manually seeking the animation to the end');
     // Check that we don't set the flag back again on the next tick.
     return waitForFrame();
-  })).then(t.step_func(function() {
+  }).then(function() {
     assert_equals(animation.isRunningOnCompositor, false,
        'Animation reports that it is NOT running on the compositor'
        + ' on the next tick after manually seeking the animation to the end');
-  }));
+  });
 }, 'isRunningOnCompositor is false when manually seeking the animation to ' +
    'the end');
 
 promise_test(function(t) {
   var div = addDiv(t, { style: 'animation: anim 100s' });
   var animation = div.getAnimations()[0];
 
-  return animation.ready.then(t.step_func(function() {
+  return animation.ready.then(function() {
     animation.cancel();
     assert_equals(animation.isRunningOnCompositor, false,
        'Animation reports that it is NOT running on the compositor'
        + ' immediately after animation.cancel() is called');
     // Check that we don't set the flag back again on the next tick.
     return waitForFrame();
-  })).then(t.step_func(function() {
+  }).then(function() {
     assert_equals(animation.isRunningOnCompositor, false,
        'Animation reports that it is NOT running on the compositor'
        + ' on the next tick after animation.cancel() is called');
-  }));
+  });
 }, 'isRunningOnCompositor is false when animation.cancel() is called');
 
 promise_test(function(t) {
   var div = addDiv(t, { style: 'animation: anim 100s 100s' });
   var animation = div.getAnimations()[0];
 
-  return animation.ready.then(t.step_func(function() {
+  return animation.ready.then(function() {
     assert_equals(animation.isRunningOnCompositor, false,
        'Animation reports that it is NOT running on the compositor'
        + ' while in the delay phase');
-  }));
+  });
 }, 'isRunningOnCompositor is false while in the delay phase');
 
 // This is to test that we don't simply clobber the flag when ticking
 // animations and then set it again during painting.
 promise_test(function(t) {
   var div = addDiv(t, { style: 'animation: anim 100s' });
   var animation = div.getAnimations()[0];
 
-  return animation.ready.then(t.step_func(function() {
-    return new Promise(t.step_func(function(resolve) {
-      window.requestAnimationFrame(t.step_func(function() {
-        assert_equals(animation.isRunningOnCompositor, omtaEnabled,
-           'Animation reports that it is running on the compositor'
-            + ' in requestAnimationFrame callback');
+  return animation.ready.then(function() {
+    return new Promise(function(resolve) {
+      window.requestAnimationFrame(function() {
+        t.step(function() {
+          assert_equals(animation.isRunningOnCompositor, omtaEnabled,
+            'Animation reports that it is running on the compositor'
+             + ' in requestAnimationFrame callback');
+        });
+
         resolve();
-      }));
-    }));
-  }));
+      });
+    });
+  });
 }, 'isRunningOnCompositor is true in requestAnimationFrame callback');
 
 promise_test(function(t) {
   var div = addDiv(t, { style: 'animation: anim 100s' });
   var animation = div.getAnimations()[0];
 
-  return animation.ready.then(t.step_func(function() {
-    return new Promise(t.step_func(function(resolve) {
-      var observer = new MutationObserver(t.step_func(function(records) {
+  return animation.ready.then(function() {
+    return new Promise(function(resolve) {
+      var observer = new MutationObserver(function(records) {
         var changedAnimation;
+
         records.forEach(function(record) {
           changedAnimation =
             record.changedAnimations.find(function(changedAnim) {
               return changedAnim == animation;
             });
         });
-        assert_true(!!changedAnimation, 'The animation should be recorded '
-          + 'as one of the changedAnimations');
-        assert_equals(animation.isRunningOnCompositor, omtaEnabled,
-          'Animation reports that it is running on the compositor'
-           + ' in MutationObserver callback');
+
+        t.step(function() {
+          assert_true(!!changedAnimation, 'The animation should be recorded '
+            + 'as one of the changedAnimations');
+
+          assert_equals(animation.isRunningOnCompositor, omtaEnabled,
+            'Animation reports that it is running on the compositor'
+             + ' in MutationObserver callback');
+        });
+
         resolve();
-      }));
+      });
       observer.observe(div, { animations: true, subtree: false });
       t.add_cleanup(function() {
         observer.disconnect();
       });
       div.style.animationDuration = "200s";
-    }));
-  }));
+    });
+  });
 }, 'isRunningOnCompositor is true in MutationObserver callback');
 
 // This is to test that we don't temporarily clear the flag when forcing
 // an unthrottled sample.
 promise_test(function(t) {
   return new Promise(function(resolve) {
     // Needs scrollbars to cause overflow.
     SpecialPowers.pushPrefEnv({ set: [["ui.showHideScrollbars", 1]] },
                               resolve);
-  }).then(t.step_func(function() {
+  }).then(function() {
     var div = addDiv(t, { style: 'animation: rotate 100s' });
     var animation = div.getAnimations()[0];
 
-    return animation.ready.then(t.step_func(function() {
-      return new Promise(t.step_func(function(resolve) {
+    return animation.ready.then(function() {
+      return new Promise(function(resolve) {
         var timeAtStart = window.performance.now();
         function handleFrame() {
-          assert_equals(animation.isRunningOnCompositor, omtaEnabled,
-             'Animation reports that it is running on the compositor'
-              + ' in requestAnimationFrame callback');
+          t.step(function() {
+            assert_equals(animation.isRunningOnCompositor, omtaEnabled,
+              'Animation reports that it is running on the compositor'
+               + ' in requestAnimationFrame callback');
+          });
 
           // we have to wait at least 200ms because this animation is
           // unthrottled on every 200ms.
           // See http://hg.mozilla.org/mozilla-central/file/cafb1c90f794/layout/style/AnimationCommon.cpp#l863
           if (window.performance.now() - timeAtStart > 200) {
             resolve();
             return;
           }
           window.requestAnimationFrame(handleFrame);
         }
         window.requestAnimationFrame(handleFrame);
-      }));
-    }));
-  }));
+      });
+    });
+  });
 }, 'isRunningOnCompositor remains true in requestAnimationFrameCallback for ' +
    'overflow animation');
 
 promise_test(function(t) {
   var div = addDiv(t, { style: 'transition: opacity 100s; opacity: 1' });
 
   getComputedStyle(div).opacity;
 
   div.style.opacity = 0;
   var animation = div.getAnimations()[0];
 
-  return animation.ready.then(t.step_func(function() {
+  return animation.ready.then(function() {
     assert_equals(animation.isRunningOnCompositor, omtaEnabled,
        'Transition reports that it is running on the compositor'
        + ' during playback for opacity transition');
-  }));
+  });
 }, 'isRunningOnCompositor for transitions');
 
 promise_test(function(t) {
   var div = addDiv(t, { style: 'animation: rotate_and_opacity 100s; ' +
                                'backface-visibility: hidden; ' +
                                'transform: none !important;' });
   var animation = div.getAnimations()[0];
 
-  return animation.ready.then(t.step_func(function() {
+  return animation.ready.then(function() {
     assert_equals(animation.isRunningOnCompositor, omtaEnabled,
        'If an animation has a property that can run on the compositor and a '
        + 'property that cannot (due to Gecko limitations) but where the latter'
        + 'property is overridden in the CSS cascade, the animation should '
        + 'still report that it is running on the compositor');
-  }));
+  });
 }, 'isRunningOnCompositor is true when a property that would otherwise block ' +
    'running on the compositor is overridden in the CSS cascade');
 
 promise_test(function(t) {
   var animation = addDivAndAnimate(t,
                                    {},
                                    { opacity: [ 0, 1 ] }, 200 * MS_PER_SEC);
 
-  return animation.ready.then(t.step_func(function() {
+  return animation.ready.then(function() {
     assert_equals(animation.isRunningOnCompositor, omtaEnabled,
       'Animation reports that it is running on the compositor');
 
     animation.currentTime = 150 * MS_PER_SEC;
     animation.effect.timing.duration = 100 * MS_PER_SEC;
 
     assert_equals(animation.isRunningOnCompositor, false,
        'Animation reports that it is NOT running on the compositor'
        + ' when the animation is set a shorter duration than current time');
-  }));
+  });
 }, 'animation is immediately removed from compositor' +
    'when timing.duration is made shorter than the current time');
 
 promise_test(function(t) {
   var animation = addDivAndAnimate(t,
                                    {},
                                    { opacity: [ 0, 1 ] }, 100 * MS_PER_SEC);
 
-  return animation.ready.then(t.step_func(function() {
+  return animation.ready.then(function() {
     assert_equals(animation.isRunningOnCompositor, omtaEnabled,
       'Animation reports that it is running on the compositor');
 
     animation.currentTime = 500 * MS_PER_SEC;
 
     assert_equals(animation.isRunningOnCompositor, false,
       'Animation reports that it is NOT running on the compositor'
       + ' when finished');
 
     animation.effect.timing.duration = 1000 * MS_PER_SEC;
     return waitForFrame();
-  })).then(t.step_func(function() {
+  }).then(function() {
     assert_equals(animation.isRunningOnCompositor, omtaEnabled,
       'Animation reports that it is running on the compositor'
       + ' when restarted');
-  }));
+  });
 }, 'animation is added to compositor' +
    ' when timing.duration is made longer than the current time');
 
 promise_test(function(t) {
   var animation = addDivAndAnimate(t,
                                    {},
                                    { opacity: [ 0, 1 ] }, 100 * MS_PER_SEC);
 
-  return animation.ready.then(t.step_func(function() {
+  return animation.ready.then(function() {
     assert_equals(animation.isRunningOnCompositor, omtaEnabled,
       'Animation reports that it is running on the compositor');
 
     animation.effect.timing.endDelay = 100 * MS_PER_SEC;
 
     assert_equals(animation.isRunningOnCompositor, omtaEnabled,
       'Animation reports that it is running on the compositor'
       + ' when endDelay is changed');
 
     animation.currentTime = 110 * MS_PER_SEC;
     return waitForFrame();
-  })).then(t.step_func(function() {
+  }).then(function() {
     assert_equals(animation.isRunningOnCompositor, false,
       'Animation reports that it is NOT running on the compositor'
       + ' when currentTime is during endDelay');
-  }));
+  });
 }, 'animation is removed from compositor' +
    ' when current time is made longer than the duration even during endDelay');
 
 promise_test(function(t) {
   var animation = addDivAndAnimate(t,
                                    {},
                                    { opacity: [ 0, 1 ] }, 100 * MS_PER_SEC);
 
-  return animation.ready.then(t.step_func(function() {
+  return animation.ready.then(function() {
     assert_equals(animation.isRunningOnCompositor, omtaEnabled,
       'Animation reports that it is running on the compositor');
 
     animation.effect.timing.endDelay = -200 * MS_PER_SEC;
     return waitForFrame();
-  })).then(t.step_func(function() {
+  }).then(function() {
     assert_equals(animation.isRunningOnCompositor, false,
       'Animation reports that it is NOT running on the compositor'
       + ' when endTime is negative value');
-  }));
+  });
 }, 'animation is removed from compositor' +
    ' when endTime is negative value');
 
 promise_test(function(t) {
   var animation = addDivAndAnimate(t,
                                    {},
                                    { opacity: [ 0, 1 ] }, 100 * MS_PER_SEC);
 
-  return animation.ready.then(t.step_func(function() {
+  return animation.ready.then(function() {
     assert_equals(animation.isRunningOnCompositor, omtaEnabled,
       'Animation reports that it is running on the compositor');
 
     animation.effect.timing.endDelay = -50 * MS_PER_SEC;
     return waitForFrame();
-  })).then(t.step_func(function() {
+  }).then(function() {
     assert_equals(animation.isRunningOnCompositor, omtaEnabled,
       'Animation reports that it is running on the compositor'
       + ' when endTime is positive and endDelay is negative');
     animation.currentTime = 60 * MS_PER_SEC;
     return waitForFrame();
-  })).then(t.step_func(function() {
+  }).then(function() {
     assert_equals(animation.isRunningOnCompositor, false,
       'Animation reports that it is NOT running on the compositor'
       + ' when currentTime is after endTime');
-  }));
+  });
 }, 'animation is NOT running on compositor' +
    ' when endTime is positive and endDelay is negative');
 
 </script>
 </script>
 </body>
--- a/dom/base/Crypto.cpp
+++ b/dom/base/Crypto.cpp
@@ -53,18 +53,16 @@ Crypto::WrapObject(JSContext* aCx, JS::H
   return CryptoBinding::Wrap(aCx, this, aGivenProto);
 }
 
 void
 Crypto::GetRandomValues(JSContext* aCx, const ArrayBufferView& aArray,
                         JS::MutableHandle<JSObject*> aRetval,
                         ErrorResult& aRv)
 {
-  MOZ_ASSERT(NS_IsMainThread(), "Called on the wrong thread");
-
   JS::Rooted<JSObject*> view(aCx, aArray.Obj());
 
   if (JS_IsTypedArrayObject(view) && JS_GetTypedArraySharedness(view)) {
     // Throw if the object is mapping shared memory (must opt in).
     aRv.ThrowTypeError<MSG_TYPEDARRAY_IS_SHARED>(NS_LITERAL_STRING("Argument of Crypto.getRandomValues"));
     return;
   }
 
@@ -90,64 +88,40 @@ Crypto::GetRandomValues(JSContext* aCx, 
     NS_WARNING("ArrayBufferView length is 0, cannot continue");
     aRetval.set(view);
     return;
   } else if (dataLen > 65536) {
     aRv.Throw(NS_ERROR_DOM_QUOTA_EXCEEDED_ERR);
     return;
   }
 
-  uint8_t* data = aArray.Data();
+  nsCOMPtr<nsIRandomGenerator> randomGenerator =
+    do_GetService("@mozilla.org/security/random-generator;1");
+  if (!randomGenerator) {
+    aRv.Throw(NS_ERROR_DOM_OPERATION_ERR);
+    return;
+  }
 
-  if (!XRE_IsParentProcess()) {
-    InfallibleTArray<uint8_t> randomValues;
-    // Tell the parent process to generate random values via PContent
-    ContentChild* cc = ContentChild::GetSingleton();
-    if (!cc->SendGetRandomValues(dataLen, &randomValues) ||
-        randomValues.Length() == 0) {
-      aRv.Throw(NS_ERROR_FAILURE);
-      return;
-    }
-    NS_ASSERTION(dataLen == randomValues.Length(),
-                 "Invalid length returned from parent process!");
-    memcpy(data, randomValues.Elements(), dataLen);
-  } else {
-    uint8_t *buf = GetRandomValues(dataLen);
+  uint8_t* buf;
+  nsresult rv = randomGenerator->GenerateRandomBytes(dataLen, &buf);
+  if (NS_FAILED(rv) || !buf) {
+    aRv.Throw(NS_ERROR_DOM_OPERATION_ERR);
+    return;
+  }
 
-    if (!buf) {
-      aRv.Throw(NS_ERROR_FAILURE);
-      return;
-    }
-
-    memcpy(data, buf, dataLen);
-    free(buf);
-  }
+  // Copy random bytes to ABV.
+  memcpy(aArray.Data(), buf, dataLen);
+  free(buf);
 
   aRetval.set(view);
 }
 
 SubtleCrypto*
 Crypto::Subtle()
 {
   if(!mSubtle) {
     mSubtle = new SubtleCrypto(GetParentObject());
   }
   return mSubtle;
 }
 
-/* static */ uint8_t*
-Crypto::GetRandomValues(uint32_t aLength)
-{
-  nsCOMPtr<nsIRandomGenerator> randomGenerator;
-  nsresult rv;
-  randomGenerator = do_GetService("@mozilla.org/security/random-generator;1");
-  NS_ENSURE_TRUE(randomGenerator, nullptr);
-
-  uint8_t* buf;
-  rv = randomGenerator->GenerateRandomBytes(aLength, &buf);
-
-  NS_ENSURE_SUCCESS(rv, nullptr);
-
-  return buf;
-}
-
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/Crypto.h
+++ b/dom/base/Crypto.h
@@ -49,19 +49,16 @@ public:
   GetParentObject() const
   {
     return mParent;
   }
 
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
-  static uint8_t*
-  GetRandomValues(uint32_t aLength);
-
 private:
   nsCOMPtr<nsIGlobalObject> mParent;
   RefPtr<SubtleCrypto> mSubtle;
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/base/FragmentOrElement.cpp
+++ b/dom/base/FragmentOrElement.cpp
@@ -1181,20 +1181,16 @@ FragmentOrElement::SetTextContentInterna
 void
 FragmentOrElement::DestroyContent()
 {
   nsIDocument *document = OwnerDoc();
   document->BindingManager()->RemovedFromDocument(this, document,
                                                   nsBindingManager::eRunDtor);
   document->ClearBoxObjectFor(this);
 
-  // XXX We really should let cycle collection do this, but that currently still
-  //     leaks (see https://bugzilla.mozilla.org/show_bug.cgi?id=406684).
-  ReleaseWrapper(this);
-
   uint32_t i, count = mAttrsAndChildren.ChildCount();
   for (i = 0; i < count; ++i) {
     // The child can remove itself from the parent in BindToTree.
     mAttrsAndChildren.ChildAt(i)->DestroyContent();
   }
   ShadowRoot* shadowRoot = GetShadowRoot();
   if (shadowRoot) {
     shadowRoot->DestroyContent();
--- a/dom/base/StructuredCloneHolder.cpp
+++ b/dom/base/StructuredCloneHolder.cpp
@@ -390,20 +390,16 @@ StructuredCloneHolder::ReadFullySerializ
                                                     JSStructuredCloneReader* aReader,
                                                     uint32_t aTag)
 {
   if (aTag == SCTAG_DOM_IMAGEDATA) {
     return ReadStructuredCloneImageData(aCx, aReader);
   }
 
   if (aTag == SCTAG_DOM_WEBCRYPTO_KEY) {
-    if (!NS_IsMainThread()) {
-      return nullptr;
-    }
-
     nsIGlobalObject *global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));
     if (!global) {
       return nullptr;
     }
 
     // Prevent the return value from being trashed by a GC during ~nsRefPtr.
     JS::Rooted<JSObject*> result(aCx);
     {
@@ -506,17 +502,16 @@ StructuredCloneHolder::WriteFullySeriali
       return WriteStructuredCloneImageData(aCx, aWriter, imageData);
     }
   }
 
   // Handle Key cloning
   {
     CryptoKey* key = nullptr;
     if (NS_SUCCEEDED(UNWRAP_OBJECT(CryptoKey, aObj, key))) {
-      MOZ_ASSERT(NS_IsMainThread());
       return JS_WriteUint32Pair(aWriter, SCTAG_DOM_WEBCRYPTO_KEY, 0) &&
              key->WriteStructuredClone(aWriter);
     }
   }
 
 #ifdef MOZ_WEBRTC
   {
     // Handle WebRTC Certificate cloning
new file mode 100644
--- /dev/null
+++ b/dom/base/crashtests/1251361.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<script>
+
+var frameRoot;
+
+function boom()
+{
+    var frameWin = f.contentWindow;
+    frameRoot = frameWin.document.documentElement;
+    frameWin.location.replace("data:text/html;charset=UTF-8,<body onload='parent.g();'>2");
+}
+
+function g()
+{
+    setTimeout(h, 0);
+}
+
+function h()
+{
+    var newDoc = document.implementation.createDocument('', '', null);
+    newDoc.adoptNode(frameRoot);
+}
+
+</script>
+<body onload="boom();">
+
+<iframe id="f" src="data:text/html;charset=UTF-8,<marquee>1"></iframe>
+
+</body>
+</html>
--- a/dom/base/crashtests/crashtests.list
+++ b/dom/base/crashtests/crashtests.list
@@ -200,8 +200,9 @@ pref(dom.webcomponents.enabled,true) loa
 load 1154598.xhtml
 load 1157995.html
 load 1181619.html
 load structured_clone_container_throws.html
 HTTP(..) load xhr_abortinprogress.html
 load xhr_empty_datauri.html
 load xhr_html_nullresponse.html
 load 1230422.html
+load 1251361.html
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -8921,20 +8921,16 @@ nsDocument::Destroy()
   mLayoutHistoryState = nullptr;
 
   // Shut down our external resource map.  We might not need this for
   // leak-fixing if we fix nsDocumentViewer to do cycle-collection, but
   // tearing down all those frame trees right now is the right thing to do.
   mExternalResourceMap.Shutdown();
 
   mRegistry = nullptr;
-
-  // XXX We really should let cycle collection do this, but that currently still
-  //     leaks (see https://bugzilla.mozilla.org/show_bug.cgi?id=406684).
-  ReleaseWrapper(static_cast<nsINode*>(this));
 }
 
 void
 nsDocument::RemovedFromDocShell()
 {
   if (mRemovedFromDocShell)
     return;
 
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -2621,18 +2621,18 @@ nsFrameLoader::DoSendAsyncMessage(JSCont
       MOZ_CRASH();
       return NS_ERROR_DOM_DATA_CLONE_ERR;
     }
     InfallibleTArray<mozilla::jsipc::CpowEntry> cpows;
     jsipc::CPOWManager* mgr = cp->GetCPOWManager();
     if (aCpows && (!mgr || !mgr->Wrap(aCx, aCpows, &cpows))) {
       return NS_ERROR_UNEXPECTED;
     }
-    if (tabParent->SendAsyncMessage(nsString(aMessage), data, cpows,
-                                    IPC::Principal(aPrincipal))) {
+    if (tabParent->SendAsyncMessage(nsString(aMessage), cpows,
+                                    IPC::Principal(aPrincipal), data)) {
       return NS_OK;
     } else {
       return NS_ERROR_UNEXPECTED;
     }
   }
 
   if (mChildMessageManager) {
     RefPtr<nsAsyncMessageToChild> ev = new nsAsyncMessageToChild(aCx, aCpows, this);
--- a/dom/base/nsFrameMessageManager.cpp
+++ b/dom/base/nsFrameMessageManager.cpp
@@ -2024,18 +2024,18 @@ public:
     ClonedMessageData data;
     if (!BuildClonedMessageDataForChild(cc, aData, data)) {
       return NS_ERROR_DOM_DATA_CLONE_ERR;
     }
     InfallibleTArray<mozilla::jsipc::CpowEntry> cpows;
     if (aCpows && !cc->GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) {
       return NS_ERROR_UNEXPECTED;
     }
-    if (!cc->SendAsyncMessage(PromiseFlatString(aMessage), data, cpows,
-                              IPC::Principal(aPrincipal))) {
+    if (!cc->SendAsyncMessage(PromiseFlatString(aMessage), cpows,
+                              IPC::Principal(aPrincipal), data)) {
       return NS_ERROR_UNEXPECTED;
     }
 
     return NS_OK;
   }
 };
 
 
--- a/dom/base/nsGenericDOMDataNode.cpp
+++ b/dom/base/nsGenericDOMDataNode.cpp
@@ -791,24 +791,16 @@ nsGenericDOMDataNode::IsNodeOfType(uint3
   return !(aFlags & ~(eCONTENT | eDATA_NODE));
 }
 
 void
 nsGenericDOMDataNode::SaveSubtreeState()
 {
 }
 
-void
-nsGenericDOMDataNode::DestroyContent()
-{
-  // XXX We really should let cycle collection do this, but that currently still
-  //     leaks (see https://bugzilla.mozilla.org/show_bug.cgi?id=406684).
-  ReleaseWrapper(this);
-}
-
 #ifdef DEBUG
 void
 nsGenericDOMDataNode::List(FILE* out, int32_t aIndent) const
 {
 }
 
 void
 nsGenericDOMDataNode::DumpContent(FILE* out, int32_t aIndent,
--- a/dom/base/nsGenericDOMDataNode.h
+++ b/dom/base/nsGenericDOMDataNode.h
@@ -136,17 +136,16 @@ public:
   virtual nsresult AppendText(const char16_t* aBuffer, uint32_t aLength,
                               bool aNotify) override;
   virtual bool TextIsOnlyWhitespace() override;
   virtual bool HasTextForTranslation() override;
   virtual void AppendTextTo(nsAString& aResult) override;
   MOZ_WARN_UNUSED_RESULT
   virtual bool AppendTextTo(nsAString& aResult,
                             const mozilla::fallible_t&) override;
-  virtual void DestroyContent() override;
   virtual void SaveSubtreeState() override;
 
 #ifdef DEBUG
   virtual void List(FILE* out, int32_t aIndent) const override;
   virtual void DumpContent(FILE* out, int32_t aIndent, bool aDumpAll) const override;
 #endif
 
   virtual nsIContent *GetBindingParent() const override;
--- a/dom/base/nsIContent.h
+++ b/dom/base/nsIContent.h
@@ -860,17 +860,19 @@ public:
    * the node is an element, this will notify the state change.
    */
   virtual void UpdateEditableState(bool aNotify);
 
   /**
    * Destroy this node and its children. Ideally this shouldn't be needed
    * but for now we need to do it to break cycles.
    */
-  virtual void DestroyContent() = 0;
+  virtual void DestroyContent()
+  {
+  }
 
   /**
    * Saves the form state of this node and its children.
    */
   virtual void SaveSubtreeState() = 0;
 
   /**
    * Getter and setter for our primary frame pointer.  This is the frame that
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -866,31 +866,66 @@ public:
   {
     mContext = nullptr;
   }
 
 private:
   CanvasRenderingContext2D *mContext;
 };
 
+class CanvasFilterChainObserver : public nsSVGFilterChainObserver
+{
+public:
+  CanvasFilterChainObserver(nsTArray<nsStyleFilter>& aFilters,
+                            Element* aCanvasElement,
+                            CanvasRenderingContext2D* aContext)
+    : nsSVGFilterChainObserver(aFilters, aCanvasElement)
+    , mContext(aContext)
+  {
+  }
+
+  virtual void DoUpdate() override
+  {
+    if (!mContext) {
+      MOZ_CRASH("This should never be called without a context");
+    }
+    // Refresh the cached FilterDescription in mContext->CurrentState().filter.
+    // If this filter is not at the top of the state stack, we'll refresh the
+    // wrong filter, but that's ok, because we'll refresh the right filter
+    // when we pop the state stack in CanvasRenderingContext2D::Restore().
+    mContext->UpdateFilter();
+  }
+
+  void DetachFromContext() { mContext = nullptr; }
+
+private:
+  CanvasRenderingContext2D *mContext;
+};
+
 NS_IMPL_CYCLE_COLLECTING_ADDREF(CanvasRenderingContext2D)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(CanvasRenderingContext2D)
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(CanvasRenderingContext2D)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CanvasRenderingContext2D)
   // Make sure we remove ourselves from the list of demotable contexts (raw pointers),
   // since we're logically destructed at this point.
   CanvasRenderingContext2D::RemoveDemotableContext(tmp);
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCanvasElement)
   for (uint32_t i = 0; i < tmp->mStyleStack.Length(); i++) {
     ImplCycleCollectionUnlink(tmp->mStyleStack[i].patternStyles[Style::STROKE]);
     ImplCycleCollectionUnlink(tmp->mStyleStack[i].patternStyles[Style::FILL]);
     ImplCycleCollectionUnlink(tmp->mStyleStack[i].gradientStyles[Style::STROKE]);
     ImplCycleCollectionUnlink(tmp->mStyleStack[i].gradientStyles[Style::FILL]);
+    CanvasFilterChainObserver *filterChainObserver =
+      static_cast<CanvasFilterChainObserver*>(tmp->mStyleStack[i].filterChainObserver.get());
+    if (filterChainObserver) {
+      filterChainObserver->DetachFromContext();
+    }
+    ImplCycleCollectionUnlink(tmp->mStyleStack[i].filterChainObserver);
   }
   for (size_t x = 0 ; x < tmp->mHitRegionsOptions.Length(); x++) {
     RegionInfo& info = tmp->mHitRegionsOptions[x];
     if (info.mElement) {
       ImplCycleCollectionUnlink(info.mElement);
     }
   }
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
@@ -898,16 +933,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CanvasRenderingContext2D)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCanvasElement)
   for (uint32_t i = 0; i < tmp->mStyleStack.Length(); i++) {
     ImplCycleCollectionTraverse(cb, tmp->mStyleStack[i].patternStyles[Style::STROKE], "Stroke CanvasPattern");
     ImplCycleCollectionTraverse(cb, tmp->mStyleStack[i].patternStyles[Style::FILL], "Fill CanvasPattern");
     ImplCycleCollectionTraverse(cb, tmp->mStyleStack[i].gradientStyles[Style::STROKE], "Stroke CanvasGradient");
     ImplCycleCollectionTraverse(cb, tmp->mStyleStack[i].gradientStyles[Style::FILL], "Fill CanvasGradient");
+    ImplCycleCollectionTraverse(cb, tmp->mStyleStack[i].filterChainObserver, "Filter Chain Observer");
   }
   for (size_t x = 0 ; x < tmp->mHitRegionsOptions.Length(); x++) {
     RegionInfo& info = tmp->mHitRegionsOptions[x];
     if (info.mElement) {
       ImplCycleCollectionTraverse(cb, info.mElement, "Hit region fallback element");
     }
   }
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
@@ -2401,40 +2437,16 @@ CanvasRenderingContext2D::ParseFilter(co
   if (!sc) {
     return false;
   }
 
   aFilterChain = sc->StyleSVGReset()->mFilters;
   return true;
 }
 
-class CanvasFilterChainObserver : public nsSVGFilterChainObserver
-{
-public:
-  CanvasFilterChainObserver(nsTArray<nsStyleFilter>& aFilters,
-                            Element* aCanvasElement,
-                            CanvasRenderingContext2D* aContext)
-    : nsSVGFilterChainObserver(aFilters, aCanvasElement)
-    , mContext(aContext)
-  {
-  }
-
-  virtual void DoUpdate() override
-  {
-    // Refresh the cached FilterDescription in mContext->CurrentState().filter.
-    // If this filter is not at the top of the state stack, we'll refresh the
-    // wrong filter, but that's ok, because we'll refresh the right filter
-    // when we pop the state stack in CanvasRenderingContext2D::Restore().
-    mContext->UpdateFilter();
-  }
-
-private:
-  CanvasRenderingContext2D *mContext;
-};
-
 void
 CanvasRenderingContext2D::SetFilter(const nsAString& aFilter, ErrorResult& aError)
 {
   nsTArray<nsStyleFilter> filterChain;
   if (ParseFilter(aFilter, filterChain, aError)) {
     CurrentState().filterString = aFilter;
     filterChain.SwapElements(CurrentState().filterChain);
     if (mCanvasElement) {
--- a/dom/canvas/test/reftest/filters/liveness.html
+++ b/dom/canvas/test/reftest/filters/liveness.html
@@ -2,17 +2,17 @@
 <html>
 <body>
 <canvas id="canvas" width="100" height="100"></canvas>
 <script>
 
 var canvas = document.getElementById('canvas');
 var ctx = canvas.getContext('2d');
 
-ctx.font = '10px sans-serif';
+ctx.font = '20px sans-serif';
 ctx.filter = 'drop-shadow(0 .5em black)';
-ctx.font = '20px sans-serif';
+ctx.font = '10px sans-serif';
 ctx.fillStyle = '#0f0';
 ctx.fillRect(25, 25, 50, 40);
 
 </script>
 </body>
 </html>
--- a/dom/crypto/CryptoKey.cpp
+++ b/dom/crypto/CryptoKey.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 "pk11pub.h"
 #include "cryptohi.h"
 #include "nsNSSComponent.h"
 #include "ScopedNSSTypes.h"
 #include "mozilla/dom/CryptoKey.h"
-#include "mozilla/dom/WebCryptoCommon.h"
 #include "mozilla/dom/SubtleCryptoBinding.h"
 #include "mozilla/dom/ToJSValue.h"
 
 // Templates taken from security/nss/lib/cryptohi/seckey.c
 // These would ideally be exported by NSS and until that
 // happens we have to keep our own copies.
 const SEC_ASN1Template SECKEY_DHPublicKeyTemplate[] = {
     { SEC_ASN1_INTEGER, offsetof(SECKEYPublicKey,u.dh.publicValue), },
--- a/dom/crypto/WebCryptoTask.cpp
+++ b/dom/crypto/WebCryptoTask.cpp
@@ -309,66 +309,37 @@ MapHashAlgorithmNameToMgfMechanism(const
     mech = CKG_MGF1_SHA384;
   } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
     mech = CKG_MGF1_SHA512;
   }
 
   return mech;
 }
 
-// Helper function to clone data from an ArrayBuffer or ArrayBufferView object
-inline bool
-CloneData(JSContext* aCx, CryptoBuffer& aDst, JS::Handle<JSObject*> aSrc)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  // Try ArrayBuffer
-  RootedTypedArray<ArrayBuffer> ab(aCx);
-  if (ab.Init(aSrc)) {
-    return !!aDst.Assign(ab);
-  }
-
-  // Try ArrayBufferView
-  RootedTypedArray<ArrayBufferView> abv(aCx);
-  if (abv.Init(aSrc)) {
-    return !!aDst.Assign(abv);
-  }
-
-  return false;
-}
-
 // Implementation of WebCryptoTask methods
 
 void
 WebCryptoTask::DispatchWithPromise(Promise* aResultPromise)
 {
-  MOZ_ASSERT(NS_IsMainThread());
   mResultPromise = aResultPromise;
 
   // Fail if an error was set during the constructor
   MAYBE_EARLY_FAIL(mEarlyRv)
 
   // Perform pre-NSS operations, and fail if they fail
   mEarlyRv = BeforeCrypto();
   MAYBE_EARLY_FAIL(mEarlyRv)
 
   // Skip NSS if we're already done, or launch a CryptoTask
   if (mEarlyComplete) {
     CallCallback(mEarlyRv);
     Skip();
     return;
   }
 
-  // Ensure that NSS is initialized, since presumably CalculateResult
-  // will use NSS functions
-  if (!EnsureNSSInitializedChromeOrContent()) {
-    mEarlyRv = NS_ERROR_DOM_UNKNOWN_ERR;
-    MAYBE_EARLY_FAIL(mEarlyRv)
-  }
-
   // Store calling thread and dispatch to thread pool.
   mOriginalThread = NS_GetCurrentThread();
   mEarlyRv = WebCryptoThreadPool::Dispatch(this);
   MAYBE_EARLY_FAIL(mEarlyRv)
 }
 
 NS_IMETHODIMP
 WebCryptoTask::Run()
@@ -1382,66 +1353,90 @@ public:
         }
       }
     }
 
     // Individual algorithms may still have to check 'use'
     return true;
   }
 
-  void SetKeyData(JSContext* aCx, JS::Handle<JSObject*> aKeyData) {
-    // First try to treat as ArrayBuffer/ABV,
-    // and if that fails, try to initialize a JWK
-    if (CloneData(aCx, mKeyData, aKeyData)) {
-      mDataIsJwk = false;
-
-      if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
-        SetJwkFromKeyData();
+  void SetKeyData(JSContext* aCx, JS::Handle<JSObject*> aKeyData)
+  {
+    mDataIsJwk = false;
+
+    // Try ArrayBuffer
+    RootedTypedArray<ArrayBuffer> ab(aCx);
+    if (ab.Init(aKeyData)) {
+      if (!mKeyData.Assign(ab)) {
+        mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
       }
-    } else {
-      ClearException ce(aCx);
-      JS::RootedValue value(aCx, JS::ObjectValue(*aKeyData));
-      if (!mJwk.Init(aCx, value)) {
-        mEarlyRv = NS_ERROR_DOM_DATA_ERR;
-        return;
+      return;
+    }
+
+    // Try ArrayBufferView
+    RootedTypedArray<ArrayBufferView> abv(aCx);
+    if (abv.Init(aKeyData)) {
+      if (!mKeyData.Assign(abv)) {
+        mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
       }
-      mDataIsJwk = true;
+      return;
     }
+
+    // Try JWK
+    ClearException ce(aCx);
+    JS::RootedValue value(aCx, JS::ObjectValue(*aKeyData));
+    if (!mJwk.Init(aCx, value)) {
+      mEarlyRv = NS_ERROR_DOM_DATA_ERR;
+      return;
+    }
+
+    mDataIsJwk = true;
   }
 
-  void SetKeyData(const CryptoBuffer& aKeyData)
+  void SetKeyDataMaybeParseJWK(const CryptoBuffer& aKeyData)
   {
     if (!mKeyData.Assign(aKeyData)) {
       mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
       return;
     }
 
     mDataIsJwk = false;
 
     if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
-      SetJwkFromKeyData();
+      nsDependentCSubstring utf8((const char*) mKeyData.Elements(),
+                                 (const char*) (mKeyData.Elements() +
+                                                mKeyData.Length()));
+      if (!IsUTF8(utf8)) {
+        mEarlyRv = NS_ERROR_DOM_DATA_ERR;
+        return;
+      }
+
+      nsString json = NS_ConvertUTF8toUTF16(utf8);
+      if (!mJwk.Init(json)) {
+        mEarlyRv = NS_ERROR_DOM_DATA_ERR;
+        return;
+      }
+
+      mDataIsJwk = true;
     }
   }
 
-  void SetJwkFromKeyData()
+  void SetRawKeyData(const CryptoBuffer& aKeyData)
   {
-    nsDependentCSubstring utf8((const char*) mKeyData.Elements(),
-                               (const char*) (mKeyData.Elements() +
-                                              mKeyData.Length()));
-    if (!IsUTF8(utf8)) {
-      mEarlyRv = NS_ERROR_DOM_DATA_ERR;
+    if (!mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
+      mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
       return;
     }
 
-    nsString json = NS_ConvertUTF8toUTF16(utf8);
-    if (!mJwk.Init(json)) {
-      mEarlyRv = NS_ERROR_DOM_DATA_ERR;
+    if (!mKeyData.Assign(aKeyData)) {
+      mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
       return;
     }
-    mDataIsJwk = true;
+
+    mDataIsJwk = false;
   }
 
 protected:
   nsString mFormat;
   RefPtr<CryptoKey> mKey;
   CryptoBuffer mKeyData;
   bool mDataIsSet;
   bool mDataIsJwk;
@@ -2849,17 +2844,17 @@ public:
   }
 
 protected:
   RefPtr<ImportSymmetricKeyTask> mTask;
   bool mResolved;
 
 private:
   virtual void Resolve() override {
-    mTask->SetKeyData(this->mResult);
+    mTask->SetRawKeyData(this->mResult);
     mTask->DispatchWithPromise(this->mResultPromise);
     mResolved = true;
   }
 
   virtual void Cleanup() override
   {
     if (mTask && !mResolved) {
       mTask->Skip();
@@ -3144,17 +3139,17 @@ public:
   {}
 
 private:
   RefPtr<ImportKeyTask> mTask;
   bool mResolved;
 
   virtual void Resolve() override
   {
-    mTask->SetKeyData(KeyEncryptTask::mResult);
+    mTask->SetKeyDataMaybeParseJWK(KeyEncryptTask::mResult);
     mTask->DispatchWithPromise(KeyEncryptTask::mResultPromise);
     mResolved = true;
   }
 
   virtual void Cleanup() override
   {
     if (mTask && !mResolved) {
       mTask->Skip();
--- a/dom/crypto/WebCryptoThreadPool.cpp
+++ b/dom/crypto/WebCryptoThreadPool.cpp
@@ -60,16 +60,18 @@ WebCryptoThreadPool::Init()
 }
 
 nsresult
 WebCryptoThreadPool::DispatchInternal(nsIRunnable* aRunnable)
 {
   MutexAutoLock lock(mMutex);
 
   if (!mPool) {
+    NS_ENSURE_TRUE(EnsureNSSInitializedChromeOrContent(), NS_ERROR_FAILURE);
+
     nsCOMPtr<nsIThreadPool> pool(do_CreateInstance(NS_THREADPOOL_CONTRACTID));
     NS_ENSURE_TRUE(pool, NS_ERROR_FAILURE);
 
     nsresult rv = pool->SetName(NS_LITERAL_CSTRING("SubtleCrypto"));
     NS_ENSURE_SUCCESS(rv, rv);
 
     pool.swap(mPool);
   }
--- a/dom/crypto/test/mochitest.ini
+++ b/dom/crypto/test/mochitest.ini
@@ -1,15 +1,16 @@
 [DEFAULT]
 # Bug 1010743 - Re-enable WebCrypto tests on b2g
 skip-if = (buildapp == 'b2g')
 support-files =
   file_indexedDB.html
   test-array.js
   test-vectors.js
+  test-worker.js
   test_WebCrypto.css
   util.js
 
 [test_indexedDB.html]
 skip-if = toolkit == 'android' # bug 1200570
 [test_WebCrypto.html]
 [test_WebCrypto_DH.html]
 [test_WebCrypto_ECDH.html]
@@ -17,9 +18,10 @@ skip-if = toolkit == 'android' # bug 120
 [test_WebCrypto_HKDF.html]
 [test_WebCrypto_JWK.html]
 [test_WebCrypto_Normalize.html]
 [test_WebCrypto_PBKDF2.html]
 [test_WebCrypto_Reject_Generating_Keys_Without_Usages.html]
 [test_WebCrypto_RSA_OAEP.html]
 [test_WebCrypto_RSA_PSS.html]
 [test_WebCrypto_Structured_Cloning.html]
+[test_WebCrypto_Workers.html]
 [test_WebCrypto_Wrap_Unwrap.html]
--- a/dom/crypto/test/test-array.js
+++ b/dom/crypto/test/test-array.js
@@ -83,33 +83,66 @@ function Test(name, test) {
     if (this.startTime &&  this.endTime) {
       this.row[2].innerHTML = (this.endTime - this.startTime) + " ms";
     } else {
       this.row[2].innerHTML = "";
     }
   };
 }
 
+function WorkerTest(worker, name, test) {
+  this.name = `${name} (Worker)`;
+  this.startTime = null;
+  this.endTime = null;
+  this.result = null;
+  this.row = null;
+
+  this.run = function() {
+    // Note the start time
+    this.startTime = new Date();
+
+    // Send the test code to the worker.
+    worker.postMessage(test.toSource());
+
+    // We expect only boolean responses from the worker script.
+    worker.onmessage = e => this.complete(e.data);
+    worker.onerror = e => this.complete(false);
+  };
+
+  var base = new Test(name, test);
+
+  // Inherit what we need from the |Test| class. We can't simply use its
+  // prototype as Test is just a function that can be used like a constructor.
+  for (var method of ["draw", "setRow", "next", "complete"]) {
+    this[method] = base[method].bind(this);
+  }
+}
+
 var TestArray = {
   tests: [],
   table: null,
   passSpan: null,
   failSpan: null,
   pendingSpan: null,
   pass: 0,
   fail: 0,
   pending: 0,
   currTest: 0,
+  worker: new Worker("test-worker.js"),
 
   addTest: function(name, testFn) {
     // Give it a reference to the array
     var test = new Test(name, testFn);
     test.ta = this;
+
     // Add test to tests
     this.tests.push(test);
+
+    // Run the test in a worker too.
+    this.tests.push(new WorkerTest(this.worker, name, testFn));
   },
 
   updateSummary: function() {
     this.pass = this.fail = this.pending = 0;
     for (var i=0; i<this.tests.length; ++i) {
       if (this.tests[i].result == true)  this.pass++;
       if (this.tests[i].result == false) this.fail++;
       if (this.tests[i].result == null)  this.pending++;
@@ -169,16 +202,17 @@ if (window.addEventListener) {
 
 function start() {
   TestArray.run();
 }
 
 MOCHITEST = ("SimpleTest" in window);
 if (MOCHITEST) {
   SimpleTest.waitForExplicitFinish();
+  SimpleTest.requestLongerTimeout(2);
   window.addEventListener("load", function() {
     SimpleTest.waitForFocus(start);
   });
 }
 
 function error(test) {
   return function(x) {
     console.log("ERROR :: " + x);
new file mode 100644
--- /dev/null
+++ b/dom/crypto/test/test-worker.js
@@ -0,0 +1,44 @@
+/* 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/. */
+
+importScripts("util.js");
+importScripts("test-vectors.js");
+
+var window = this;
+
+function finish(result) {
+  postMessage(result);
+}
+
+function complete(test, valid) {
+  return function(x) {
+    if (valid) {
+      finish(valid(x));
+    } else {
+      finish(true);
+    }
+  };
+}
+
+function memcmp_complete(test, value) {
+  return function (x) {
+    finish(util.memcmp(x, value));
+  };
+}
+
+function error(test) {
+  return function (x) {
+    throw x;
+  };
+}
+
+onmessage = function (msg) {
+  var test = eval("(" + msg.data + ")");
+
+  try {
+    test.call({complete: finish});
+  } catch (err) {
+    error(`Failed to run worker test: ${err}\n`);
+  }
+};
--- a/dom/crypto/test/test_WebCrypto.html
+++ b/dom/crypto/test/test_WebCrypto.html
@@ -281,16 +281,17 @@ TestArray.addTest(
     }
 
     function doGet() {
       var req = db.transaction([dbstore], "readwrite")
                   .objectStore(dbstore)
                   .get(dbkey);
       req.onerror = error(that);
       req.onsuccess = complete(that, function(e) {
+        db.close();
         return hasKeyFields(e.target.result.val);
       });
     }
 
     crypto.subtle.importKey("raw", tv.raw, alg, false, ['encrypt'])
       .then(doIndexedDB, error(that));
   }
 );
@@ -987,16 +988,21 @@ TestArray.addTest(
     doCheckRSASSA().then(error(that), complete(that));
   }
 );
 
 // -----------------------------------------------------------------------------
 TestArray.addTest(
   "Test that we're using the right globals when creating objects",
   function() {
+    // This test isn't supported in workers.
+    if (window.importScripts) {
+      return this.complete(true);
+    }
+
     var that = this;
     var data = crypto.getRandomValues(new Uint8Array(10));
     var hmacAlg = {name: "HMAC", length: 256, hash: "SHA-1"};
 
     var rsaAlg = {
       name: "RSA-PSS",
       hash: "SHA-1",
       modulusLength: 1024,
--- a/dom/crypto/test/test_WebCrypto_JWK.html
+++ b/dom/crypto/test/test_WebCrypto_JWK.html
@@ -172,18 +172,21 @@ TestArray.addTest(
                  x.k == jwk.k
         }),
         error(that)
       );
   }
 );
 
 // -----------------------------------------------------------------------------
-function importExportRSAPrivateKey(jwk) {
-  return function () {
+TestArray.addTest(
+  "JWK import/export of an RSA private key",
+  function () {
+    var jwk = tv.rsassa.jwk_priv;
+
     var that = this;
     var alg = { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" };
 
     function doExport(k) {
       return crypto.subtle.exportKey("jwk", k);
     }
 
     crypto.subtle.importKey("jwk", jwk, alg, true, ['sign'])
@@ -203,26 +206,53 @@ function importExportRSAPrivateKey(jwk) 
                  x.q  == jwk.q  &&
                  x.dp == jwk.dp &&
                  x.dq == jwk.dq &&
                  x.qi == jwk.qi;
           }),
         error(that)
       );
   }
-}
-
-TestArray.addTest(
-  "JWK import/export of an RSA private key",
-  importExportRSAPrivateKey(tv.rsassa.jwk_priv)
 );
 
+// -----------------------------------------------------------------------------
 TestArray.addTest(
   "JWK import/export of an RSA private key where p < q",
-  importExportRSAPrivateKey(tv.rsassa.jwk_priv_pLTq)
+  function () {
+    var jwk = tv.rsassa.jwk_priv_pLTq;
+
+    var that = this;
+    var alg = { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" };
+
+    function doExport(k) {
+      return crypto.subtle.exportKey("jwk", k);
+    }
+
+    crypto.subtle.importKey("jwk", jwk, alg, true, ['sign'])
+      .then(doExport)
+      .then(
+        complete(that, function(x) {
+          return hasBaseJwkFields(x) &&
+                 hasFields(x, ['n', 'e', 'd', 'p', 'q', 'dp', 'dq', 'qi']) &&
+                 x.kty == 'RSA' &&
+                 x.alg == 'RS256' &&
+                 x.ext &&
+                 shallowArrayEquals(x.key_ops, ['sign']) &&
+                 x.n  == jwk.n  &&
+                 x.e  == jwk.e  &&
+                 x.d  == jwk.d  &&
+                 x.p  == jwk.p  &&
+                 x.q  == jwk.q  &&
+                 x.dp == jwk.dp &&
+                 x.dq == jwk.dq &&
+                 x.qi == jwk.qi;
+          }),
+        error(that)
+      );
+  }
 );
 
 // -----------------------------------------------------------------------------
 TestArray.addTest(
   "JWK export of an RSA public key",
   function () {
     var that = this;
     var alg = { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" };
new file mode 100644
--- /dev/null
+++ b/dom/crypto/test/test_WebCrypto_Workers.html
@@ -0,0 +1,116 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+<title>WebCrypto Test Suite</title>
+<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
+<link rel="stylesheet" href="./test_WebCrypto.css"/>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+
+<!-- Utilities for manipulating ABVs -->
+<script src="util.js"></script>
+
+<!-- A simple wrapper around IndexedDB -->
+<script src="simpledb.js"></script>
+
+<!-- Test vectors drawn from the literature -->
+<script src="./test-vectors.js"></script>
+
+<!-- General testing framework -->
+<script src="./test-array.js"></script>
+
+<script>/*<![CDATA[*/
+"use strict";
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "Send a CryptoKey to a Worker and use it to encrypt data",
+  function () {
+    var worker = new Worker(`data:text/plain,
+      onmessage = ({data: {key, data, nonce}}) => {
+        var alg = { name: "AES-GCM", iv: nonce };
+        crypto.subtle.encrypt(alg, key, data).then(postMessage);
+      };
+    `);
+
+    var data = crypto.getRandomValues(new Uint8Array(128));
+    var nonce = crypto.getRandomValues(new Uint8Array(16));
+    var alg = { name: "AES-GCM", length: 128 };
+    var that = this;
+
+    // Generate a new AES key.
+    crypto.subtle.generateKey(alg, false, ["encrypt", "decrypt"]).then(key => {
+      // Wait for ciphertext, check and decrypt.
+      worker.addEventListener("message", ({data: ciphertext}) => {
+        var alg = { name: "AES-GCM", iv: nonce };
+        crypto.subtle.decrypt(alg, key, ciphertext)
+          .then(memcmp_complete(that, data), error(that));
+      });
+
+      // Send it to the worker.
+      worker.postMessage({key, data, nonce});
+    });
+  }
+);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "Get a CryptoKey from a Worker and encrypt/decrypt data",
+  function () {
+    var worker = new Worker(`data:text/plain,
+      var alg = { name: "AES-GCM", length: 128 };
+      crypto.subtle.generateKey(alg, false, ["encrypt", "decrypt"])
+        .then(postMessage);
+    `);
+
+    var data = crypto.getRandomValues(new Uint8Array(128));
+    var nonce = crypto.getRandomValues(new Uint8Array(16));
+    var alg = { name: "AES-GCM", iv: nonce };
+    var that = this;
+
+    // Wait for the key from the worker.
+    worker.addEventListener("message", ({data: key}) => {
+      // Encrypt some data with the key.
+      crypto.subtle.encrypt(alg, key, data).then(ciphertext => {
+        // Verify and decrypt.
+        crypto.subtle.decrypt(alg, key, ciphertext)
+          .then(memcmp_complete(that, data), error(that));
+      });
+    });
+  }
+);
+/*]]>*/</script>
+</head>
+
+<body>
+
+<div id="content">
+	<div id="head">
+		<b>Web</b>Crypto<br>
+	</div>
+
+    <div id="start" onclick="start();">RUN ALL</div>
+
+    <div id="resultDiv" class="content">
+    Summary:
+    <span class="pass"><span id="passN">0</span> passed, </span>
+    <span class="fail"><span id="failN">0</span> failed, </span>
+    <span class="pending"><span id="pendingN">0</span> pending.</span>
+    <br/>
+    <br/>
+
+    <table id="results">
+        <tr>
+            <th>Test</th>
+            <th>Result</th>
+            <th>Time</th>
+        </tr>
+    </table>
+
+    </div>
+
+    <div id="foot"></div>
+</div>
+
+</body>
+</html>
--- a/dom/filesystem/CreateDirectoryTask.cpp
+++ b/dom/filesystem/CreateDirectoryTask.cpp
@@ -4,33 +4,42 @@
  * 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 "CreateDirectoryTask.h"
 
 #include "mozilla/dom/Directory.h"
 #include "mozilla/dom/FileSystemBase.h"
 #include "mozilla/dom/FileSystemUtils.h"
+#include "mozilla/dom/PFileSystemParams.h"
 #include "mozilla/dom/Promise.h"
+#include "mozilla/ipc/BackgroundParent.h"
 #include "nsIFile.h"
 #include "nsStringGlue.h"
 
 namespace mozilla {
+
+using namespace ipc;
+
 namespace dom {
 
-/* static */ already_AddRefed<CreateDirectoryTask>
-CreateDirectoryTask::Create(FileSystemBase* aFileSystem,
-                            nsIFile* aTargetPath,
-                            ErrorResult& aRv)
+/**
+ * CreateDirectoryTaskChild
+ */
+
+/* static */ already_AddRefed<CreateDirectoryTaskChild>
+CreateDirectoryTaskChild::Create(FileSystemBase* aFileSystem,
+                                 nsIFile* aTargetPath,
+                                 ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aFileSystem);
 
-  RefPtr<CreateDirectoryTask> task =
-    new CreateDirectoryTask(aFileSystem, aTargetPath);
+  RefPtr<CreateDirectoryTaskChild> task =
+    new CreateDirectoryTaskChild(aFileSystem, aTargetPath);
 
   // aTargetPath can be null. In this case SetError will be called.
 
   nsCOMPtr<nsIGlobalObject> globalObject =
     do_QueryInterface(aFileSystem->GetParentObject());
   if (NS_WARN_IF(!globalObject)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
@@ -39,113 +48,149 @@ CreateDirectoryTask::Create(FileSystemBa
   task->mPromise = Promise::Create(globalObject, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   return task.forget();
 }
 
-/* static */ already_AddRefed<CreateDirectoryTask>
-CreateDirectoryTask::Create(FileSystemBase* aFileSystem,
-                            const FileSystemCreateDirectoryParams& aParam,
-                            FileSystemRequestParent* aParent,
-                            ErrorResult& aRv)
-{
-  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  MOZ_ASSERT(aFileSystem);
-
-  RefPtr<CreateDirectoryTask> task =
-    new CreateDirectoryTask(aFileSystem, aParam, aParent);
-
-  NS_ConvertUTF16toUTF8 path(aParam.realPath());
-  aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
-  }
-
-  return task.forget();
-}
-
-CreateDirectoryTask::CreateDirectoryTask(FileSystemBase* aFileSystem,
-                                         nsIFile* aTargetPath)
-  : FileSystemTaskBase(aFileSystem)
+CreateDirectoryTaskChild::CreateDirectoryTaskChild(FileSystemBase* aFileSystem,
+                                                   nsIFile* aTargetPath)
+  : FileSystemTaskChildBase(aFileSystem)
   , mTargetPath(aTargetPath)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aFileSystem);
 }
 
-CreateDirectoryTask::CreateDirectoryTask(FileSystemBase* aFileSystem,
-                                         const FileSystemCreateDirectoryParams& aParam,
-                                         FileSystemRequestParent* aParent)
-  : FileSystemTaskBase(aFileSystem, aParam, aParent)
+CreateDirectoryTaskChild::~CreateDirectoryTaskChild()
 {
-  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  MOZ_ASSERT(aFileSystem);
-}
-
-CreateDirectoryTask::~CreateDirectoryTask()
-{
-  MOZ_ASSERT(!mPromise || NS_IsMainThread(),
-             "mPromise should be released on main thread!");
+  MOZ_ASSERT(NS_IsMainThread());
 }
 
 already_AddRefed<Promise>
-CreateDirectoryTask::GetPromise()
+CreateDirectoryTaskChild::GetPromise()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   return RefPtr<Promise>(mPromise).forget();
 }
 
 FileSystemParams
-CreateDirectoryTask::GetRequestParams(const nsString& aSerializedDOMPath,
-                                      ErrorResult& aRv) const
+CreateDirectoryTaskChild::GetRequestParams(const nsString& aSerializedDOMPath,
+                                           ErrorResult& aRv) const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
 
   nsAutoString path;
   aRv = mTargetPath->GetPath(path);
   if (NS_WARN_IF(aRv.Failed())) {
     return FileSystemCreateDirectoryParams();
   }
 
   return FileSystemCreateDirectoryParams(aSerializedDOMPath, path);
 }
 
-FileSystemResponseValue
-CreateDirectoryTask::GetSuccessRequestResult(ErrorResult& aRv) const
+void
+CreateDirectoryTaskChild::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
+                                                  ErrorResult& aRv)
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
+
+  const FileSystemDirectoryResponse& r =
+    aValue.get_FileSystemDirectoryResponse();
+
+  aRv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(r.realPath()), true,
+                              getter_AddRefs(mTargetPath));
+  NS_WARN_IF(aRv.Failed());
+}
+
+void
+CreateDirectoryTaskChild::HandlerCallback()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
+  if (mFileSystem->IsShutdown()) {
+    mPromise = nullptr;
+    return;
+  }
+
+  if (HasError()) {
+    mPromise->MaybeReject(mErrorValue);
+    mPromise = nullptr;
+    return;
+  }
+
+  RefPtr<Directory> dir = Directory::Create(mFileSystem->GetParentObject(),
+                                            mTargetPath,
+                                            Directory::eNotDOMRootDirectory,
+                                            mFileSystem);
+  MOZ_ASSERT(dir);
+
+  mPromise->MaybeResolve(dir);
+  mPromise = nullptr;
+}
+
+void
+CreateDirectoryTaskChild::GetPermissionAccessType(nsCString& aAccess) const
+{
+  aAccess.AssignLiteral(CREATE_DIRECTORY_TASK_PERMISSION);
+}
+
+/**
+ * CreateDirectoryTaskParent
+ */
+
+/* static */ already_AddRefed<CreateDirectoryTaskParent>
+CreateDirectoryTaskParent::Create(FileSystemBase* aFileSystem,
+                                  const FileSystemCreateDirectoryParams& aParam,
+                                  FileSystemRequestParent* aParent,
+                                  ErrorResult& aRv)
+{
+  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aFileSystem);
+
+  RefPtr<CreateDirectoryTaskParent> task =
+    new CreateDirectoryTaskParent(aFileSystem, aParam, aParent);
+
+  aRv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(aParam.realPath()), true,
+                              getter_AddRefs(task->mTargetPath));
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  return task.forget();
+}
+
+CreateDirectoryTaskParent::CreateDirectoryTaskParent(FileSystemBase* aFileSystem,
+                                                     const FileSystemCreateDirectoryParams& aParam,
+                                                     FileSystemRequestParent* aParent)
+  : FileSystemTaskParentBase(aFileSystem, aParam, aParent)
+{
+  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aFileSystem);
+}
+
+FileSystemResponseValue
+CreateDirectoryTaskParent::GetSuccessRequestResult(ErrorResult& aRv) const
+{
+  AssertIsOnBackgroundThread();
 
   nsAutoString path;
   aRv = mTargetPath->GetPath(path);
   if (NS_WARN_IF(aRv.Failed())) {
     return FileSystemDirectoryResponse();
   }
 
   return FileSystemDirectoryResponse(path);
 }
 
-void
-CreateDirectoryTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
-                                             ErrorResult& aRv)
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  FileSystemDirectoryResponse r = aValue;
-
-  NS_ConvertUTF16toUTF8 path(r.realPath());
-  aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(mTargetPath));
-  NS_WARN_IF(aRv.Failed());
-}
-
 nsresult
-CreateDirectoryTask::Work()
+CreateDirectoryTaskParent::IOWork()
 {
   MOZ_ASSERT(XRE_IsParentProcess(),
              "Only call from parent process!");
   MOZ_ASSERT(!NS_IsMainThread(), "Only call on worker thread!");
 
   if (mFileSystem->IsShutdown()) {
     return NS_ERROR_FAILURE;
   }
@@ -164,39 +209,15 @@ CreateDirectoryTask::Work()
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
 void
-CreateDirectoryTask::HandlerCallback()
+CreateDirectoryTaskParent::GetPermissionAccessType(nsCString& aAccess) const
 {
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  if (mFileSystem->IsShutdown()) {
-    mPromise = nullptr;
-    return;
-  }
-
-  if (HasError()) {
-    mPromise->MaybeReject(mErrorValue);
-    mPromise = nullptr;
-    return;
-  }
-  RefPtr<Directory> dir = Directory::Create(mFileSystem->GetParentObject(),
-                                            mTargetPath,
-                                            Directory::eNotDOMRootDirectory,
-                                            mFileSystem);
-  MOZ_ASSERT(dir);
-
-  mPromise->MaybeResolve(dir);
-  mPromise = nullptr;
-}
-
-void
-CreateDirectoryTask::GetPermissionAccessType(nsCString& aAccess) const
-{
-  aAccess.AssignLiteral("create");
+  aAccess.AssignLiteral(CREATE_DIRECTORY_TASK_PERMISSION);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/CreateDirectoryTask.h
+++ b/dom/filesystem/CreateDirectoryTask.h
@@ -6,70 +6,85 @@
 
 #ifndef mozilla_dom_CreateDirectoryTask_h
 #define mozilla_dom_CreateDirectoryTask_h
 
 #include "mozilla/dom/FileSystemTaskBase.h"
 #include "nsAutoPtr.h"
 #include "mozilla/ErrorResult.h"
 
+#define CREATE_DIRECTORY_TASK_PERMISSION "create"
+
 namespace mozilla {
 namespace dom {
 
+class FileSystemCreateDirectoryParams;
 class Promise;
 
-class CreateDirectoryTask final : public FileSystemTaskBase
+class CreateDirectoryTaskChild final : public FileSystemTaskChildBase
 {
 public:
-  static already_AddRefed<CreateDirectoryTask>
+  static already_AddRefed<CreateDirectoryTaskChild>
   Create(FileSystemBase* aFileSystem,
          nsIFile* aTargetPath,
          ErrorResult& aRv);
 
-  static already_AddRefed<CreateDirectoryTask>
-  Create(FileSystemBase* aFileSystem,
-         const FileSystemCreateDirectoryParams& aParam,
-         FileSystemRequestParent* aParent,
-         ErrorResult& aRv);
-
   virtual
-  ~CreateDirectoryTask();
+  ~CreateDirectoryTaskChild();
 
   already_AddRefed<Promise>
   GetPromise();
 
   virtual void
   GetPermissionAccessType(nsCString& aAccess) const override;
 
+  virtual void
+  HandlerCallback() override;
+
 protected:
   virtual FileSystemParams
   GetRequestParams(const nsString& aSerializedDOMPath,
                    ErrorResult& aRv) const override;
 
-  virtual FileSystemResponseValue
-  GetSuccessRequestResult(ErrorResult& aRv) const override;
-
   virtual void
   SetSuccessRequestResult(const FileSystemResponseValue& aValue,
                           ErrorResult& aRv) override;
 
-  virtual nsresult
-  Work() override;
-
-  virtual void
-  HandlerCallback() override;
 
 private:
-  CreateDirectoryTask(FileSystemBase* aFileSystem,
-                      nsIFile* aTargetPath);
-
-  CreateDirectoryTask(FileSystemBase* aFileSystem,
-                      const FileSystemCreateDirectoryParams& aParam,
-                      FileSystemRequestParent* aParent);
+  CreateDirectoryTaskChild(FileSystemBase* aFileSystem,
+                           nsIFile* aTargetPath);
 
   RefPtr<Promise> mPromise;
   nsCOMPtr<nsIFile> mTargetPath;
 };
 
+class CreateDirectoryTaskParent final : public FileSystemTaskParentBase
+{
+public:
+  static already_AddRefed<CreateDirectoryTaskParent>
+  Create(FileSystemBase* aFileSystem,
+         const FileSystemCreateDirectoryParams& aParam,
+         FileSystemRequestParent* aParent,
+         ErrorResult& aRv);
+
+  virtual void
+  GetPermissionAccessType(nsCString& aAccess) const override;
+
+protected:
+  virtual nsresult
+  IOWork() override;
+
+  virtual FileSystemResponseValue
+  GetSuccessRequestResult(ErrorResult& aRv) const override;
+
+private:
+  CreateDirectoryTaskParent(FileSystemBase* aFileSystem,
+                            const FileSystemCreateDirectoryParams& aParam,
+                            FileSystemRequestParent* aParent);
+
+  nsCOMPtr<nsIFile> mTargetPath;
+};
+
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_CreateDirectoryTask_h
--- a/dom/filesystem/CreateFileTask.cpp
+++ b/dom/filesystem/CreateFileTask.cpp
@@ -1,62 +1,67 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=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 "CreateFileTask.h"
+#include "CreateDirectoryTask.h"
+#include "RemoveTask.h"
 
 #include <algorithm>
 
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/FileSystemBase.h"
 #include "mozilla/dom/FileSystemUtils.h"
+#include "mozilla/dom/PFileSystemParams.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/dom/ipc/BlobParent.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/PBackgroundChild.h"
 #include "nsIFile.h"
 #include "nsNetUtil.h"
 #include "nsIOutputStream.h"
 #include "nsStringGlue.h"
 
+#define GET_PERMISSION_ACCESS_TYPE(aAccess)                \
+  if (mReplace) {                                          \
+    aAccess.AssignLiteral(REMOVE_TASK_PERMISSION);         \
+    return;                                                \
+  }                                                        \
+  aAccess.AssignLiteral(CREATE_DIRECTORY_TASK_PERMISSION);
+
 namespace mozilla {
 namespace dom {
 
-uint32_t CreateFileTask::sOutputBufferSize = 0;
+/**
+ *CreateFileTaskChild
+ */
 
-/* static */ already_AddRefed<CreateFileTask>
-CreateFileTask::Create(FileSystemBase* aFileSystem,
-                       nsIFile* aTargetPath,
-                       Blob* aBlobData,
-                       InfallibleTArray<uint8_t>& aArrayData,
-                       bool aReplace,
-                       ErrorResult& aRv)
+/* static */ already_AddRefed<CreateFileTaskChild>
+CreateFileTaskChild::Create(FileSystemBase* aFileSystem,
+                            nsIFile* aTargetPath,
+                            Blob* aBlobData,
+                            InfallibleTArray<uint8_t>& aArrayData,
+                            bool aReplace,
+                            ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aFileSystem);
 
-  RefPtr<CreateFileTask> task =
-    new CreateFileTask(aFileSystem, aTargetPath, aReplace);
+  RefPtr<CreateFileTaskChild> task =
+    new CreateFileTaskChild(aFileSystem, aTargetPath, aReplace);
 
   // aTargetPath can be null. In this case SetError will be called.
 
-  task->GetOutputBufferSize();
-
   if (aBlobData) {
-    if (XRE_IsParentProcess()) {
-      aBlobData->GetInternalStream(getter_AddRefs(task->mBlobStream), aRv);
-      if (NS_WARN_IF(aRv.Failed())) {
-        return nullptr;
-      }
-    } else {
-      task->mBlobData = aBlobData;
-    }
+    task->mBlobImpl = aBlobData->Impl();
   }
 
   task->mArrayData.SwapElements(aArrayData);
 
   nsCOMPtr<nsIGlobalObject> globalObject =
     do_QueryInterface(aFileSystem->GetParentObject());
   if (NS_WARN_IF(!globalObject)) {
     aRv.Throw(NS_ERROR_FAILURE);
@@ -66,149 +71,187 @@ CreateFileTask::Create(FileSystemBase* a
   task->mPromise = Promise::Create(globalObject, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   return task.forget();
 }
 
-/* static */ already_AddRefed<CreateFileTask>
-CreateFileTask::Create(FileSystemBase* aFileSystem,
-                       const FileSystemCreateFileParams& aParam,
-                       FileSystemRequestParent* aParent,
-                       ErrorResult& aRv)
-{
-  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  MOZ_ASSERT(aFileSystem);
-
-  RefPtr<CreateFileTask> task =
-    new CreateFileTask(aFileSystem, aParam, aParent);
-
-  task->GetOutputBufferSize();
-
-  NS_ConvertUTF16toUTF8 path(aParam.realPath());
-  aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
-  }
-
-  task->mReplace = aParam.replace();
-
-  auto& data = aParam.data();
-
-  if (data.type() == FileSystemFileDataValue::TArrayOfuint8_t) {
-    task->mArrayData = data;
-    return task.forget();
-  }
-
-  BlobParent* bp = static_cast<BlobParent*>(static_cast<PBlobParent*>(data));
-  RefPtr<BlobImpl> blobImpl = bp->GetBlobImpl();
-  MOZ_ASSERT(blobImpl, "blobData should not be null.");
-
-  ErrorResult rv;
-  blobImpl->GetInternalStream(getter_AddRefs(task->mBlobStream), rv);
-  if (NS_WARN_IF(rv.Failed())) {
-    rv.SuppressException();
-  }
-
-  return task.forget();
-}
-
-CreateFileTask::CreateFileTask(FileSystemBase* aFileSystem,
-                               nsIFile* aTargetPath,
-                               bool aReplace)
-  : FileSystemTaskBase(aFileSystem)
+CreateFileTaskChild::CreateFileTaskChild(FileSystemBase* aFileSystem,
+                                         nsIFile* aTargetPath,
+                                         bool aReplace)
+  : FileSystemTaskChildBase(aFileSystem)
   , mTargetPath(aTargetPath)
   , mReplace(aReplace)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aFileSystem);
 }
 
-CreateFileTask::CreateFileTask(FileSystemBase* aFileSystem,
-                               const FileSystemCreateFileParams& aParam,
-                               FileSystemRequestParent* aParent)
-  : FileSystemTaskBase(aFileSystem, aParam, aParent)
-  , mReplace(false)
+CreateFileTaskChild::~CreateFileTaskChild()
 {
-  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  MOZ_ASSERT(aFileSystem);
-}
-
-CreateFileTask::~CreateFileTask()
-{
-  MOZ_ASSERT((!mPromise && !mBlobData) || NS_IsMainThread(),
-             "mPromise and mBlobData should be released on main thread!");
-
-  if (mBlobStream) {
-    mBlobStream->Close();
-  }
+  MOZ_ASSERT(NS_IsMainThread());
 }
 
 already_AddRefed<Promise>
-CreateFileTask::GetPromise()
+CreateFileTaskChild::GetPromise()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   return RefPtr<Promise>(mPromise).forget();
 }
 
 FileSystemParams
-CreateFileTask::GetRequestParams(const nsString& aSerializedDOMPath,
-                                 ErrorResult& aRv) const
+CreateFileTaskChild::GetRequestParams(const nsString& aSerializedDOMPath,
+                                      ErrorResult& aRv) const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   FileSystemCreateFileParams param;
   param.filesystem() = aSerializedDOMPath;
 
   aRv = mTargetPath->GetPath(param.realPath());
   if (NS_WARN_IF(aRv.Failed())) {
     return param;
   }
 
+  // If we are here, PBackground must be up and running: this method is called
+  // when the task has been already started by FileSystemPermissionRequest
+  // class and this happens only when PBackground actor has already been
+  // created.
+  PBackgroundChild* actor =
+    mozilla::ipc::BackgroundChild::GetForCurrentThread();
+  MOZ_ASSERT(actor);
+
   param.replace() = mReplace;
-  if (mBlobData) {
-    BlobChild* actor =
-      ContentChild::GetSingleton()->GetOrCreateActorForBlob(mBlobData);
-    if (actor) {
-      param.data() = actor;
+  if (mBlobImpl) {
+    PBlobChild* blobActor =
+      mozilla::ipc::BackgroundChild::GetOrCreateActorForBlobImpl(actor,
+                                                                 mBlobImpl);
+    if (blobActor) {
+      param.data() = blobActor;
     }
   } else {
     param.data() = mArrayData;
   }
   return param;
 }
 
-FileSystemResponseValue
-CreateFileTask::GetSuccessRequestResult(ErrorResult& aRv) const
+void
+CreateFileTaskChild::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
+                                             ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  BlobParent* actor = GetBlobParent(mTargetBlobImpl);
-  if (!actor) {
-    return FileSystemErrorResponse(NS_ERROR_DOM_FILESYSTEM_UNKNOWN_ERR);
+
+  const FileSystemFileResponse& r = aValue.get_FileSystemFileResponse();
+
+  NS_ConvertUTF16toUTF8 path(r.realPath());
+  aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(mTargetPath));
+  if (NS_WARN_IF(aRv.Failed())) {
+    return;
   }
-  FileSystemFileResponse response;
-  response.blobParent() = actor;
-  return response;
+}
+
+void
+CreateFileTaskChild::HandlerCallback()
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
+
+  if (mFileSystem->IsShutdown()) {
+    mPromise = nullptr;
+    return;
+  }
+
+  if (HasError()) {
+    mPromise->MaybeReject(mErrorValue);
+    mPromise = nullptr;
+    return;
+  }
+
+  RefPtr<File> file = File::CreateFromFile(mFileSystem->GetParentObject(),
+                                           mTargetPath);
+  mPromise->MaybeResolve(file);
+  mPromise = nullptr;
 }
 
 void
-CreateFileTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
-                                        ErrorResult& aRv)
+CreateFileTaskChild::GetPermissionAccessType(nsCString& aAccess) const
+{
+  GET_PERMISSION_ACCESS_TYPE(aAccess)
+}
+
+/**
+ * CreateFileTaskParent
+ */
+
+uint32_t CreateFileTaskParent::sOutputBufferSize = 0;
+
+/* static */ already_AddRefed<CreateFileTaskParent>
+CreateFileTaskParent::Create(FileSystemBase* aFileSystem,
+                             const FileSystemCreateFileParams& aParam,
+                             FileSystemRequestParent* aParent,
+                             ErrorResult& aRv)
 {
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  FileSystemFileResponse r = aValue;
-  BlobChild* actor = static_cast<BlobChild*>(r.blobChild());
-  mTargetBlobImpl = actor->GetBlobImpl();
+  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aFileSystem);
+
+  RefPtr<CreateFileTaskParent> task =
+    new CreateFileTaskParent(aFileSystem, aParam, aParent);
+
+  NS_ConvertUTF16toUTF8 path(aParam.realPath());
+  aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  task->mReplace = aParam.replace();
+
+  const FileSystemFileDataValue& data = aParam.data();
+
+  if (data.type() == FileSystemFileDataValue::TArrayOfuint8_t) {
+    task->mArrayData = data;
+    return task.forget();
+  }
+
+  MOZ_ASSERT(data.type() == FileSystemFileDataValue::TPBlobParent);
+
+  BlobParent* bp = static_cast<BlobParent*>(static_cast<PBlobParent*>(data));
+  task->mBlobImpl = bp->GetBlobImpl();
+  MOZ_ASSERT(task->mBlobImpl, "blobData should not be null.");
+
+  return task.forget();
+}
+
+CreateFileTaskParent::CreateFileTaskParent(FileSystemBase* aFileSystem,
+                                           const FileSystemCreateFileParams& aParam,
+                                           FileSystemRequestParent* aParent)
+  : FileSystemTaskParentBase(aFileSystem, aParam, aParent)
+  , mReplace(false)
+{
+  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aFileSystem);
+}
+
+FileSystemResponseValue
+CreateFileTaskParent::GetSuccessRequestResult(ErrorResult& aRv) const
+{
+  AssertIsOnBackgroundThread();
+
+  nsAutoString path;
+  aRv = mTargetPath->GetPath(path);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return FileSystemDirectoryResponse();
+  }
+
+  return FileSystemFileResponse(path);
 }
 
 nsresult
-CreateFileTask::Work()
+CreateFileTaskParent::IOWork()
 {
   class MOZ_RAII AutoClose final
   {
   public:
     explicit AutoClose(nsIOutputStream* aStream)
       : mStream(aStream)
     {
       MOZ_ASSERT(aStream);
@@ -270,54 +313,59 @@ CreateFileTask::Work()
 
   nsCOMPtr<nsIOutputStream> outputStream;
   rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), mTargetPath);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   AutoClose acOutputStream(outputStream);
+  MOZ_ASSERT(sOutputBufferSize);
 
   nsCOMPtr<nsIOutputStream> bufferedOutputStream;
   rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream),
                                   outputStream,
                                   sOutputBufferSize);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   AutoClose acBufferedOutputStream(bufferedOutputStream);
 
-  if (mBlobStream) {
-    // Write the file content from blob data.
+  // Write the file content from blob data.
+  if (mBlobImpl) {
+    ErrorResult error;
+    nsCOMPtr<nsIInputStream> blobStream;
+    mBlobImpl->GetInternalStream(getter_AddRefs(blobStream), error);
+    if (NS_WARN_IF(error.Failed())) {
+      return error.StealNSResult();
+    }
 
     uint64_t bufSize = 0;
-    rv = mBlobStream->Available(&bufSize);
+    rv = blobStream->Available(&bufSize);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     while (bufSize && !mFileSystem->IsShutdown()) {
       uint32_t written = 0;
       uint32_t writeSize = bufSize < UINT32_MAX ? bufSize : UINT32_MAX;
-      rv = bufferedOutputStream->WriteFrom(mBlobStream, writeSize, &written);
+      rv = bufferedOutputStream->WriteFrom(blobStream, writeSize, &written);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       bufSize -= written;
     }
 
-    mBlobStream->Close();
-    mBlobStream = nullptr;
+    blobStream->Close();
 
     if (mFileSystem->IsShutdown()) {
       return NS_ERROR_FAILURE;
     }
 
-    mTargetBlobImpl = new BlobImplFile(mTargetPath);
     return NS_OK;
   }
 
   // Write file content from array data.
 
   uint32_t written;
   rv = bufferedOutputStream->Write(
     reinterpret_cast<char*>(mArrayData.Elements()),
@@ -326,59 +374,32 @@ CreateFileTask::Work()
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (mArrayData.Length() != written) {
     return NS_ERROR_DOM_FILESYSTEM_UNKNOWN_ERR;
   }
 
-  mTargetBlobImpl = new BlobImplFile(mTargetPath);
   return NS_OK;
 }
 
+nsresult
+CreateFileTaskParent::MainThreadWork()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!sOutputBufferSize) {
+    sOutputBufferSize =
+      mozilla::Preferences::GetUint("dom.filesystem.outputBufferSize", 4096 * 4);
+  }
+
+  return FileSystemTaskParentBase::MainThreadWork();
+}
+
 void
-CreateFileTask::HandlerCallback()
+CreateFileTaskParent::GetPermissionAccessType(nsCString& aAccess) const
 {
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  if (mFileSystem->IsShutdown()) {
-    mPromise = nullptr;
-    mBlobData = nullptr;
-    return;
-  }
-
-  if (HasError()) {
-    mPromise->MaybeReject(mErrorValue);
-    mPromise = nullptr;
-    mBlobData = nullptr;
-    return;
-  }
-
-  RefPtr<Blob> blob = Blob::Create(mFileSystem->GetParentObject(),
-                                   mTargetBlobImpl);
-  mPromise->MaybeResolve(blob);
-  mPromise = nullptr;
-  mBlobData = nullptr;
-}
-
-void
-CreateFileTask::GetPermissionAccessType(nsCString& aAccess) const
-{
-  if (mReplace) {
-    aAccess.AssignLiteral("write");
-    return;
-  }
-
-  aAccess.AssignLiteral("create");
-}
-
-void
-CreateFileTask::GetOutputBufferSize() const
-{
-  if (sOutputBufferSize || !XRE_IsParentProcess()) {
-    return;
-  }
-  sOutputBufferSize =
-    mozilla::Preferences::GetUint("dom.filesystem.outputBufferSize", 4096 * 4);
+  GET_PERMISSION_ACCESS_TYPE(aAccess)
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/CreateFileTask.h
+++ b/dom/filesystem/CreateFileTask.h
@@ -15,84 +15,104 @@ class nsIInputStream;
 
 namespace mozilla {
 namespace dom {
 
 class Blob;
 class BlobImpl;
 class Promise;
 
-class CreateFileTask final : public FileSystemTaskBase
+class CreateFileTaskChild final : public FileSystemTaskChildBase
 {
 public:
-  static already_AddRefed<CreateFileTask>
+  static already_AddRefed<CreateFileTaskChild>
   Create(FileSystemBase* aFileSystem,
          nsIFile* aFile,
          Blob* aBlobData,
          InfallibleTArray<uint8_t>& aArrayData,
          bool replace,
          ErrorResult& aRv);
 
-  static already_AddRefed<CreateFileTask>
-  Create(FileSystemBase* aFileSystem,
-         const FileSystemCreateFileParams& aParam,
-         FileSystemRequestParent* aParent,
-         ErrorResult& aRv);
-
   virtual
-  ~CreateFileTask();
+  ~CreateFileTaskChild();
 
   already_AddRefed<Promise>
   GetPromise();
 
   virtual void
   GetPermissionAccessType(nsCString& aAccess) const override;
 
 protected:
   virtual FileSystemParams
   GetRequestParams(const nsString& aSerializedDOMPath,
                    ErrorResult& aRv) const override;
 
-  virtual FileSystemResponseValue
-  GetSuccessRequestResult(ErrorResult& aRv) const override;
-
   virtual void
   SetSuccessRequestResult(const FileSystemResponseValue& aValue,
                           ErrorResult& aRv) override;
 
-  virtual nsresult
-  Work() override;
-
   virtual void
   HandlerCallback() override;
 
 private:
-  CreateFileTask(FileSystemBase* aFileSystem,
-                 nsIFile* aFile,
-                 bool aReplace);
+  CreateFileTaskChild(FileSystemBase* aFileSystem,
+                      nsIFile* aFile,
+                      bool aReplace);
 
-  CreateFileTask(FileSystemBase* aFileSystem,
-                 const FileSystemCreateFileParams& aParam,
-                 FileSystemRequestParent* aParent);
-
-  void
-  GetOutputBufferSize() const;
-
-  static uint32_t sOutputBufferSize;
   RefPtr<Promise> mPromise;
   nsCOMPtr<nsIFile> mTargetPath;
 
-  // Not thread-safe and should be released on main thread.
-  RefPtr<Blob> mBlobData;
+  RefPtr<BlobImpl> mBlobImpl;
 
-  nsCOMPtr<nsIInputStream> mBlobStream;
+  // This is going to be the content of the file, received by createFile()
+  // params.
   InfallibleTArray<uint8_t> mArrayData;
+
   bool mReplace;
+};
+
+class CreateFileTaskParent final : public FileSystemTaskParentBase
+{
+public:
+  static already_AddRefed<CreateFileTaskParent>
+  Create(FileSystemBase* aFileSystem,
+         const FileSystemCreateFileParams& aParam,
+         FileSystemRequestParent* aParent,
+         ErrorResult& aRv);
+
+  virtual bool
+  NeedToGoToMainThread() const override { return true; }
+
+  virtual nsresult
+  MainThreadWork() override;
+
+  virtual void
+  GetPermissionAccessType(nsCString& aAccess) const override;
 
-  // This cannot be a File because this object is created on a different
-  // thread and File is not thread-safe. Let's use the BlobImpl instead.
-  RefPtr<BlobImpl> mTargetBlobImpl;
+protected:
+  virtual FileSystemResponseValue
+  GetSuccessRequestResult(ErrorResult& aRv) const override;
+
+  virtual nsresult
+  IOWork() override;
+
+private:
+  CreateFileTaskParent(FileSystemBase* aFileSystem,
+                       const FileSystemCreateFileParams& aParam,
+                       FileSystemRequestParent* aParent);
+
+  static uint32_t sOutputBufferSize;
+
+  nsCOMPtr<nsIFile> mTargetPath;
+
+  RefPtr<BlobImpl> mBlobImpl;
+
+  // This is going to be the content of the file, received by createFile()
+  // params.
+  InfallibleTArray<uint8_t> mArrayData;
+
+  bool mReplace;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_CreateFileTask_h
--- a/dom/filesystem/DeviceStorageFileSystem.cpp
+++ b/dom/filesystem/DeviceStorageFileSystem.cpp
@@ -18,25 +18,31 @@
 #include "nsPIDOMWindow.h"
 #include "nsGlobalWindow.h"
 
 namespace mozilla {
 namespace dom {
 
 DeviceStorageFileSystem::DeviceStorageFileSystem(const nsAString& aStorageType,
                                                  const nsAString& aStorageName)
-  : mWindowId(0)
+  : mStorageType(aStorageType)
+  , mStorageName(aStorageName)
+  , mWindowId(0)
 {
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
+  mPermissionCheckType = ePermissionCheckByTestingPref;
 
-  mStorageType = aStorageType;
-  mStorageName = aStorageName;
-
-  mRequiresPermissionChecks =
-    !mozilla::Preferences::GetBool("device.storage.prompt.testing", false);
+  if (NS_IsMainThread()) {
+    if (mozilla::Preferences::GetBool("device.storage.prompt.testing", false)) {
+      mPermissionCheckType = ePermissionCheckNotRequired;
+    } else {
+      mPermissionCheckType = ePermissionCheckRequired;
+    }
+  } else {
+    AssertIsOnBackgroundThread();
+  }
 
   // Get the permission name required to access the file system.
   nsresult rv =
     DeviceStorageTypeChecker::GetPermissionForType(mStorageType, mPermission);
   NS_WARN_IF(NS_FAILED(rv));
 
   // Get the local path of the file system root.
   nsCOMPtr<nsIFile> rootFile;
@@ -48,73 +54,83 @@ DeviceStorageFileSystem::DeviceStorageFi
 
   if (!XRE_IsParentProcess()) {
     return;
   }
 
   // DeviceStorageTypeChecker is a singleton object and must be initialized on
   // the main thread. We initialize it here so that we can use it on the worker
   // thread.
-  DebugOnly<DeviceStorageTypeChecker*> typeChecker
-    = DeviceStorageTypeChecker::CreateOrGet();
-  MOZ_ASSERT(typeChecker);
+  if (NS_IsMainThread()) {
+    DebugOnly<DeviceStorageTypeChecker*> typeChecker =
+      DeviceStorageTypeChecker::CreateOrGet();
+    MOZ_ASSERT(typeChecker);
+  }
 }
 
 DeviceStorageFileSystem::~DeviceStorageFileSystem()
 {
+  AssertIsOnOwningThread();
 }
 
 already_AddRefed<FileSystemBase>
 DeviceStorageFileSystem::Clone()
 {
+  AssertIsOnOwningThread();
+
   RefPtr<DeviceStorageFileSystem> fs =
     new DeviceStorageFileSystem(mStorageType, mStorageName);
 
   fs->mWindowId = mWindowId;
 
   return fs.forget();
 }
 
 void
 DeviceStorageFileSystem::Init(nsDOMDeviceStorage* aDeviceStorage)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
+  AssertIsOnOwningThread();
   MOZ_ASSERT(aDeviceStorage);
+
   nsCOMPtr<nsPIDOMWindowInner> window = aDeviceStorage->GetOwner();
   MOZ_ASSERT(window->IsInnerWindow());
   mWindowId = window->WindowID();
 }
 
 void
 DeviceStorageFileSystem::Shutdown()
 {
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
+  AssertIsOnOwningThread();
   mShutdown = true;
 }
 
 nsISupports*
 DeviceStorageFileSystem::GetParentObject() const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
+  AssertIsOnOwningThread();
+
   nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowId);
   MOZ_ASSERT_IF(!mShutdown, window);
   return window ? window->AsInner() : nullptr;
 }
 
 void
 DeviceStorageFileSystem::GetRootName(nsAString& aRetval) const
 {
+  AssertIsOnOwningThread();
   aRetval = mStorageName;
 }
 
 bool
 DeviceStorageFileSystem::IsSafeFile(nsIFile* aFile) const
 {
-  MOZ_ASSERT(XRE_IsParentProcess(),
-             "Should be on parent process!");
+  MOZ_ASSERT(XRE_IsParentProcess(), "Should be on parent process!");
+  MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aFile);
 
   nsCOMPtr<nsIFile> rootPath;
   nsresult rv = NS_NewLocalFile(LocalOrDeviceStorageRootPath(), false,
                                 getter_AddRefs(rootPath));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return false;
   }
@@ -129,17 +145,17 @@ DeviceStorageFileSystem::IsSafeFile(nsIF
     = DeviceStorageTypeChecker::CreateOrGet();
   MOZ_ASSERT(typeChecker);
   return typeChecker->Check(mStorageType, aFile);
 }
 
 bool
 DeviceStorageFileSystem::IsSafeDirectory(Directory* aDir) const
 {
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
+  AssertIsOnOwningThread();
   MOZ_ASSERT(aDir);
 
   ErrorResult rv;
   RefPtr<FileSystemBase> fs = aDir->GetFileSystem(rv);
   if (NS_WARN_IF(rv.Failed())) {
     rv.SuppressException();
     return false;
   }
@@ -152,17 +168,30 @@ DeviceStorageFileSystem::IsSafeDirectory
 
   // Check if the given directory is from this storage.
   return fsSerialization == thisSerialization;
 }
 
 void
 DeviceStorageFileSystem::SerializeDOMPath(nsAString& aString) const
 {
+  AssertIsOnOwningThread();
+
   // Generate the string representation of the file system.
   aString.AssignLiteral("devicestorage-");
   aString.Append(mStorageType);
   aString.Append('-');
   aString.Append(mStorageName);
 }
 
+nsresult
+DeviceStorageFileSystem::MainThreadWork()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  DebugOnly<DeviceStorageTypeChecker*> typeChecker =
+    DeviceStorageTypeChecker::CreateOrGet();
+  MOZ_ASSERT(typeChecker);
+  return NS_OK;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/DeviceStorageFileSystem.h
+++ b/dom/filesystem/DeviceStorageFileSystem.h
@@ -43,16 +43,22 @@ public:
   IsSafeFile(nsIFile* aFile) const override;
 
   virtual bool
   IsSafeDirectory(Directory* aDir) const override;
 
   virtual void
   SerializeDOMPath(nsAString& aSerializedString) const override;
 
+  virtual bool
+  NeedToGoToMainThread() const override { return true; }
+
+  virtual nsresult
+  MainThreadWork() override;
+
 private:
   virtual
   ~DeviceStorageFileSystem();
 
   nsString mStorageType;
   nsString mStorageName;
 
   uint64_t mWindowId;
--- a/dom/filesystem/Directory.cpp
+++ b/dom/filesystem/Directory.cpp
@@ -119,18 +119,19 @@ Directory::GetRoot(FileSystemBase* aFile
 
   nsCOMPtr<nsIFile> path;
   aRv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(aFileSystem->LocalOrDeviceStorageRootPath()),
                               true, getter_AddRefs(path));
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
-  RefPtr<GetFileOrDirectoryTask> task =
-    GetFileOrDirectoryTask::Create(aFileSystem, path, eDOMRootDirectory, true, aRv);
+  RefPtr<GetFileOrDirectoryTaskChild> task =
+    GetFileOrDirectoryTaskChild::Create(aFileSystem, path, eDOMRootDirectory,
+                                        true, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   FileSystemPermissionRequest::RequestForTask(task);
   return task->GetPromise();
 }
 
@@ -141,16 +142,23 @@ Directory::Create(nsISupports* aParent, 
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aParent);
   MOZ_ASSERT(aFile);
 
 #ifdef DEBUG
   bool isDir;
   nsresult rv = aFile->IsDirectory(&isDir);
   MOZ_ASSERT(NS_SUCCEEDED(rv) && isDir);
+
+  if (aType == eNotDOMRootDirectory) {
+    RefPtr<nsIFile> parent;
+    rv = aFile->GetParent(getter_AddRefs(parent));
+    // We must have a parent if this is not the root directory.
+    MOZ_ASSERT(NS_SUCCEEDED(rv) && parent);
+  }
 #endif
 
   RefPtr<Directory> directory =
     new Directory(aParent, aFile, aType, aFileSystem);
   return directory.forget();
 }
 
 Directory::Directory(nsISupports* aParent,
@@ -240,18 +248,19 @@ Directory::CreateFile(const nsAString& a
   nsCOMPtr<nsIFile> realPath;
   nsresult error = DOMPathToRealPath(aPath, getter_AddRefs(realPath));
 
   RefPtr<FileSystemBase> fs = GetFileSystem(aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
-  RefPtr<CreateFileTask> task =
-    CreateFileTask::Create(fs, realPath, blobData, arrayData, replace, aRv);
+  RefPtr<CreateFileTaskChild> task =
+    CreateFileTaskChild::Create(fs, realPath, blobData, arrayData, replace,
+                                aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   task->SetError(error);
   FileSystemPermissionRequest::RequestForTask(task);
   return task->GetPromise();
 }
@@ -262,18 +271,18 @@ Directory::CreateDirectory(const nsAStri
   nsCOMPtr<nsIFile> realPath;
   nsresult error = DOMPathToRealPath(aPath, getter_AddRefs(realPath));
 
   RefPtr<FileSystemBase> fs = GetFileSystem(aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
-  RefPtr<CreateDirectoryTask> task =
-    CreateDirectoryTask::Create(fs, realPath, aRv);
+  RefPtr<CreateDirectoryTaskChild> task =
+    CreateDirectoryTaskChild::Create(fs, realPath, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   task->SetError(error);
   FileSystemPermissionRequest::RequestForTask(task);
   return task->GetPromise();
 }
@@ -284,19 +293,19 @@ Directory::Get(const nsAString& aPath, E
   nsCOMPtr<nsIFile> realPath;
   nsresult error = DOMPathToRealPath(aPath, getter_AddRefs(realPath));
 
   RefPtr<FileSystemBase> fs = GetFileSystem(aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
-  RefPtr<GetFileOrDirectoryTask> task =
-    GetFileOrDirectoryTask::Create(fs, realPath, eNotDOMRootDirectory, false,
-                                   aRv);
+  RefPtr<GetFileOrDirectoryTaskChild> task =
+    GetFileOrDirectoryTaskChild::Create(fs, realPath, eNotDOMRootDirectory,
+                                        false, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   task->SetError(error);
   FileSystemPermissionRequest::RequestForTask(task);
   return task->GetPromise();
 }
@@ -314,44 +323,56 @@ Directory::RemoveDeep(const StringOrFile
 }
 
 already_AddRefed<Promise>
 Directory::RemoveInternal(const StringOrFileOrDirectory& aPath, bool aRecursive,
                           ErrorResult& aRv)
 {
   nsresult error = NS_OK;
   nsCOMPtr<nsIFile> realPath;
-  RefPtr<BlobImpl> blob;
 
   // Check and get the target path.
 
   RefPtr<FileSystemBase> fs = GetFileSystem(aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
+  // If this is a File
   if (aPath.IsFile()) {
-    blob = aPath.GetAsFile().Impl();
+    if (!fs->GetRealPath(aPath.GetAsFile().Impl(),
+                         getter_AddRefs(realPath))) {
+      error = NS_ERROR_DOM_SECURITY_ERR;
+    }
+
+  // If this is a string
   } else if (aPath.IsString()) {
     error = DOMPathToRealPath(aPath.GetAsString(), getter_AddRefs(realPath));
-  } else if (!fs->IsSafeDirectory(&aPath.GetAsDirectory())) {
-    error = NS_ERROR_DOM_SECURITY_ERR;
+
+  // Directory
   } else {
-    realPath = aPath.GetAsDirectory().mFile;
-    // The target must be a descendant of this directory.
-    if (!FileSystemUtils::IsDescendantPath(mFile, realPath)) {
-      error = NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR;
+    MOZ_ASSERT(aPath.IsDirectory());
+    if (!fs->IsSafeDirectory(&aPath.GetAsDirectory())) {
+      error = NS_ERROR_DOM_SECURITY_ERR;
+    } else {
+      realPath = aPath.GetAsDirectory().mFile;
     }
   }
 
-  RefPtr<RemoveTask> task =
-    RemoveTask::Create(fs, mFile, blob, realPath, aRecursive, aRv);
+  // The target must be a descendant of this directory.
+  if (!FileSystemUtils::IsDescendantPath(mFile, realPath)) {
+    error = NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR;
+  }
+
+  RefPtr<RemoveTaskChild> task =
+    RemoveTaskChild::Create(fs, mFile, realPath, aRecursive, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
+
   task->SetError(error);
   FileSystemPermissionRequest::RequestForTask(task);
   return task->GetPromise();
 }
 
 void
 Directory::GetPath(nsAString& aRetval, ErrorResult& aRv)
 {
@@ -385,18 +406,18 @@ Directory::GetFullRealPath(nsAString& aP
 already_AddRefed<Promise>
 Directory::GetFilesAndDirectories(ErrorResult& aRv)
 {
   RefPtr<FileSystemBase> fs = GetFileSystem(aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
-  RefPtr<GetDirectoryListingTask> task =
-    GetDirectoryListingTask::Create(fs, mFile, mType, mFilters, aRv);
+  RefPtr<GetDirectoryListingTaskChild> task =
+    GetDirectoryListingTaskChild::Create(fs, mFile, mType, mFilters, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   FileSystemPermissionRequest::RequestForTask(task);
   return task->GetPromise();
 }
 
--- a/dom/filesystem/Directory.h
+++ b/dom/filesystem/Directory.h
@@ -35,23 +35,22 @@ class FileSystemBase;
 class Promise;
 class StringOrFileOrDirectory;
 
 class Directory final
   : public nsISupports
   , public nsWrapperCache
 {
 public:
-  struct BlobImplOrDirectoryPath
+  struct FileOrDirectoryPath
   {
-    RefPtr<BlobImpl> mBlobImpl;
-    nsString mDirectoryPath;
+    nsString mPath;
 
     enum {
-      eBlobImpl,
+      eFilePath,
       eDirectoryPath
     } mType;
   };
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Directory)
 
   static already_AddRefed<Promise>
--- a/dom/filesystem/FileSystemBase.cpp
+++ b/dom/filesystem/FileSystemBase.cpp
@@ -12,16 +12,19 @@
 
 namespace mozilla {
 namespace dom {
 
 // static
 already_AddRefed<FileSystemBase>
 FileSystemBase::DeserializeDOMPath(const nsAString& aString)
 {
+  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
+  AssertIsOnBackgroundThread();
+
   if (StringBeginsWith(aString, NS_LITERAL_STRING("devicestorage-"))) {
     // The string representation of devicestorage file system is of the format:
     // devicestorage-StorageType-StorageName
 
     nsCharSeparatedTokenizer tokenizer(aString, char16_t('-'));
     tokenizer.nextToken();
 
     nsString storageType;
@@ -34,47 +37,53 @@ FileSystemBase::DeserializeDOMPath(const
       storageName = tokenizer.nextToken();
     }
 
     RefPtr<DeviceStorageFileSystem> f =
       new DeviceStorageFileSystem(storageType, storageName);
     return f.forget();
   }
 
-  return RefPtr<OSFileSystem>(new OSFileSystem(aString)).forget();
+  return RefPtr<OSFileSystemParent>(new OSFileSystemParent(aString)).forget();
 }
 
 FileSystemBase::FileSystemBase()
   : mShutdown(false)
-  , mRequiresPermissionChecks(true)
+  , mPermissionCheckType(eNotSet)
+#ifdef DEBUG
+  , mOwningThread(PR_GetCurrentThread())
+#endif
 {
 }
 
 FileSystemBase::~FileSystemBase()
 {
+  AssertIsOnOwningThread();
 }
 
 void
 FileSystemBase::Shutdown()
 {
+  AssertIsOnOwningThread();
   mShutdown = true;
 }
 
 nsISupports*
 FileSystemBase::GetParentObject() const
 {
+  AssertIsOnOwningThread();
   return nullptr;
 }
 
 bool
 FileSystemBase::GetRealPath(BlobImpl* aFile, nsIFile** aPath) const
 {
-  MOZ_ASSERT(XRE_IsParentProcess(),
-             "Should be on parent process!");
+  AssertIsOnOwningThread();
   MOZ_ASSERT(aFile, "aFile Should not be null.");
+  MOZ_ASSERT(aPath);
 
   nsAutoString filePath;
   ErrorResult rv;
   aFile->GetMozFullPathInternal(filePath, rv);
   if (NS_WARN_IF(rv.Failed())) {
     return false;
   }
 
@@ -85,31 +94,34 @@ FileSystemBase::GetRealPath(BlobImpl* aF
   }
 
   return true;
 }
 
 bool
 FileSystemBase::IsSafeFile(nsIFile* aFile) const
 {
+  AssertIsOnOwningThread();
   return false;
 }
 
 bool
 FileSystemBase::IsSafeDirectory(Directory* aDir) const
 {
+  AssertIsOnOwningThread();
   return false;
 }
 
 void
 FileSystemBase::GetDOMPath(nsIFile* aFile,
                            Directory::DirectoryType aType,
                            nsAString& aRetval,
                            ErrorResult& aRv) const
 {
+  AssertIsOnOwningThread();
   MOZ_ASSERT(aFile);
 
   if (aType == Directory::eDOMRootDirectory) {
     aRetval.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL);
     return;
   }
 
   nsCOMPtr<nsIFile> fileSystemPath;
@@ -167,10 +179,17 @@ FileSystemBase::GetDOMPath(nsIFile* aFil
   aRetval.Truncate();
 
   for (int32_t i = parts.Length() - 1; i >= 0; --i) {
     aRetval.AppendLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL);
     aRetval.Append(parts[i]);
   }
 }
 
+void
+FileSystemBase::AssertIsOnOwningThread() const
+{
+  MOZ_ASSERT(mOwningThread);
+  MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread);
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/FileSystemBase.h
+++ b/dom/filesystem/FileSystemBase.h
@@ -13,18 +13,18 @@
 
 namespace mozilla {
 namespace dom {
 
 class BlobImpl;
 
 class FileSystemBase
 {
+public:
   NS_INLINE_DECL_REFCOUNTING(FileSystemBase)
-public:
 
   // Create file system object from its string representation.
   static already_AddRefed<FileSystemBase>
   DeserializeDOMPath(const nsAString& aString);
 
   FileSystemBase();
 
   virtual void
@@ -82,47 +82,88 @@ public:
    * Get the permission name required to access this file system.
    */
   const nsCString&
   GetPermission() const
   {
     return mPermission;
   }
 
-  bool
-  RequiresPermissionChecks() const
+  // The decision about doing or not doing the permission check cannot be done
+  // everywhere because, for some FileSystemBase implementation, this depends on
+  // a preference.
+  // This enum describes all the possible decisions. The implementation will do
+  // the check on the main-thread in the child and in the parent process when
+  // needed.
+  // Note: the permission check should not fail in PBackground because that
+  // means that the child has been compromised. If this happens the child
+  // process is killed.
+  enum ePermissionCheckType {
+    // When on the main-thread, we must check if we have
+    // device.storage.prompt.testing set to true.
+    ePermissionCheckByTestingPref,
+
+    // No permission check must be done.
+    ePermissionCheckNotRequired,
+
+    // Permission check is required.
+    ePermissionCheckRequired,
+
+    // This is the default value. We crash if this is let like this.
+    eNotSet
+  };
+
+  ePermissionCheckType
+  PermissionCheckType() const
   {
-    return mRequiresPermissionChecks;
+    MOZ_ASSERT(mPermissionCheckType != eNotSet);
+    return mPermissionCheckType;
   }
 
+  // IPC initialization
+  // See how these 2 methods are used in FileSystemTaskChildBase.
+
+  virtual bool
+  NeedToGoToMainThread() const { return false; }
+
+  virtual nsresult
+  MainThreadWork() { return NS_ERROR_FAILURE; }
+
   // CC methods
   virtual void Unlink() {}
   virtual void Traverse(nsCycleCollectionTraversalCallback &cb) {}
 
+  void
+  AssertIsOnOwningThread() const;
+
 protected:
   virtual ~FileSystemBase();
 
   // The local path of the root (i.e. the OS path, with OS path separators, of
   // the OS directory that acts as the root of this OSFileSystem).
   // This path must be set by the FileSystem implementation immediately
-  // because it will be used for the validation of any FileSystemTaskBase.
+  // because it will be used for the validation of any FileSystemTaskChildBase.
   // The concept of this path is that, any task will never go out of it and this
   // must be considered the OS 'root' of the current FileSystem. Different
   // Directory object can have different OS 'root' path.
   // To be more clear, any path managed by this FileSystem implementation must
   // be discendant of this local root path.
   // The reason why it's not just called 'localRootPath' is because for
   // DeviceStorage this contains the path of the device storage SDCard, that is
   // the parent directory of the exposed root path.
   nsString mLocalOrDeviceStorageRootPath;
 
   bool mShutdown;
 
   // The permission name required to access the file system.
   nsCString mPermission;
 
-  bool mRequiresPermissionChecks;
+  ePermissionCheckType mPermissionCheckType;
+
+#ifdef DEBUG
+  PRThread* mOwningThread;
+#endif
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_FileSystemBase_h
--- a/dom/filesystem/FileSystemPermissionRequest.cpp
+++ b/dom/filesystem/FileSystemPermissionRequest.cpp
@@ -3,38 +3,41 @@
 /* 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 "FileSystemPermissionRequest.h"
 
 #include "mozilla/dom/FileSystemBase.h"
 #include "mozilla/dom/FileSystemTaskBase.h"
 #include "mozilla/dom/FileSystemUtils.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/PBackgroundChild.h"
 #include "nsIDocument.h"
 #include "nsPIDOMWindow.h"
 #include "nsContentPermissionHelper.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_ISUPPORTS(FileSystemPermissionRequest, nsIRunnable,
-                  nsIContentPermissionRequest)
+                  nsIContentPermissionRequest,
+                  nsIIPCBackgroundChildCreateCallback)
 
-// static
-void
-FileSystemPermissionRequest::RequestForTask(FileSystemTaskBase* aTask)
+/* static */ void
+FileSystemPermissionRequest::RequestForTask(FileSystemTaskChildBase* aTask)
 {
   MOZ_ASSERT(aTask, "aTask should not be null!");
   MOZ_ASSERT(NS_IsMainThread());
+
   RefPtr<FileSystemPermissionRequest> request =
     new FileSystemPermissionRequest(aTask);
   NS_DispatchToCurrentThread(request);
 }
 
-FileSystemPermissionRequest::FileSystemPermissionRequest(FileSystemTaskBase* aTask)
+FileSystemPermissionRequest::FileSystemPermissionRequest(FileSystemTaskChildBase* aTask)
   : mTask(aTask)
 {
   MOZ_ASSERT(mTask, "aTask should not be null!");
   MOZ_ASSERT(NS_IsMainThread());
 
   mTask->GetPermissionAccessType(mPermissionAccess);
 
   RefPtr<FileSystemBase> filesystem = mTask->GetFileSystem();
@@ -93,41 +96,47 @@ FileSystemPermissionRequest::GetElement(
   return NS_OK;
 }
 
 NS_IMETHODIMP
 FileSystemPermissionRequest::Cancel()
 {
   MOZ_ASSERT(NS_IsMainThread());
   mTask->SetError(NS_ERROR_DOM_SECURITY_ERR);
-  mTask->Start();
+  ScheduleTask();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 FileSystemPermissionRequest::Allow(JS::HandleValue aChoices)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aChoices.isUndefined());
-  mTask->Start();
+  ScheduleTask();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 FileSystemPermissionRequest::Run()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   RefPtr<FileSystemBase> filesystem = mTask->GetFileSystem();
   if (!filesystem) {
     Cancel();
     return NS_OK;
   }
 
-  if (!filesystem->RequiresPermissionChecks()) {
+  if (filesystem->PermissionCheckType() == FileSystemBase::ePermissionCheckNotRequired) {
+    Allow(JS::UndefinedHandleValue);
+    return NS_OK;
+  }
+
+  if (filesystem->PermissionCheckType() == FileSystemBase::ePermissionCheckByTestingPref &&
+      mozilla::Preferences::GetBool("device.storage.prompt.testing", false)) {
     Allow(JS::UndefinedHandleValue);
     return NS_OK;
   }
 
   if (!mWindow) {
     Cancel();
     return NS_OK;
   }
@@ -141,10 +150,37 @@ FileSystemPermissionRequest::GetRequeste
 {
   NS_ENSURE_ARG_POINTER(aRequester);
 
   nsCOMPtr<nsIContentPermissionRequester> requester = mRequester;
   requester.forget(aRequester);
   return NS_OK;
 }
 
+void
+FileSystemPermissionRequest::ActorFailed()
+{
+  MOZ_CRASH("Failed to create a PBackgroundChild actor!");
+}
+
+void
+FileSystemPermissionRequest::ActorCreated(mozilla::ipc::PBackgroundChild* aActor)
+{
+  mTask->Start();
+}
+
+void
+FileSystemPermissionRequest::ScheduleTask()
+{
+  PBackgroundChild* actor =
+    mozilla::ipc::BackgroundChild::GetForCurrentThread();
+  if (actor) {
+    ActorCreated(actor);
+  } else {
+    if (NS_WARN_IF(
+        !mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(this))) {
+      MOZ_CRASH();
+    }
+  }
+}
+
 } /* namespace dom */
 } /* namespace mozilla */
--- a/dom/filesystem/FileSystemPermissionRequest.h
+++ b/dom/filesystem/FileSystemPermissionRequest.h
@@ -5,46 +5,55 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_FileSystemPermissionRequest_h
 #define mozilla_dom_FileSystemPermissionRequest_h
 
 #include "nsAutoPtr.h"
 #include "nsIRunnable.h"
 #include "nsIContentPermissionPrompt.h"
+#include "nsIIPCBackgroundChildCreateCallback.h"
 #include "nsString.h"
 
 class nsPIDOMWindowInner;
 
 namespace mozilla {
 namespace dom {
 
-class FileSystemTaskBase;
+class FileSystemTaskChildBase;
 
 class FileSystemPermissionRequest final
   : public nsIContentPermissionRequest
   , public nsIRunnable
+  , public nsIIPCBackgroundChildCreateCallback
 {
 public:
   // Request permission for the given task.
   static void
-  RequestForTask(FileSystemTaskBase* aTask);
+  RequestForTask(FileSystemTaskChildBase* aTask);
 
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSICONTENTPERMISSIONREQUEST
   NS_DECL_NSIRUNNABLE
+  NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK
+
 private:
-  explicit FileSystemPermissionRequest(FileSystemTaskBase* aTask);
+  explicit FileSystemPermissionRequest(FileSystemTaskChildBase* aTask);
 
-  virtual
   ~FileSystemPermissionRequest();
 
+  // Once the permission check has been done, we must run the task using IPC and
+  // PBackground. This method checks if the PBackground thread is ready to
+  // receive the task and in case waits for ActorCreated() to be called.
+  void
+  ScheduleTask();
+
   nsCString mPermissionType;
   nsCString mPermissionAccess;
-  RefPtr<FileSystemTaskBase> mTask;
+  RefPtr<FileSystemTaskChildBase> mTask;
   nsCOMPtr<nsPIDOMWindowInner> mWindow;
   nsCOMPtr<nsIPrincipal> mPrincipal;
   nsCOMPtr<nsIContentPermissionRequester> mRequester;
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/filesystem/FileSystemRequestParent.cpp
+++ b/dom/filesystem/FileSystemRequestParent.cpp
@@ -13,85 +13,96 @@
 
 #include "mozilla/AppProcessChecker.h"
 #include "mozilla/dom/FileSystemBase.h"
 
 namespace mozilla {
 namespace dom {
 
 FileSystemRequestParent::FileSystemRequestParent()
+  : mDestroyed(false)
 {
+  AssertIsOnBackgroundThread();
 }
 
 FileSystemRequestParent::~FileSystemRequestParent()
 {
+  AssertIsOnBackgroundThread();
 }
 
 #define FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(name)                         \
     case FileSystemParams::TFileSystem##name##Params: {                        \
       const FileSystem##name##Params& p = aParams;                             \
       mFileSystem = FileSystemBase::DeserializeDOMPath(p.filesystem());        \
-      task = name##Task::Create(mFileSystem, p, this, rv);                     \
+      MOZ_ASSERT(mFileSystem);                                                 \
+      mTask = name##TaskParent::Create(mFileSystem, p, this, rv);              \
       if (NS_WARN_IF(rv.Failed())) {                                           \
         return false;                                                          \
       }                                                                        \
       break;                                                                   \
     }
 
 bool
-FileSystemRequestParent::Dispatch(ContentParent* aParent,
-                                  const FileSystemParams& aParams)
+FileSystemRequestParent::Initialize(const FileSystemParams& aParams)
 {
-  MOZ_ASSERT(aParent, "aParent should not be null.");
-  RefPtr<FileSystemTaskBase> task;
+  AssertIsOnBackgroundThread();
+
   ErrorResult rv;
 
   switch (aParams.type()) {
 
     FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(CreateDirectory)
     FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(CreateFile)
     FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(GetDirectoryListing)
     FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(GetFileOrDirectory)
     FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(Remove)
 
     default: {
       NS_RUNTIMEABORT("not reached");
       break;
     }
   }
 
-  if (NS_WARN_IF(!task || !mFileSystem)) {
+  if (NS_WARN_IF(!mTask || !mFileSystem)) {
     // Should never reach here.
     return false;
   }
 
-  if (mFileSystem->RequiresPermissionChecks()) {
-    // Check the content process permission.
-
-    nsCString access;
-    task->GetPermissionAccessType(access);
+  if (mFileSystem->PermissionCheckType() != FileSystemBase::ePermissionCheckNotRequired) {
+    nsAutoCString access;
+    mTask->GetPermissionAccessType(access);
 
-    nsAutoCString permissionName;
-    permissionName = mFileSystem->GetPermission();
-    permissionName.Append('-');
-    permissionName.Append(access);
-
-    if (!AssertAppProcessPermission(aParent, permissionName.get())) {
-      return false;
-    }
+    mPermissionName = mFileSystem->GetPermission();
+    mPermissionName.Append('-');
+    mPermissionName.Append(access);
   }
 
-  task->Start();
   return true;
 }
 
 void
-FileSystemRequestParent::ActorDestroy(ActorDestroyReason why)
+FileSystemRequestParent::Start()
 {
+  MOZ_ASSERT(!mDestroyed);
+  MOZ_ASSERT(mFileSystem);
+  MOZ_ASSERT(mTask);
+
+  mTask->Start();
+}
+
+void
+FileSystemRequestParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(!mDestroyed);
+
   if (!mFileSystem) {
     return;
   }
+
   mFileSystem->Shutdown();
   mFileSystem = nullptr;
+  mTask = nullptr;
+  mDestroyed = true;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/FileSystemRequestParent.h
+++ b/dom/filesystem/FileSystemRequestParent.h
@@ -3,47 +3,65 @@
 /* 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_FileSystemRequestParent_h
 #define mozilla_dom_FileSystemRequestParent_h
 
 #include "mozilla/dom/PFileSystemRequestParent.h"
-#include "mozilla/dom/ContentChild.h"
-#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/FileSystemBase.h"
 
 namespace mozilla {
 namespace dom {
 
-class FileSystemBase;
+class FileSystemParams;
+class FileSystemTaskParentBase;
 
-class FileSystemRequestParent final
-  : public PFileSystemRequestParent
+class FileSystemRequestParent final : public PFileSystemRequestParent
 {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FileSystemRequestParent)
+
 public:
   FileSystemRequestParent();
 
-  bool
-  IsRunning()
+  const nsCString&
+  PermissionName() const
   {
-    return state() == PFileSystemRequest::__Start;
+    return mPermissionName;
+  }
+
+  FileSystemBase::ePermissionCheckType
+  PermissionCheckType() const
+  {
+    return mFileSystem ? mFileSystem->PermissionCheckType()
+                       : FileSystemBase::eNotSet;
   }
 
   bool
-  Dispatch(ContentParent* aParent, const FileSystemParams& aParams);
+  Initialize(const FileSystemParams& aParams);
+
+  void
+  Start();
+
+  bool Destroyed() const
+  {
+    return mDestroyed;
+  }
 
   virtual void
   ActorDestroy(ActorDestroyReason why) override;
 
 private:
-  // Private destructor, to discourage deletion outside of Release():
-  virtual
   ~FileSystemRequestParent();
 
   RefPtr<FileSystemBase> mFileSystem;
+  RefPtr<FileSystemTaskParentBase> mTask;
+
+  nsCString mPermissionName;
+
+  bool mDestroyed;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_FileSystemRequestParent_h
--- a/dom/filesystem/FileSystemTaskBase.cpp
+++ b/dom/filesystem/FileSystemTaskBase.cpp
@@ -2,284 +2,328 @@
 /* vim: set ts=8 sts=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/FileSystemTaskBase.h"
 
 #include "nsNetCID.h"
-#include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/FileSystemBase.h"
 #include "mozilla/dom/FileSystemRequestParent.h"
 #include "mozilla/dom/FileSystemUtils.h"
 #include "mozilla/dom/Promise.h"
-#include "mozilla/dom/PContent.h"
 #include "mozilla/dom/ipc/BlobParent.h"
+#include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/unused.h"
 #include "nsProxyRelease.h"
 
 namespace mozilla {
 namespace dom {
 
 namespace {
 
-class FileSystemReleaseRunnable final : public nsRunnable
+nsresult
+FileSystemErrorFromNsError(const nsresult& aErrorValue)
 {
-public:
-  explicit FileSystemReleaseRunnable(RefPtr<FileSystemBase>& aDoomed)
-    : mDoomed(nullptr)
-  {
-    aDoomed.swap(mDoomed);
+  uint16_t module = NS_ERROR_GET_MODULE(aErrorValue);
+  if (module == NS_ERROR_MODULE_DOM_FILESYSTEM ||
+      module == NS_ERROR_MODULE_DOM_FILE ||
+      module == NS_ERROR_MODULE_DOM) {
+    return aErrorValue;
   }
 
-  NS_IMETHOD Run()
-  {
-    mDoomed->Release();
-    return NS_OK;
+  switch (aErrorValue) {
+    case NS_OK:
+      return NS_OK;
+
+    case NS_ERROR_FILE_INVALID_PATH:
+    case NS_ERROR_FILE_UNRECOGNIZED_PATH:
+      return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
+
+    case NS_ERROR_FILE_DESTINATION_NOT_DIR:
+      return NS_ERROR_DOM_FILESYSTEM_INVALID_MODIFICATION_ERR;
+
+    case NS_ERROR_FILE_ACCESS_DENIED:
+    case NS_ERROR_FILE_DIR_NOT_EMPTY:
+      return NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR;
+
+    case NS_ERROR_FILE_TARGET_DOES_NOT_EXIST:
+    case NS_ERROR_NOT_AVAILABLE:
+      return NS_ERROR_DOM_FILE_NOT_FOUND_ERR;
+
+    case NS_ERROR_FILE_ALREADY_EXISTS:
+      return NS_ERROR_DOM_FILESYSTEM_PATH_EXISTS_ERR;
+
+    case NS_ERROR_FILE_NOT_DIRECTORY:
+      return NS_ERROR_DOM_FILESYSTEM_TYPE_MISMATCH_ERR;
+
+    case NS_ERROR_UNEXPECTED:
+    default:
+      return NS_ERROR_DOM_FILESYSTEM_UNKNOWN_ERR;
   }
+}
 
-private:
-  FileSystemBase* MOZ_OWNING_REF mDoomed;
-};
+nsresult
+DispatchToIOThread(nsIRunnable* aRunnable)
+{
+  MOZ_ASSERT(aRunnable);
+
+  nsCOMPtr<nsIEventTarget> target
+    = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
+  MOZ_ASSERT(target);
+
+  return target->Dispatch(aRunnable, NS_DISPATCH_NORMAL);
+}
 
 } // anonymous namespace
 
-FileSystemTaskBase::FileSystemTaskBase(FileSystemBase* aFileSystem)
+/**
+ * FileSystemTaskBase class
+ */
+
+FileSystemTaskChildBase::FileSystemTaskChildBase(FileSystemBase* aFileSystem)
   : mErrorValue(NS_OK)
   , mFileSystem(aFileSystem)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aFileSystem, "aFileSystem should not be null.");
 }
 
-FileSystemTaskBase::FileSystemTaskBase(FileSystemBase* aFileSystem,
-                                       const FileSystemParams& aParam,
-                                       FileSystemRequestParent* aParent)
-  : mErrorValue(NS_OK)
-  , mFileSystem(aFileSystem)
-  , mRequestParent(aParent)
+FileSystemTaskChildBase::~FileSystemTaskChildBase()
 {
-  MOZ_ASSERT(XRE_IsParentProcess(),
-             "Only call from parent process!");
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  MOZ_ASSERT(aFileSystem, "aFileSystem should not be null.");
-}
-
-FileSystemTaskBase::~FileSystemTaskBase()
-{
-  if (!NS_IsMainThread()) {
-    RefPtr<FileSystemReleaseRunnable> runnable =
-      new FileSystemReleaseRunnable(mFileSystem);
-    MOZ_ASSERT(!mFileSystem);
-    NS_DispatchToMainThread(runnable);
-  }
+  mFileSystem->AssertIsOnOwningThread();
 }
 
 FileSystemBase*
-FileSystemTaskBase::GetFileSystem() const
+FileSystemTaskChildBase::GetFileSystem() const
 {
+  mFileSystem->AssertIsOnOwningThread();
   return mFileSystem.get();
 }
 
 void
-FileSystemTaskBase::Start()
+FileSystemTaskChildBase::Start()
 {
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
+  mFileSystem->AssertIsOnOwningThread();
 
   if (HasError()) {
-    NS_DispatchToMainThread(this);
+    nsCOMPtr<nsIRunnable> runnable =
+      NS_NewRunnableMethod(this, &FileSystemTaskChildBase::HandlerCallback);
+    NS_DispatchToMainThread(runnable);
     return;
   }
 
-  if (XRE_IsParentProcess()) {
-    // Run in parent process.
-    // Start worker thread.
-    nsCOMPtr<nsIEventTarget> target
-      = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
-    NS_ASSERTION(target, "Must have stream transport service.");
-    target->Dispatch(this, NS_DISPATCH_NORMAL);
-    return;
-  }
-
-  // Run in child process.
   if (mFileSystem->IsShutdown()) {
     return;
   }
 
   nsAutoString serialization;
   mFileSystem->SerializeDOMPath(serialization);
 
   ErrorResult rv;
   FileSystemParams params = GetRequestParams(serialization, rv);
   if (NS_WARN_IF(rv.Failed())) {
     return;
   }
 
   // Retain a reference so the task object isn't deleted without IPDL's
   // knowledge. The reference will be released by
-  // mozilla::dom::ContentChild::DeallocPFileSystemRequestChild.
+  // mozilla::ipc::BackgroundChildImpl::DeallocPFileSystemRequestChild.
   NS_ADDREF_THIS();
 
-  ContentChild::GetSingleton()->SendPFileSystemRequestConstructor(this,
-                                                                  params);
-}
+  // If we are here, PBackground must be up and running, because Start() is
+  // called only by FileSystemPermissionRequest, and that class takes care of
+  // PBackground initialization.
+  PBackgroundChild* actor =
+    mozilla::ipc::BackgroundChild::GetForCurrentThread();
+  MOZ_ASSERT(actor);
 
-NS_IMETHODIMP
-FileSystemTaskBase::Run()
-{
-  if (!NS_IsMainThread()) {
-    // Run worker thread tasks
-    nsresult rv = Work();
-    if (NS_FAILED(rv)) {
-      SetError(rv);
-    }
-    // Dispatch itself to main thread
-    NS_DispatchToMainThread(this);
-    return NS_OK;
-  }
-
-  // Run main thread tasks
-  HandleResult();
-  return NS_OK;
+  actor->SendPFileSystemRequestConstructor(this, params);
 }
 
 void
-FileSystemTaskBase::HandleResult()
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  if (mFileSystem->IsShutdown()) {
-    return;
-  }
-  if (mRequestParent && mRequestParent->IsRunning()) {
-    Unused << mRequestParent->Send__delete__(mRequestParent,
-      GetRequestResult());
-  } else {
-    HandlerCallback();
-  }
-}
-
-FileSystemResponseValue
-FileSystemTaskBase::GetRequestResult() const
+FileSystemTaskChildBase::SetRequestResult(const FileSystemResponseValue& aValue)
 {
-  MOZ_ASSERT(XRE_IsParentProcess(),
-             "Only call from parent process!");
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  if (!HasError()) {
-    ErrorResult rv;
-    FileSystemResponseValue value = GetSuccessRequestResult(rv);
-    if (NS_WARN_IF(rv.Failed())) {
-      return FileSystemErrorResponse(rv.StealNSResult());
-    }
+  mFileSystem->AssertIsOnOwningThread();
 
-    return value;
-  }
-
-  return FileSystemErrorResponse(mErrorValue);
-}
-
-void
-FileSystemTaskBase::SetRequestResult(const FileSystemResponseValue& aValue)
-{
-  MOZ_ASSERT(!XRE_IsParentProcess(),
-             "Only call from child process!");
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   if (aValue.type() == FileSystemResponseValue::TFileSystemErrorResponse) {
     FileSystemErrorResponse r = aValue;
     mErrorValue = r.error();
   } else {
     ErrorResult rv;
     SetSuccessRequestResult(aValue, rv);
     mErrorValue = rv.StealNSResult();
   }
 }
 
 bool
-FileSystemTaskBase::Recv__delete__(const FileSystemResponseValue& aValue)
+FileSystemTaskChildBase::Recv__delete__(const FileSystemResponseValue& aValue)
 {
+  mFileSystem->AssertIsOnOwningThread();
+
   SetRequestResult(aValue);
   HandlerCallback();
   return true;
 }
 
-BlobParent*
-FileSystemTaskBase::GetBlobParent(BlobImpl* aFile) const
+void
+FileSystemTaskChildBase::SetError(const nsresult& aErrorValue)
+{
+  mErrorValue = FileSystemErrorFromNsError(aErrorValue);
+}
+
+/**
+ * FileSystemTaskParentBase class
+ */
+
+FileSystemTaskParentBase::FileSystemTaskParentBase(FileSystemBase* aFileSystem,
+                                                  const FileSystemParams& aParam,
+                                                  FileSystemRequestParent* aParent)
+  : mErrorValue(NS_OK)
+  , mFileSystem(aFileSystem)
+  , mRequestParent(aParent)
+  , mBackgroundEventTarget(NS_GetCurrentThread())
 {
   MOZ_ASSERT(XRE_IsParentProcess(),
              "Only call from parent process!");
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  MOZ_ASSERT(aFile);
-
-  // Load the lazy dom file data from the parent before sending to the child.
-  nsString mimeType;
-  aFile->GetType(mimeType);
+  MOZ_ASSERT(aFileSystem, "aFileSystem should not be null.");
+  MOZ_ASSERT(aParent);
+  MOZ_ASSERT(mBackgroundEventTarget);
+  AssertIsOnBackgroundThread();
+}
 
-  // We call GetSize and GetLastModified to prepopulate the value in the
-  // BlobImpl.
-  {
-    ErrorResult rv;
-    aFile->GetSize(rv);
-    rv.SuppressException();
+FileSystemTaskParentBase::~FileSystemTaskParentBase()
+{
+  // This task can be released on different threads because we dispatch it (as
+  // runnable) to main-thread, I/O and then back to the PBackground thread.
+  NS_ProxyRelease(mBackgroundEventTarget, mFileSystem.forget());
+  NS_ProxyRelease(mBackgroundEventTarget, mRequestParent.forget());
+}
+
+void
+FileSystemTaskParentBase::Start()
+{
+  AssertIsOnBackgroundThread();
+  mFileSystem->AssertIsOnOwningThread();
+
+  if (NeedToGoToMainThread()) {
+    nsresult rv = NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL);
+    NS_WARN_IF(NS_FAILED(rv));
+    return;
   }
 
-  {
-    ErrorResult rv;
-    aFile->GetLastModified(rv);
-    rv.SuppressException();
-  }
-
-  ContentParent* cp = static_cast<ContentParent*>(mRequestParent->Manager());
-  return cp->GetOrCreateActorForBlobImpl(aFile);
+  nsresult rv = DispatchToIOThread(this);
+  NS_WARN_IF(NS_FAILED(rv));
 }
 
 void
-FileSystemTaskBase::SetError(const nsresult& aErrorValue)
+FileSystemTaskParentBase::HandleResult()
 {
-  uint16_t module = NS_ERROR_GET_MODULE(aErrorValue);
-  if (module == NS_ERROR_MODULE_DOM_FILESYSTEM ||
-      module == NS_ERROR_MODULE_DOM_FILE ||
-      module == NS_ERROR_MODULE_DOM) {
-    mErrorValue = aErrorValue;
+  AssertIsOnBackgroundThread();
+  mFileSystem->AssertIsOnOwningThread();
+
+  if (mFileSystem->IsShutdown()) {
     return;
   }
 
-  switch (aErrorValue) {
-    case NS_OK:
-      mErrorValue = NS_OK;
-      return;
+  MOZ_ASSERT(mRequestParent);
+  Unused << mRequestParent->Send__delete__(mRequestParent, GetRequestResult());
+}
+
+FileSystemResponseValue
+FileSystemTaskParentBase::GetRequestResult() const
+{
+  AssertIsOnBackgroundThread();
+  mFileSystem->AssertIsOnOwningThread();
+
+  if (HasError()) {
+    return FileSystemErrorResponse(mErrorValue);
+  }
+
+  ErrorResult rv;
+  FileSystemResponseValue value = GetSuccessRequestResult(rv);
+  if (NS_WARN_IF(rv.Failed())) {
+    return FileSystemErrorResponse(rv.StealNSResult());
+  }
 
-    case NS_ERROR_FILE_INVALID_PATH:
-    case NS_ERROR_FILE_UNRECOGNIZED_PATH:
-      mErrorValue = NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
-      return;
+  return value;
+}
+
+void
+FileSystemTaskParentBase::SetError(const nsresult& aErrorValue)
+{
+  mErrorValue = FileSystemErrorFromNsError(aErrorValue);
+}
 
-    case NS_ERROR_FILE_DESTINATION_NOT_DIR:
-      mErrorValue = NS_ERROR_DOM_FILESYSTEM_INVALID_MODIFICATION_ERR;
-      return;
+bool
+FileSystemTaskParentBase::NeedToGoToMainThread() const
+{
+  return mFileSystem->NeedToGoToMainThread();
+}
 
-    case NS_ERROR_FILE_ACCESS_DENIED:
-    case NS_ERROR_FILE_DIR_NOT_EMPTY:
-      mErrorValue = NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR;
-      return;
+nsresult
+FileSystemTaskParentBase::MainThreadWork()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  return mFileSystem->MainThreadWork();
+}
 
-    case NS_ERROR_FILE_TARGET_DOES_NOT_EXIST:
-    case NS_ERROR_NOT_AVAILABLE:
-      mErrorValue = NS_ERROR_DOM_FILE_NOT_FOUND_ERR;
-      return;
+NS_IMETHODIMP
+FileSystemTaskParentBase::Run()
+{
+  // This method can run in 3 different threads. Here why:
+  // 1. if we are on the main-thread it's because the task must do something
+  //    here. If no errors are returned we go the step 2.
+  // 2. We can be here directly if the task doesn't have nothing to do on the
+  //    main-thread. We are are on the I/O thread and we call IOWork().
+  // 3. Both step 1 (in case of error) and step 2 end up here where return the
+  //    value back to the PBackground thread.
+  if (NS_IsMainThread()) {
+    MOZ_ASSERT(NeedToGoToMainThread());
 
-    case NS_ERROR_FILE_ALREADY_EXISTS:
-      mErrorValue = NS_ERROR_DOM_FILESYSTEM_PATH_EXISTS_ERR;
-      return;
+    nsresult rv = MainThreadWork();
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      SetError(rv);
+
+      // Something when wrong. Let's go to the Background thread directly
+      // skipping the I/O thread step.
+      rv = mBackgroundEventTarget->Dispatch(this, NS_DISPATCH_NORMAL);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+    }
 
-    case NS_ERROR_FILE_NOT_DIRECTORY:
-      mErrorValue = NS_ERROR_DOM_FILESYSTEM_TYPE_MISMATCH_ERR;
-      return;
+    // Next step must happen on the I/O thread.
+    rv = DispatchToIOThread(this);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    return NS_OK;
+  }
 
-    case NS_ERROR_UNEXPECTED:
-    default:
-      mErrorValue = NS_ERROR_DOM_FILESYSTEM_UNKNOWN_ERR;
-      return;
+  // Run I/O thread tasks
+  if (!IsOnBackgroundThread()) {
+    nsresult rv = IOWork();
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      SetError(rv);
+    }
+
+    // Let's go back to PBackground thread to finish the work.
+    rv = mBackgroundEventTarget->Dispatch(this, NS_DISPATCH_NORMAL);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    return NS_OK;
   }
+
+  // If we are here, it's because the I/O work has been done and we have to
+  // handle the result back via IPC.
+  AssertIsOnBackgroundThread();
+  HandleResult();
+  return NS_OK;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/FileSystemTaskBase.h
+++ b/dom/filesystem/FileSystemTaskBase.h
@@ -5,121 +5,128 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_FileSystemTaskBase_h
 #define mozilla_dom_FileSystemTaskBase_h
 
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/FileSystemRequestParent.h"
 #include "mozilla/dom/PFileSystemRequestChild.h"
+#include "nsThreadUtils.h"
 
 namespace mozilla {
 namespace dom {
 
 class BlobImpl;
-class BlobParent;
 class FileSystemBase;
 class FileSystemParams;
+class PBlobParent;
 
 /*
  * The base class to implement a Task class.
- * The task is used to handle the OOP (out of process) operations.
- * The file system operations can only be performed in the parent process. When
- * performing such a parent-process-only operation, a task will delivered the
- * operation to the parent process if needed.
+ * The file system operations can only be performed in the parent process. In
+ * order to avoid duplicated code, we used PBackground for child-parent and
+ * parent-parent communications.
  *
  * The following diagram illustrates the how a API call from the content page
  * starts a task and gets call back results.
  *
- * The left block is the call sequence inside the child process, while the
- * right block is the call sequence inside the parent process.
- *
- * There are two types of API call. One is from the content page of the child
- * process and we mark the steps as (1) to (8). The other is from the content
- * page of the parent process and we mark the steps as (1') to (4').
+ * The left block is the call sequence inside any process loading content, while
+ * the right block is the call sequence only inside the parent process.
  *
- *       Page                                             Page
- *        |                                                |
- *        | (1)                                            |  (1')
- *  ______|________________     |     _____________________|_____________
- * |      |                |    |    |                     |             |
- * |      |  Task in       |    |    |  Task in            |             |
- * |      |  Child Process |    |    |  Parent Process     |             |
- * |      V                |   IPC   |                     V             |
- * [new FileSystemTaskBase()]   |    |     [new FileSystemTaskBase()]    |
- * |         |             |    |    |                         |         |
- * |         | (2)         |         |                         | (2')    |
- * |         V             |   (3)   |                         |         |
- * |    [GetRequestParams]------------->[new FileSystemTaskBase(...)]    |
- * |                       |         |          |              |         |
- * |                       |    |    |          | (4)          |         |
- * |                       |    |    |          |              V         |
- * |                       |    |    |          -----------> [Work]      |
- * |                       |   IPC   |                         |         |
- * |                       |    |    |                     (5) | (3')    |
- * |                       |    |    |                         V         |
- * |                       |    |    |          --------[HandleResult]   |
- * |                       |    |    |          |              |         |
- * |                       |         |          | (6)          |         |
- * |                       |   (7)   |          V              |         |
- * |   [SetRequestResult]<-------------[GetRequestResult]      |         |
- * |       |               |         |                         | (4')    |
- * |       | (8)           |    |    |                         |         |
- * |       V               |    |    |                         V         |
- * |[HandlerCallback]      |   IPC   |               [HandlerCallback]   |
- * |_______|_______________|    |    |_________________________|_________|
- *         |                    |                              |
- *         V                                                   V
- *        Page                                                Page
+ *       Page
+ *        |
+ *        | (1)
+ *  ______|_______________________     |     __________________________________
+ * |      |                       |    |    |                                  |
+ * |      |                          |   |   |                                 |
+ * |      V                          |  IPC  | PBackground thread on           |
+ * | [new FileSystemTaskChildBase()] |   |   | the parent process              |
+ * |       |                         |   |   |                                 |
+ * |       | (2)                     |   |   |                                 |
+ * |       V                         |   |   |                                 |
+ * | [FileSystemPermissionRequest------------------\                           |
+ * |  ::RequestForTask()] <------------------------/                           |
+ * |         |                      |    |    |                                |
+ * |         | (3)                  |         |                                |
+ * |         V                      |   (4)   |                                |
+ * |    [GetRequestParams]------------------->[new FileSystemTaskParentBase()] |
+ * |                                 |       |          |                      |
+ * |                                 |   |   |          | (5)   _____________  |
+ * |                                 |   |   |          |      |             | |
+ * |                                 |   |   |          |      | I/O Thread  | |
+ * |                                 |   |   |          |      |             | |
+ * |                                 |   |   |          ---------> [IOWork]  | |
+ * |                                 |  IPC  |                 |     |       | |
+ * |                                 |   |   |                 |     | (6)   | |
+ * |                                 |   |   |          --------------       | |
+ * |                                 |   |   |          |      |_____________| |
+ * |                                 |   |   |          |                      |
+ * |                                 |   |   |          V                      |
+ * |                                 |   |   |     [HandleResult]              |
+ * |                                 |   |   |          |                      |
+ * |                                 |       |          | (7)                  |
+ * |                                 |  (8)  |          V                      |
+ * |   [SetRequestResult]<---------------------[GetRequestResult]              |
+ * |       |                         |       |                                 |
+ * |       | (9)                     |   |   |                                 |
+ * |       V                         |   |   |                                 |
+ * |[HandlerCallback]                |  IPC  |                                 |
+ * |_______|_________________________|   |   |_________________________________|
+ *         |                             |
+ *         V
+ *        Page
  *
- * 1. From child process page
- * Child:
+ * 1. From the process that is handling the request
+ * Child/Parent (it can be in any process):
  *   (1) Call FileSystem API from content page with JS. Create a task and run.
- *   The base constructor [FileSystemTaskBase()] of the task should be called.
- *   (2) Forward the task to the parent process through the IPC and call
+ *   The base constructor [FileSystemTaskChildBase()] of the task should be
+ *   called.
+ *   (2) The FileSystemTaskChildBase object is given to
+ *   [FileSystemPermissionRequest::RequestForTask()] that will perform a
+ *   permission check step if needed (See ePermissionCheckType enum). The real
+ *   operation is done on the parent process but it's hidden by
+ *   [nsContentPermissionUtils::AskPermission()]. If the permission check is not
+ *   needed or if the page has the right permission, the
+ *   FileSystemPermissionRequest will start the task (only once PBackground
+ *   actor is fully initialized).
+ *   (3) Forward the task to the parent process through the IPC and call
  *   [GetRequestParams] to prepare the parameters of the IPC.
  * Parent:
- *   (3) The parent process receives IPC and handle it in
- *   FileystemRequestParent.
- *   Get the IPC parameters and create a task to run the IPC task. The base
- *   constructor [FileSystemTaskBase(aParam, aParent)] of the task should be
- *   called to set the task as an IPC task.
- *   (4) The task operation will be performed in the member function of [Work].
- *   A worker thread will be created to run that function. If error occurs
+ *   (4) The parent process receives IPC and handle it in
+ *   FileystemRequestParent. Get the IPC parameters and create a task to run the
+ *   IPC task. The base constructor [FileSystemTaskParentBase(aParam, aParent)]
+ *   For security reasons, we do an additional permission check if needed. In
+ *   the check fails, the child process will be killed.
+ *   of the task should be called to set the task as an IPC task.
+ *   (5) The task operation will be performed in the member function of [IOWork].
+ *   A I/O  thread will be created to run that function. If error occurs
  *   during the operation, call [SetError] to record the error and then abort.
- *   (5) After finishing the task operation, call [HandleResult] to send the
+ *   (6) After finishing the task operation, call [HandleResult] to send the
  *   result back to the child process though the IPC.
- *   (6) Call [GetRequestResult] request result to prepare the parameters of the
+ *   (7) Call [GetRequestResult] request result to prepare the parameters of the
  *   IPC. Because the formats of the error result for different task are the
- *   same, FileSystemTaskBase can handle the error message without interfering.
+ *   same, FileSystemTaskChildBase can handle the error message without
+ *   interfering.
  *   Each task only needs to implement its specific success result preparation
  *   function -[GetSuccessRequestResult].
- * Child:
- *   (7) The child process receives IPC and calls [SetRequestResult] to get the
+ * Child/Parent:
+ *   (8) The process receives IPC and calls [SetRequestResult] to get the
  *   task result. Each task needs to implement its specific success result
  *   parsing function [SetSuccessRequestResult] to get the success result.
- *   (8) Call [HandlerCallback] to send the task result to the content page.
- * 2. From parent process page
- * We don't need to send the task parameters and result to other process. So
- * there are less steps, but their functions are the same. The correspondence
- * between the two types of steps is:
- *   (1') = (1),
- *   (2') = (4),
- *   (3') = (5),
- *   (4') = (8).
+ *   (9) Call [HandlerCallback] to send the task result to the content page.
  */
-class FileSystemTaskBase
-  : public nsRunnable
-  , public PFileSystemRequestChild
+class FileSystemTaskChildBase : public PFileSystemRequestChild
 {
 public:
+  NS_INLINE_DECL_REFCOUNTING(FileSystemTaskChildBase)
+
   /*
-   * Start the task. If the task is running the child process, it will be
-   * forwarded to parent process by IPC, or else, creates a worker thread to
-   * do the task work.
+   * Start the task. It will dispatch all the information to the parent process,
+   * PBackground thread. This method must be called from the owning thread.
    */
   void
   Start();
 
   /*
    * The error codes are defined in xpcom/base/ErrorList.h and their
    * corresponding error name and message are defined in dom/base/domerr.msg.
    */
@@ -130,115 +137,165 @@ public:
   GetFileSystem() const;
 
   /*
    * Get the type of permission access required to perform this task.
    */
   virtual void
   GetPermissionAccessType(nsCString& aAccess) const = 0;
 
-  NS_DECL_NSIRUNNABLE
-protected:
-  /*
-   * To create a task to handle the page content request.
-   */
-  explicit FileSystemTaskBase(FileSystemBase* aFileSystem);
-
-  /*
-   * To create a parent process task delivered from the child process through
-   * IPC.
-   */
-  FileSystemTaskBase(FileSystemBase* aFileSystem,
-                     const FileSystemParams& aParam,
-                     FileSystemRequestParent* aParent);
-
-  virtual
-  ~FileSystemTaskBase();
-
-  /*
-   * The function to perform task operation. It will be run on the worker
-   * thread of the parent process.
-   * Overrides this function to define the task operation for individual task.
-   */
-  virtual nsresult
-  Work() = 0;
-
   /*
    * After the task is completed, this function will be called to pass the task
-   * result to the content page.
+   * result to the content page. This method is called in the owning thread.
    * Override this function to handle the call back to the content page.
    */
   virtual void
   HandlerCallback() = 0;
 
+  bool
+  HasError() const { return NS_FAILED(mErrorValue); }
+
+protected:
+  /*
+   * To create a task to handle the page content request.
+   */
+  explicit FileSystemTaskChildBase(FileSystemBase* aFileSystem);
+
+  virtual
+  ~FileSystemTaskChildBase();
+
   /*
    * Wrap the task parameter to FileSystemParams for sending it through IPC.
    * It will be called when we need to forward a task from the child process to
-   * the prarent process.
+   * the parent process. This method runs in the owning thread.
    * @param filesystem The string representation of the file system.
    */
   virtual FileSystemParams
   GetRequestParams(const nsString& aSerializedDOMPath,
                    ErrorResult& aRv) const = 0;
 
   /*
+   * Unwrap the IPC message to get the task success result.
+   * It will be called when the task is completed successfully and an IPC
+   * message is received in the child process and we want to get the task
+   * success result. This method runs in the owning thread.
+   */
+  virtual void
+  SetSuccessRequestResult(const FileSystemResponseValue& aValue,
+                          ErrorResult& aRv) = 0;
+
+  // Overrides PFileSystemRequestChild
+  virtual bool
+  Recv__delete__(const FileSystemResponseValue& value) override;
+
+  nsresult mErrorValue;
+  RefPtr<FileSystemBase> mFileSystem;
+
+private:
+
+  /*
+   * Unwrap the IPC message to get the task result.
+   * It will be called when the task is completed and an IPC message is received
+   * in the content process and we want to get the task result. This runs on the
+   * owning thread.
+   */
+  void
+  SetRequestResult(const FileSystemResponseValue& aValue);
+};
+
+// This class is the 'alter ego' of FileSystemTaskChildBase in the PBackground
+// world.
+class FileSystemTaskParentBase : public nsRunnable
+{
+public:
+  /*
+   * Start the task. This must be called from the PBackground thread only.
+   */
+  void
+  Start();
+
+  /*
+   * The error codes are defined in xpcom/base/ErrorList.h and their
+   * corresponding error name and message are defined in dom/base/domerr.msg.
+   */
+  void
+  SetError(const nsresult& aErrorCode);
+
+  /*
+   * The function to perform task operation. It will be run on the I/O
+   * thread of the parent process.
+   * Overrides this function to define the task operation for individual task.
+   */
+  virtual nsresult
+  IOWork() = 0;
+
+  /*
    * Wrap the task success result to FileSystemResponseValue for sending it
-   * through IPC.
+   * through IPC. This method runs in the PBackground thread.
    * It will be called when the task is completed successfully and we need to
    * send the task success result back to the child process.
    */
   virtual FileSystemResponseValue
   GetSuccessRequestResult(ErrorResult& aRv) const = 0;
 
   /*
-   * Unwrap the IPC message to get the task success result.
-   * It will be called when the task is completed successfully and an IPC
-   * message is received in the child process and we want to get the task
-   * success result.
-   */
-  virtual void
-  SetSuccessRequestResult(const FileSystemResponseValue& aValue,
-                          ErrorResult& aRv) = 0;
-
-  bool
-  HasError() const { return mErrorValue != NS_OK; }
-
-  // Overrides PFileSystemRequestChild
-  virtual bool
-  Recv__delete__(const FileSystemResponseValue& value) override;
-
-  BlobParent*
-  GetBlobParent(BlobImpl* aBlob) const;
-
-  nsresult mErrorValue;
-
-  RefPtr<FileSystemBase> mFileSystem;
-  RefPtr<FileSystemRequestParent> mRequestParent;
-private:
-  /*
    * After finishing the task operation, handle the task result.
-   * If it is an IPC task, send back the IPC result. Or else, send the result
-   * to the content page.
+   * If it is an IPC task, send back the IPC result. It runs on the PBackground
+   * thread.
    */
   void
   HandleResult();
 
+  // If this task must do something on the main-thread before IOWork(), it must
+  // overwrite this method. Otherwise it returns true if the FileSystem must be
+  // initialized on the main-thread. It's called from the Background thread.
+  virtual bool
+  NeedToGoToMainThread() const;
+
+  // This method is called only if NeedToGoToMainThread() returns true.
+  // Of course, it runs on the main-thread.
+  virtual nsresult
+  MainThreadWork();
+
+  /*
+   * Get the type of permission access required to perform this task.
+   */
+  virtual void
+  GetPermissionAccessType(nsCString& aAccess) const = 0;
+
+  bool
+  HasError() const { return NS_FAILED(mErrorValue); }
+
+  NS_IMETHOD
+  Run() override;
+
+private:
   /*
    * Wrap the task result to FileSystemResponseValue for sending it through IPC.
    * It will be called when the task is completed and we need to
-   * send the task result back to the child process.
+   * send the task result back to the content. This runs on the PBackground
+   * thread.
    */
   FileSystemResponseValue
   GetRequestResult() const;
 
+protected:
   /*
-   * Unwrap the IPC message to get the task result.
-   * It will be called when the task is completed and an IPC message is received
-   * in the child process and we want to get the task result.
+   * To create a parent process task delivered from the child process through
+   * IPC.
    */
-  void
-  SetRequestResult(const FileSystemResponseValue& aValue);
+  FileSystemTaskParentBase(FileSystemBase* aFileSystem,
+                           const FileSystemParams& aParam,
+                           FileSystemRequestParent* aParent);
+
+  virtual
+  ~FileSystemTaskParentBase();
+
+  nsresult mErrorValue;
+  RefPtr<FileSystemBase> mFileSystem;
+  RefPtr<FileSystemRequestParent> mRequestParent;
+  nsCOMPtr<nsIEventTarget> mBackgroundEventTarget;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_FileSystemTaskBase_h
--- a/dom/filesystem/GetDirectoryListingTask.cpp
+++ b/dom/filesystem/GetDirectoryListingTask.cpp
@@ -6,37 +6,44 @@
 
 #include "GetDirectoryListingTask.h"
 
 #include "HTMLSplitOnSpacesTokenizer.h"
 #include "js/Value.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/FileSystemBase.h"
 #include "mozilla/dom/FileSystemUtils.h"
+#include "mozilla/dom/PFileSystemParams.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/dom/ipc/BlobParent.h"
 #include "nsIFile.h"
 #include "nsStringGlue.h"
 
+#define GET_DIRECTORY_LISTING_PERMISSION "read"
+
 namespace mozilla {
 namespace dom {
 
-/* static */ already_AddRefed<GetDirectoryListingTask>
-GetDirectoryListingTask::Create(FileSystemBase* aFileSystem,
-                                nsIFile* aTargetPath,
-                                Directory::DirectoryType aType,
-                                const nsAString& aFilters,
-                                ErrorResult& aRv)
+/**
+ * GetDirectoryListingTaskChild
+ */
+
+/* static */ already_AddRefed<GetDirectoryListingTaskChild>
+GetDirectoryListingTaskChild::Create(FileSystemBase* aFileSystem,
+                                     nsIFile* aTargetPath,
+                                     Directory::DirectoryType aType,
+                                     const nsAString& aFilters,
+                                     ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aFileSystem);
 
-  RefPtr<GetDirectoryListingTask> task =
-    new GetDirectoryListingTask(aFileSystem, aTargetPath, aType, aFilters);
+  RefPtr<GetDirectoryListingTaskChild> task =
+    new GetDirectoryListingTaskChild(aFileSystem, aTargetPath, aType, aFilters);
 
   // aTargetPath can be null. In this case SetError will be called.
 
   nsCOMPtr<nsIGlobalObject> globalObject =
     do_QueryInterface(aFileSystem->GetParentObject());
   if (NS_WARN_IF(!globalObject)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
@@ -45,161 +52,235 @@ GetDirectoryListingTask::Create(FileSyst
   task->mPromise = Promise::Create(globalObject, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   return task.forget();
 }
 
-/* static */ already_AddRefed<GetDirectoryListingTask>
-GetDirectoryListingTask::Create(FileSystemBase* aFileSystem,
-                                const FileSystemGetDirectoryListingParams& aParam,
-                                FileSystemRequestParent* aParent,
-                                ErrorResult& aRv)
-{
-  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  MOZ_ASSERT(aFileSystem);
-
-  RefPtr<GetDirectoryListingTask> task =
-    new GetDirectoryListingTask(aFileSystem, aParam, aParent);
-
-  NS_ConvertUTF16toUTF8 path(aParam.realPath());
-  aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
-  }
-
-  task->mType = aParam.isRoot()
-                  ? Directory::eDOMRootDirectory : Directory::eNotDOMRootDirectory;
-  return task.forget();
-}
-
-GetDirectoryListingTask::GetDirectoryListingTask(FileSystemBase* aFileSystem,
-                                                 nsIFile* aTargetPath,
-                                                 Directory::DirectoryType aType,
-                                                 const nsAString& aFilters)
-  : FileSystemTaskBase(aFileSystem)
+GetDirectoryListingTaskChild::GetDirectoryListingTaskChild(FileSystemBase* aFileSystem,
+                                                           nsIFile* aTargetPath,
+                                                           Directory::DirectoryType aType,
+                                                           const nsAString& aFilters)
+  : FileSystemTaskChildBase(aFileSystem)
   , mTargetPath(aTargetPath)
   , mFilters(aFilters)
   , mType(aType)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aFileSystem);
 }
 
-GetDirectoryListingTask::GetDirectoryListingTask(FileSystemBase* aFileSystem,
-                                                 const FileSystemGetDirectoryListingParams& aParam,
-                                                 FileSystemRequestParent* aParent)
-  : FileSystemTaskBase(aFileSystem, aParam, aParent)
-  , mFilters(aParam.filters())
+GetDirectoryListingTaskChild::~GetDirectoryListingTaskChild()
 {
-  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  MOZ_ASSERT(aFileSystem);
-}
-
-GetDirectoryListingTask::~GetDirectoryListingTask()
-{
-  MOZ_ASSERT(!mPromise || NS_IsMainThread(),
-             "mPromise should be released on main thread!");
+  MOZ_ASSERT(NS_IsMainThread());
 }
 
 already_AddRefed<Promise>
-GetDirectoryListingTask::GetPromise()
+GetDirectoryListingTaskChild::GetPromise()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   return RefPtr<Promise>(mPromise).forget();
 }
 
 FileSystemParams
-GetDirectoryListingTask::GetRequestParams(const nsString& aSerializedDOMPath,
-                                          ErrorResult& aRv) const
+GetDirectoryListingTaskChild::GetRequestParams(const nsString& aSerializedDOMPath,
+                                               ErrorResult& aRv) const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
 
   nsAutoString path;
   aRv = mTargetPath->GetPath(path);
   if (NS_WARN_IF(aRv.Failed())) {
     return FileSystemGetDirectoryListingParams();
   }
 
   return FileSystemGetDirectoryListingParams(aSerializedDOMPath, path,
                                              mType == Directory::eDOMRootDirectory,
                                              mFilters);
 }
 
-FileSystemResponseValue
-GetDirectoryListingTask::GetSuccessRequestResult(ErrorResult& aRv) const
+void
+GetDirectoryListingTaskChild::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
+                                                      ErrorResult& aRv)
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
+  MOZ_ASSERT(aValue.type() ==
+               FileSystemResponseValue::TFileSystemDirectoryListingResponse);
+
+  FileSystemDirectoryListingResponse r = aValue;
+  for (uint32_t i = 0; i < r.data().Length(); ++i) {
+    const FileSystemDirectoryListingResponseData& data = r.data()[i];
+
+    Directory::FileOrDirectoryPath element;
+
+    if (data.type() == FileSystemDirectoryListingResponseData::TFileSystemDirectoryListingResponseFile) {
+      element.mType = Directory::FileOrDirectoryPath::eFilePath;
+      element.mPath = data.get_FileSystemDirectoryListingResponseFile().fileRealPath();
+    } else {
+      MOZ_ASSERT(data.type() == FileSystemDirectoryListingResponseData::TFileSystemDirectoryListingResponseDirectory);
+
+      element.mType = Directory::FileOrDirectoryPath::eDirectoryPath;
+      element.mPath = data.get_FileSystemDirectoryListingResponseDirectory().directoryRealPath();
+    }
+
+    if (!mTargetData.AppendElement(element, fallible)) {
+      aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+      return;
+    }
+  }
+}
+
+void
+GetDirectoryListingTaskChild::HandlerCallback()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
+  if (mFileSystem->IsShutdown()) {
+    mPromise = nullptr;
+    return;
+  }
+
+  if (HasError()) {
+    mPromise->MaybeReject(mErrorValue);
+    mPromise = nullptr;
+    return;
+  }
+
+  size_t count = mTargetData.Length();
+
+  Sequence<OwningFileOrDirectory> listing;
+
+  if (!listing.SetLength(count, mozilla::fallible_t())) {
+    mPromise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
+    mPromise = nullptr;
+    return;
+  }
+
+  for (unsigned i = 0; i < count; i++) {
+    nsCOMPtr<nsIFile> path;
+    NS_ConvertUTF16toUTF8 fullPath(mTargetData[i].mPath);
+    nsresult rv = NS_NewNativeLocalFile(fullPath, true, getter_AddRefs(path));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      mPromise->MaybeReject(rv);
+      mPromise = nullptr;
+      return;
+    }
+
+#ifdef DEBUG
+    nsCOMPtr<nsIFile> rootPath;
+    rv = NS_NewLocalFile(mFileSystem->LocalOrDeviceStorageRootPath(), false,
+                         getter_AddRefs(rootPath));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      mPromise->MaybeReject(rv);
+      mPromise = nullptr;
+      return;
+    }
+
+    MOZ_ASSERT(FileSystemUtils::IsDescendantPath(rootPath, path));
+#endif
+
+    if (mTargetData[i].mType == Directory::FileOrDirectoryPath::eDirectoryPath) {
+      RefPtr<Directory> directory =
+        Directory::Create(mFileSystem->GetParentObject(), path,
+                          Directory::eNotDOMRootDirectory, mFileSystem);
+      MOZ_ASSERT(directory);
+
+      // Propogate mFilter onto sub-Directory object:
+      directory->SetContentFilters(mFilters);
+      listing[i].SetAsDirectory() = directory;
+    } else {
+      MOZ_ASSERT(mTargetData[i].mType == Directory::FileOrDirectoryPath::eFilePath);
+
+      RefPtr<File> file =
+        File::CreateFromFile(mFileSystem->GetParentObject(), path);
+      MOZ_ASSERT(file);
+
+      listing[i].SetAsFile() = file;
+    }
+  }
+
+  mPromise->MaybeResolve(listing);
+  mPromise = nullptr;
+}
+
+void
+GetDirectoryListingTaskChild::GetPermissionAccessType(nsCString& aAccess) const
+{
+  aAccess.AssignLiteral(GET_DIRECTORY_LISTING_PERMISSION);
+}
+
+/**
+ * GetDirectoryListingTaskParent
+ */
+
+/* static */ already_AddRefed<GetDirectoryListingTaskParent>
+GetDirectoryListingTaskParent::Create(FileSystemBase* aFileSystem,
+                                      const FileSystemGetDirectoryListingParams& aParam,
+                                      FileSystemRequestParent* aParent,
+                                      ErrorResult& aRv)
+{
+  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aFileSystem);
+
+  RefPtr<GetDirectoryListingTaskParent> task =
+    new GetDirectoryListingTaskParent(aFileSystem, aParam, aParent);
+
+  NS_ConvertUTF16toUTF8 path(aParam.realPath());
+  aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  task->mType = aParam.isRoot()
+                  ? Directory::eDOMRootDirectory : Directory::eNotDOMRootDirectory;
+  return task.forget();
+}
+
+GetDirectoryListingTaskParent::GetDirectoryListingTaskParent(FileSystemBase* aFileSystem,
+                                                             const FileSystemGetDirectoryListingParams& aParam,
+                                                             FileSystemRequestParent* aParent)
+  : FileSystemTaskParentBase(aFileSystem, aParam, aParent)
+  , mFilters(aParam.filters())
+{
+  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aFileSystem);
+}
+
+FileSystemResponseValue
+GetDirectoryListingTaskParent::GetSuccessRequestResult(ErrorResult& aRv) const
+{
+  AssertIsOnBackgroundThread();
 
   InfallibleTArray<PBlobParent*> blobs;
 
   nsTArray<FileSystemDirectoryListingResponseData> inputs;
 
   for (unsigned i = 0; i < mTargetData.Length(); i++) {
-    if (mTargetData[i].mType == Directory::BlobImplOrDirectoryPath::eBlobImpl) {
-      BlobParent* blobParent = GetBlobParent(mTargetData[i].mBlobImpl);
-      if (!blobParent) {
-        continue;
-      }
-
-      FileSystemDirectoryListingResponseBlob blobData;
-      blobData.blobParent() = blobParent;
-      inputs.AppendElement(blobData);
+    if (mTargetData[i].mType == Directory::FileOrDirectoryPath::eFilePath) {
+      FileSystemDirectoryListingResponseFile fileData;
+      fileData.fileRealPath() = mTargetData[i].mPath;
+      inputs.AppendElement(fileData);
     } else {
-      MOZ_ASSERT(mTargetData[i].mType == Directory::BlobImplOrDirectoryPath::eDirectoryPath);
+      MOZ_ASSERT(mTargetData[i].mType == Directory::FileOrDirectoryPath::eDirectoryPath);
       FileSystemDirectoryListingResponseDirectory directoryData;
-      directoryData.directoryRealPath() = mTargetData[i].mDirectoryPath;
+      directoryData.directoryRealPath() = mTargetData[i].mPath;
       inputs.AppendElement(directoryData);
     }
   }
 
   FileSystemDirectoryListingResponse response;
   response.data().SwapElements(inputs);
   return response;
 }
 
-void
-GetDirectoryListingTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
-                                                 ErrorResult& aRv)
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  MOZ_ASSERT(aValue.type() ==
-               FileSystemResponseValue::TFileSystemDirectoryListingResponse);
-
-  FileSystemDirectoryListingResponse r = aValue;
-  for (uint32_t i = 0; i < r.data().Length(); ++i) {
-    const FileSystemDirectoryListingResponseData& data = r.data()[i];
-
-    Directory::BlobImplOrDirectoryPath element;
-
-    if (data.type() == FileSystemDirectoryListingResponseData::TFileSystemDirectoryListingResponseBlob) {
-      PBlobChild* blob = data.get_FileSystemDirectoryListingResponseBlob().blobChild();
-
-      element.mType = Directory::BlobImplOrDirectoryPath::eBlobImpl;
-      element.mBlobImpl = static_cast<BlobChild*>(blob)->GetBlobImpl();
-    } else {
-      MOZ_ASSERT(data.type() == FileSystemDirectoryListingResponseData::TFileSystemDirectoryListingResponseDirectory);
-
-      element.mType = Directory::BlobImplOrDirectoryPath::eDirectoryPath;
-      element.mDirectoryPath = data.get_FileSystemDirectoryListingResponseDirectory().directoryRealPath();
-    }
-
-    if (!mTargetData.AppendElement(element, fallible)) {
-      aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
-      return;
-    }
-  }
-}
-
 nsresult
-GetDirectoryListingTask::Work()
+GetDirectoryListingTaskParent::IOWork()
 {
   MOZ_ASSERT(XRE_IsParentProcess(),
              "Only call from parent process!");
   MOZ_ASSERT(!NS_IsMainThread(), "Only call on worker thread!");
 
   if (mFileSystem->IsShutdown()) {
     return NS_ERROR_FAILURE;
   }
@@ -287,110 +368,33 @@ GetDirectoryListingTask::Work()
       if (NS_WARN_IF(NS_FAILED(currFile->GetLeafName(leafName)))) {
         continue;
       }
       if (leafName[0] == char16_t('.')) {
         continue;
       }
     }
 
-    Directory::BlobImplOrDirectoryPath element;
-    if (isDir) {
-      nsAutoString path;
-      if (NS_WARN_IF(NS_FAILED(currFile->GetPath(path)))) {
-        continue;
-      }
+    nsAutoString path;
+    if (NS_WARN_IF(NS_FAILED(currFile->GetPath(path)))) {
+      continue;
+    }
 
-      element.mType = Directory::BlobImplOrDirectoryPath::eDirectoryPath;
-      element.mDirectoryPath = path;
-    } else {
-      BlobImplFile* impl = new BlobImplFile(currFile);
-
-      element.mType = Directory::BlobImplOrDirectoryPath::eBlobImpl;
-      element.mBlobImpl = impl;
-    }
+    Directory::FileOrDirectoryPath element;
+    element.mPath = path;
+    element.mType = isDir ? Directory::FileOrDirectoryPath::eDirectoryPath
+                          : Directory::FileOrDirectoryPath::eFilePath;
 
     if (!mTargetData.AppendElement(element, fallible)) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
   }
   return NS_OK;
 }
 
 void
-GetDirectoryListingTask::HandlerCallback()
+GetDirectoryListingTaskParent::GetPermissionAccessType(nsCString& aAccess) const
 {
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  if (mFileSystem->IsShutdown()) {
-    mPromise = nullptr;
-    return;
-  }
-
-  if (HasError()) {
-    mPromise->MaybeReject(mErrorValue);
-    mPromise = nullptr;
-    return;
-  }
-
-  size_t count = mTargetData.Length();
-
-  Sequence<OwningFileOrDirectory> listing;
-
-  if (!listing.SetLength(count, mozilla::fallible_t())) {
-    mPromise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
-    mPromise = nullptr;
-    return;
-  }
-
-  for (unsigned i = 0; i < count; i++) {
-    if (mTargetData[i].mType == Directory::BlobImplOrDirectoryPath::eDirectoryPath) {
-      nsCOMPtr<nsIFile> directoryPath;
-      NS_ConvertUTF16toUTF8 path(mTargetData[i].mDirectoryPath);
-      nsresult rv = NS_NewNativeLocalFile(path, true,
-                                          getter_AddRefs(directoryPath));
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        mPromise->MaybeReject(rv);
-        mPromise = nullptr;
-        return;
-      }
-
-#ifdef DEBUG
-      nsCOMPtr<nsIFile> rootPath;
-      rv = NS_NewLocalFile(mFileSystem->LocalOrDeviceStorageRootPath(), false,
-                           getter_AddRefs(rootPath));
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        mPromise->MaybeReject(rv);
-        mPromise = nullptr;
-        return;
-      }
-
-      MOZ_ASSERT(FileSystemUtils::IsDescendantPath(rootPath, directoryPath));
-#endif
-
-      RefPtr<Directory> directory =
-        Directory::Create(mFileSystem->GetParentObject(),
-                          directoryPath,
-                          Directory::eNotDOMRootDirectory,
-                          mFileSystem);
-      MOZ_ASSERT(directory);
-
-      // Propogate mFilter onto sub-Directory object:
-      directory->SetContentFilters(mFilters);
-      listing[i].SetAsDirectory() = directory;
-    } else {
-      MOZ_ASSERT(mTargetData[i].mType == Directory::BlobImplOrDirectoryPath::eBlobImpl);
-      listing[i].SetAsFile() =
-        File::Create(mFileSystem->GetParentObject(), mTargetData[i].mBlobImpl);
-    }
-  }
-
-  mPromise->MaybeResolve(listing);
-  mPromise = nullptr;
-}
-
-void
-GetDirectoryListingTask::GetPermissionAccessType(nsCString& aAccess) const
-{
-  aAccess.AssignLiteral("read");
+  aAccess.AssignLiteral(GET_DIRECTORY_LISTING_PERMISSION);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/GetDirectoryListingTask.h
+++ b/dom/filesystem/GetDirectoryListingTask.h
@@ -12,75 +12,91 @@
 #include "mozilla/ErrorResult.h"
 #include "nsAutoPtr.h"
 
 namespace mozilla {
 namespace dom {
 
 class BlobImpl;
 
-class GetDirectoryListingTask final : public FileSystemTaskBase
+class GetDirectoryListingTaskChild final : public FileSystemTaskChildBase
 {
 public:
-  static already_AddRefed<GetDirectoryListingTask>
+  static already_AddRefed<GetDirectoryListingTaskChild>
   Create(FileSystemBase* aFileSystem,
          nsIFile* aTargetPath,
          Directory::DirectoryType aType,
          const nsAString& aFilters,
          ErrorResult& aRv);
 
-  static already_AddRefed<GetDirectoryListingTask>
-  Create(FileSystemBase* aFileSystem,
-         const FileSystemGetDirectoryListingParams& aParam,
-         FileSystemRequestParent* aParent,
-         ErrorResult& aRv);
-
   virtual
-  ~GetDirectoryListingTask();
+  ~GetDirectoryListingTaskChild();
 
   already_AddRefed<Promise>
   GetPromise();
 
   virtual void
   GetPermissionAccessType(nsCString& aAccess) const override;
 
 private:
   // If aDirectoryOnly is set, we should ensure that the target is a directory.
-  GetDirectoryListingTask(FileSystemBase* aFileSystem,
-                          nsIFile* aTargetPath,
-                          Directory::DirectoryType aType,
-                          const nsAString& aFilters);
-
-  GetDirectoryListingTask(FileSystemBase* aFileSystem,
-                          const FileSystemGetDirectoryListingParams& aParam,
-                          FileSystemRequestParent* aParent);
+  GetDirectoryListingTaskChild(FileSystemBase* aFileSystem,
+                               nsIFile* aTargetPath,
+                               Directory::DirectoryType aType,
+                               const nsAString& aFilters);
 
   virtual FileSystemParams
   GetRequestParams(const nsString& aSerializedDOMPath,
                    ErrorResult& aRv) const override;
 
-  virtual FileSystemResponseValue
-  GetSuccessRequestResult(ErrorResult& aRv) const override;
-
   virtual void
   SetSuccessRequestResult(const FileSystemResponseValue& aValue,
                           ErrorResult& aRv) override;
 
-  virtual nsresult
-  Work() override;
-
   virtual void
   HandlerCallback() override;
 
   RefPtr<Promise> mPromise;
   nsCOMPtr<nsIFile> mTargetPath;
   nsString mFilters;
   Directory::DirectoryType mType;
 
   // We cannot store File or Directory objects bacause this object is created
   // on a different thread and File and Directory are not thread-safe.
-  FallibleTArray<Directory::BlobImplOrDirectoryPath> mTargetData;
+  FallibleTArray<Directory::FileOrDirectoryPath> mTargetData;
+};
+
+class GetDirectoryListingTaskParent final : public FileSystemTaskParentBase
+{
+public:
+  static already_AddRefed<GetDirectoryListingTaskParent>
+  Create(FileSystemBase* aFileSystem,
+         const FileSystemGetDirectoryListingParams& aParam,
+         FileSystemRequestParent* aParent,
+         ErrorResult& aRv);
+
+  virtual void
+  GetPermissionAccessType(nsCString& aAccess) const override;
+
+private:
+  GetDirectoryListingTaskParent(FileSystemBase* aFileSystem,
+                                const FileSystemGetDirectoryListingParams& aParam,
+                                FileSystemRequestParent* aParent);
+
+  virtual FileSystemResponseValue
+  GetSuccessRequestResult(ErrorResult& aRv) const override;
+
+  virtual nsresult
+  IOWork() override;
+
+  nsCOMPtr<nsIFile> mTargetPath;
+  nsString mFilters;
+  Directory::DirectoryType mType;
+
+  // We cannot store File or Directory objects bacause this object is created
+  // on a different thread and File and Directory are not thread-safe.
+  FallibleTArray<Directory::FileOrDirectoryPath> mTargetData;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_GetDirectoryListing_h
--- a/dom/filesystem/GetFileOrDirectoryTask.cpp
+++ b/dom/filesystem/GetFileOrDirectoryTask.cpp
@@ -5,37 +5,45 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "GetFileOrDirectoryTask.h"
 
 #include "js/Value.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/FileSystemBase.h"
 #include "mozilla/dom/FileSystemUtils.h"
+#include "mozilla/dom/PFileSystemParams.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/dom/ipc/BlobParent.h"
 #include "nsIFile.h"
 #include "nsStringGlue.h"
 
+#define GET_FILE_OR_DIRECTORY_PERMISSION "read"
+
 namespace mozilla {
 namespace dom {
 
-/* static */ already_AddRefed<GetFileOrDirectoryTask>
-GetFileOrDirectoryTask::Create(FileSystemBase* aFileSystem,
-                               nsIFile* aTargetPath,
-                               Directory::DirectoryType aType,
-                               bool aDirectoryOnly,
-                               ErrorResult& aRv)
+/**
+ * GetFileOrDirectoryTaskChild
+ */
+
+/* static */ already_AddRefed<GetFileOrDirectoryTaskChild>
+GetFileOrDirectoryTaskChild::Create(FileSystemBase* aFileSystem,
+                                    nsIFile* aTargetPath,
+                                    Directory::DirectoryType aType,
+                                    bool aDirectoryOnly,
+                                    ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aFileSystem);
 
-  RefPtr<GetFileOrDirectoryTask> task =
-    new GetFileOrDirectoryTask(aFileSystem, aTargetPath, aType, aDirectoryOnly);
+  RefPtr<GetFileOrDirectoryTaskChild> task =
+    new GetFileOrDirectoryTaskChild(aFileSystem, aTargetPath, aType,
+                                    aDirectoryOnly);
 
   // aTargetPath can be null. In this case SetError will be called.
 
   nsCOMPtr<nsIGlobalObject> globalObject =
     do_QueryInterface(aFileSystem->GetParentObject());
   if (NS_WARN_IF(!globalObject)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
@@ -44,126 +52,72 @@ GetFileOrDirectoryTask::Create(FileSyste
   task->mPromise = Promise::Create(globalObject, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   return task.forget();
 }
 
-/* static */ already_AddRefed<GetFileOrDirectoryTask>
-GetFileOrDirectoryTask::Create(FileSystemBase* aFileSystem,
-                               const FileSystemGetFileOrDirectoryParams& aParam,
-                               FileSystemRequestParent* aParent,
-                               ErrorResult& aRv)
-{
-  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  MOZ_ASSERT(aFileSystem);
-
-  RefPtr<GetFileOrDirectoryTask> task =
-    new GetFileOrDirectoryTask(aFileSystem, aParam, aParent);
-
-  NS_ConvertUTF16toUTF8 path(aParam.realPath());
-  aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
-  }
-
-  task->mType = aParam.isRoot()
-                  ? Directory::eDOMRootDirectory : Directory::eNotDOMRootDirectory;
-  return task.forget();
-}
-
-GetFileOrDirectoryTask::GetFileOrDirectoryTask(FileSystemBase* aFileSystem,
-                                               nsIFile* aTargetPath,
-                                               Directory::DirectoryType aType,
-                                               bool aDirectoryOnly)
-  : FileSystemTaskBase(aFileSystem)
+GetFileOrDirectoryTaskChild::GetFileOrDirectoryTaskChild(FileSystemBase* aFileSystem,
+                                                         nsIFile* aTargetPath,
+                                                         Directory::DirectoryType aType,
+                                                         bool aDirectoryOnly)
+  : FileSystemTaskChildBase(aFileSystem)
   , mTargetPath(aTargetPath)
   , mIsDirectory(aDirectoryOnly)
   , mType(aType)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aFileSystem);
 }
 
-GetFileOrDirectoryTask::GetFileOrDirectoryTask(FileSystemBase* aFileSystem,
-                                               const FileSystemGetFileOrDirectoryParams& aParam,
-                                               FileSystemRequestParent* aParent)
-  : FileSystemTaskBase(aFileSystem, aParam, aParent)
-  , mIsDirectory(false)
+GetFileOrDirectoryTaskChild::~GetFileOrDirectoryTaskChild()
 {
-  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  MOZ_ASSERT(aFileSystem);
-}
-
-GetFileOrDirectoryTask::~GetFileOrDirectoryTask()
-{
-  MOZ_ASSERT(!mPromise || NS_IsMainThread(),
-             "mPromise should be released on main thread!");
+  MOZ_ASSERT(NS_IsMainThread());
 }
 
 already_AddRefed<Promise>
-GetFileOrDirectoryTask::GetPromise()
+GetFileOrDirectoryTaskChild::GetPromise()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   return RefPtr<Promise>(mPromise).forget();
 }
 
 FileSystemParams
-GetFileOrDirectoryTask::GetRequestParams(const nsString& aSerializedDOMPath,
-                                         ErrorResult& aRv) const
+GetFileOrDirectoryTaskChild::GetRequestParams(const nsString& aSerializedDOMPath,
+                                              ErrorResult& aRv) const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
 
   nsAutoString path;
   aRv = mTargetPath->GetPath(path);
   if (NS_WARN_IF(aRv.Failed())) {
     return FileSystemGetFileOrDirectoryParams();
   }
 
   return FileSystemGetFileOrDirectoryParams(aSerializedDOMPath, path,
                                             mType == Directory::eDOMRootDirectory);
 }
 
-FileSystemResponseValue
-GetFileOrDirectoryTask::GetSuccessRequestResult(ErrorResult& aRv) const
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  if (mIsDirectory) {
-    nsAutoString path;
-    aRv = mTargetPath->GetPath(path);
-    if (NS_WARN_IF(aRv.Failed())) {
-      return FileSystemDirectoryResponse();
-    }
-
-    return FileSystemDirectoryResponse(path);
-  }
-
-  BlobParent* actor = GetBlobParent(mTargetBlobImpl);
-  if (!actor) {
-    return FileSystemErrorResponse(NS_ERROR_DOM_FILESYSTEM_UNKNOWN_ERR);
-  }
-  FileSystemFileResponse response;
-  response.blobParent() = actor;
-  return response;
-}
-
 void
-GetFileOrDirectoryTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
-                                                ErrorResult& aRv)
+GetFileOrDirectoryTaskChild::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
+                                                     ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   switch (aValue.type()) {
     case FileSystemResponseValue::TFileSystemFileResponse: {
       FileSystemFileResponse r = aValue;
-      BlobChild* actor = static_cast<BlobChild*>(r.blobChild());
-      mTargetBlobImpl = actor->GetBlobImpl();
+
+      NS_ConvertUTF16toUTF8 path(r.realPath());
+      aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(mTargetPath));
+      if (NS_WARN_IF(aRv.Failed())) {
+        return;
+      }
+
       mIsDirectory = false;
       break;
     }
     case FileSystemResponseValue::TFileSystemDirectoryResponse: {
       FileSystemDirectoryResponse r = aValue;
 
       NS_ConvertUTF16toUTF8 path(r.realPath());
       aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(mTargetPath));
@@ -176,18 +130,114 @@ GetFileOrDirectoryTask::SetSuccessReques
     }
     default: {
       NS_RUNTIMEABORT("not reached");
       break;
     }
   }
 }
 
+void
+GetFileOrDirectoryTaskChild::HandlerCallback()
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
+  if (mFileSystem->IsShutdown()) {
+    mPromise = nullptr;
+    return;
+  }
+
+  if (HasError()) {
+    mPromise->MaybeReject(mErrorValue);
+    mPromise = nullptr;
+    return;
+  }
+
+  if (mIsDirectory) {
+    RefPtr<Directory> dir = Directory::Create(mFileSystem->GetParentObject(),
+                                              mTargetPath,
+                                              mType,
+                                              mFileSystem);
+    MOZ_ASSERT(dir);
+
+    mPromise->MaybeResolve(dir);
+    mPromise = nullptr;
+    return;
+  }
+
+  RefPtr<File> file = File::CreateFromFile(mFileSystem->GetParentObject(),
+                                           mTargetPath);
+  mPromise->MaybeResolve(file);
+  mPromise = nullptr;
+}
+
+void
+GetFileOrDirectoryTaskChild::GetPermissionAccessType(nsCString& aAccess) const
+{
+  aAccess.AssignLiteral(GET_FILE_OR_DIRECTORY_PERMISSION);
+}
+
+/**
+ * GetFileOrDirectoryTaskParent
+ */
+
+/* static */ already_AddRefed<GetFileOrDirectoryTaskParent>
+GetFileOrDirectoryTaskParent::Create(FileSystemBase* aFileSystem,
+                                     const FileSystemGetFileOrDirectoryParams& aParam,
+                                     FileSystemRequestParent* aParent,
+                                     ErrorResult& aRv)
+{
+  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aFileSystem);
+
+  RefPtr<GetFileOrDirectoryTaskParent> task =
+    new GetFileOrDirectoryTaskParent(aFileSystem, aParam, aParent);
+
+  NS_ConvertUTF16toUTF8 path(aParam.realPath());
+  aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  task->mType = aParam.isRoot()
+                  ? Directory::eDOMRootDirectory : Directory::eNotDOMRootDirectory;
+  return task.forget();
+}
+
+GetFileOrDirectoryTaskParent::GetFileOrDirectoryTaskParent(FileSystemBase* aFileSystem,
+                                                           const FileSystemGetFileOrDirectoryParams& aParam,
+                                                           FileSystemRequestParent* aParent)
+  : FileSystemTaskParentBase(aFileSystem, aParam, aParent)
+  , mIsDirectory(false)
+{
+  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aFileSystem);
+}
+
+FileSystemResponseValue
+GetFileOrDirectoryTaskParent::GetSuccessRequestResult(ErrorResult& aRv) const
+{
+  AssertIsOnBackgroundThread();
+
+  nsAutoString path;
+  aRv = mTargetPath->GetPath(path);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return FileSystemDirectoryResponse();
+  }
+
+  if (mIsDirectory) {
+    return FileSystemDirectoryResponse(path);
+  }
+
+  return FileSystemFileResponse(path);
+}
+
 nsresult
-GetFileOrDirectoryTask::Work()
+GetFileOrDirectoryTaskParent::IOWork()
 {
   MOZ_ASSERT(XRE_IsParentProcess(),
              "Only call from parent process!");
   MOZ_ASSERT(!NS_IsMainThread(), "Only call on worker thread!");
 
   if (mFileSystem->IsShutdown()) {
     return NS_ERROR_FAILURE;
   }
@@ -237,54 +287,19 @@ GetFileOrDirectoryTask::Work()
     // Neither directory or file.
     return NS_ERROR_DOM_FILESYSTEM_TYPE_MISMATCH_ERR;
   }
 
   if (!mFileSystem->IsSafeFile(mTargetPath)) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
-  mTargetBlobImpl = new BlobImplFile(mTargetPath);
-
   return NS_OK;
 }
 
 void
-GetFileOrDirectoryTask::HandlerCallback()
+GetFileOrDirectoryTaskParent::GetPermissionAccessType(nsCString& aAccess) const
 {
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  if (mFileSystem->IsShutdown()) {
-    mPromise = nullptr;
-    return;
-  }
-
-  if (HasError()) {
-    mPromise->MaybeReject(mErrorValue);
-    mPromise = nullptr;
-    return;
-  }
-
-  if (mIsDirectory) {
-    RefPtr<Directory> dir = Directory::Create(mFileSystem->GetParentObject(),
-                                              mTargetPath,
-                                              mType,
-                                              mFileSystem);
-    MOZ_ASSERT(dir);
-
-    mPromise->MaybeResolve(dir);
-    mPromise = nullptr;
-    return;
-  }
-
-  RefPtr<Blob> blob = Blob::Create(mFileSystem->GetParentObject(),
-                                   mTargetBlobImpl);
-  mPromise->MaybeResolve(blob);
-  mPromise = nullptr;
-}
-
-void
-GetFileOrDirectoryTask::GetPermissionAccessType(nsCString& aAccess) const
-{
-  aAccess.AssignLiteral("read");
+  aAccess.AssignLiteral(GET_FILE_OR_DIRECTORY_PERMISSION);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/GetFileOrDirectoryTask.h
+++ b/dom/filesystem/GetFileOrDirectoryTask.h
@@ -12,77 +12,88 @@
 #include "nsAutoPtr.h"
 #include "mozilla/ErrorResult.h"
 
 namespace mozilla {
 namespace dom {
 
 class BlobImpl;
 
-class GetFileOrDirectoryTask final : public FileSystemTaskBase
+class GetFileOrDirectoryTaskChild final : public FileSystemTaskChildBase
 {
 public:
-  static already_AddRefed<GetFileOrDirectoryTask>
+  static already_AddRefed<GetFileOrDirectoryTaskChild>
   Create(FileSystemBase* aFileSystem,
          nsIFile* aTargetPath,
          Directory::DirectoryType aType,
          bool aDirectoryOnly,
          ErrorResult& aRv);
 
-  static already_AddRefed<GetFileOrDirectoryTask>
-  Create(FileSystemBase* aFileSystem,
-         const FileSystemGetFileOrDirectoryParams& aParam,
-         FileSystemRequestParent* aParent,
-         ErrorResult& aRv);
-
   virtual
-  ~GetFileOrDirectoryTask();
+  ~GetFileOrDirectoryTaskChild();
 
   already_AddRefed<Promise>
   GetPromise();
 
   virtual void
   GetPermissionAccessType(nsCString& aAccess) const override;
+
 protected:
   virtual FileSystemParams
   GetRequestParams(const nsString& aSerializedDOMPath,
                    ErrorResult& aRv) const override;
 
-  virtual FileSystemResponseValue
-  GetSuccessRequestResult(ErrorResult& aRv) const override;
-
   virtual void
   SetSuccessRequestResult(const FileSystemResponseValue& aValue,
                           ErrorResult& aRv) override;
-
-  virtual nsresult
-  Work() override;
-
   virtual void
   HandlerCallback() override;
 
 private:
   // If aDirectoryOnly is set, we should ensure that the target is a directory.
-  GetFileOrDirectoryTask(FileSystemBase* aFileSystem,
-                         nsIFile* aTargetPath,
-                         Directory::DirectoryType aType,
-                         bool aDirectoryOnly);
-
-  GetFileOrDirectoryTask(FileSystemBase* aFileSystem,
-                         const FileSystemGetFileOrDirectoryParams& aParam,
-                         FileSystemRequestParent* aParent);
+  GetFileOrDirectoryTaskChild(FileSystemBase* aFileSystem,
+                              nsIFile* aTargetPath,
+                              Directory::DirectoryType aType,
+                              bool aDirectoryOnly);
 
   RefPtr<Promise> mPromise;
   nsCOMPtr<nsIFile> mTargetPath;
 
   // Whether we get a directory.
   bool mIsDirectory;
   Directory::DirectoryType mType;
+};
 
-  // This cannot be a File bacause this object is created on a different
-  // thread and File is not thread-safe. Let's use the BlobImpl instead.
-  RefPtr<BlobImpl> mTargetBlobImpl;
+class GetFileOrDirectoryTaskParent final : public FileSystemTaskParentBase
+{
+public:
+  static already_AddRefed<GetFileOrDirectoryTaskParent>
+  Create(FileSystemBase* aFileSystem,
+         const FileSystemGetFileOrDirectoryParams& aParam,
+         FileSystemRequestParent* aParent,
+         ErrorResult& aRv);
+
+  virtual void
+  GetPermissionAccessType(nsCString& aAccess) const override;
+
+protected:
+  virtual FileSystemResponseValue
+  GetSuccessRequestResult(ErrorResult& aRv) const override;
+
+  virtual nsresult
+  IOWork() override;
+
+private:
+  GetFileOrDirectoryTaskParent(FileSystemBase* aFileSystem,
+                               const FileSystemGetFileOrDirectoryParams& aParam,
+                               FileSystemRequestParent* aParent);
+
+  nsCOMPtr<nsIFile> mTargetPath;
+
+  // Whether we get a directory.
+  bool mIsDirectory;
+  Directory::DirectoryType mType;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_GetFileOrDirectory_h
--- a/dom/filesystem/OSFileSystem.cpp
+++ b/dom/filesystem/OSFileSystem.cpp
@@ -15,61 +15,64 @@
 #include "nsIFile.h"
 
 namespace mozilla {
 namespace dom {
 
 OSFileSystem::OSFileSystem(const nsAString& aRootDir)
 {
   mLocalOrDeviceStorageRootPath = aRootDir;
-
-  // Non-mobile devices don't have the concept of separate permissions to
-  // access different parts of devices storage like Pictures, or Videos, etc.
-  mRequiresPermissionChecks = false;
+  mPermissionCheckType = ePermissionCheckNotRequired;
 
 #ifdef DEBUG
   mPermission.AssignLiteral("never-used");
 #endif
 }
 
 already_AddRefed<FileSystemBase>
 OSFileSystem::Clone()
 {
+  AssertIsOnOwningThread();
+
   RefPtr<OSFileSystem> fs = new OSFileSystem(mLocalOrDeviceStorageRootPath);
   if (mParent) {
     fs->Init(mParent);
   }
 
   return fs.forget();
 }
 
 void
 OSFileSystem::Init(nsISupports* aParent)
 {
+  AssertIsOnOwningThread();
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(!mParent, "No duple Init() calls");
   MOZ_ASSERT(aParent);
+
   mParent = aParent;
 
 #ifdef DEBUG
   nsCOMPtr<nsIGlobalObject> obj = do_QueryInterface(aParent);
   MOZ_ASSERT(obj);
 #endif
 }
 
 nsISupports*
 OSFileSystem::GetParentObject() const
 {
+  AssertIsOnOwningThread();
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   return mParent;
 }
 
 void
 OSFileSystem::GetRootName(nsAString& aRetval) const
 {
+  AssertIsOnOwningThread();
   aRetval.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL);
 }
 
 bool
 OSFileSystem::IsSafeFile(nsIFile* aFile) const
 {
   // The concept of "safe files" is specific to the Device Storage API where
   // files are only "safe" if they're of a type that is appropriate for the
@@ -86,26 +89,47 @@ OSFileSystem::IsSafeDirectory(Directory*
   // storage that it is being used with.
   MOZ_CRASH("Don't use OSFileSystem with the Device Storage API");
   return true;
 }
 
 void
 OSFileSystem::Unlink()
 {
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
+
   mParent = nullptr;
 }
 
 void
 OSFileSystem::Traverse(nsCycleCollectionTraversalCallback &cb)
 {
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
+
   OSFileSystem* tmp = this;
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent);
 }
 
 void
 OSFileSystem::SerializeDOMPath(nsAString& aOutput) const
 {
+  AssertIsOnOwningThread();
   aOutput = mLocalOrDeviceStorageRootPath;
 }
 
+/**
+ * OSFileSystemParent
+ */
+
+OSFileSystemParent::OSFileSystemParent(const nsAString& aRootDir)
+{
+  mLocalOrDeviceStorageRootPath = aRootDir;
+  mPermissionCheckType = ePermissionCheckNotRequired;
+
+#ifdef DEBUG
+  mPermission.AssignLiteral("never-used");
+#endif
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/OSFileSystem.h
+++ b/dom/filesystem/OSFileSystem.h
@@ -42,15 +42,79 @@ public:
 
   // CC methods
   virtual void Unlink() override;
   virtual void Traverse(nsCycleCollectionTraversalCallback &cb) override;
 
 private:
   virtual ~OSFileSystem() {}
 
-   nsCOMPtr<nsISupports> mParent;
+  nsCOMPtr<nsISupports> mParent;
+};
+
+class OSFileSystemParent final : public FileSystemBase
+{
+public:
+  explicit OSFileSystemParent(const nsAString& aRootDir);
+
+  // Overrides FileSystemBase
+
+  virtual already_AddRefed<FileSystemBase>
+  Clone() override
+  {
+    MOZ_CRASH("This should not be called on the PBackground thread.");
+    return nullptr;
+  }
+
+  virtual nsISupports*
+  GetParentObject() const override
+  {
+    MOZ_CRASH("This should not be called on the PBackground thread.");
+    return nullptr;
+  }
+
+  virtual void
+  GetRootName(nsAString& aRetval) const override
+  {
+    MOZ_CRASH("This should not be called on the PBackground thread.");
+  }
+
+  virtual bool
+  IsSafeFile(nsIFile* aFile) const override
+  {
+    MOZ_CRASH("This should not be called on the PBackground thread.");
+    return true;
+  }
+
+  virtual bool
+  IsSafeDirectory(Directory* aDir) const override
+  {
+    MOZ_CRASH("This should not be called on the PBackground thread.");
+    return true;
+  }
+
+  virtual void
+  SerializeDOMPath(nsAString& aOutput) const override
+  {
+    MOZ_CRASH("This should not be called on the PBackground thread.");
+  }
+
+  // CC methods
+  virtual void
+  Unlink() override
+  {
+    MOZ_CRASH("This should not be called on the PBackground thread.");
+  }
+
+  virtual void
+  Traverse(nsCycleCollectionTraversalCallback &cb) override
+  {
+    MOZ_CRASH("This should not be called on the PBackground thread.");
+  }
+
+private:
+  virtual ~OSFileSystemParent() {}
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_OSFileSystem_h
new file mode 100644
--- /dev/null
+++ b/dom/filesystem/PFileSystemParams.ipdlh
@@ -0,0 +1,72 @@
+/* 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 protocol PBlob;
+
+namespace mozilla {
+namespace dom {
+
+struct FileSystemCreateDirectoryParams
+{
+  nsString filesystem;
+  nsString realPath;
+};
+
+union FileSystemFileDataValue
+{
+  uint8_t[];
+  PBlob;
+};
+
+struct FileSystemCreateFileParams
+{
+  nsString filesystem;
+  nsString realPath;
+  FileSystemFileDataValue data;
+  bool replace;
+};
+
+struct FileSystemGetDirectoryListingParams
+{
+  nsString filesystem;
+  nsString realPath;
+  bool isRoot;
+  // 'filters' could be an array rather than a semicolon separated string
+  // (we'd then use InfallibleTArray<nsString> internally), but that is
+  // wasteful.  E10s requires us to pass the filters over as a string anyway,
+  // so avoiding using an array avoids serialization on the side passing the
+  // filters.  Since an nsString can share its buffer when copied,
+  // using that instead of InfallibleTArray<nsString> makes copying the filters
+  // around in any given process a bit more efficient too, since copying a
+  // single nsString is cheaper than copying InfallibleTArray member data and
+  // each nsString that it contains.
+  nsString filters;
+};
+
+struct FileSystemGetFileOrDirectoryParams
+{
+  nsString filesystem;
+  nsString realPath;
+  bool isRoot;
+};
+
+struct FileSystemRemoveParams
+{
+  nsString filesystem;
+  nsString directory;
+  nsString targetDirectory;
+  bool recursive;
+};
+
+union FileSystemParams
+{
+  FileSystemCreateDirectoryParams;
+  FileSystemCreateFileParams;
+  FileSystemGetDirectoryListingParams;
+  FileSystemGetFileOrDirectoryParams;
+  FileSystemRemoveParams;
+};
+
+} // dom namespace
+} // mozilla namespace
--- a/dom/filesystem/PFileSystemRequest.ipdl
+++ b/dom/filesystem/PFileSystemRequest.ipdl
@@ -1,44 +1,45 @@
 /* -*- 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 protocol PBackground;
 include protocol PBlob;
-include protocol PContent;
 
 namespace mozilla {
 namespace dom {
 
 struct FileSystemFileResponse
 {
-  PBlob blob;
+  nsString realPath;
 };
 
 struct FileSystemDirectoryResponse
 {
   nsString realPath;
 };
 
-struct FileSystemDirectoryListingResponseBlob
+struct FileSystemDirectoryListingResponseFile
 {
-  PBlob blob;
+  // This is the full real path for the file that we are sending via IPC.
+  nsString fileRealPath;
 };
 
 struct FileSystemDirectoryListingResponseDirectory
 {
   // This is the full real path for the directory that we are sending via IPC.
   nsString directoryRealPath;
 };
 
 union FileSystemDirectoryListingResponseData
 {
-  FileSystemDirectoryListingResponseBlob;
+  FileSystemDirectoryListingResponseFile;
   FileSystemDirectoryListingResponseDirectory;
 };
 
 struct FileSystemDirectoryListingResponse
 {
   FileSystemDirectoryListingResponseData[] data;
 };
 
@@ -56,18 +57,18 @@ union FileSystemResponseValue
 {
   FileSystemBooleanResponse;
   FileSystemDirectoryResponse;
   FileSystemDirectoryListingResponse;
   FileSystemFileResponse;
   FileSystemErrorResponse;
 };
 
-sync protocol PFileSystemRequest
+protocol PFileSystemRequest
 {
-  manager PContent;
+  manager PBackground;
 
 child:
   async __delete__(FileSystemResponseValue response);
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/RemoveTask.cpp
+++ b/dom/filesystem/RemoveTask.cpp
@@ -4,39 +4,44 @@
  * 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 "RemoveTask.h"
 
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/FileSystemBase.h"
 #include "mozilla/dom/FileSystemUtils.h"
+#include "mozilla/dom/PFileSystemParams.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/dom/ipc/BlobParent.h"
 #include "nsIFile.h"
 #include "nsStringGlue.h"
 
 namespace mozilla {
 namespace dom {
 
-/* static */ already_AddRefed<RemoveTask>
-RemoveTask::Create(FileSystemBase* aFileSystem,
-                   nsIFile* aDirPath,
-                   BlobImpl* aTargetBlob,
-                   nsIFile* aTargetPath,
-                   bool aRecursive,
-                   ErrorResult& aRv)
+/**
+ * RemoveTaskChild
+ */
+
+/* static */ already_AddRefed<RemoveTaskChild>
+RemoveTaskChild::Create(FileSystemBase* aFileSystem,
+                        nsIFile* aDirPath,
+                        nsIFile* aTargetPath,
+                        bool aRecursive,
+                        ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aFileSystem);
   MOZ_ASSERT(aDirPath);
+  MOZ_ASSERT(aTargetPath);
 
-  RefPtr<RemoveTask> task =
-    new RemoveTask(aFileSystem, aDirPath, aTargetBlob, aTargetPath, aRecursive);
+  RefPtr<RemoveTaskChild> task =
+    new RemoveTaskChild(aFileSystem, aDirPath, aTargetPath, aRecursive);
 
   // aTargetPath can be null. In this case SetError will be called.
 
   nsCOMPtr<nsIGlobalObject> globalObject =
     do_QueryInterface(aFileSystem->GetParentObject());
   if (NS_WARN_IF(!globalObject)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
@@ -45,170 +50,178 @@ RemoveTask::Create(FileSystemBase* aFile
   task->mPromise = Promise::Create(globalObject, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   return task.forget();
 }
 
-/* static */ already_AddRefed<RemoveTask>
-RemoveTask::Create(FileSystemBase* aFileSystem,
-                   const FileSystemRemoveParams& aParam,
-                   FileSystemRequestParent* aParent,
-                   ErrorResult& aRv)
-{
-  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  MOZ_ASSERT(aFileSystem);
-
-  RefPtr<RemoveTask> task =
-    new RemoveTask(aFileSystem, aParam, aParent);
-
-  NS_ConvertUTF16toUTF8 directoryPath(aParam.directory());
-  aRv = NS_NewNativeLocalFile(directoryPath, true,
-                              getter_AddRefs(task->mDirPath));
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
-  }
-
-  task->mRecursive = aParam.recursive();
-
-  const FileSystemPathOrFileValue& target = aParam.target();
-
-  if (target.type() == FileSystemPathOrFileValue::TnsString) {
-    NS_ConvertUTF16toUTF8 path(target);
-    aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
-    if (NS_WARN_IF(aRv.Failed())) {
-      return nullptr;
-    }
-
-    return task.forget();
-  }
-
-  BlobParent* bp = static_cast<BlobParent*>(static_cast<PBlobParent*>(target));
-  task->mTargetBlobImpl = bp->GetBlobImpl();
-  MOZ_ASSERT(task->mTargetBlobImpl);
-
-  return task.forget();
-}
-
-RemoveTask::RemoveTask(FileSystemBase* aFileSystem,
-                       nsIFile* aDirPath,
-                       BlobImpl* aTargetBlob,
-                       nsIFile* aTargetPath,
-                       bool aRecursive)
-  : FileSystemTaskBase(aFileSystem)
+RemoveTaskChild::RemoveTaskChild(FileSystemBase* aFileSystem,
+                                 nsIFile* aDirPath,
+                                 nsIFile* aTargetPath,
+                                 bool aRecursive)
+  : FileSystemTaskChildBase(aFileSystem)
   , mDirPath(aDirPath)
-  , mTargetBlobImpl(aTargetBlob)
   , mTargetPath(aTargetPath)
   , mRecursive(aRecursive)
   , mReturnValue(false)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aFileSystem);
   MOZ_ASSERT(aDirPath);
+  MOZ_ASSERT(aTargetPath);
 }
 
-RemoveTask::RemoveTask(FileSystemBase* aFileSystem,
-                       const FileSystemRemoveParams& aParam,
-                       FileSystemRequestParent* aParent)
-  : FileSystemTaskBase(aFileSystem, aParam, aParent)
-  , mRecursive(false)
-  , mReturnValue(false)
+RemoveTaskChild::~RemoveTaskChild()
 {
-  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  MOZ_ASSERT(aFileSystem);
-}
-
-RemoveTask::~RemoveTask()
-{
-  MOZ_ASSERT(!mPromise || NS_IsMainThread(),
-             "mPromise should be released on main thread!");
+  MOZ_ASSERT(NS_IsMainThread());
 }
 
 already_AddRefed<Promise>
-RemoveTask::GetPromise()
+RemoveTaskChild::GetPromise()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   return RefPtr<Promise>(mPromise).forget();
 }
 
 FileSystemParams
-RemoveTask::GetRequestParams(const nsString& aSerializedDOMPath,
-                             ErrorResult& aRv) const
+RemoveTaskChild::GetRequestParams(const nsString& aSerializedDOMPath,
+                                  ErrorResult& aRv) const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   FileSystemRemoveParams param;
   param.filesystem() = aSerializedDOMPath;
 
   aRv = mDirPath->GetPath(param.directory());
   if (NS_WARN_IF(aRv.Failed())) {
     return param;
   }
 
   param.recursive() = mRecursive;
-  if (mTargetBlobImpl) {
-    RefPtr<Blob> blob = Blob::Create(mFileSystem->GetParentObject(),
-                                     mTargetBlobImpl);
-    BlobChild* actor
-      = ContentChild::GetSingleton()->GetOrCreateActorForBlob(blob);
-    if (actor) {
-      param.target() = actor;
-    }
-  } else {
-    nsAutoString path;
-    aRv = mTargetPath->GetPath(path);
-    if (NS_WARN_IF(aRv.Failed())) {
-      return param;
-    }
 
-    param.target() = path;
+  nsAutoString path;
+  aRv = mTargetPath->GetPath(path);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return param;
   }
+
+  param.targetDirectory() = path;
+
   return param;
 }
 
-FileSystemResponseValue
-RemoveTask::GetSuccessRequestResult(ErrorResult& aRv) const
+void
+RemoveTaskChild::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
+                                         ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  return FileSystemBooleanResponse(mReturnValue);
-}
 
-void
-RemoveTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
-                                    ErrorResult& aRv)
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   FileSystemBooleanResponse r = aValue;
   mReturnValue = r.success();
 }
 
+void
+RemoveTaskChild::HandlerCallback()
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
+
+  if (mFileSystem->IsShutdown()) {
+    mPromise = nullptr;
+    return;
+  }
+
+  if (HasError()) {
+    mPromise->MaybeReject(mErrorValue);
+    mPromise = nullptr;
+    return;
+  }
+
+  mPromise->MaybeResolve(mReturnValue);
+  mPromise = nullptr;
+}
+
+void
+RemoveTaskChild::GetPermissionAccessType(nsCString& aAccess) const
+{
+  aAccess.AssignLiteral(REMOVE_TASK_PERMISSION);
+}
+
+/**
+ * RemoveTaskParent
+ */
+
+/* static */ already_AddRefed<RemoveTaskParent>
+RemoveTaskParent::Create(FileSystemBase* aFileSystem,
+                         const FileSystemRemoveParams& aParam,
+                         FileSystemRequestParent* aParent,
+                         ErrorResult& aRv)
+{
+  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aFileSystem);
+
+  RefPtr<RemoveTaskParent> task =
+    new RemoveTaskParent(aFileSystem, aParam, aParent);
+
+  NS_ConvertUTF16toUTF8 directoryPath(aParam.directory());
+  aRv = NS_NewNativeLocalFile(directoryPath, true,
+                              getter_AddRefs(task->mDirPath));
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  task->mRecursive = aParam.recursive();
+
+  NS_ConvertUTF16toUTF8 path(aParam.targetDirectory());
+  aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  if (!FileSystemUtils::IsDescendantPath(task->mDirPath, task->mTargetPath)) {
+    aRv.Throw(NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR);
+    return nullptr;
+  }
+
+  return task.forget();
+}
+
+RemoveTaskParent::RemoveTaskParent(FileSystemBase* aFileSystem,
+                                   const FileSystemRemoveParams& aParam,
+                                   FileSystemRequestParent* aParent)
+  : FileSystemTaskParentBase(aFileSystem, aParam, aParent)
+  , mRecursive(false)
+  , mReturnValue(false)
+{
+  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aFileSystem);
+}
+
+FileSystemResponseValue
+RemoveTaskParent::GetSuccessRequestResult(ErrorResult& aRv) const
+{
+  AssertIsOnBackgroundThread();
+
+  return FileSystemBooleanResponse(mReturnValue);
+}
+
 nsresult
-RemoveTask::Work()
+RemoveTaskParent::IOWork()
 {
   MOZ_ASSERT(XRE_IsParentProcess(),
              "Only call from parent process!");
   MOZ_ASSERT(!NS_IsMainThread(), "Only call on worker thread!");
 
   if (mFileSystem->IsShutdown()) {
     return NS_ERROR_FAILURE;
   }
 
-  // Get the path if a File is passed as the target.
-  if (mTargetBlobImpl) {
-    if (!mFileSystem->GetRealPath(mTargetBlobImpl,
-                                  getter_AddRefs(mTargetPath))) {
-      return NS_ERROR_DOM_SECURITY_ERR;
-    }
-    if (!FileSystemUtils::IsDescendantPath(mDirPath, mTargetPath)) {
-      return NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR;
-    }
-  }
+  MOZ_ASSERT(FileSystemUtils::IsDescendantPath(mDirPath, mTargetPath));
 
   bool exists = false;
   nsresult rv = mTargetPath->Exists(&exists);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (!exists) {
@@ -232,34 +245,15 @@ RemoveTask::Work()
   }
 
   mReturnValue = true;
 
   return NS_OK;
 }
 
 void
-RemoveTask::HandlerCallback()
+RemoveTaskParent::GetPermissionAccessType(nsCString& aAccess) const
 {
-  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  if (mFileSystem->IsShutdown()) {
-    mPromise = nullptr;
-    return;
-  }
-
-  if (HasError()) {
-    mPromise->MaybeReject(mErrorValue);
-    mPromise = nullptr;
-    return;
-  }
-
-  mPromise->MaybeResolve(mReturnValue);
-  mPromise = nullptr;
-}
-
-void
-RemoveTask::GetPermissionAccessType(nsCString& aAccess) const
-{
-  aAccess.AssignLiteral("write");
+  aAccess.AssignLiteral(REMOVE_TASK_PERMISSION);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/RemoveTask.h
+++ b/dom/filesystem/RemoveTask.h
@@ -6,84 +6,103 @@
 
 #ifndef mozilla_dom_RemoveTask_h
 #define mozilla_dom_RemoveTask_h
 
 #include "mozilla/dom/FileSystemTaskBase.h"
 #include "nsAutoPtr.h"
 #include "mozilla/ErrorResult.h"
 
+#define REMOVE_TASK_PERMISSION "write"
+
 namespace mozilla {
 namespace dom {
 
 class BlobImpl;
 class Promise;
 
-class RemoveTask final : public FileSystemTaskBase
+class RemoveTaskChild final : public FileSystemTaskChildBase
 {
 public:
-  static already_AddRefed<RemoveTask>
+  static already_AddRefed<RemoveTaskChild>
   Create(FileSystemBase* aFileSystem,
          nsIFile* aDirPath,
-         BlobImpl* aTargetBlob,
          nsIFile* aTargetPath,
          bool aRecursive,
          ErrorResult& aRv);
 
-  static already_AddRefed<RemoveTask>
-  Create(FileSystemBase* aFileSystem,
-         const FileSystemRemoveParams& aParam,
-         FileSystemRequestParent* aParent,
-         ErrorResult& aRv);
-
   virtual
-  ~RemoveTask();
+  ~RemoveTaskChild();
 
   already_AddRefed<Promise>
   GetPromise();
 
   virtual void
   GetPermissionAccessType(nsCString& aAccess) const override;
 
 protected:
   virtual FileSystemParams
   GetRequestParams(const nsString& aSerializedDOMPath,
                    ErrorResult& aRv) const override;
 
-  virtual FileSystemResponseValue
-  GetSuccessRequestResult(ErrorResult& aRv) const override;
-
   virtual void
   SetSuccessRequestResult(const FileSystemResponseValue& aValue,
                           ErrorResult& aRv) override;
 
-  virtual nsresult
-  Work() override;
-
   virtual void
   HandlerCallback() override;
 
 private:
-  RemoveTask(FileSystemBase* aFileSystem,
-             nsIFile* aDirPath,
-             BlobImpl* aTargetBlob,
-             nsIFile* aTargetPath,
-             bool aRecursive);
-
-  RemoveTask(FileSystemBase* aFileSystem,
-             const FileSystemRemoveParams& aParam,
-             FileSystemRequestParent* aParent);
+  RemoveTaskChild(FileSystemBase* aFileSystem,
+                  nsIFile* aDirPath,
+                  nsIFile* aTargetPath,
+                  bool aRecursive);
 
   RefPtr<Promise> mPromise;
+
+  // This path is the Directory::mFile.
   nsCOMPtr<nsIFile> mDirPath;
 
-  // This cannot be a File because this object will be used on a different
-  // thread and File is not thread-safe. Let's use the BlobImpl instead.
-  RefPtr<BlobImpl> mTargetBlobImpl;
+  // This is what we want to remove. mTargetPath is discendant path of mDirPath.
   nsCOMPtr<nsIFile> mTargetPath;
+
+  bool mRecursive;
+  bool mReturnValue;
+};
+
+class RemoveTaskParent final : public FileSystemTaskParentBase
+{
+public:
+  static already_AddRefed<RemoveTaskParent>
+  Create(FileSystemBase* aFileSystem,
+         const FileSystemRemoveParams& aParam,
+         FileSystemRequestParent* aParent,
+         ErrorResult& aRv);
+
+  virtual void
+  GetPermissionAccessType(nsCString& aAccess) const override;
+
+protected:
+  virtual FileSystemResponseValue
+  GetSuccessRequestResult(ErrorResult& aRv) const override;
+
+  virtual nsresult
+  IOWork() override;
+
+private:
+  RemoveTaskParent(FileSystemBase* aFileSystem,
+                   const FileSystemRemoveParams& aParam,
+                   FileSystemRequestParent* aParent);
+
+  // This path is the Directory::mFile.
+  nsCOMPtr<nsIFile> mDirPath;
+
+  // This is what we want to remove. mTargetPath is discendant path of mDirPath.
+  nsCOMPtr<nsIFile> mTargetPath;
+
   bool mRecursive;
   bool mReturnValue;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_RemoveTask_h
--- a/dom/filesystem/moz.build
+++ b/dom/filesystem/moz.build
@@ -30,16 +30,17 @@ UNIFIED_SOURCES += [
     'GetFileOrDirectoryTask.cpp',
     'OSFileSystem.cpp',
     'RemoveTask.cpp',
 ]
 
 FINAL_LIBRARY = 'xul'
 
 IPDL_SOURCES += [
+    'PFileSystemParams.ipdlh',
     'PFileSystemRequest.ipdl',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 LOCAL_INCLUDES += [
     '/dom/base',
 ]
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -249,26 +249,26 @@ class HTMLInputElementState final : publ
       mValue = aValue;
     }
 
     void
     GetFilesOrDirectories(nsPIDOMWindowInner* aWindow,
                           nsTArray<OwningFileOrDirectory>& aResult) const
     {
       for (uint32_t i = 0; i < mBlobImplsOrDirectoryPaths.Length(); ++i) {
-        if (mBlobImplsOrDirectoryPaths[i].mType == Directory::BlobImplOrDirectoryPath::eBlobImpl) {
+        if (mBlobImplsOrDirectoryPaths[i].mType == BlobImplOrDirectoryPath::eBlobImpl) {
           RefPtr<File> file =
             File::Create(aWindow,
                          mBlobImplsOrDirectoryPaths[i].mBlobImpl);
           MOZ_ASSERT(file);
 
           OwningFileOrDirectory* element = aResult.AppendElement();
           element->SetAsFile() = file;
         } else {
-          MOZ_ASSERT(mBlobImplsOrDirectoryPaths[i].mType == Directory::BlobImplOrDirectoryPath::eDirectoryPath);
+          MOZ_ASSERT(mBlobImplsOrDirectoryPaths[i].mType == BlobImplOrDirectoryPath::eDirectoryPath);
 
           nsCOMPtr<nsIFile> file;
           NS_ConvertUTF16toUTF8 path(mBlobImplsOrDirectoryPaths[i].mDirectoryPath);
           nsresult rv = NS_NewNativeLocalFile(path, true, getter_AddRefs(file));
           if (NS_WARN_IF(NS_FAILED(rv))) {
             continue;
           }
 
@@ -282,50 +282,60 @@ class HTMLInputElementState final : publ
       }
     }
 
     void SetFilesOrDirectories(const nsTArray<OwningFileOrDirectory>& aArray)
     {
       mBlobImplsOrDirectoryPaths.Clear();
       for (uint32_t i = 0; i < aArray.Length(); ++i) {
         if (aArray[i].IsFile()) {
-          Directory::BlobImplOrDirectoryPath* data =
-            mBlobImplsOrDirectoryPaths.AppendElement();
+          BlobImplOrDirectoryPath* data = mBlobImplsOrDirectoryPaths.AppendElement();
 
           data->mBlobImpl = aArray[i].GetAsFile()->Impl();
-          data->mType = Directory::BlobImplOrDirectoryPath::eBlobImpl;
+          data->mType = BlobImplOrDirectoryPath::eBlobImpl;
         } else {
           MOZ_ASSERT(aArray[i].IsDirectory());
           nsAutoString fullPath;
           nsresult rv = aArray[i].GetAsDirectory()->GetFullRealPath(fullPath);
           if (NS_WARN_IF(NS_FAILED(rv))) {
             continue;
           }
 
-          Directory::BlobImplOrDirectoryPath* data =
+          BlobImplOrDirectoryPath* data =
             mBlobImplsOrDirectoryPaths.AppendElement();
 
           data->mDirectoryPath = fullPath;
-          data->mType = Directory::BlobImplOrDirectoryPath::eDirectoryPath;
+          data->mType = BlobImplOrDirectoryPath::eDirectoryPath;
         }
       }
     }
 
     HTMLInputElementState()
       : mValue()
       , mChecked(false)
       , mCheckedSet(false)
     {}
 
   protected:
     ~HTMLInputElementState() {}
 
     nsString mValue;
 
-    nsTArray<Directory::BlobImplOrDirectoryPath> mBlobImplsOrDirectoryPaths;
+    struct BlobImplOrDirectoryPath
+    {
+      RefPtr<BlobImpl> mBlobImpl;
+      nsString mDirectoryPath;
+
+      enum {
+        eBlobImpl,
+        eDirectoryPath
+      } mType;
+    };
+
+    nsTArray<BlobImplOrDirectoryPath> mBlobImplsOrDirectoryPaths;
 
     bool mChecked;
     bool mCheckedSet;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(HTMLInputElementState, NS_INPUT_ELEMENT_STATE_IID)
 
 NS_IMPL_ISUPPORTS(HTMLInputElementState, HTMLInputElementState)
--- a/dom/html/test/test_fullscreen-api.html
+++ b/dom/html/test/test_fullscreen-api.html
@@ -18,17 +18,16 @@
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 /** Tests for Bug 545812 **/
 SimpleTest.requestFlakyTimeout("untriaged");
-SimpleTest.expectAssertions(0, 1);
 
 // Run the tests which go full-screen in new windows, as mochitests normally
 // run in an iframe, which by default will not have the allowfullscreen
 // attribute set, so full-screen won't work.
 var gTestWindows = [
   "file_fullscreen-multiple.html",
   "file_fullscreen-rollback.html",
   "file_fullscreen-esc-exit.html",
--- a/dom/ipc/ContentBridgeChild.cpp
+++ b/dom/ipc/ContentBridgeChild.cpp
@@ -55,21 +55,21 @@ void
 ContentBridgeChild::DeferredDestroy()
 {
   mSelfRef = nullptr;
   // |this| was just destroyed, hands off
 }
 
 bool
 ContentBridgeChild::RecvAsyncMessage(const nsString& aMsg,
-                                     const ClonedMessageData& aData,
                                      InfallibleTArray<jsipc::CpowEntry>&& aCpows,
-                                     const IPC::Principal& aPrincipal)
+                                     const IPC::Principal& aPrincipal,
+                                     const ClonedMessageData& aData)
 {
-  return nsIContentChild::RecvAsyncMessage(aMsg, aData, Move(aCpows), aPrincipal);
+  return nsIContentChild::RecvAsyncMessage(aMsg, Move(aCpows), aPrincipal, aData);
 }
 
 PBlobChild*
 ContentBridgeChild::SendPBlobConstructor(PBlobChild* actor,
                                          const BlobConstructorParams& params)
 {
   return PContentBridgeChild::SendPBlobConstructor(actor, params);
 }
--- a/dom/ipc/ContentBridgeChild.h
+++ b/dom/ipc/ContentBridgeChild.h
@@ -23,19 +23,19 @@ public:
 
   static ContentBridgeChild*
   Create(Transport* aTransport, ProcessId aOtherProcess);
 
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
   void DeferredDestroy();
 
   virtual bool RecvAsyncMessage(const nsString& aMsg,
-                                const ClonedMessageData& aData,
                                 InfallibleTArray<jsipc::CpowEntry>&& aCpows,
-                                const IPC::Principal& aPrincipal) override;
+                                const IPC::Principal& aPrincipal,
+                                const ClonedMessageData& aData) override;
 
   virtual PBlobChild*
   SendPBlobConstructor(PBlobChild* actor,
                        const BlobConstructorParams& aParams) override;
 
   jsipc::CPOWManager* GetCPOWManager() override;
 
   virtual bool SendPBrowserConstructor(PBrowserChild* aActor,
--- a/dom/ipc/ContentBridgeParent.cpp
+++ b/dom/ipc/ContentBridgeParent.cpp
@@ -79,22 +79,22 @@ ContentBridgeParent::RecvSyncMessage(con
                                      nsTArray<StructuredCloneData>* aRetvals)
 {
   return nsIContentParent::RecvSyncMessage(aMsg, aData, Move(aCpows),
                                            aPrincipal, aRetvals);
 }
 
 bool
 ContentBridgeParent::RecvAsyncMessage(const nsString& aMsg,
-                                      const ClonedMessageData& aData,
                                       InfallibleTArray<jsipc::CpowEntry>&& aCpows,
-                                      const IPC::Principal& aPrincipal)
+                                      const IPC::Principal& aPrincipal,
+                                      const ClonedMessageData& aData)
 {
-  return nsIContentParent::RecvAsyncMessage(aMsg, aData, Move(aCpows),
-                                            aPrincipal);
+  return nsIContentParent::RecvAsyncMessage(aMsg, Move(aCpows),
+                                            aPrincipal, aData);
 }
 
 PBlobParent*
 ContentBridgeParent::SendPBlobConstructor(PBlobParent* actor,
                                           const BlobConstructorParams& params)
 {
   return PContentBridgeParent::SendPBlobConstructor(actor, params);
 }
--- a/dom/ipc/ContentBridgeParent.h
+++ b/dom/ipc/ContentBridgeParent.h
@@ -83,19 +83,19 @@ protected:
   virtual bool
   RecvSyncMessage(const nsString& aMsg,
                   const ClonedMessageData& aData,
                   InfallibleTArray<jsipc::CpowEntry>&& aCpows,
                   const IPC::Principal& aPrincipal,
                   nsTArray<StructuredCloneData>* aRetvals) override;
 
   virtual bool RecvAsyncMessage(const nsString& aMsg,
-                                const ClonedMessageData& aData,
                                 InfallibleTArray<jsipc::CpowEntry>&& aCpows,
-                                const IPC::Principal& aPrincipal) override;
+                                const IPC::Principal& aPrincipal,
+                                const ClonedMessageData& aData) override;
 
   virtual jsipc::PJavaScriptParent* AllocPJavaScriptParent() override;
 
   virtual bool
   DeallocPJavaScriptParent(jsipc::PJavaScriptParent*) override;
 
   virtual PBrowserParent*
   AllocPBrowserParent(const TabId& aTabId,
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -171,18 +171,16 @@
 #endif
 
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/cellbroadcast/CellBroadcastIPCService.h"
 #include "mozilla/dom/icc/IccChild.h"
 #include "mozilla/dom/mobileconnection/MobileConnectionChild.h"
 #include "mozilla/dom/mobilemessage/SmsChild.h"
 #include "mozilla/dom/devicestorage/DeviceStorageRequestChild.h"
-#include "mozilla/dom/PFileSystemRequestChild.h"
-#include "mozilla/dom/FileSystemTaskBase.h"
 #include "mozilla/dom/bluetooth/PBluetoothChild.h"
 #include "mozilla/dom/PFMRadioChild.h"
 #include "mozilla/dom/PPresentationChild.h"
 #include "mozilla/dom/PresentationIPCService.h"
 #include "mozilla/ipc/InputStreamUtils.h"
 
 #ifdef MOZ_WEBSPEECH
 #include "mozilla/dom/PSpeechSynthesisChild.h"
@@ -1823,34 +1821,16 @@ ContentChild::AllocPDeviceStorageRequest
 
 bool
 ContentChild::DeallocPDeviceStorageRequestChild(PDeviceStorageRequestChild* aDeviceStorage)
 {
   delete aDeviceStorage;
   return true;
 }
 
-PFileSystemRequestChild*
-ContentChild::AllocPFileSystemRequestChild(const FileSystemParams& aParams)
-{
-  MOZ_CRASH("Should never get here!");
-  return nullptr;
-}
-
-bool
-ContentChild::DeallocPFileSystemRequestChild(PFileSystemRequestChild* aFileSystem)
-{
-  mozilla::dom::FileSystemTaskBase* child =
-    static_cast<mozilla::dom::FileSystemTaskBase*>(aFileSystem);
-  // The reference is increased in FileSystemTaskBase::Start of
-  // FileSystemTaskBase.cpp. We should decrease it after IPC.
-  NS_RELEASE(child);
-  return true;
-}
-
 PMobileConnectionChild*
 ContentChild::SendPMobileConnectionConstructor(PMobileConnectionChild* aActor,
                                                const uint32_t& aClientId)
 {
 #ifdef MOZ_B2G_RIL
   // Add an extra ref for IPDL. Will be released in
   // ContentChild::DeallocPMobileConnectionChild().
   static_cast<MobileConnectionChild*>(aActor)->AddRef();
@@ -2317,17 +2297,25 @@ ContentChild::ProcessingError(Result aCo
 #endif
   NS_RUNTIMEABORT("Content child abort due to IPC error");
 }
 
 void
 ContentChild::QuickExit()
 {
   NS_WARNING("content process _exit()ing");
+
+#ifdef XP_WIN
+  // In bug 1254829, the destructor got called when dll got detached on windows,
+  // switch to TerminateProcess to bypass dll detach handler during the process
+  // termination.
+  TerminateProcess(GetCurrentProcess(), 0);
+#else
   _exit(0);
+#endif
 }
 
 nsresult
 ContentChild::AddRemoteAlertObserver(const nsString& aData,
                                      nsIObserver* aObserver)
 {
   NS_ASSERTION(aObserver, "Adding a null observer?");
   mAlertObservers.AppendElement(new AlertObserver(aObserver, aData));
@@ -2428,19 +2416,19 @@ ContentChild::RecvLoadProcessScript(cons
 {
   ProcessGlobal* global = ProcessGlobal::Get();
   global->LoadScript(aURL);
   return true;
 }
 
 bool
 ContentChild::RecvAsyncMessage(const nsString& aMsg,
-                               const ClonedMessageData& aData,
                                InfallibleTArray<CpowEntry>&& aCpows,
-                               const IPC::Principal& aPrincipal)
+                               const IPC::Principal& aPrincipal,
+                               const ClonedMessageData& aData)
 {
   RefPtr<nsFrameMessageManager> cpm =
     nsFrameMessageManager::GetChildProcessManager();
   if (cpm) {
     StructuredCloneData data;
     ipc::UnpackClonedMessageDataForChild(aData, data);
     CrossProcessCpowHolder cpows(this, aCpows);
     cpm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(cpm.get()),
@@ -3080,17 +3068,16 @@ ContentChild::GetBrowserOrId(TabChild* a
 
 bool
 ContentChild::RecvUpdateWindow(const uintptr_t& aChildId)
 {
 #if defined(XP_WIN)
   NS_ASSERTION(aChildId, "Expected child hwnd value for remote plugin instance.");
   mozilla::plugins::PluginInstanceParent* parentInstance =
   mozilla::plugins::PluginInstanceParent::LookupPluginInstanceByID(aChildId);
-  NS_ASSERTION(parentInstance, "Expected matching plugin instance");
   if (parentInstance) {
   // sync! update call to the plugin instance that forces the
   // plugin to paint its child window.
   parentInstance->CallUpdateWindow();
   }
   return true;
 #else
   MOZ_ASSERT(false, "ContentChild::RecvUpdateWindow calls unexpected on this platform.");
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -186,22 +186,16 @@ public:
   virtual bool DeallocPBrowserChild(PBrowserChild*) override;
 
   virtual PDeviceStorageRequestChild*
   AllocPDeviceStorageRequestChild(const DeviceStorageParams&) override;
 
   virtual bool
   DeallocPDeviceStorageRequestChild(PDeviceStorageRequestChild*) override;
 
-  virtual PFileSystemRequestChild*
-  AllocPFileSystemRequestChild(const FileSystemParams&) override;
-
-  virtual bool
-  DeallocPFileSystemRequestChild(PFileSystemRequestChild*) override;
-
   virtual PBlobChild*
   AllocPBlobChild(const BlobConstructorParams& aParams) override;
 
   virtual bool DeallocPBlobChild(PBlobChild* aActor) override;
 
   virtual PCrashReporterChild*
   AllocPCrashReporterChild(const mozilla::dom::NativeThreadId& id,
                            const uint32_t& processType) override;
@@ -424,19 +418,19 @@ public:
   virtual bool RecvDataStorageClear(const nsString& aFilename) override;
 
   virtual bool RecvNotifyAlertsObserver(const nsCString& aType,
                                         const nsString& aData) override;
 
   virtual bool RecvLoadProcessScript(const nsString& aURL) override;
 
   virtual bool RecvAsyncMessage(const nsString& aMsg,
-                                const ClonedMessageData& aData,
                                 InfallibleTArray<CpowEntry>&& aCpows,
-                                const IPC::Principal& aPrincipal) override;
+                                const IPC::Principal& aPrincipal,
+                                const ClonedMessageData& aData) override;
 
   virtual bool RecvGeolocationUpdate(const GeoPosition& somewhere) override;
 
   virtual bool RecvGeolocationError(const uint16_t& errorCode) override;
 
   virtual bool RecvUpdateDictionaryList(InfallibleTArray<nsString>&& aDictionaries) override;
 
   virtual bool RecvAddPermission(const IPC::Permission& permission) override;
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -42,17 +42,16 @@
 #include "mozilla/devtools/HeapSnapshotTempFileHelperParent.h"
 #include "mozilla/docshell/OfflineCacheUpdateParent.h"
 #include "mozilla/dom/DataStoreService.h"
 #include "mozilla/dom/DataTransfer.h"
 #include "mozilla/dom/DOMStorageIPC.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/ExternalHelperAppParent.h"
-#include "mozilla/dom/FileSystemRequestParent.h"
 #include "mozilla/dom/GeolocationBinding.h"
 #ifdef MOZ_EME
 #include "mozilla/dom/MediaKeySystemAccess.h"
 #endif
 #include "mozilla/dom/Notification.h"
 #include "mozilla/dom/NuwaParent.h"
 #include "mozilla/dom/PContentBridgeParent.h"
 #include "mozilla/dom/PContentPermissionRequestParent.h"
@@ -3516,34 +3515,16 @@ ContentParent::AllocPDeviceStorageReques
 bool
 ContentParent::DeallocPDeviceStorageRequestParent(PDeviceStorageRequestParent* doomed)
 {
   DeviceStorageRequestParent *parent = static_cast<DeviceStorageRequestParent*>(doomed);
   NS_RELEASE(parent);
   return true;
 }
 
-PFileSystemRequestParent*
-ContentParent::AllocPFileSystemRequestParent(const FileSystemParams& aParams)
-{
-  RefPtr<FileSystemRequestParent> result = new FileSystemRequestParent();
-  if (!result->Dispatch(this, aParams)) {
-    return nullptr;
-  }
-  return result.forget().take();
-}
-
-bool
-ContentParent::DeallocPFileSystemRequestParent(PFileSystemRequestParent* doomed)
-{
-  FileSystemRequestParent* parent = static_cast<FileSystemRequestParent*>(doomed);
-  NS_RELEASE(parent);
-  return true;
-}
-
 PBlobParent*
 ContentParent::AllocPBlobParent(const BlobConstructorParams& aParams)
 {
   return nsIContentParent::AllocPBlobParent(aParams);
 }
 
 bool
 ContentParent::DeallocPBlobParent(PBlobParent* aActor)
@@ -4261,35 +4242,16 @@ ContentParent::RecvSetURITitle(const URI
   nsCOMPtr<IHistory> history = services::GetHistoryService();
   if (history) {
     history->SetURITitle(ourURI, title);
   }
   return true;
 }
 
 bool
-ContentParent::RecvGetRandomValues(const uint32_t& length,
-                                   InfallibleTArray<uint8_t>* randomValues)
-{
-  uint8_t* buf = Crypto::GetRandomValues(length);
-  if (!buf) {
-    return true;
-  }
-
-  randomValues->SetCapacity(length);
-  randomValues->SetLength(length);
-
-  memcpy(randomValues->Elements(), buf, length);
-
-  free(buf);
-
-  return true;
-}
-
-bool
 ContentParent::RecvGetSystemMemory(const uint64_t& aGetterId)
 {
   uint32_t memoryTotal = 0;
 
 #if defined(XP_LINUX)
   memoryTotal = mozilla::hal::GetTotalSystemMemoryLevel();
 #endif
 
@@ -4443,22 +4405,22 @@ ContentParent::RecvRpcMessage(const nsSt
                               nsTArray<StructuredCloneData>* aRetvals)
 {
   return nsIContentParent::RecvRpcMessage(aMsg, aData, Move(aCpows), aPrincipal,
                                           aRetvals);
 }
 
 bool
 ContentParent::RecvAsyncMessage(const nsString& aMsg,
-                                const ClonedMessageData& aData,
                                 InfallibleTArray<CpowEntry>&& aCpows,
-                                const IPC::Principal& aPrincipal)
-{
-  return nsIContentParent::RecvAsyncMessage(aMsg, aData, Move(aCpows),
-                                            aPrincipal);
+                                const IPC::Principal& aPrincipal,
+                                const ClonedMessageData& aData)
+{
+  return nsIContentParent::RecvAsyncMessage(aMsg, Move(aCpows), aPrincipal,
+                                            aData);
 }
 
 bool
 ContentParent::RecvFilePathUpdateNotify(const nsString& aType,
                                         const nsString& aStorageName,
                                         const nsString& aFilePath,
                                         const nsCString& aReason)
 {
@@ -4692,17 +4654,17 @@ ContentParent::DoSendAsyncMessage(JSCont
   jsipc::CPOWManager* mgr = GetCPOWManager();
   if (aCpows && (!mgr || !mgr->Wrap(aCx, aCpows, &cpows))) {
     return NS_ERROR_UNEXPECTED;
   }
   if (IsReadyNuwaProcess()) {
     // Nuwa won't receive frame messages after it is frozen.
     return NS_OK;
   }
-  if (!SendAsyncMessage(nsString(aMessage), data, cpows, Principal(aPrincipal))) {
+  if (!SendAsyncMessage(nsString(aMessage), cpows, Principal(aPrincipal), data)) {
     return NS_ERROR_UNEXPECTED;
   }
   return NS_OK;
 }
 
 bool
 ContentParent::CheckPermission(const nsAString& aPermission)
 {
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -732,37 +732,28 @@ private:
   virtual bool DeallocPBrowserParent(PBrowserParent* frame) override;
 
   virtual PDeviceStorageRequestParent*
   AllocPDeviceStorageRequestParent(const DeviceStorageParams&) override;
 
   virtual bool
   DeallocPDeviceStorageRequestParent(PDeviceStorageRequestParent*) override;
 
-  virtual PFileSystemRequestParent*
-  AllocPFileSystemRequestParent(const FileSystemParams&) override;
-
-  virtual bool
-  DeallocPFileSystemRequestParent(PFileSystemRequestParent*) override;
-
   virtual PBlobParent*
   AllocPBlobParent(const BlobConstructorParams& aParams) override;
 
   virtual bool DeallocPBlobParent(PBlobParent* aActor) override;
 
   virtual bool
   RecvPBlobConstructor(PBlobParent* aActor,
                        const BlobConstructorParams& params) override;
 
   virtual bool
   DeallocPCrashReporterParent(PCrashReporterParent* crashreporter) override;
 
-  virtual bool RecvGetRandomValues(const uint32_t& length,
-                                   InfallibleTArray<uint8_t>* randomValues) override;
-
   virtual bool RecvIsSecureURI(const uint32_t& aType, const URIParams& aURI,
                                const uint32_t& aFlags, bool* aIsSecureURI) override;
 
   virtual bool RecvAccumulateMixedContentHSTS(const URIParams& aURI,
                                               const bool& aActive) override;
 
   virtual bool DeallocPHalParent(PHalParent*) override;
 
@@ -944,19 +935,19 @@ private:
 
   virtual bool RecvRpcMessage(const nsString& aMsg,
                               const ClonedMessageData& aData,
                               InfallibleTArray<CpowEntry>&& aCpows,
                               const IPC::Principal& aPrincipal,
                               nsTArray<StructuredCloneData>* aRetvals) override;
 
   virtual bool RecvAsyncMessage(const nsString& aMsg,
-                                const ClonedMessageData& aData,
                                 InfallibleTArray<CpowEntry>&& aCpows,
-                                const IPC::Principal& aPrincipal) override;
+                                const IPC::Principal& aPrincipal,
+                                const ClonedMessageData& aData) override;
 
   virtual bool RecvFilePathUpdateNotify(const nsString& aType,
                                         const nsString& aStorageName,
                                         const nsString& aFilePath,
                                         const nsCString& aReason) override;
 
   virtual bool RecvAddGeolocationListener(const IPC::Principal& aPrincipal,
                                           const bool& aHighAccuracy) override;
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -102,18 +102,18 @@ prio(normal upto urgent) sync protocol P
     manages PDocAccessible;
     manages PDocumentRenderer;
     manages PFilePicker;
     manages PIndexedDBPermissionRequest;
     manages PRenderFrame;
     manages PPluginWidget;
 
 both:
-    async AsyncMessage(nsString aMessage, ClonedMessageData aData, CpowEntry[] aCpows,
-                       Principal aPrincipal);
+    async AsyncMessage(nsString aMessage, CpowEntry[] aCpows,
+                       Principal aPrincipal, ClonedMessageData aData);
 
     /**
      * Create a layout frame (encapsulating a remote layer tree) for
      * the page that is currently loaded in the <browser>.
      */
     async PRenderFrame();
 
 parent:
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -16,17 +16,16 @@ include protocol PContentPermissionReque
 include protocol PCycleCollectWithLogs;
 include protocol PCrashReporter;
 include protocol PPSMContentDownloader;
 include protocol PExternalHelperApp;
 include protocol PHandlerService;
 include protocol PDeviceStorageRequest;
 include protocol PFileDescriptorSet;
 include protocol PFMRadio;
-include protocol PFileSystemRequest;
 include protocol PHal;
 include protocol PHeapSnapshotTempFileHelper;
 include protocol PIcc;
 include protocol PProcessHangMonitor;
 include protocol PImageBridge;
 include protocol PMedia;
 include protocol PMemoryReportRequest;
 include protocol PMobileConnection;
@@ -262,83 +261,16 @@ union FMRadioRequestParams
 {
   FMRadioRequestEnableParams;
   FMRadioRequestDisableParams;
   FMRadioRequestSetFrequencyParams;
   FMRadioRequestSeekParams;
   FMRadioRequestCancelSeekParams;
 };
 
-struct FileSystemCreateDirectoryParams
-{
-  nsString filesystem;
-  nsString realPath;
-};
-
-union FileSystemFileDataValue
-{
-  uint8_t[];
-  PBlob;
-};
-
-struct FileSystemCreateFileParams
-{
-  nsString filesystem;
-  nsString realPath;
-  FileSystemFileDataValue data;
-  bool replace;
-};
-
-struct FileSystemGetDirectoryListingParams
-{
-  nsString filesystem;
-  nsString realPath;
-  bool isRoot;
-  // 'filters' could be an array rather than a semicolon separated string
-  // (we'd then use InfallibleTArray<nsString> internally), but that is
-  // wasteful.  E10s requires us to pass the filters over as a string anyway,
-  // so avoiding using an array avoids serialization on the side passing the
-  // filters.  Since an nsString can share its buffer when copied,
-  // using that instead of InfallibleTArray<nsString> makes copying the filters
-  // around in any given process a bit more efficient too, since copying a
-  // single nsString is cheaper than copying InfallibleTArray member data and
-  // each nsString that it contains.
-  nsString filters;
-};
-
-struct FileSystemGetFileOrDirectoryParams
-{
-  nsString filesystem;
-  nsString realPath;
-  bool isRoot;
-};
-
-union FileSystemPathOrFileValue
-{
-  nsString;
-  PBlob;
-};
-
-struct FileSystemRemoveParams
-{
-  nsString filesystem;
-  nsString directory;
-  FileSystemPathOrFileValue target;
-  bool recursive;
-};
-
-union FileSystemParams
-{
-  FileSystemCreateDirectoryParams;
-  FileSystemCreateFileParams;
-  FileSystemGetDirectoryListingParams;
-  FileSystemGetFileOrDirectoryParams;
-  FileSystemRemoveParams;
-};
-
 union PrefValue {
   nsCString;
   int32_t;
   bool;
 };
 
 union MaybePrefValue {
   PrefValue;
@@ -475,17 +407,16 @@ prio(normal upto urgent) sync protocol P
     manages PBlob;
     manages PBluetooth;
     manages PBrowser;
     manages PCellBroadcast;
     manages PContentPermissionRequest;
     manages PCrashReporter;
     manages PCycleCollectWithLogs;
     manages PDeviceStorageRequest;
-    manages PFileSystemRequest;
     manages PPSMContentDownloader;
     manages PExternalHelperApp;
     manages PFileDescriptorSet;
     manages PFMRadio;
     manages PHal;
     manages PHandlerService;
     manages PHeapSnapshotTempFileHelper;
     manages PIcc;
@@ -826,23 +757,18 @@ parent:
      */
     sync FindPlugins(uint32_t pluginEpoch) returns (nsresult aResult, PluginTag[] plugins, uint32_t newPluginEpoch);
 
     async PJavaScript();
 
     async PRemoteSpellcheckEngine();
     async PDeviceStorageRequest(DeviceStorageParams params);
 
-    async PFileSystemRequest(FileSystemParams params);
-
     sync PCrashReporter(NativeThreadId tid, uint32_t processType);
 
-    prio(urgent) sync GetRandomValues(uint32_t length)
-        returns (uint8_t[] randomValues);
-
     async GetSystemMemory(uint64_t getterId);
 
     sync IsSecureURI(uint32_t type, URIParams uri, uint32_t flags)
         returns (bool isSecureURI);
 
     async AccumulateMixedContentHSTS(URIParams uri, bool active);
 
     sync GetLookAndFeelCache()
@@ -1214,14 +1140,14 @@ parent:
 
     /**
      * Tell the parent that a decoder's' benchmark has been completed.
      * The result can then be stored in permanent storage.
      */
     async NotifyBenchmarkResult(nsString aCodecName, uint32_t aDecodeFPS);
 
 both:
-     async AsyncMessage(nsString aMessage, ClonedMessageData aData,
-                        CpowEntry[] aCpows, Principal aPrincipal);
+     async AsyncMessage(nsString aMessage, CpowEntry[] aCpows,
+                        Principal aPrincipal, ClonedMessageData aData);
 };
 
 }
 }
--- a/dom/ipc/PContentBridge.ipdl
+++ b/dom/ipc/PContentBridge.ipdl
@@ -46,14 +46,14 @@ parent:
 both:
     // Both the parent and the child can construct the PBrowser.
     // See the comment in PContent::PBrowser().
     async PBrowser(TabId tabId, IPCTabContext context, uint32_t chromeFlags,
                    ContentParentId cpId, bool isForApp, bool isForBrowser);
 
     async PBlob(BlobConstructorParams params);
 
-    async AsyncMessage(nsString aMessage, ClonedMessageData aData,
-                       CpowEntry[] aCpows, Principal aPrincipal);
+    async AsyncMessage(nsString aMessage, CpowEntry[] aCpows,
+                       Principal aPrincipal, ClonedMessageData aData);
 };
 
 }
 }
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -2250,19 +2250,19 @@ TabChild::RecvLoadRemoteScript(const nsS
     return true;
 
   LoadScriptInternal(aURL, aRunInGlobalScope);
   return true;
 }
 
 bool
 TabChild::RecvAsyncMessage(const nsString& aMessage,
-                           const ClonedMessageData& aData,
                            InfallibleTArray<CpowEntry>&& aCpows,
-                           const IPC::Principal& aPrincipal)
+                           const IPC::Principal& aPrincipal,
+                           const ClonedMessageData& aData)
 {
   if (mTabChildGlobal) {
     nsCOMPtr<nsIXPConnectJSObjectHolder> kungFuDeathGrip(GetGlobal());
     StructuredCloneData data;
     UnpackClonedMessageDataForChild(aData, data);
     RefPtr<nsFrameMessageManager> mm =
       static_cast<nsFrameMessageManager*>(mTabChildGlobal->mMessageManager.get());
     CrossProcessCpowHolder cpows(Manager(), aCpows);
@@ -2789,18 +2789,18 @@ TabChild::DoSendAsyncMessage(JSContext* 
   ClonedMessageData data;
   if (!BuildClonedMessageDataForChild(Manager(), aData, data)) {
     return NS_ERROR_DOM_DATA_CLONE_ERR;
   }
   InfallibleTArray<CpowEntry> cpows;
   if (aCpows && !Manager()->GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) {
     return NS_ERROR_UNEXPECTED;
   }
-  if (!SendAsyncMessage(PromiseFlatString(aMessage), data, cpows,
-                        Principal(aPrincipal))) {
+  if (!SendAsyncMessage(PromiseFlatString(aMessage), cpows,
+                        Principal(aPrincipal), data)) {
     return NS_ERROR_UNEXPECTED;
   }
   return NS_OK;
 }
 
 TabChild*
 TabChild::GetFrom(nsIPresShell* aPresShell)
 {
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -405,19 +405,19 @@ public:
 
   virtual bool
   RecvActivateFrameEvent(const nsString& aType, const bool& aCapture) override;
 
   virtual bool RecvLoadRemoteScript(const nsString& aURL,
                                     const bool& aRunInGlobalScope) override;
 
   virtual bool RecvAsyncMessage(const nsString& aMessage,
-                                const ClonedMessageData& aData,
                                 InfallibleTArray<CpowEntry>&& aCpows,
-                                const IPC::Principal& aPrincipal) override;
+                                const IPC::Principal& aPrincipal,
+                                const ClonedMessageData& aData) override;
 
   virtual bool RecvAppOfflineStatus(const uint32_t& aId,
                                     const bool& aOffline) override;
 
   virtual bool RecvSwappedWithOtherRemoteLoader() override;
 
   virtual PDocAccessibleChild*
   AllocPDocAccessibleChild(PDocAccessibleChild*, const uint64_t&) override;
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -1735,19 +1735,19 @@ TabParent::RecvRpcMessage(const nsString
   ipc::UnpackClonedMessageDataForParent(aData, data);
 
   CrossProcessCpowHolder cpows(Manager(), aCpows);
   return ReceiveMessage(aMessage, true, &data, &cpows, aPrincipal, aRetVal);
 }
 
 bool
 TabParent::RecvAsyncMessage(const nsString& aMessage,
-                            const ClonedMessageData& aData,
                             InfallibleTArray<CpowEntry>&& aCpows,
-                            const IPC::Principal& aPrincipal)
+                            const IPC::Principal& aPrincipal,
+                            const ClonedMessageData& aData)
 {
   // FIXME Permission check for TabParent in Content process
   nsIPrincipal* principal = aPrincipal;
   if (Manager()->IsContentParent()) {
     ContentParent* parent = Manager()->AsContentParent();
     if (!ContentParent::IgnoreIPCPrincipal() &&
         parent && principal && !AssertAppPrincipal(parent, principal)) {
       return false;
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -185,19 +185,19 @@ public:
   virtual bool
   RecvRpcMessage(const nsString& aMessage,
                  const ClonedMessageData& aData,
                  InfallibleTArray<CpowEntry>&& aCpows,
                  const IPC::Principal& aPrincipal,
                  nsTArray<ipc::StructuredCloneData>* aRetVal) override;
 
   virtual bool RecvAsyncMessage(const nsString& aMessage,
-                                const ClonedMessageData& aData,
                                 InfallibleTArray<CpowEntry>&& aCpows,
-                                const IPC::Principal& aPrincipal) override;
+                                const IPC::Principal& aPrincipal,
+                                const ClonedMessageData& aData) override;
 
   virtual bool
   RecvNotifyIMEFocus(const ContentCache& aContentCache,
                      const widget::IMENotification& aEventMessage,
                      nsIMEUpdatePreference* aPreference) override;
 
   virtual bool
   RecvNotifyIMETextChange(const ContentCache& aContentCache,
--- a/dom/ipc/nsIContentChild.cpp
+++ b/dom/ipc/nsIContentChild.cpp
@@ -106,19 +106,19 @@ nsIContentChild::GetOrCreateActorForBlob
   BlobChild* actor = BlobChild::GetOrCreate(this, aImpl);
   NS_ENSURE_TRUE(actor, nullptr);
 
   return actor;
 }
 
 bool
 nsIContentChild::RecvAsyncMessage(const nsString& aMsg,
-                                  const ClonedMessageData& aData,
                                   InfallibleTArray<CpowEntry>&& aCpows,
-                                  const IPC::Principal& aPrincipal)
+                                  const IPC::Principal& aPrincipal,
+                                  const ClonedMessageData& aData)
 {
   RefPtr<nsFrameMessageManager> cpm = nsFrameMessageManager::GetChildProcessManager();
   if (cpm) {
     ipc::StructuredCloneData data;
     ipc::UnpackClonedMessageDataForChild(aData, data);
 
     CrossProcessCpowHolder cpows(this, aCpows);
     cpm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(cpm.get()), nullptr,
--- a/dom/ipc/nsIContentChild.h
+++ b/dom/ipc/nsIContentChild.h
@@ -75,19 +75,19 @@ protected:
                                             const bool& aIsForBrowser);
   virtual bool DeallocPBrowserChild(PBrowserChild*);
 
   virtual PBlobChild* AllocPBlobChild(const BlobConstructorParams& aParams);
 
   virtual bool DeallocPBlobChild(PBlobChild* aActor);
 
   virtual bool RecvAsyncMessage(const nsString& aMsg,
-                                const ClonedMessageData& aData,
                                 InfallibleTArray<jsipc::CpowEntry>&& aCpows,
-                                const IPC::Principal& aPrincipal);
+                                const IPC::Principal& aPrincipal,
+                                const ClonedMessageData& aData);
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIContentChild, NS_ICONTENTCHILD_IID)
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_nsIContentChild_h */
--- a/dom/ipc/nsIContentParent.cpp
+++ b/dom/ipc/nsIContentParent.cpp
@@ -264,19 +264,19 @@ nsIContentParent::RecvRpcMessage(const n
     ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()), nullptr,
                         aMsg, true, &data, &cpows, aPrincipal, aRetvals);
   }
   return true;
 }
 
 bool
 nsIContentParent::RecvAsyncMessage(const nsString& aMsg,
-                                   const ClonedMessageData& aData,
                                    InfallibleTArray<CpowEntry>&& aCpows,
-                                   const IPC::Principal& aPrincipal)
+                                   const IPC::Principal& aPrincipal,
+                                   const ClonedMessageData& aData)
 {
   // FIXME Permission check in Content process
   nsIPrincipal* principal = aPrincipal;
   if (IsContentParent()) {
     ContentParent* parent = AsContentParent();
     if (!ContentParent::IgnoreIPCPrincipal() &&
         parent && principal && !AssertAppPrincipal(parent, principal)) {
       return false;
--- a/dom/ipc/nsIContentParent.h
+++ b/dom/ipc/nsIContentParent.h
@@ -105,19 +105,19 @@ protected: // IPDL methods
                                const IPC::Principal& aPrincipal,
                                nsTArray<ipc::StructuredCloneData>* aRetvals);
   virtual bool RecvRpcMessage(const nsString& aMsg,
                               const ClonedMessageData& aData,
                               InfallibleTArray<jsipc::CpowEntry>&& aCpows,
                               const IPC::Principal& aPrincipal,
                               nsTArray<ipc::StructuredCloneData>* aRetvals);
   virtual bool RecvAsyncMessage(const nsString& aMsg,
-                                const ClonedMessageData& aData,
                                 InfallibleTArray<jsipc::CpowEntry>&& aCpows,
-                                const IPC::Principal& aPrincipal);
+                                const IPC::Principal& aPrincipal,
+                                const ClonedMessageData& aData);
 
 protected: // members
   RefPtr<nsFrameMessageManager> mMessageManager;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIContentParent, NS_ICONTENTPARENT_IID)
 
 } // namespace dom
--- a/dom/media/MediaStreamGraph.cpp
+++ b/dom/media/MediaStreamGraph.cpp
@@ -415,19 +415,16 @@ namespace {
   const uint32_t NOT_VISITED = UINT32_MAX;
   // Value of mCycleMarker for ordered streams in muted cycles.
   const uint32_t IN_MUTED_CYCLE = 1;
 } // namespace
 
 bool
 MediaStreamGraphImpl::AudioTrackPresent(bool& aNeedsAEC)
 {
-#ifdef MOZ_WEBRTC
-  bool shouldAEC = false;
-#endif
   bool audioTrackPresent = false;
   for (uint32_t i = 0; i < mStreams.Length() && audioTrackPresent == false; ++i) {
     MediaStream* stream = mStreams[i];
     SourceMediaStream* source = stream->AsSourceStream();
 #ifdef MOZ_WEBRTC
     if (source && source->NeedsMixing()) {
       aNeedsAEC = true;
     }
@@ -453,23 +450,20 @@ MediaStreamGraphImpl::AudioTrackPresent(
 
   // XXX For some reason, there are race conditions when starting an audio input where
   // we find no active audio tracks.  In any case, if we have an active audio input we
   // should not allow a switch back to a SystemClockDriver
   if (!audioTrackPresent && mInputDeviceUsers.Count() != 0) {
     NS_WARNING("No audio tracks, but full-duplex audio is enabled!!!!!");
     audioTrackPresent = true;
 #ifdef MOZ_WEBRTC
-    shouldAEC = true;
+    aNeedsAEC = true;
 #endif
   }
 
-#ifdef MOZ_WEBRTC
-  aNeedsAEC = shouldAEC;
-#endif
   return audioTrackPresent;
 }
 
 void
 MediaStreamGraphImpl::UpdateStreamOrder()
 {
   bool shouldAEC = false;
   bool audioTrackPresent = AudioTrackPresent(shouldAEC);
--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -272,54 +272,44 @@ function RTCStatsReport(win, dict) {
   this._report = convertToRTCStatsReport(dict);
 }
 RTCStatsReport.prototype = {
   classDescription: "RTCStatsReport",
   classID: PC_STATS_CID,
   contractID: PC_STATS_CONTRACT,
   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
 
-  // TODO: Change to use webidl getters once available (Bug 952122)
+  setInternal: function(aKey, aObj) {
+    return this.__DOM_IMPL__.__set(aKey, aObj);
+  },
+
+  // TODO: Remove legacy API eventually
   //
-  // Since webidl getters are not available, we make the stats available as
+  // Since maplike is recent, we still also make the stats available as legacy
   // enumerable read-only properties directly on our content-facing object.
   // Must be called after our webidl sandwich is made.
 
-  makeStatsPublic: function() {
-    let props = {};
-    this.forEach(function(stat) {
-        props[stat.id] = { enumerable: true, configurable: false,
-                           writable: false, value: stat };
-      });
-    Object.defineProperties(this.__DOM_IMPL__.wrappedJSObject, props);
-  },
-
-  forEach: function(cb, thisArg) {
-    for (var key in this._report) {
-      cb.call(thisArg || this._report, this.get(key), key, this._report);
-    }
-  },
+  makeStatsPublic: function(warnNullable) {
+    let legacyProps = {};
+    for (let key in this._report) {
+      let value = Cu.cloneInto(this._report[key], this._win);
+      this.setInternal(key, value);
 
-  get: function(key) {
-    function publifyReadonly(win, obj) {
-      let props = {};
-      for (let k in obj) {
-        props[k] = {enumerable:true, configurable:false, writable:false, value:obj[k]};
-      }
-      let pubobj = Cu.createObjectIn(win);
-      Object.defineProperties(pubobj, props);
-      return pubobj;
+      legacyProps[key] = {
+        enumerable: true, configurable: false,
+        get: Cu.exportFunction(function() {
+          if (warnNullable.warn) {
+            warnNullable.warn();
+            warnNullable.warn = null;
+          }
+          return value;
+        }, this.__DOM_IMPL__.wrappedJSObject)
+      };
     }
-
-    // Return a content object rather than a wrapped chrome one.
-    return publifyReadonly(this._win, this._report[key]);
-  },
-
-  has: function(key) {
-    return this._report[key] !== undefined;
+    Object.defineProperties(this.__DOM_IMPL__.wrappedJSObject, legacyProps);
   },
 
   get mozPcid() { return this._pcid; }
 };
 
 function RTCPeerConnection() {
   this._senders = [];
   this._receivers = [];
@@ -429,16 +419,21 @@ RTCPeerConnection.prototype = {
 
     this.__DOM_IMPL__._innerObject = this;
     this._observer = new this._win.PeerConnectionObserver(this.__DOM_IMPL__);
 
     var location = "" + this._win.location;
     this._isLoop = location.startsWith("about:loop") ||
                    location.startsWith("https://hello.firefox.com/");
 
+    // Warn just once per PeerConnection about deprecated getStats usage.
+    this._warnDeprecatedStatsAccessNullable = { warn: () =>
+      this.logWarning("non-maplike pc.getStats access is deprecated! " +
+                      "See http://w3c.github.io/webrtc-pc/#example for usage.") };
+
     // Add a reference to the PeerConnection to global list (before init).
     _globalPCList.addPC(this);
 
     this._impl.initialize(this._observer, this._win, rtcConfig,
                           Services.tm.currentThread);
     this._initCertificate(rtcConfig.certificates);
     this._initIdp();
     _globalPCList.notifyLifecycleObservers(this, "initialized");
@@ -1504,17 +1499,17 @@ PeerConnectionObserver.prototype = {
         break;
     }
   },
 
   onGetStatsSuccess: function(dict) {
     let pc = this._dompc;
     let chromeobj = new RTCStatsReport(pc._win, dict);
     let webidlobj = pc._win.RTCStatsReport._create(pc._win, chromeobj);
-    chromeobj.makeStatsPublic();
+    chromeobj.makeStatsPublic(pc._warnDeprecatedStatsAccessNullable);
     pc._onGetStatsSuccess(webidlobj);
   },
 
   onGetStatsError: function(code, message) {
     this._dompc._onGetStatsFailure(this.newError(message, code));
   },
 
   onAddStream: function(stream) {
--- a/dom/media/gmp/GMPLoader.h
+++ b/dom/media/gmp/GMPLoader.h
@@ -27,28 +27,25 @@ public:
   // objects are created.
   virtual void SetSandboxInfo(MacSandboxInfo* aSandboxInfo) = 0;
 #endif
 };
 
 // Encapsulates generating the device-bound node id, activating the sandbox,
 // loading the GMP, and passing the node id to the GMP (in that order).
 //
-// In Desktop Gecko, the implementation of this lives in the content process
-// host binary, and is passed into XUL code from on startup. The GMP IPC child
-// protocol actor uses this interface to load and retrieve interfaces from the
-// GMPs.
+// In Desktop Gecko, the implementation of this lives in plugin-container,
+// and is passed into XUL code from on startup. The GMP IPC child protocol actor
+// uses this interface to load and retrieve interfaces from the GMPs.
 //
-// In Desktop Gecko the implementation lives in the firefox-plugin-container
-// (Windows) or firefox-webcontent (other platforms) so that it can be covered
-// by DRM vendor's voucher. The reason for the extra executable on Windows is
-// because we can't programmatically change the executable name at runtime.
+// In Desktop Gecko the implementation lives in the plugin-container so that
+// it can be covered by DRM vendor's voucher.
 //
 // On Android the GMPLoader implementation lives in libxul (because for the time
-// being GMPLoader relies upon NSPR, which we can't use in firefox-webcontent
+// being GMPLoader relies upon NSPR, which we can't use in plugin-container
 // on Android).
 //
 // There is exactly one GMPLoader per GMP child process, and only one GMP
 // per child process (so the GMPLoader only loads one GMP).
 class GMPLoader {
 public:
   virtual ~GMPLoader() {}
 
@@ -71,18 +68,16 @@ public:
 #if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
   // On OS X we need to set Mac-specific sandbox info just before we start the
   // sandbox, which we don't yet know when the GMPLoader and SandboxStarter
   // objects are created.
   virtual void SetSandboxInfo(MacSandboxInfo* aSandboxInfo) = 0;
 #endif
 };
 
-// On Desktop, this function resides in firefox-plugin-container on Windows, firefox-webcontent
-// for all other platforms.
-//
+// On Desktop, this function resides in plugin-container.
 // On Mobile, this function resides in XUL.
 GMPLoader* CreateGMPLoader(SandboxStarter* aStarter);
 
 } // namespace gmp
 } // namespace mozilla
 
 #endif // GMP_LOADER_H__
--- a/dom/media/tests/mochitest/mochitest.ini
+++ b/dom/media/tests/mochitest/mochitest.ini
@@ -147,16 +147,28 @@ skip-if = toolkit == 'gonk' # B2G emulat
 skip-if = toolkit == 'gonk' # B2G emulator is too slow to handle a two-way audio call reliably
 [test_peerConnection_offerRequiresReceiveAudio.html]
 [test_peerConnection_offerRequiresReceiveVideo.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_offerRequiresReceiveVideoAudio.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' || (android_version == '18' && debug) # b2g(Bug 960442, video support for WebRTC is disabled on b2g), android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_promiseSendOnly.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' || (android_version == '18' && debug) # b2g(Bug 960442, video support for WebRTC is disabled on b2g), android(Bug 1189784, timeouts on 4.3 emulator)
+[test_peerConnection_restartIce.html]
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' || android_version # b2g (Bug 1059867)
+[test_peerConnection_restartIceNoBundle.html]
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' || android_version # b2g (Bug 1059867)
+[test_peerConnection_restartIceNoBundleNoRtcpMux.html]
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' || android_version # b2g (Bug 1059867)
+[test_peerConnection_restartIceNoRtcpMux.html]
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' || android_version # b2g (Bug 1059867)
+[test_peerConnection_restartIceLocalRollback.html]
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' || android_version # b2g (Bug 1059867)
+[test_peerConnection_restartIceLocalAndRemoteRollback.html]
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' || android_version # b2g (Bug 1059867)
 [test_peerConnection_scaleResolution.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' || (android_version == '18') # b2g(Bug 960442, video support for WebRTC is disabled on b2g), android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_simulcastOffer.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' || android_version # b2g(Bug 960442, video support for WebRTC is disabled on b2g), no simulcast support on android
 #[test_peerConnection_relayOnly.html]
 #skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_callbacks.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' || (android_version == '18' && debug) # b2g(Bug 960442, video support for WebRTC is disabled on b2g), android(Bug 1189784, timeouts on 4.3 emulator)
--- a/dom/media/tests/mochitest/pc.js
+++ b/dom/media/tests/mochitest/pc.js
@@ -724,16 +724,17 @@ function PeerConnectionWrapper(label, co
 
   this.expectedLocalTrackInfoById = {};
   this.expectedRemoteTrackInfoById = {};
   this.observedRemoteTrackInfoById = {};
 
   this.disableRtpCountChecking = false;
 
   this.iceCheckingRestartExpected = false;
+  this.iceCheckingIceRollbackExpected = false;
 
   info("Creating " + this);
   this._pc = new RTCPeerConnection(this.configuration);
 
   /**
    * Setup callback handlers
    */
   // This allows test to register their own callbacks for ICE connection state changes
@@ -1209,16 +1210,21 @@ PeerConnectionWrapper.prototype = {
       var newstate = this._pc.iceConnectionState;
       var oldstate = this.iceConnectionLog[this.iceConnectionLog.length - 1]
       if (Object.keys(iceStateTransitions).indexOf(oldstate) != -1) {
         if (this.iceCheckingRestartExpected) {
           is(newstate, "checking",
              "iceconnectionstate event \'" + newstate +
              "\' matches expected state \'checking\'");
           this.iceCheckingRestartExpected = false;
+        } else if (this.iceCheckingIceRollbackExpected) {
+          is(newstate, "connected",
+             "iceconnectionstate event \'" + newstate +
+             "\' matches expected state \'connected\'");
+          this.iceCheckingIceRollbackExpected = false;
         } else {
           ok(iceStateTransitions[oldstate].indexOf(newstate) != -1, this + ": legal ICE state transition from " + oldstate + " to " + newstate);
         }
       } else {
         ok(false, this + ": old ICE state " + oldstate + " missing in ICE transition array");
       }
       this.iceConnectionLog.push(newstate);
     };
@@ -1385,23 +1391,22 @@ PeerConnectionWrapper.prototype = {
    *
    * @param {object} track
    *        A MediaStreamTrack to wait for data flow on.
    * @returns {Promise}
    *        A promise that resolves when media is flowing.
    */
   waitForRtpFlow(track) {
     var hasFlow = stats => {
-      var rtpStatsKey = Object.keys(stats)
-        .find(key => !stats[key].isRemote && stats[key].type.endsWith("boundrtp"));
-      ok(rtpStatsKey, "Should have RTP stats for track " + track.id);
-      if (!rtpStatsKey) {
+      var rtp = stats.get([...stats.keys()].find(key =>
+        !stats.get(key).isRemote && stats.get(key).type.endsWith("boundrtp")));
+      ok(rtp, "Should have RTP stats for track " + track.id);
+      if (!rtp) {
         return false;
       }
-      var rtp = stats[rtpStatsKey];
       var nrPackets = rtp[rtp.type == "outboundrtp" ? "packetsSent"
                                                     : "packetsReceived"];
       info("Track " + track.id + " has " + nrPackets + " " +
            rtp.type + " RTP packets.");
       return nrPackets > 0;
     };
 
     info("Checking RTP packet flow for track " + track.id);
@@ -1491,123 +1496,123 @@ PeerConnectionWrapper.prototype = {
 
   /**
    * Checks that we are getting the media streams we expect.
    *
    * @param {object} stats
    *        The stats to check from this PeerConnectionWrapper
    */
   checkStats : function(stats, twoMachines) {
-    var toNum = obj => obj? obj : 0;
-
     const isWinXP = navigator.userAgent.indexOf("Windows NT 5.1") != -1;
 
     // Use spec way of enumerating stats
     var counters = {};
-    for (var key in stats) {
-      if (stats.hasOwnProperty(key)) {
-        var res = stats[key];
-        // validate stats
-        ok(res.id == key, "Coherent stats id");
-        var nowish = Date.now() + 1000;        // TODO: clock drift observed
-        var minimum = this.whenCreated - 1000; // on Windows XP (Bug 979649)
-        if (isWinXP) {
-          todo(false, "Can't reliably test rtcp timestamps on WinXP (Bug 979649)");
-        } else if (!twoMachines) {
-          // Bug 1225729: On android, sometimes the first RTCP of the first
-          // test run gets this value, likely because no RTP has been sent yet.
-          if (res.timestamp != 2085978496000) {
-            ok(res.timestamp >= minimum,
-               "Valid " + (res.isRemote? "rtcp" : "rtp") + " timestamp " +
-                   res.timestamp + " >= " + minimum + " (" +
-                   (res.timestamp - minimum) + " ms)");
-            ok(res.timestamp <= nowish,
-               "Valid " + (res.isRemote? "rtcp" : "rtp") + " timestamp " +
-                   res.timestamp + " <= " + nowish + " (" +
-                   (res.timestamp - nowish) + " ms)");
+    for (let [key, res] of stats) {
+      // validate stats
+      ok(res.id == key, "Coherent stats id");
+      var nowish = Date.now() + 1000;        // TODO: clock drift observed
+      var minimum = this.whenCreated - 1000; // on Windows XP (Bug 979649)
+      if (isWinXP) {
+        todo(false, "Can't reliably test rtcp timestamps on WinXP (Bug 979649)");
+      } else if (!twoMachines) {
+        // Bug 1225729: On android, sometimes the first RTCP of the first
+        // test run gets this value, likely because no RTP has been sent yet.
+        if (res.timestamp != 2085978496000) {
+          ok(res.timestamp >= minimum,
+             "Valid " + (res.isRemote? "rtcp" : "rtp") + " timestamp " +
+                 res.timestamp + " >= " + minimum + " (" +
+                 (res.timestamp - minimum) + " ms)");
+          ok(res.timestamp <= nowish,
+             "Valid " + (res.isRemote? "rtcp" : "rtp") + " timestamp " +
+                 res.timestamp + " <= " + nowish + " (" +
+                 (res.timestamp - nowish) + " ms)");
+        } else {
+          info("Bug 1225729: Uninitialized timestamp (" + res.timestamp +
+                "), should be >=" + minimum + " and <= " + nowish);
+        }
+      }
+      if (res.isRemote) {
+        continue;
+      }
+      counters[res.type] = (counters[res.type] || 0) + 1;
+
+      switch (res.type) {
+        case "inboundrtp":
+        case "outboundrtp": {
+          // ssrc is a 32 bit number returned as a string by spec
+          ok(res.ssrc.length > 0, "Ssrc has length");
+          ok(res.ssrc.length < 11, "Ssrc not lengthy");
+          ok(!/[^0-9]/.test(res.ssrc), "Ssrc numeric");
+          ok(parseInt(res.ssrc) < Math.pow(2,32), "Ssrc within limits");
+
+          if (res.type == "outboundrtp") {
+            ok(res.packetsSent !== undefined, "Rtp packetsSent");
+            // We assume minimum payload to be 1 byte (guess from RFC 3550)
+            ok(res.bytesSent >= res.packetsSent, "Rtp bytesSent");
           } else {
-            info("Bug 1225729: Uninitialized timestamp (" + res.timestamp +
-                 "), should be >=" + minimum + " and <= " + nowish);
+            ok(res.packetsReceived !== undefined, "Rtp packetsReceived");
+            ok(res.bytesReceived >= res.packetsReceived, "Rtp bytesReceived");
+          }
+          if (res.remoteId) {
+            var rem = stats[res.remoteId];
+            ok(rem.isRemote, "Remote is rtcp");
+            ok(rem.remoteId == res.id, "Remote backlink match");
+            if(res.type == "outboundrtp") {
+              ok(rem.type == "inboundrtp", "Rtcp is inbound");
+              ok(rem.packetsReceived !== undefined, "Rtcp packetsReceived");
+              ok(rem.packetsLost !== undefined, "Rtcp packetsLost");
+              ok(rem.bytesReceived >= rem.packetsReceived, "Rtcp bytesReceived");
+              if (!this.disableRtpCountChecking) {
+                ok(rem.packetsReceived <= res.packetsSent, "No more than sent packets");
+                ok(rem.bytesReceived <= res.bytesSent, "No more than sent bytes");
+              }
+              ok(rem.jitter !== undefined, "Rtcp jitter");
+              ok(rem.mozRtt !== undefined, "Rtcp rtt");
+              ok(rem.mozRtt >= 0, "Rtcp rtt " + rem.mozRtt + " >= 0");
+              ok(rem.mozRtt < 60000, "Rtcp rtt " + rem.mozRtt + " < 1 min");
+            } else {
+              ok(rem.type == "outboundrtp", "Rtcp is outbound");
+              ok(rem.packetsSent !== undefined, "Rtcp packetsSent");
+              // We may have received more than outdated Rtcp packetsSent
+              ok(rem.bytesSent >= rem.packetsSent, "Rtcp bytesSent");
+            }
+            ok(rem.ssrc == res.ssrc, "Remote ssrc match");
+          } else {
+            info("No rtcp info received yet");
           }
         }
-        if (!res.isRemote) {
-          counters[res.type] = toNum(counters[res.type]) + 1;
-
-          switch (res.type) {
-            case "inboundrtp":
-            case "outboundrtp": {
-              // ssrc is a 32 bit number returned as a string by spec
-              ok(res.ssrc.length > 0, "Ssrc has length");
-              ok(res.ssrc.length < 11, "Ssrc not lengthy");
-              ok(!/[^0-9]/.test(res.ssrc), "Ssrc numeric");
-              ok(parseInt(res.ssrc) < Math.pow(2,32), "Ssrc within limits");
-
-              if (res.type == "outboundrtp") {
-                ok(res.packetsSent !== undefined, "Rtp packetsSent");
-                // We assume minimum payload to be 1 byte (guess from RFC 3550)
-                ok(res.bytesSent >= res.packetsSent, "Rtp bytesSent");
-              } else {
-                ok(res.packetsReceived !== undefined, "Rtp packetsReceived");
-                ok(res.bytesReceived >= res.packetsReceived, "Rtp bytesReceived");
-              }
-              if (res.remoteId) {
-                var rem = stats[res.remoteId];
-                ok(rem.isRemote, "Remote is rtcp");
-                ok(rem.remoteId == res.id, "Remote backlink match");
-                if(res.type == "outboundrtp") {
-                  ok(rem.type == "inboundrtp", "Rtcp is inbound");
-                  ok(rem.packetsReceived !== undefined, "Rtcp packetsReceived");
-                  ok(rem.packetsLost !== undefined, "Rtcp packetsLost");
-                  ok(rem.bytesReceived >= rem.packetsReceived, "Rtcp bytesReceived");
-                  if (!this.disableRtpCountChecking) {
-                    ok(rem.packetsReceived <= res.packetsSent, "No more than sent packets");
-                    ok(rem.bytesReceived <= res.bytesSent, "No more than sent bytes");
-                  }
-                  ok(rem.jitter !== undefined, "Rtcp jitter");
-                  ok(rem.mozRtt !== undefined, "Rtcp rtt");
-                  ok(rem.mozRtt >= 0, "Rtcp rtt " + rem.mozRtt + " >= 0");
-                  ok(rem.mozRtt < 60000, "Rtcp rtt " + rem.mozRtt + " < 1 min");
-                } else {
-                  ok(rem.type == "outboundrtp", "Rtcp is outbound");
-                  ok(rem.packetsSent !== undefined, "Rtcp packetsSent");
-                  // We may have received more than outdated Rtcp packetsSent
-                  ok(rem.bytesSent >= rem.packetsSent, "Rtcp bytesSent");
-                }
-                ok(rem.ssrc == res.ssrc, "Remote ssrc match");
-              } else {
-                info("No rtcp info received yet");
-              }
-            }
-            break;
-          }
-        }
+        break;
       }
     }
 
-    // Use MapClass way of enumerating stats
+    // Use legacy way of enumerating stats
     var counters2 = {};
-    stats.forEach(res => {
-        if (!res.isRemote) {
-          counters2[res.type] = toNum(counters2[res.type]) + 1;
-        }
-      });
+    for (let key in stats) {
+      if (!stats.hasOwnProperty(key)) {
+        continue;
+      }
+      var res = stats[key];
+      if (!res.isRemote) {
+        counters2[res.type] = (counters2[res.type] || 0) + 1;
+      }
+    }
     is(JSON.stringify(counters), JSON.stringify(counters2),
-       "Spec and MapClass variant of RTCStatsReport enumeration agree");
+       "Spec and legacy variant of RTCStatsReport enumeration agree");
     var nin = Object.keys(this.expectedRemoteTrackInfoById).length;
     var nout = Object.keys(this.expectedLocalTrackInfoById).length;
     var ndata = this.dataChannels.length;
 
     // TODO(Bug 957145): Restore stronger inboundrtp test once Bug 948249 is fixed
-    //is(toNum(counters["inboundrtp"]), nin, "Have " + nin + " inboundrtp stat(s)");
-    ok(toNum(counters.inboundrtp) >= nin, "Have at least " + nin + " inboundrtp stat(s) *");
+    //is((counters["inboundrtp"] || 0), nin, "Have " + nin + " inboundrtp stat(s)");
+    ok((counters.inboundrtp || 0) >= nin, "Have at least " + nin + " inboundrtp stat(s) *");
 
-    is(toNum(counters.outboundrtp), nout, "Have " + nout + " outboundrtp stat(s)");
+    is(counters.outboundrtp || 0, nout, "Have " + nout + " outboundrtp stat(s)");
 
-    var numLocalCandidates  = toNum(counters.localcandidate);
-    var numRemoteCandidates = toNum(counters.remotecandidate);
+    var numLocalCandidates  = counters.localcandidate || 0;
+    var numRemoteCandidates = counters.remotecandidate || 0;
     // If there are no tracks, there will be no stats either.
     if (nin + nout + ndata > 0) {
       ok(numLocalCandidates, "Have localcandidate stat(s)");
       ok(numRemoteCandidates, "Have remotecandidate stat(s)");
     } else {
       is(numLocalCandidates, 0, "Have no localcandidate stats");
       is(numRemoteCandidates, 0, "Have no remotecandidate stats");
     }
@@ -1616,51 +1621,48 @@ PeerConnectionWrapper.prototype = {
   /**
    * Compares the Ice server configured for this PeerConnectionWrapper
    * with the ICE candidates received in the RTCP stats.
    *
    * @param {object} stats
    *        The stats to be verified for relayed vs. direct connection.
    */
   checkStatsIceConnectionType : function(stats) {
-    var lId;
-    var rId;
-    Object.keys(stats).forEach(name => {
-      if ((stats[name].type === "candidatepair") &&
-          (stats[name].selected)) {
-        lId = stats[name].localCandidateId;
-        rId = stats[name].remoteCandidateId;
+    let lId;
+    let rId;
+    for (let stat of stats.values()) {
+      if (stat.type == "candidatepair" && stat.selected) {
+        lId = stat.localCandidateId;
+        rId = stat.remoteCandidateId;
+        break;
       }
-    });
-    ok(typeof lId !== 'undefined', "Got local candidate ID " +
-       JSON.stringify(lId) + " for selected pair");
-    ok(typeof rId !== 'undefined', "Got remote candidate ID " +
-       JSON.stringify(rId) + " for selected pair");
-    if ((typeof stats[lId] === 'undefined') ||
-        (typeof stats[rId] === 'undefined')) {
-      ok(false, "failed to find candidatepair IDs or stats for local: " +
-         JSON.stringify(lId) + " remote: " + JSON.stringify(rId));
+    }
+    isnot(lId, undefined, "Got local candidate ID " + lId + " for selected pair");
+    isnot(rId, undefined, "Got remote candidate ID " + rId + " for selected pair");
+    let lCand = stats.get(lId);
+    let rCand = stats.get(rId);
+    if (!lCand || !rCand) {
+      ok(false,
+         "failed to find candidatepair IDs or stats for local: "+ lId +" remote: "+ rId);
       return;
     }
     info("checkStatsIceConnectionType verifying: local=" +
-         JSON.stringify(stats[lId]) + " remote=" + JSON.stringify(stats[rId]));
-    var lType = stats[lId].candidateType;
-    var rType = stats[rId].candidateType;
-    var lIp = stats[lId].ipAddress;
-    var rIp = stats[rId].ipAddress;
+         JSON.stringify(lCand) + " remote=" + JSON.stringify(rCand));
     if ((this.configuration) && (typeof this.configuration.iceServers !== 'undefined')) {
       info("Ice Server configured");
       // Note: the IP comparising is a workaround for bug 1097333
       //       And this will fail if a TURN server address is a DNS name!
       var serverIp = this.configuration.iceServers[0].url.split(':')[1];
-      ok((lType === "relayed" || rType === "relayed") ||
-         (lIp === serverIp || rIp === serverIp), "One peer uses a relay");
+      ok(lCand.candidateType == "relayed" || rCand.candidateType == "relayed" ||
+         lCand.ipAddress === serverIp || rCand.ipAddress === serverIp,
+         "One peer uses a relay");
     } else {
       info("P2P configured");
-      ok(((lType !== "relayed") && (rType !== "relayed")), "Pure peer to peer call without a relay");
+      ok(lCand.candidateType != "relayed" && rCand.candidateType != "relayed",
+         "Pure peer to peer call without a relay");
     }
   },
 
   /**
    * Compares amount of established ICE connection according to ICE candidate
    * pairs in the stats reporting with the expected amount of connection based
    * on the constraints.
    *
@@ -1725,29 +1727,26 @@ PeerConnectionWrapper.prototype = {
    *
    * @param {object} stats
    *        The stats to check from this PeerConnectionWrapper
    * @param {object} props
    *        The properties to look for
    * @returns {boolean} Whether an entry containing all match-props was found.
    */
   hasStat : function(stats, props) {
-    for (var key in stats) {
-      if (stats.hasOwnProperty(key)) {
-        var res = stats[key];
-        var match = true;
-        for (var prop in props) {
-          if (res[prop] !== props[prop]) {
-            match = false;
-            break;
-          }
+    for (let res of stats.values()) {
+      var match = true;
+      for (let prop in props) {
+        if (res[prop] !== props[prop]) {
+          match = false;
+          break;
         }
-        if (match) {
-          return true;
-        }
+      }
+      if (match) {
+        return true;
       }
     }
     return false;
   },
 
   /**
    * Closes the connection
    */
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_peerConnection_restartIce.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <script type="application/javascript" src="pc.js"></script>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+  createHTML({
+    bug: "906986",
+    title: "Renegotiation: restart ice"
+  });
+
+  var test;
+  runNetworkTest(function (options) {
+    test = new PeerConnectionTest(options);
+
+    addRenegotiation(test.chain,
+      [
+        // causes a full, normal ice restart
+        function PC_LOCAL_SET_OFFER_OPTION(test) {
+          test.setOfferOptions({ iceRestart: true });
+        },
+        function PC_LOCAL_EXPECT_ICE_CHECKING(test) {
+          test.pcLocal.iceCheckingRestartExpected = true;
+        },
+        function PC_REMOTE_EXPECT_ICE_CHECKING(test) {
+          test.pcRemote.iceCheckingRestartExpected = true;
+        }
+      ]
+    );
+
+    test.setMediaConstraints([{audio: true}, {video: true}],
+                             [{audio: true}, {video: true}]);
+    test.run();
+  });
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_peerConnection_restartIceLocalAndRemoteRollback.html
@@ -0,0 +1,99 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <script type="application/javascript" src="pc.js"></script>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+  createHTML({
+    bug: "906986",
+    title: "Renegotiation: restart ice, local and remote rollback"
+  });
+
+  var test;
+  runNetworkTest(function (options) {
+    test = new PeerConnectionTest(options);
+    var firstNegotiationSize = test.chain.commands.length;
+
+    addRenegotiation(test.chain,
+      [
+        // causes a full, normal ice restart
+        function PC_LOCAL_SET_OFFER_OPTION(test) {
+          test.setOfferOptions({ iceRestart: true });
+        }
+      ]
+    );
+
+    test.chain.replaceAfter('PC_REMOTE_CREATE_ANSWER',
+      [
+        function PC_LOCAL_SETUP_ICE_HANDLER(test) {
+          test.pcLocal.setupIceCandidateHandler(test);
+          if (test.testOptions.steeplechase) {
+            test.pcLocal.endOfTrickleIce.then(() => {
+              send_message({"type": "end_of_trickle_ice"});
+            });
+          }
+        },
+        function PC_REMOTE_SETUP_ICE_HANDLER(test) {
+          test.pcRemote.setupIceCandidateHandler(test);
+          if (test.testOptions.steeplechase) {
+            test.pcRemote.endOfTrickleIce.then(() => {
+              send_message({"type": "end_of_trickle_ice"});
+            });
+          }
+        },
+
+        function PC_LOCAL_EXPECT_ICE_CONNECTED(test) {
+          test.pcLocal.iceCheckingIceRollbackExpected = true;
+        },
+        function PC_REMOTE_EXPECT_ICE_CONNECTED(test) {
+          test.pcRemote.iceCheckingIceRollbackExpected = true;
+        },
+
+        function PC_REMOTE_ROLLBACK(test) {
+          return test.setRemoteDescription(
+              test.pcRemote,
+              new RTCSessionDescription({ type: "rollback" }),
+              STABLE);
+        },
+
+        function PC_LOCAL_ROLLBACK(test) {
+          // We haven't negotiated the new stream yet.
+          test.pcLocal.expectNegotiationNeeded();
+          return test.setLocalDescription(
+              test.pcLocal,
+              new RTCSessionDescription({ type: "rollback", sdp: ""}),
+              STABLE);
+        },
+
+        // Rolling back should shut down gathering
+        function PC_LOCAL_WAIT_FOR_END_OF_TRICKLE(test) {
+          return test.pcLocal.endOfTrickleIce;
+        },
+        function PC_REMOTE_WAIT_FOR_END_OF_TRICKLE(test) {
+          return test.pcRemote.endOfTrickleIce;
+        },
+
+        function PC_LOCAL_EXPECT_ICE_CHECKING(test) {
+          test.pcLocal.iceCheckingRestartExpected = true;
+        },
+        function PC_REMOTE_EXPECT_ICE_CHECKING(test) {
+          test.pcRemote.iceCheckingRestartExpected = true;
+        }
+      ],
+      firstNegotiationSize // Replaces after second PC_REMOTE_CREATE_ANSWER
+    );
+    test.chain.append(commandsPeerConnectionOfferAnswer);
+
+    // for now, only use one stream, because rollback doesn't seem to
+    // like multiple streams.  See bug 1259465.
+    test.setMediaConstraints([{audio: true}],
+                             [{audio: true}]);
+    test.run();
+  });
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_peerConnection_restartIceLocalRollback.html
@@ -0,0 +1,74 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <script type="application/javascript" src="pc.js"></script>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+  createHTML({
+    bug: "906986",
+    title: "Renegotiation: restart ice, local rollback"
+  });
+
+  var test;
+  runNetworkTest(function (options) {
+    test = new PeerConnectionTest(options);
+
+    addRenegotiation(test.chain,
+      [
+        // causes a full, normal ice restart
+        function PC_LOCAL_SET_OFFER_OPTION(test) {
+          test.setOfferOptions({ iceRestart: true });
+        },
+        // causes an ice restart and then rolls it back
+        // (does not result in sending an offer)
+        function PC_LOCAL_SETUP_ICE_HANDLER(test) {
+          test.pcLocal.setupIceCandidateHandler(test);
+          if (test.testOptions.steeplechase) {
+            test.pcLocal.endOfTrickleIce.then(() => {
+              send_message({"type": "end_of_trickle_ice"});
+            });
+          }
+        },
+        function PC_LOCAL_CREATE_AND_SET_OFFER(test) {
+          return test.createOffer(test.pcLocal).then(offer => {
+            return test.setLocalDescription(test.pcLocal,
+                                            offer,
+                                            HAVE_LOCAL_OFFER);
+          });
+        },
+        function PC_LOCAL_EXPECT_ICE_CONNECTED(test) {
+          test.pcLocal.iceCheckingIceRollbackExpected = true;
+        },
+        function PC_LOCAL_ROLLBACK(test) {
+          return test.setLocalDescription(
+              test.pcLocal,
+              new RTCSessionDescription({ type: "rollback",
+                                          sdp: ""}),
+              STABLE);
+        },
+        // Rolling back should shut down gathering
+        function PC_LOCAL_WAIT_FOR_END_OF_TRICKLE(test) {
+          return test.pcLocal.endOfTrickleIce;
+        },
+        function PC_LOCAL_EXPECT_ICE_CHECKING(test) {
+          test.pcLocal.iceCheckingRestartExpected = true;
+        },
+        function PC_REMOTE_EXPECT_ICE_CHECKING(test) {
+          test.pcRemote.iceCheckingRestartExpected = true;
+        }
+      ]
+    );
+
+    // for now, only use one stream, because rollback doesn't seem to
+    // like multiple streams.  See bug 1259465.
+    test.setMediaConstraints([{audio: true}],
+                             [{audio: true}]);
+    test.run();
+  });
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_peerConnection_restartIceNoBundle.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <script type="application/javascript" src="pc.js"></script>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+  createHTML({
+    bug: "906986",
+    title: "Renegotiation: restart ice, no bundle"
+  });
+
+  var test;
+  runNetworkTest(function (options) {
+    options = options || { };
+    options.bundle = false;
+    test = new PeerConnectionTest(options);
+
+    addRenegotiation(test.chain,
+      [
+        // causes a full, normal ice restart
+        function PC_LOCAL_SET_OFFER_OPTION(test) {
+          test.setOfferOptions({ iceRestart: true });
+        },
+        function PC_LOCAL_EXPECT_ICE_CHECKING(test) {
+          test.pcLocal.iceCheckingRestartExpected = true;
+        },
+        function PC_REMOTE_EXPECT_ICE_CHECKING(test) {
+          test.pcRemote.iceCheckingRestartExpected = true;
+        }
+      ]
+    );
+
+    test.setMediaConstraints([{audio: true}, {video: true}],
+                             [{audio: true}, {video: true}]);
+    test.run();
+  });
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_peerConnection_restartIceNoBundleNoRtcpMux.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <script type="application/javascript" src="pc.js"></script>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+  createHTML({
+    bug: "906986",
+    title: "Renegotiation: restart ice, no bundle and disabled RTCP-Mux"
+  });
+
+  var test;
+  runNetworkTest(function (options) {
+    options = options || { };
+    options.bundle = false;
+    options.rtcpmux = false;
+    test = new PeerConnectionTest(options);
+
+    addRenegotiation(test.chain,
+      [
+        // causes a full, normal ice restart
+        function PC_LOCAL_SET_OFFER_OPTION(test) {
+          test.setOfferOptions({ iceRestart: true });
+        },
+        function PC_LOCAL_EXPECT_ICE_CHECKING(test) {
+          test.pcLocal.iceCheckingRestartExpected = true;
+        },
+        function PC_REMOTE_EXPECT_ICE_CHECKING(test) {
+          test.pcRemote.iceCheckingRestartExpected = true;
+        }
+      ]
+    );
+
+    test.setMediaConstraints([{audio: true}, {video: true}],
+                             [{audio: true}, {video: true}]);
+    test.run();
+  });
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_peerConnection_restartIceNoRtcpMux.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <script type="application/javascript" src="pc.js"></script>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+  createHTML({
+    bug: "906986",
+    title: "Renegotiation: restart ice, with disabled RTCP-Mux"
+  });
+
+  var test;
+  runNetworkTest(function (options) {
+    options = options || { };
+    options.rtcpmux = false;
+    test = new PeerConnectionTest(options);
+
+    addRenegotiation(test.chain,
+      [
+        // causes a full, normal ice restart
+        function PC_LOCAL_SET_OFFER_OPTION(test) {
+          test.setOfferOptions({ iceRestart: true });
+        },
+        function PC_LOCAL_EXPECT_ICE_CHECKING(test) {
+          test.pcLocal.iceCheckingRestartExpected = true;
+        },
+        function PC_REMOTE_EXPECT_ICE_CHECKING(test) {
+          test.pcRemote.iceCheckingRestartExpected = true;
+        }
+      ]
+    );
+
+    test.setMediaConstraints([{audio: true}, {video: true}],
+                             [{audio: true}, {video: true}]);
+    test.run();
+  });
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/plugins/ipc/BrowserStreamChild.cpp
+++ b/dom/plugins/ipc/BrowserStreamChild.cpp
@@ -76,18 +76,18 @@ BrowserStreamChild::StreamConstructed(
 
 BrowserStreamChild::~BrowserStreamChild()
 {
   NS_ASSERTION(!mStreamNotify, "Should have nulled it by now!");
 }
 
 bool
 BrowserStreamChild::RecvWrite(const int32_t& offset,
-                              const Buffer& data,
-                              const uint32_t& newlength)
+                              const uint32_t& newlength,
+                              const Buffer& data)
 {
   PLUGIN_LOG_DEBUG_FUNCTION;
 
   AssertPluginThread();
 
   if (ALIVE != mState)
     NS_RUNTIMEABORT("Unexpected state: received data after NPP_DestroyStream?");
 
--- a/dom/plugins/ipc/BrowserStreamChild.h
+++ b/dom/plugins/ipc/BrowserStreamChild.h
@@ -29,18 +29,18 @@ public:
   virtual bool IsBrowserStream() override { return true; }
 
   NPError StreamConstructed(
             const nsCString& mimeType,
             const bool& seekable,
             uint16_t* stype);
 
   virtual bool RecvWrite(const int32_t& offset,
-                         const Buffer& data,
-                         const uint32_t& newsize) override;
+                         const uint32_t& newsize,
+                         const Buffer& data) override;
   virtual bool RecvNPP_StreamAsFile(const nsCString& fname) override;
   virtual bool RecvNPP_DestroyStream(const NPReason& reason) override;
   virtual bool Recv__delete__() override;
 
   void EnsureCorrectInstance(PluginInstanceChild* i)
   {
     if (i != mInstance)
       NS_RUNTIMEABORT("Incorrect stream instance");
--- a/dom/plugins/ipc/BrowserStreamParent.cpp
+++ b/dom/plugins/ipc/BrowserStreamParent.cpp
@@ -188,18 +188,18 @@ BrowserStreamParent::Write(int32_t offse
 
   NS_ASSERTION(ALIVE == mState, "Sending data after NPP_DestroyStream?");
   NS_ASSERTION(len > 0, "Non-positive length to NPP_Write");
 
   if (len > kSendDataChunk)
     len = kSendDataChunk;
 
   return SendWrite(offset,
-                   nsCString(static_cast<char*>(buffer), len),
-                   mStream->end) ?
+                   mStream->end,
+                   nsCString(static_cast<char*>(buffer), len)) ?
     len : -1;
 }
 
 void
 BrowserStreamParent::StreamAsFile(const char* fname)
 {
   PLUGIN_LOG_DEBUG_FUNCTION;
 
--- a/dom/plugins/ipc/PBrowserStream.ipdl
+++ b/dom/plugins/ipc/PBrowserStream.ipdl
@@ -19,18 +19,18 @@ namespace plugins {
  * NPBrowserStream represents a NPStream sent from the browser to the plugin.
  */
 
 intr protocol PBrowserStream
 {
   manager PPluginInstance;
 
 child:
-  async Write(int32_t offset, Buffer data,
-              uint32_t newlength);
+  async Write(int32_t offset, uint32_t newlength,
+              Buffer data);
   async NPP_StreamAsFile(nsCString fname);
 
   /**
    * NPP_DestroyStream may race with other messages: the child acknowledges
    * the message with StreamDestroyed before this actor is deleted.
    */
   async NPP_DestroyStream(NPReason reason);
   async __delete__();
--- a/dom/plugins/test/mochitest/mochitest.ini
+++ b/dom/plugins/test/mochitest/mochitest.ini
@@ -20,37 +20,37 @@ support-files =
   npruntime_identifiers_subpage.html
   plugin-stream-referer.sjs
   plugin_window.html
   pluginstream.js
   post.sjs
   plugin-utils.js
   !/toolkit/components/passwordmgr/test/authenticate.sjs
 
-[test_GCrace.html]
-[test_NPNVdocumentOrigin.html]
-[test_NPPVpluginWantsAllNetworkStreams.html]
 [test_bug406541.html]
 [test_bug532208.html]
 [test_bug539565-1.html]
 [test_bug539565-2.html]
 [test_bug738396.html]
 [test_bug771202.html]
 [test_bug777098.html]
 [test_bug784131.html]
 [test_bug813906.html]
 [test_bug827160.html]
-skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' #needs plugin support # b2g(needs plugin support) b2g-debug(debug-only failure) b2g-desktop(needs plugin support)
+skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' # needs plugin support
 [test_bug852315.html]
 [test_bug854082.html]
 [test_bug863792.html]
 [test_bug967694.html]
 [test_bug985859.html]
 [test_bug986930.html]
 [test_bug1092842.html]
+[test_bug1165981.html]
+skip-if = !(os == "win" && processor == "x86_64")
+[test_bug1245545.html]
 [test_cocoa_focus.html]
 skip-if = toolkit != "cocoa" || e10s # Bug 1194534
 support-files = cocoa_focus.html
 [test_cocoa_window_focus.html]
 skip-if = toolkit != "cocoa" # Bug 1194534
 support-files = cocoa_window_focus.html
 [test_cookies.html]
 [test_copyText.html]
@@ -63,38 +63,41 @@ skip-if = !crashreporter
 skip-if = (!crashreporter) || true # Bug 566049
 [test_CrashService_crash.html]
 skip-if = !crashreporter || e10s
 [test_CrashService_hang.html]
 skip-if = !crashreporter || e10s
 [test_defaultValue.html]
 [test_enumerate.html]
 [test_fullpage.html]
+[test_GCrace.html]
 [test_getauthenticationinfo.html]
 [test_hanging.html]
 skip-if = !crashreporter || e10s
 [test_instance_re-parent.html]
 skip-if = release_build # Bug 1172627
 [test_instance_unparent1.html]
 [test_instance_unparent2.html]
 [test_instance_unparent3.html]
 [test_instantiation.html]
 [test_mixed_case_mime.html]
 [test_multipleinstanceobjects.html]
 [test_newstreamondestroy.html]
 [test_npn_asynccall.html]
 [test_npn_timers.html]
 [test_npobject_getters.html]
+[test_NPNVdocumentOrigin.html]
+[test_NPPVpluginWantsAllNetworkStreams.html]
 [test_npruntime_construct.html]
 [test_npruntime_identifiers.html]
 [test_npruntime_npnevaluate.html]
 [test_npruntime_npninvoke.html]
 [test_npruntime_npninvokedefault.html]
 [test_object.html]
-skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' # b2g(needs plugin support) b2g-debug(needs plugin support) b2g-desktop(needs plugin support)
+skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' # needs plugin support
 [test_painting.html]
 skip-if = (toolkit == "cocoa" && e10s) # bug 1252230
 [test_plugin_scroll_invalidation.html]
 skip-if = toolkit != "gtk2"
 support-files = plugin_scroll_invalidation.html
 [test_plugin_scroll_painting.html]
 skip-if = true # Bug 596491
 [test_pluginstream_3rdparty.html]
@@ -119,24 +122,21 @@ support-files =
 skip-if = true # disabled due to oddness, perhaps scrolling of the mochitest window?
 [test_propertyAndMethod.html]
 [test_queryCSSZoomFactor.html]
 [test_queryContentsScaleFactor.html]
 skip-if = toolkit != "cocoa"
 [test_redirect_handling.html]
 [test_secondPlugin.html]
 [test_src_url_change.html]
+[test_streamatclose.html]
 [test_streamNotify.html]
 [test_stringHandling.html]
-[test_streamatclose.html]
 [test_twostreams.html]
+[test_visibility.html]
+skip-if = toolkit == "cocoa"
 [test_windowed_invalidate.html]
 skip-if = os != "win"
 [test_windowless_flash.html]
 skip-if = !(os == "win" && processor == "x86_64" && !e10s) # Bug 1253957
 [test_windowless_ime.html]
 skip-if = os != "win" || e10s
-[test_visibility.html]
-skip-if = toolkit == "cocoa"
 [test_zero_opacity.html]
-[test_bug1165981.html]
-skip-if = !(os == "win" && processor == "x86_64")
-[test_bug1245545.html]
--- a/dom/plugins/test/mochitest/test_GCrace.html
+++ b/dom/plugins/test/mochitest/test_GCrace.html
@@ -10,20 +10,16 @@
   <p id="display"></p>
 
   <script class="testbody" type="application/javascript">
     SimpleTest.waitForExplicitFinish();
     SimpleTest.requestFlakyTimeout("untriaged");
     setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
 
     function start() {
-      if (navigator.platform.startsWith("Win")) {
-        SimpleTest.expectAssertions(0, 350);
-      }
-
       setTimeout(checkGCRace, 1000);
     }
 
     var nested = false;
 
     function cb(f) {
       ok(!nested, "Callback shouldn't occur in a nested stack frame");
       try {
--- a/dom/plugins/test/mochitest/test_bug1092842.html
+++ b/dom/plugins/test/mochitest/test_bug1092842.html
@@ -4,17 +4,16 @@
   <title>Bug 1092842</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="plugin-utils.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 
 <body onload="startTest()">
   <script type="application/javascript;version=1.8">
   SimpleTest.waitForExplicitFinish();
-  SimpleTest.expectAssertions(0, 1);
   setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
 
   var p = null;
 
   function startTest() {
     p = document.getElementById('theplugin');
     if (!p.hasWidget()) {
       todo(false, "This test is only relevant for windowed plugins");
--- a/dom/plugins/test/mochitest/test_crashing.html
+++ b/dom/plugins/test/mochitest/test_crashing.html
@@ -1,17 +1,16 @@
 <head>
   <title>Plugin crashing</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="plugin-utils.js"></script>
 
 <body>
   <script class="testbody" type="application/javascript">
   SimpleTest.waitForExplicitFinish();
-  SimpleTest.expectAssertions(0, 1);
   setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
 
   window.frameLoaded = function frameLoaded_toCrash() {
     SimpleTest.expectChildProcessCrash();
 
     var iframe = document.getElementById('iframe1');
     var p = iframe.contentDocument.getElementById('plugin1');
 
--- a/dom/plugins/test/mochitest/test_defaultValue.html
+++ b/dom/plugins/test/mochitest/test_defaultValue.html
@@ -6,17 +6,16 @@
     <script type="text/javascript" src="plugin-utils.js"></script>
     <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   </head>
 
   <body onload="run()">
 
     <script class="testbody" type="application/javascript">
       SimpleTest.waitForExplicitFinish();
-      SimpleTest.expectAssertions(0, 1);
       setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
 
       function run() {
         var plugin = document.getElementById("plugin");
         var pluginProto = Object.getPrototypeOf(plugin);
 
         plugin.propertyAndMethod = {};
         plugin.propertyAndMethod + "baz";
--- a/dom/plugins/test/mochitest/test_enumerate.html
+++ b/dom/plugins/test/mochitest/test_enumerate.html
@@ -6,17 +6,16 @@
 
   <link rel="stylesheet" type="text/css" 
         href="/tests/SimpleTest/test.css" />
 </head>
 
 <body onload="runTests()">
   <script class="testbody" type="application/javascript">
     SimpleTest.waitForExplicitFinish();
-    SimpleTest.expectAssertions(0, 1);
     setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
 
     function runTests() {
       var pluginElement = document.getElementById("plugin1");
       var c = 0;
       var foundSetColor = false;
       for (var n in pluginElement) {
         ++c;
--- a/dom/plugins/test/mochitest/test_instance_re-parent.html
+++ b/dom/plugins/test/mochitest/test_instance_re-parent.html
@@ -5,17 +5,16 @@
   <script type="text/javascript" src="/MochiKit/packed.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="plugin-utils.js"></script>
 </head>
 <body onload="begin()">
   <script type="application/javascript;version=1.8">
   SimpleTest.waitForExplicitFinish();
-  SimpleTest.expectAssertions(0, 4);
   setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
 
   const MAX_CHECK_PLUGIN_STOPPED_ATTEMPTS = 50;
   var numCheckPluginStoppedAttempts = 0;
   var exceptionThrown = false;
   var p = null;
   var d1 = null;
   var d2 = null;
--- a/dom/plugins/test/mochitest/test_propertyAndMethod.html
+++ b/dom/plugins/test/mochitest/test_propertyAndMethod.html
@@ -19,17 +19,16 @@
           Object.getPrototypeOf = function(object) {
             // May break if the constructor has been tampered with
             return object.constructor.prototype;
           };
         }
       }
 
       SimpleTest.waitForExplicitFinish();
-      SimpleTest.expectAssertions(0, 1);
       setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
 
       function run() {
         var plugin = document.getElementById("plugin");
         var pluginProto = Object.getPrototypeOf(plugin);
 
         delete pluginProto.propertyAndMethod;
         ok(isNaN(plugin.propertyAndMethod + 0), "Shouldn't be set yet!");
--- a/dom/plugins/test/mochitest/test_redirect_handling.html
+++ b/dom/plugins/test/mochitest/test_redirect_handling.html
@@ -2,17 +2,16 @@
 <head>
   <title>Basic NPAPI Redirect Handling</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="plugin-utils.js"></script>
 </head>
 <body onload="runTests()">
   <script class="testbody" type="application/javascript">
     SimpleTest.waitForExplicitFinish();
-    SimpleTest.expectAssertions(0, 1);
     setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
 
     var p = null;
 
     var redirectingURL = "307-xo-redirect.sjs";
     var redirectTargetURL = "http://example.org/tests/dom/plugins/test/mochitest/loremipsum.txt";
 
     var expectedWriteURL = "";
--- a/dom/plugins/test/mochitest/test_windowed_invalidate.html
+++ b/dom/plugins/test/mochitest/test_windowed_invalidate.html
@@ -5,17 +5,16 @@
           src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="plugin-utils.js"></script>
   <link rel="stylesheet" type="text/css" 
         href="/tests/SimpleTest/test.css" />
 </head>
 <body onload="runTests()">
   <script class="testbody" type="application/javascript">
   SimpleTest.waitForExplicitFinish();
-  SimpleTest.expectAssertions(0, 1);
   SimpleTest.requestFlakyTimeout("untriaged");
   setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
 
   var lastPaintCount;
   var p = null;
 
   function checkPainted() {
     if (p.getPaintCount() > lastPaintCount) {
--- a/dom/plugins/test/mochitest/test_zero_opacity.html
+++ b/dom/plugins/test/mochitest/test_zero_opacity.html
@@ -4,17 +4,16 @@
   <title>Test whether windowed plugins with opacity:0 get their window set correctly</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="plugin-utils.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 
 <body onload="startTest()">
   <script type="application/javascript;version=1.8">
   SimpleTest.waitForExplicitFinish();
-  SimpleTest.expectAssertions(0, 2);
   setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
 
   var p = null;
 
   function startTest() {
     p = document.getElementById('theplugin');
     if (!p.hasWidget()) {
       todo(false, "This test is only relevant for windowed plugins");
--- a/dom/webidl/Crypto.webidl
+++ b/dom/webidl/Crypto.webidl
@@ -2,20 +2,20 @@
 /* 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/.
  *
  * The origin of this IDL file is
  * https://dvcs.w3.org/hg/webcrypto-api/raw-file/tip/spec/Overview.html#crypto-interface
  */
 
-[NoInterfaceObject]
+[NoInterfaceObject, Exposed=(Window,Worker)]
 interface GlobalCrypto {
   [Throws] readonly attribute Crypto crypto;
 };
 
-//[Exposed=(Window,Worker)]
+[Exposed=(Window,Worker)]
 interface Crypto {
   readonly attribute SubtleCrypto subtle;
 
   [Throws]
   ArrayBufferView getRandomValues(ArrayBufferView array);
 };
--- a/dom/webidl/RTCPeerConnection.webidl
+++ b/dom/webidl/RTCPeerConnection.webidl
@@ -57,17 +57,17 @@ dictionary RTCOfferAnswerOptions {
 };
 
 dictionary RTCAnswerOptions : RTCOfferAnswerOptions {
 };
 
 dictionary RTCOfferOptions : RTCOfferAnswerOptions {
   long    offerToReceiveVideo;
   long    offerToReceiveAudio;
-  // boolean iceRestart = false; // Not implemented (Bug 906986)
+  boolean iceRestart = false;
 
   // Mozilla proprietary options (at risk: Bug 1196974)
   boolean mozDontOfferDataChannel;
   boolean mozBundleOnly;
 
   // TODO: Remove old constraint-like RTCOptions support soon (Bug 1064223).
   DeprecatedRTCOfferOptionsSet mandatory;
   sequence<DeprecatedRTCOfferOptionsSet> _optional;
--- a/dom/webidl/RTCStatsReport.webidl
+++ b/dom/webidl/RTCStatsReport.webidl
@@ -141,18 +141,16 @@ dictionary RTCIceCandidateStats : RTCSta
 dictionary RTCCodecStats : RTCStats {
   unsigned long payloadType;       // As used in RTP encoding.
   DOMString codec;                 // video/vp8 or equivalent
   unsigned long clockRate;
   unsigned long channels;          // 2=stereo, missing for most other cases.
   DOMString parameters;            // From SDP description line
 };
 
-callback RTCStatsReportCallback = void (RTCStatsReport obj);
-
 // This is the internal representation of the report in this implementation
 // to be received from c++
 
 dictionary RTCStatsReportInternal {
   DOMString                           pcid = "";
   sequence<RTCInboundRTPStreamStats>  inboundRTPStreamStats;
   sequence<RTCOutboundRTPStreamStats> outboundRTPStreamStats;
   sequence<RTCMediaStreamTrackStats>  mediaStreamTrackStats;
@@ -168,14 +166,12 @@ dictionary RTCStatsReportInternal {
   boolean                             closed; // Is the PC now closed
 };
 
 [Pref="media.peerconnection.enabled",
 // TODO: Use MapClass here once it's available (Bug 928114)
 // MapClass(DOMString, object)